1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-22 11:52:21 +01:00

Merge branch 'dev' into staging

This commit is contained in:
Du Peng 2023-09-11 18:18:37 +02:00
commit 19b6bd8d8a
54 changed files with 409 additions and 132 deletions

View File

@ -2,9 +2,16 @@
## next deploy
- improves api/notification controller to avoid failing when there is a notification with wrong notification_type in db
- Add extra_authorize_params to OpenIdConnect config
- Improvement : add a notification to remind users to upload their supporting documents
## v6.0.14 2023 September 6
- Fix a bug: for project categories, if there is no category : do not show categories panel in show view, do not show categories input field in edit view
- Fix a bug: unable to update status to paid for latest payment schedule item
- Fix a bug: unable to generate statistic
- Fix a bug: unable to update user profile by admin
- Feature: add a filter in members list (admin) to show only "not validated" members
- Concerning statistics:
- removes age and type column from all statistics tabs (only in web, not in xlsx export file)

View File

@ -99,7 +99,7 @@ class API::AuthProvidersController < API::APIController
providable_attributes: [:id, :issuer, :discovery, :client_auth_method, :prompt, :send_scope_to_token_endpoint,
:client__identifier, :client__secret, :client__authorization_endpoint, :client__token_endpoint,
:client__userinfo_endpoint, :client__jwks_uri, :client__end_session_endpoint, :profile_url,
{ scope: [] }],
:extra_authorize_params, { scope: [] }],
auth_provider_mappings_attributes: [:id, :local_model, :local_field, :api_field, :api_endpoint, :api_data_type,
:_destroy, { transformation: [:type, :format, :true_value, :false_value,
{ mapping: %i[from to] }] }])

View File

@ -15,6 +15,7 @@ class API::NotificationsController < API::APIController
def index
loop do
@notifications = current_user.notifications
.with_valid_notification_type
.delivered_in_system(current_user)
.includes(:attached_object)
.page(params[:page])
@ -24,8 +25,8 @@ class API::NotificationsController < API::APIController
break unless delete_obsoletes(@notifications)
end
@totals = {
total: current_user.notifications.delivered_in_system(current_user).count,
unread: current_user.notifications.delivered_in_system(current_user).where(is_read: false).count
total: current_user.notifications.with_valid_notification_type.delivered_in_system(current_user).count,
unread: current_user.notifications.with_valid_notification_type.delivered_in_system(current_user).where(is_read: false).count
}
render :index
end
@ -33,6 +34,7 @@ class API::NotificationsController < API::APIController
def last_unread
loop do
@notifications = current_user.notifications
.with_valid_notification_type
.delivered_in_system(current_user)
.includes(:attached_object)
.where(is_read: false)
@ -42,19 +44,20 @@ class API::NotificationsController < API::APIController
break unless delete_obsoletes(@notifications)
end
@totals = {
total: current_user.notifications.delivered_in_system(current_user).count,
unread: current_user.notifications.delivered_in_system(current_user).where(is_read: false).count
total: current_user.notifications.with_valid_notification_type.delivered_in_system(current_user).count,
unread: current_user.notifications.with_valid_notification_type.delivered_in_system(current_user).where(is_read: false).count
}
render :index
end
def polling
@notifications = current_user.notifications
.where('is_read = false AND created_at >= :date', date: params[:last_poll])
.order('created_at DESC')
.with_valid_notification_type
.where('notifications.is_read = false AND notifications.created_at >= :date', date: params[:last_poll])
.order('notifications.created_at DESC')
@totals = {
total: current_user.notifications.delivered_in_system(current_user).count,
unread: current_user.notifications.delivered_in_system(current_user).where(is_read: false).count
total: current_user.notifications.with_valid_notification_type.delivered_in_system(current_user).count,
unread: current_user.notifications.with_valid_notification_type.delivered_in_system(current_user).where(is_read: false).count
}
render :index
end

View File

@ -34,6 +34,15 @@ export const OpenidConnectForm = <TFieldValues extends FieldValues, TContext ext
// this is a workaround for https://github.com/JedWatson/react-select/issues/1879
const [selectKey, setSelectKey] = useState<number>(0);
useEffect(() => {
if (!currentFormValues?.extra_authorize_params) {
setValue(
'providable_attributes.extra_authorize_params' as Path<TFieldValues>,
'{}' as UnpackNestedValue<FieldPathValue<TFieldValues, Path<TFieldValues>>>
);
}
}, []);
// when we have detected a discovery endpoint, we mark it as available
useEffect(() => {
setValue(
@ -160,6 +169,12 @@ export const OpenidConnectForm = <TFieldValues extends FieldValues, TContext ext
tooltip={t('app.admin.authentication.openid_connect_form.profile_edition_url_help')}
rules={{ required: false, pattern: ValidationLib.urlRegex }}
formState={formState} />
<FormInput id="providable_attributes.extra_authorize_params"
register={register}
label={t('app.admin.authentication.openid_connect_form.extra_authorize_params')}
tooltip={t('app.admin.authentication.openid_connect_form.extra_authorize_params_help')}
rules={{ required: false }}
formState={formState} />
<h4>{t('app.admin.authentication.openid_connect_form.client_options')}</h4>
<FormInput id="providable_attributes.client__identifier"
label={t('app.admin.authentication.openid_connect_form.client__identifier')}

View File

@ -136,6 +136,7 @@ export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size,
}
return handleSubmit((data: User) => {
['events_reservations', 'space_reservations', 'training_reservations', 'machine_reservations', 'all_projects', 'invoices', 'subscribed_plan', 'subscription'].forEach(key => delete data[key]);
MemberAPI[action](data)
.then(res => {
reset(res);

View File

@ -61,7 +61,8 @@ export interface OpenIdConnectProvider {
client__userinfo_endpoint?: string,
client__jwks_uri?: string,
client__end_session_endpoint?: string,
profile_url?: string
profile_url?: string,
extra_authorize_parameters?: string,
}
export interface MappingFields {

View File

@ -74,6 +74,7 @@ export const notificationTypeNames = [
'notify_user_is_validated',
'notify_user_is_invalidated',
'notify_user_supporting_document_refusal',
'notify_user_supporting_document_reminder',
'notify_admin_user_supporting_document_refusal',
'notify_user_order_is_ready',
'notify_user_order_is_canceled',

View File

@ -989,6 +989,11 @@ p, .widget p {
cursor: pointer;
}
.list-none {
list-style-type: none;
padding-inline-start: 0;
}
@media screen and (min-width: $screen-lg-min) {
.b-r-lg {
border-right: 1px solid $border-color;

View File

@ -288,7 +288,7 @@
<td ng-repeat="field in selectedIndex.additional_fields">
<ng-switch on="field.data_type">
<span ng-switch-when="date">{{formatDate(datum._source[field.key])}}</span>
<ul ng-switch-when="list">
<ul ng-switch-when="list" class="list-none">
<li ng-repeat="elem in uniq(datum._source[field.key])">{{elem.name}}</li>
</ul>
<span ng-switch-default>{{datum._source[field.key]}}</span>

View File

@ -19,6 +19,8 @@ class Notification < ApplicationRecord
SQL
}
scope :with_valid_notification_type, -> { joins(:notification_type).where(notification_types: { name: NOTIFICATIONS_TYPES.map { |nt| nt[:name] } }) }
validates :receiver_id,
:receiver_type,
:attached_object_id,

View File

@ -2,7 +2,7 @@
# NotificationType defines the different types of Notification.
# To add a new notification type in db, you must add it in:
# - db/seeds/notification_types.rb
# - config/initializers/notification_types.rb
# - app/views/api/notifications/_XXXXXX.json.jbuilder
# - app/views/notifications_mailer/XXXXXX.html.erb
# - app/frontend/src/javascript/models/notification-type.ts

View File

@ -93,6 +93,7 @@ class User < ApplicationRecord
scope :not_confirmed, -> { where(confirmed_at: nil) }
scope :inactive_for_3_years, -> { where('users.last_sign_in_at < ?', 3.years.ago) }
scope :not_validated, -> { where(validated_at: nil) }
scope :supporting_documents_reminder_not_sent, -> { where(supporting_documents_reminder_sent_at: nil) }
def to_json(*)
ApplicationController.new.view_context.render(

View File

@ -27,6 +27,7 @@ class Members::MembersService
if @member.validated_at? && !(new_types - current_types).empty?
validated_at_changed = true
@member.validated_at = nil
@member.supporting_documents_reminder_sent_at = nil
end
end

View File

@ -50,7 +50,6 @@ class Statistics::FetcherService
.eager_load(:slots, :slots_reservations, :invoice_items, :reservation_context, statistic_profile: [:group])
.find_each do |r|
next unless r.reservable
next unless r.original_invoice
next if r.slots.empty?
profile = r.statistic_profile
@ -63,7 +62,7 @@ class Statistics::FetcherService
nb_hours: (r.slots.map(&:duration).map(&:to_i).reduce(:+) / 3600.0).to_f,
ca: calcul_ca(r.original_invoice),
reservation_context_id: r.reservation_context_id,
coupon: r.original_invoice.coupon&.code }.merge(user_info(profile))
coupon: r&.original_invoice&.coupon&.code }.merge(user_info(profile))
yield result
end
end
@ -78,7 +77,6 @@ class Statistics::FetcherService
.eager_load(:slots, :slots_reservations, :invoice_items, :reservation_context, statistic_profile: [:group])
.find_each do |r|
next unless r.reservable
next unless r.original_invoice
next if r.slots.empty?
profile = r.statistic_profile
@ -91,7 +89,7 @@ class Statistics::FetcherService
nb_hours: (r.slots.map(&:duration).map(&:to_i).reduce(:+) / 3600.0).to_f,
ca: calcul_ca(r.original_invoice),
reservation_context_id: r.reservation_context_id,
coupon: r.original_invoice.coupon&.code }.merge(user_info(profile))
coupon: r&.original_invoice&.coupon&.code }.merge(user_info(profile))
yield result
end
end
@ -106,7 +104,7 @@ class Statistics::FetcherService
.eager_load(:slots, :slots_reservations, :invoice_items, :reservation_context, statistic_profile: [:group])
.find_each do |r|
next unless r.reservable
next unless r.original_invoice
next if r.slots.empty?
profile = r.statistic_profile
slot = r.slots.first
@ -119,7 +117,7 @@ class Statistics::FetcherService
nb_hours: difference_in_hours(slot.start_at, slot.end_at),
ca: calcul_ca(r.original_invoice),
reservation_context_id: r.reservation_context_id,
coupon: r.original_invoice&.coupon&.code }.merge(user_info(profile))
coupon: r&.original_invoice&.coupon&.code }.merge(user_info(profile))
yield result
end
end
@ -134,7 +132,7 @@ class Statistics::FetcherService
.eager_load(:slots, :slots_reservations, :invoice_items, statistic_profile: [:group])
.find_each do |r|
next unless r.reservable
next unless r.original_invoice
next if r.slots.empty?
profile = r.statistic_profile
slot = r.slots.first
@ -148,7 +146,7 @@ class Statistics::FetcherService
age_range: (r.reservable.age_range_id ? r.reservable.age_range.name : ''),
nb_places: r.total_booked_seats,
nb_hours: difference_in_hours(slot.start_at, slot.end_at),
coupon: r.original_invoice.coupon&.code,
coupon: r&.original_invoice&.coupon&.code,
ca: calcul_ca(r.original_invoice) }.merge(user_info(profile))
yield result
end

View File

@ -16,6 +16,6 @@ if @provider.providable_type == OpenIdConnectProvider.name
:prompt, :send_scope_to_token_endpoint, :client__identifier, :client__secret, :client__authorization_endpoint,
:client__token_endpoint, :client__userinfo_endpoint, :client__jwks_uri, :client__end_session_endpoint, :profile_url
json.scope @provider.providable[:scope]
json.extra_authorize_params @provider.providable[:extra_authorize_params]
end
end

View File

@ -0,0 +1,4 @@
# frozen_string_literal: true
json.title notification.notification_type
json.description t('.reminder_message')

View File

@ -16,7 +16,7 @@ if provider.providable_type == 'OpenIdConnectProvider'
json.extract! provider.providable, :id, :issuer, :discovery, :client_auth_method, :scope, :response_type, :response_mode,
:display, :prompt, :send_scope_to_token_endpoint, :client__identifier, :client__secret, :client__authorization_endpoint,
:client__token_endpoint, :client__userinfo_endpoint, :client__jwks_uri, :client__end_session_endpoint, :profile_url,
:post_logout_redirect_uri, :uid_field, :client__redirect_uri, :client__scheme, :client__host, :client__port
:post_logout_redirect_uri, :uid_field, :client__redirect_uri, :client__scheme, :client__host, :client__port,
:extra_authorize_params
end
end

View File

@ -0,0 +1,5 @@
<%= render 'notifications_mailer/shared/hello', recipient: @recipient %>
<p>
<%= t('.body.user_supporting_document_reminder') %>
</p>

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
# Send a notification to users who did not upload their supporting document files yet
class SupportingDocumentsReminderWorker
include Sidekiq::Worker
def perform
users_to_notify = User.members
.supporting_documents_reminder_not_sent
.where("users.created_at < ?", 2.days.ago)
.left_outer_joins(supporting_document_files: { supporting_document_type: :groups })
.where("groups.id = users.group_id OR groups.id IS NULL")
.select("users.*, count(supporting_document_files.id)")
.group("users.id")
.having("(count(supporting_document_files.id)) < (SELECT count(supporting_document_types.id) "\
"FROM supporting_document_types "\
"INNER JOIN supporting_document_types_groups "\
"ON supporting_document_types_groups.supporting_document_type_id = supporting_document_types.id "\
"WHERE supporting_document_types_groups.group_id = users.group_id)")
users_to_notify.each do |user|
NotificationCenter.call type: 'notify_user_supporting_document_reminder',
receiver: user,
attached_object: user
user.update_column(:supporting_documents_reminder_sent_at, DateTime.current)
end
end
end

View File

@ -0,0 +1,81 @@
# frozen_string_literal: true
NOTIFICATIONS_TYPES = [
{ name: 'notify_admin_when_project_published', category: 'projects', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_project_collaborator_to_valid', category: 'projects', is_configurable: false },
{ name: 'notify_project_author_when_collaborator_valid', category: 'projects', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_user_training_valid', category: 'trainings', is_configurable: false },
{ name: 'notify_member_subscribed_plan', category: 'subscriptions', is_configurable: false },
{ name: 'notify_member_create_reservation', category: 'agenda', is_configurable: false },
{ name: 'notify_member_subscribed_plan_is_changed', category: 'deprecated', is_configurable: false },
{ name: 'notify_admin_member_create_reservation', category: 'agenda', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_member_slot_is_modified', category: 'agenda', is_configurable: false },
{ name: 'notify_admin_slot_is_modified', category: 'agenda', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_when_user_is_created', category: 'users_accounts', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_subscribed_plan', category: 'subscriptions', is_configurable: true, roles: ['admin'] },
{ name: 'notify_user_when_invoice_ready', category: 'payments', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_member_subscription_will_expire_in_7_days', category: 'subscriptions', is_configurable: false },
{ name: 'notify_member_subscription_is_expired', category: 'subscriptions', is_configurable: false },
{ name: 'notify_admin_subscription_will_expire_in_7_days', category: 'subscriptions', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_subscription_is_expired', category: 'subscriptions', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_subscription_canceled', category: 'subscriptions', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_member_subscription_canceled', category: 'subscriptions', is_configurable: false },
{ name: 'notify_user_when_avoir_ready', category: 'wallet', is_configurable: false },
{ name: 'notify_member_slot_is_canceled', category: 'agenda', is_configurable: false },
{ name: 'notify_admin_slot_is_canceled', category: 'agenda', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_partner_subscribed_plan', category: 'subscriptions', is_configurable: false },
{ name: 'notify_member_subscription_extended', category: 'subscriptions', is_configurable: false },
{ name: 'notify_admin_subscription_extended', category: 'subscriptions', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_user_group_changed', category: 'users_accounts', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_user_user_group_changed', category: 'users_accounts', is_configurable: false },
{ name: 'notify_admin_when_user_is_imported', category: 'users_accounts', is_configurable: true, roles: ['admin'] },
{ name: 'notify_user_profile_complete', category: 'users_accounts', is_configurable: false },
{ name: 'notify_user_auth_migration', category: 'user', is_configurable: false },
{ name: 'notify_admin_user_merged', category: 'users_accounts', is_configurable: true, roles: ['admin'] },
{ name: 'notify_admin_profile_complete', category: 'users_accounts', is_configurable: true, roles: ['admin'] },
{ name: 'notify_admin_abuse_reported', category: 'projects', is_configurable: true, roles: ['admin'] },
{ name: 'notify_admin_invoicing_changed', category: 'deprecated', is_configurable: false },
{ name: 'notify_user_wallet_is_credited', category: 'wallet', is_configurable: false },
{ name: 'notify_admin_user_wallet_is_credited', category: 'wallet', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_export_complete', category: 'exports', is_configurable: false },
{ name: 'notify_member_about_coupon', category: 'agenda', is_configurable: false },
{ name: 'notify_member_reservation_reminder', category: 'agenda', is_configurable: false },
{ name: 'notify_admin_free_disk_space', category: 'app_management', is_configurable: false },
{ name: 'notify_admin_close_period_reminder', category: 'accountings', is_configurable: true, roles: ['admin'] },
{ name: 'notify_admin_archive_complete', category: 'accountings', is_configurable: true, roles: ['admin'] },
{ name: 'notify_privacy_policy_changed', category: 'app_management', is_configurable: false },
{ name: 'notify_admin_import_complete', category: 'app_management', is_configurable: false },
{ name: 'notify_admin_refund_created', category: 'wallet', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admins_role_update', category: 'users_accounts', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_user_role_update', category: 'users_accounts', is_configurable: false },
{ name: 'notify_admin_objects_stripe_sync', category: 'payments', is_configurable: false },
{ name: 'notify_user_when_payment_schedule_ready', category: 'payments', is_configurable: false },
{ name: 'notify_admin_payment_schedule_failed', category: 'payments', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_member_payment_schedule_failed', category: 'payments', is_configurable: false },
{ name: 'notify_admin_payment_schedule_check_deadline', category: 'payments', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_payment_schedule_transfer_deadline', category: 'payments', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_payment_schedule_error', category: 'payments', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_member_payment_schedule_error', category: 'payments', is_configurable: false },
{ name: 'notify_admin_payment_schedule_gateway_canceled', category: 'payments', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_member_payment_schedule_gateway_canceled', category: 'payments', is_configurable: false },
{ name: 'notify_admin_user_supporting_document_files_created', category: 'supporting_documents', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_user_supporting_document_files_updated', category: 'supporting_documents', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_user_is_validated', category: 'users_accounts', is_configurable: false },
{ name: 'notify_user_is_invalidated', category: 'users_accounts', is_configurable: false },
{ name: 'notify_user_supporting_document_refusal', category: 'supporting_documents', is_configurable: false },
{ name: 'notify_admin_user_supporting_document_refusal', category: 'supporting_documents', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_order_is_paid', category: 'shop', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_user_order_is_ready', category: 'shop', is_configurable: false },
{ name: 'notify_user_order_is_canceled', category: 'shop', is_configurable: false },
{ name: 'notify_user_order_is_refunded', category: 'shop', is_configurable: false },
{ name: 'notify_admin_low_stock_threshold', category: 'shop', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_training_auto_cancelled', category: 'trainings', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_member_training_auto_cancelled', category: 'trainings', is_configurable: false },
{ name: 'notify_user_supporting_document_reminder', category: 'supporting_documents', is_configurable: false }
].freeze

View File

@ -111,6 +111,10 @@ de:
save: "Speichern"
create_success: "Der Raum wurde erfolgreich erstellt"
update_success: "Der Raum wurde erfolgreich aktualisiert"
associated_machines: "Included machines"
children_spaces: "Included spaces"
associated_objects: "Associated objects"
associated_objects_warning: "Only use these fields if you want interblocking reservation between spaces, child spaces and machines. If you want machine and space reservations to remain independent, please leave the following fields blank."
event_form:
ACTION_title: "{ACTION, select, create{Neue} other{Aktualisiere die}} Veranstaltung"
title: "Titel"
@ -1477,6 +1481,8 @@ de:
client__jwks_uri: "JWKS URI"
client__end_session_endpoint: "End session endpoint"
client__end_session_endpoint_help: "The url to call to log the user out at the authorization server."
extra_authorize_params: "Extra authorize parameters"
extra_authorize_params_help: "A hash of extra fixed parameters that will be merged to the authorization request"
provider_form:
name: "Name"
authentication_type: "Authentifizierungsart"

View File

@ -1544,6 +1544,8 @@ en:
client__jwks_uri: "JWKS URI"
client__end_session_endpoint: "End session endpoint"
client__end_session_endpoint_help: "The url to call to log the user out at the authorization server."
extra_authorize_params: "Extra authorize parameters"
extra_authorize_params_help: "A hash of extra fixed parameters that will be merged to the authorization request"
provider_form:
name: "Name"
authentication_type: "Authentication type"

View File

@ -111,6 +111,10 @@ es:
save: "Guardar"
create_success: "El espacio se ha creado correctamente"
update_success: "El espacio se ha actualizado correctamente"
associated_machines: "Included machines"
children_spaces: "Included spaces"
associated_objects: "Associated objects"
associated_objects_warning: "Only use these fields if you want interblocking reservation between spaces, child spaces and machines. If you want machine and space reservations to remain independent, please leave the following fields blank."
event_form:
ACTION_title: "{ACTION, select, create{Nuevo} other{Actualiza el}} evento"
title: "Título"
@ -1477,6 +1481,8 @@ es:
client__jwks_uri: "JWKS URI"
client__end_session_endpoint: "Endpoint de fin de sesión"
client__end_session_endpoint_help: "La URL a llamar para cerrar la sesión del usuario en el servidor de autorización."
extra_authorize_params: "Extra authorize parameters"
extra_authorize_params_help: "A hash of extra fixed parameters that will be merged to the authorization request"
provider_form:
name: "Nombre"
authentication_type: "Tipo de autenticación"

View File

@ -111,10 +111,10 @@ fr:
save: "Enregistrer"
create_success: "L'espace a bien été créé"
update_success: "L'espace a bien été mis à jour"
associated_machines: "Machines"
children_spaces: "Espaces"
associated_objects: "Machines et sous-espaces"
associated_objects_warning: "Utilisez ces champs uniquement si vous souhaitez que la réservation de l'espace bloque la réservation des machines associées et des sous-espaces (et vice-versa). Si vous souhaitez que les réservations restent indépendantes, veuillez laisser les champs suivants vides."
associated_machines: "Machines incluses"
children_spaces: "Espaces inclus"
associated_objects: "Objet associé"
associated_objects_warning: "Utilisez uniquement ces champs si vous souhaitez une réservation d'interblocage entre les espaces, les sous-espaces et les machines. Si vous voulez que les réservations de machines et d'espaces restent indépendantes, veuillez laisser les champs suivants vides."
event_form:
ACTION_title: "{ACTION, select, create{Nouvel } other{Mettre à jour l''}}événement"
title: "Titre"
@ -1536,6 +1536,8 @@ fr:
client__jwks_uri: "URI JWKS"
client__end_session_endpoint: "Point d'accès pour terminer la session"
client__end_session_endpoint_help: "L'url à appeler pour déconnecter l'utilisateur sur le serveur d'autorisation."
extra_authorize_params: "Paramètres d'autorisation supplémentaires"
extra_authorize_params_help: "Un hachage de paramètres supplémentaires fixes qui seront fusionnés à la demande d'autorisation"
provider_form:
name: "Nom"
authentication_type: "Type d'authentification"

View File

@ -111,6 +111,10 @@ it:
save: "Salva"
create_success: "Lo spazio è stato creato correttamente"
update_success: "Lo spazio è stato aggiornato correttamente"
associated_machines: "Included machines"
children_spaces: "Included spaces"
associated_objects: "Associated objects"
associated_objects_warning: "Only use these fields if you want interblocking reservation between spaces, child spaces and machines. If you want machine and space reservations to remain independent, please leave the following fields blank."
event_form:
ACTION_title: "{ACTION, select, create{Nuovo} other{Aggiorna}} evento"
title: "Titolo"
@ -1477,6 +1481,8 @@ it:
client__jwks_uri: "JWKS URI"
client__end_session_endpoint: "Endpoint di fine sessione"
client__end_session_endpoint_help: "L'url da chiamare per effettuare il log out dell'utente al server di autorizzazione."
extra_authorize_params: "Extra authorize parameters"
extra_authorize_params_help: "A hash of extra fixed parameters that will be merged to the authorization request"
provider_form:
name: "Nome"
authentication_type: "Tipo di autenticazione"

View File

@ -111,6 +111,10 @@
save: "Save"
create_success: "The space was created successfully"
update_success: "The space was updated successfully"
associated_machines: "Included machines"
children_spaces: "Included spaces"
associated_objects: "Associated objects"
associated_objects_warning: "Only use these fields if you want interblocking reservation between spaces, child spaces and machines. If you want machine and space reservations to remain independent, please leave the following fields blank."
event_form:
ACTION_title: "{ACTION, select, create{New} other{Update the}} event"
title: "Title"
@ -1477,6 +1481,8 @@
client__jwks_uri: "JWKS URI"
client__end_session_endpoint: "End session endpoint"
client__end_session_endpoint_help: "The url to call to log the user out at the authorization server."
extra_authorize_params: "Extra authorize parameters"
extra_authorize_params_help: "A hash of extra fixed parameters that will be merged to the authorization request"
provider_form:
name: "Name"
authentication_type: "Authentication type"

View File

@ -111,6 +111,10 @@ pt:
save: "Save"
create_success: "The space was created successfully"
update_success: "The space was updated successfully"
associated_machines: "Included machines"
children_spaces: "Included spaces"
associated_objects: "Associated objects"
associated_objects_warning: "Only use these fields if you want interblocking reservation between spaces, child spaces and machines. If you want machine and space reservations to remain independent, please leave the following fields blank."
event_form:
ACTION_title: "{ACTION, select, create{New} other{Update the}} event"
title: "Title"
@ -1477,6 +1481,8 @@ pt:
client__jwks_uri: "JWKS URI"
client__end_session_endpoint: "Endpoint de término de sessão"
client__end_session_endpoint_help: "O Url para efetuar uma chamada para desconectar o usuário no servidor de autorização."
extra_authorize_params: "Extra authorize parameters"
extra_authorize_params_help: "A hash of extra fixed parameters that will be merged to the authorization request"
provider_form:
name: "Nome"
authentication_type: "Tipo de autenticação"

View File

@ -111,6 +111,10 @@ zu:
save: "crwdns36885:0crwdne36885:0"
create_success: "crwdns31813:0crwdne31813:0"
update_success: "crwdns31815:0crwdne31815:0"
associated_machines: "crwdns37729:0crwdne37729:0"
children_spaces: "crwdns37731:0crwdne37731:0"
associated_objects: "crwdns37733:0crwdne37733:0"
associated_objects_warning: "crwdns37735:0crwdne37735:0"
event_form:
ACTION_title: "crwdns36887:0ACTION={ACTION}crwdne36887:0"
title: "crwdns31817:0crwdne31817:0"
@ -1477,6 +1481,8 @@ zu:
client__jwks_uri: "crwdns26198:0crwdne26198:0"
client__end_session_endpoint: "crwdns26200:0crwdne26200:0"
client__end_session_endpoint_help: "crwdns26202:0crwdne26202:0"
extra_authorize_params: "crwdns37725:0crwdne37725:0"
extra_authorize_params_help: "crwdns37727:0crwdne37727:0"
provider_form:
name: "crwdns26204:0crwdne26204:0"
authentication_type: "crwdns26206:0crwdne26206:0"

View File

@ -100,7 +100,7 @@ fr:
_the_fablab_policy: "les conditions d'utilisation"
field_required: "Champ requis"
profile_custom_field_is_required: "{FEILD} est requis"
user_supporting_documents_required: "Attention !<br>Vous avez déclarez être \"{GROUP}\", des pièces justificatives pourront vous être demandées."
user_supporting_documents_required: "Attention !<br>Vous avez déclaré être \"{GROUP}\", des pièces justificatives pourront vous être demandées."
unexpected_error_occurred: "Une erreur inattendue s'est produite. Veuillez réessayer ultérieurement."
used_for_statistics: "Cette donnée sera utilisée à des fins statistiques"
used_for_invoicing: "Cette donnée sera utilisée à des fins de facturation"

View File

@ -65,6 +65,7 @@ de:
not_available: "Nicht verfügbar"
reserving: "Ich reserviere"
i_ve_reserved: "Ich reservierte"
blocked: "Blocked"
length_must_be_slot_multiple: "muss mindestens %{MIN} Minuten nach dem Startdatum liegen"
must_be_associated_with_at_least_1_machine: "muss mindestens einer Maschine zugeordnet sein"
deleted_user: "Gelöschte Benutzer"
@ -446,6 +447,8 @@ de:
account_invalidated: "Dein Account ist ungültig."
notify_user_supporting_document_refusal:
refusal: "Your supporting documents were refused"
notify_user_supporting_document_reminder:
reminder_message: "This is a reminder for you to upload your supporting documents."
notify_admin_user_supporting_document_refusal:
refusal: "Member's supporting document <strong><em>%{NAME}</strong></em> was refused."
notify_user_order_is_ready:
@ -538,6 +541,7 @@ de:
space: "Dieser Space ist deaktiviert"
machine: "Diese Maschine ist deaktiviert"
reservable: "Diese Maschine ist nicht reservierbar"
blocked_by_another_reservation: "This slot is blocked by another reservation"
cart_validation:
select_user: "Please select a user before continuing"
settings:

View File

@ -462,6 +462,8 @@ en:
child_invalidated: "Your account child is invalid."
notify_user_supporting_document_refusal:
refusal: "Your supporting documents were refused"
notify_user_supporting_document_reminder:
reminder_message: "This is a reminder for you to upload your supporting documents."
notify_admin_user_supporting_document_refusal:
refusal: "Member's supporting document <strong><em>%{NAME}</strong></em> was refused."
notify_user_child_supporting_document_refusal:

View File

@ -65,6 +65,7 @@ es:
not_available: "No disponible"
reserving: "Me reservo"
i_ve_reserved: "He reservado"
blocked: "Blocked"
length_must_be_slot_multiple: "Debe ser al menos %{MIN} minutos después de la fecha de inicio"
must_be_associated_with_at_least_1_machine: "debe estar asociado con al menos 1 máquina"
deleted_user: "Usuario eliminado"
@ -446,6 +447,8 @@ es:
account_invalidated: "Su cuenta no es válida."
notify_user_supporting_document_refusal:
refusal: "Sus justificantes han sido rechazados"
notify_user_supporting_document_reminder:
reminder_message: "This is a reminder for you to upload your supporting documents."
notify_admin_user_supporting_document_refusal:
refusal: "El justificante del afiliado <strong><em>%{NAME}</em></strong> ha sido rechazado."
notify_user_order_is_ready:
@ -538,6 +541,7 @@ es:
space: "Este espacio está desactivado"
machine: "Esta máquina está desactivada"
reservable: "Esta máquina no se puede reservar"
blocked_by_another_reservation: "This slot is blocked by another reservation"
cart_validation:
select_user: "Por favor, seleccione un usuario antes de continuar"
settings:

View File

@ -66,7 +66,7 @@ fr:
not_available: "Non disponible"
reserving: "Je réserve"
i_ve_reserved: "J'ai réservé"
blocked: "Bloquée"
blocked: "Bloqué"
length_must_be_slot_multiple: "doit être au moins %{MIN} minutes après la date de début"
must_be_associated_with_at_least_1_machine: "doit être associé avec au moins 1 machine"
deleted_user: "Utilisateur supprimé"
@ -462,6 +462,8 @@ fr:
child_invalidated: "Votre compte enfant est invalide."
notify_user_supporting_document_refusal:
refusal: "Vos pièces justificatives ont été refusées"
notify_user_supporting_document_reminder:
reminder_message: "Ceci est un rappel pour vous demander de télécharger vos documents justificatifs."
notify_admin_user_supporting_document_refusal:
refusal: "Le justificatif du membre <strong><em>%{NAME}</strong></em> a été refusé."
notify_user_child_supporting_document_refusal:

View File

@ -65,6 +65,7 @@ it:
not_available: "Non disponibile"
reserving: "Sto prenotando"
i_ve_reserved: "Ho prenotato"
blocked: "Blocked"
length_must_be_slot_multiple: "deve essere almeno %{MIN} minuti dopo la data di inizio"
must_be_associated_with_at_least_1_machine: "deve essere associata ad almeno 1 macchina"
deleted_user: "Utente eliminato"
@ -446,6 +447,8 @@ it:
account_invalidated: "Il tuo account non è valido."
notify_user_supporting_document_refusal:
refusal: "I tuoi documenti aggiuntivi sono stati rifiutati"
notify_user_supporting_document_reminder:
reminder_message: "This is a reminder for you to upload your supporting documents."
notify_admin_user_supporting_document_refusal:
refusal: "Il documento aggiuntivo del membro <strong><em>%{NAME}</strong></em> è stato rifiutato."
notify_user_order_is_ready:
@ -538,6 +541,7 @@ it:
space: "Questo spazio è disabilitato"
machine: "Questa macchina è disabilitata"
reservable: "Questa macchina non è prenotabile"
blocked_by_another_reservation: "This slot is blocked by another reservation"
cart_validation:
select_user: "Seleziona un utente prima di continuare"
settings:

View File

@ -402,6 +402,10 @@ de:
body:
user_supporting_document_files_refusal: "Your supporting documents were refused:"
action: "Please re-upload some new supporting documents."
notify_user_supporting_document_reminder:
subject: "Reminder to upload your supporting documents"
body:
user_supporting_document_reminder: "This is a reminder for you to upload your supporting documents."
notify_admin_user_supporting_document_refusal:
subject: "A member's supporting documents were refused"
body:

View File

@ -434,6 +434,10 @@ en:
body:
user_supporting_document_files_refusal: "Your supporting documents were refused:"
action: "Please re-upload some new supporting documents."
notify_user_supporting_document_reminder:
subject: "Reminder to upload your supporting documents"
body:
user_supporting_document_reminder: "This is a reminder for you to upload your supporting documents."
notify_admin_user_supporting_document_refusal:
subject: "A member's supporting documents were refused"
body:

View File

@ -402,6 +402,10 @@ es:
body:
user_supporting_document_files_refusal: "Sus justificantes han sido rechazados:"
action: "Por favor, vuelva a subir nuevos documentos justificativos."
notify_user_supporting_document_reminder:
subject: "Reminder to upload your supporting documents"
body:
user_supporting_document_reminder: "This is a reminder for you to upload your supporting documents."
notify_admin_user_supporting_document_refusal:
subject: "Los justificantes del afiliado de un miembro han sido rechazados"
body:

View File

@ -434,6 +434,10 @@ fr:
body:
user_supporting_document_files_refusal: "Vos pièces justificatives ont été refusées :"
action: "Veuillez téléverser de nouvelles pièces justificatives."
notify_user_supporting_document_reminder:
subject: "Rappel de télécharger vos documents justificatifs"
body:
user_supporting_document_reminder: "Ceci est un rappel pour vous demander de télécharger vos documents justificatifs."
notify_admin_user_supporting_document_refusal:
subject: "Les justificatifs d'un membre ont été refusés"
body:

View File

@ -402,6 +402,10 @@ it:
body:
user_supporting_document_files_refusal: "I tuoi documenti aggiuntivi sono stati rifiutati:"
action: "Si prega di ricaricare nuovi documenti di supporto."
notify_user_supporting_document_reminder:
subject: "Reminder to upload your supporting documents"
body:
user_supporting_document_reminder: "This is a reminder for you to upload your supporting documents."
notify_admin_user_supporting_document_refusal:
subject: "I documenti aggiuntivi di un membro sono stati rifiutati"
body:

View File

@ -402,6 +402,10 @@
body:
user_supporting_document_files_refusal: "Your supporting documents were refused:"
action: "Please re-upload some new supporting documents."
notify_user_supporting_document_reminder:
subject: "Reminder to upload your supporting documents"
body:
user_supporting_document_reminder: "This is a reminder for you to upload your supporting documents."
notify_admin_user_supporting_document_refusal:
subject: "A member's supporting documents were refused"
body:

View File

@ -402,6 +402,10 @@ pt:
body:
user_supporting_document_files_refusal: "Os seus documentos de apoio foram recusados:"
action: "Por favor, recarregue novos documentos de apoio."
notify_user_supporting_document_reminder:
subject: "Reminder to upload your supporting documents"
body:
user_supporting_document_reminder: "This is a reminder for you to upload your supporting documents."
notify_admin_user_supporting_document_refusal:
subject: "Documentos de apoio de um membro foram recusados"
body:

View File

@ -402,6 +402,10 @@ zu:
body:
user_supporting_document_files_refusal: "crwdns37363:0crwdne37363:0"
action: "crwdns37365:0crwdne37365:0"
notify_user_supporting_document_reminder:
subject: "crwdns37743:0crwdne37743:0"
body:
user_supporting_document_reminder: "crwdns37745:0crwdne37745:0"
notify_admin_user_supporting_document_refusal:
subject: "crwdns37367:0crwdne37367:0"
body:

View File

@ -65,6 +65,7 @@
not_available: "Ikke tilgjengelig"
reserving: "I'm reserving"
i_ve_reserved: "Jeg har reservert"
blocked: "Blocked"
length_must_be_slot_multiple: "må være minst %{MIN} minutter etter startdatoen"
must_be_associated_with_at_least_1_machine: "må være tilknyttet minst 1 maskin"
deleted_user: "Deleted user"
@ -446,6 +447,8 @@
account_invalidated: "Your account is invalid."
notify_user_supporting_document_refusal:
refusal: "Your supporting documents were refused"
notify_user_supporting_document_reminder:
reminder_message: "This is a reminder for you to upload your supporting documents."
notify_admin_user_supporting_document_refusal:
refusal: "Member's supporting document <strong><em>%{NAME}</strong></em> was refused."
notify_user_order_is_ready:
@ -538,6 +541,7 @@
space: "This space is disabled"
machine: "This machine is disabled"
reservable: "This machine is not reservable"
blocked_by_another_reservation: "This slot is blocked by another reservation"
cart_validation:
select_user: "Please select a user before continuing"
settings:

View File

@ -65,6 +65,7 @@ pt:
not_available: "Não disponível "
reserving: "I'm reserving"
i_ve_reserved: "Eu reservei"
blocked: "Blocked"
length_must_be_slot_multiple: "deve ser pelo menos %{MIN} minutos após a data de início"
must_be_associated_with_at_least_1_machine: "deve estar associada a pelo menos uma máquina"
deleted_user: "Usuário deletado"
@ -446,6 +447,8 @@ pt:
account_invalidated: "Sua conta é inválida."
notify_user_supporting_document_refusal:
refusal: "Your supporting documents were refused"
notify_user_supporting_document_reminder:
reminder_message: "This is a reminder for you to upload your supporting documents."
notify_admin_user_supporting_document_refusal:
refusal: "Member's supporting document <strong><em>%{NAME}</strong></em> was refused."
notify_user_order_is_ready:
@ -538,6 +541,7 @@ pt:
space: "Este espaço está desativado"
machine: "Esta máquina está desativada"
reservable: "Esta máquina não é reservável"
blocked_by_another_reservation: "This slot is blocked by another reservation"
cart_validation:
select_user: "Por favor, selecione um usuário antes de continuar"
settings:

View File

@ -65,6 +65,7 @@ zu:
not_available: "crwdns3267:0crwdne3267:0"
reserving: "crwdns37199:0crwdne37199:0"
i_ve_reserved: "crwdns3269:0crwdne3269:0"
blocked: "crwdns37737:0crwdne37737:0"
length_must_be_slot_multiple: "crwdns3271:0%{MIN}crwdne3271:0"
must_be_associated_with_at_least_1_machine: "crwdns3273:0crwdne3273:0"
deleted_user: "crwdns22460:0crwdne22460:0"
@ -446,6 +447,8 @@ zu:
account_invalidated: "crwdns23006:0crwdne23006:0"
notify_user_supporting_document_refusal:
refusal: "crwdns37345:0crwdne37345:0"
notify_user_supporting_document_reminder:
reminder_message: "crwdns37739:0crwdne37739:0"
notify_admin_user_supporting_document_refusal:
refusal: "crwdns37347:0%{NAME}crwdne37347:0"
notify_user_order_is_ready:
@ -538,6 +541,7 @@ zu:
space: "crwdns36283:0crwdne36283:0"
machine: "crwdns36285:0crwdne36285:0"
reservable: "crwdns36287:0crwdne36287:0"
blocked_by_another_reservation: "crwdns37741:0crwdne37741:0"
cart_validation:
select_user: "crwdns37211:0crwdne37211:0"
settings:

View File

@ -62,6 +62,10 @@ auto_cancel_authorizations:
class: TrainingAuthorizationWorker
queue: default
supporting_documents_reminder_worker:
cron: "0 8 * * *" # every day, at 8
class: SupportingDocumentsReminderWorker
child_age_will_be_18:
cron: "0 0 0 * * *" # every day, at midnight
class: ChildAgeWorker

View File

@ -0,0 +1,5 @@
class AddSupportingDocumentsReminderSentAtToUsers < ActiveRecord::Migration[7.0]
def change
add_column :users, :supporting_documents_reminder_sent_at, :datetime
end
end

View File

@ -0,0 +1,5 @@
class AddExtraAuthorizeParamsToOpenIdConnectProvider < ActiveRecord::Migration[7.0]
def change
add_column :open_id_connect_providers, :extra_authorize_params, :jsonb, default: {}
end
end

View File

@ -1,104 +1,5 @@
# frozen_string_literal: true
NOTIFICATIONS_TYPES = [
{ name: 'notify_admin_when_project_published', category: 'projects', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_project_collaborator_to_valid', category: 'projects', is_configurable: false },
{ name: 'notify_project_author_when_collaborator_valid', category: 'projects', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_user_training_valid', category: 'trainings', is_configurable: false },
{ name: 'notify_member_subscribed_plan', category: 'subscriptions', is_configurable: false },
{ name: 'notify_member_create_reservation', category: 'agenda', is_configurable: false },
{ name: 'notify_member_subscribed_plan_is_changed', category: 'deprecated', is_configurable: false },
{ name: 'notify_admin_member_create_reservation', category: 'agenda', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_member_slot_is_modified', category: 'agenda', is_configurable: false },
{ name: 'notify_admin_slot_is_modified', category: 'agenda', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_when_user_is_created', category: 'users_accounts', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_subscribed_plan', category: 'subscriptions', is_configurable: true, roles: ['admin'] },
{ name: 'notify_user_when_invoice_ready', category: 'payments', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_member_subscription_will_expire_in_7_days', category: 'subscriptions', is_configurable: false },
{ name: 'notify_member_subscription_is_expired', category: 'subscriptions', is_configurable: false },
{ name: 'notify_admin_subscription_will_expire_in_7_days', category: 'subscriptions', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_subscription_is_expired', category: 'subscriptions', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_subscription_canceled', category: 'subscriptions', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_member_subscription_canceled', category: 'subscriptions', is_configurable: false },
{ name: 'notify_user_when_avoir_ready', category: 'wallet', is_configurable: false },
{ name: 'notify_member_slot_is_canceled', category: 'agenda', is_configurable: false },
{ name: 'notify_admin_slot_is_canceled', category: 'agenda', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_partner_subscribed_plan', category: 'subscriptions', is_configurable: false },
{ name: 'notify_member_subscription_extended', category: 'subscriptions', is_configurable: false },
{ name: 'notify_admin_subscription_extended', category: 'subscriptions', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_user_group_changed', category: 'users_accounts', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_user_user_group_changed', category: 'users_accounts', is_configurable: false },
{ name: 'notify_admin_when_user_is_imported', category: 'users_accounts', is_configurable: true, roles: ['admin'] },
{ name: 'notify_user_profile_complete', category: 'users_accounts', is_configurable: false },
{ name: 'notify_user_auth_migration', category: 'user', is_configurable: false },
{ name: 'notify_admin_user_merged', category: 'users_accounts', is_configurable: true, roles: ['admin'] },
{ name: 'notify_admin_profile_complete', category: 'users_accounts', is_configurable: true, roles: ['admin'] },
{ name: 'notify_admin_abuse_reported', category: 'projects', is_configurable: true, roles: ['admin'] },
{ name: 'notify_admin_invoicing_changed', category: 'deprecated', is_configurable: false },
{ name: 'notify_user_wallet_is_credited', category: 'wallet', is_configurable: false },
{ name: 'notify_admin_user_wallet_is_credited', category: 'wallet', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_export_complete', category: 'exports', is_configurable: false },
{ name: 'notify_member_about_coupon', category: 'agenda', is_configurable: false },
{ name: 'notify_member_reservation_reminder', category: 'agenda', is_configurable: false },
{ name: 'notify_admin_free_disk_space', category: 'app_management', is_configurable: false },
{ name: 'notify_admin_close_period_reminder', category: 'accountings', is_configurable: true, roles: ['admin'] },
{ name: 'notify_admin_archive_complete', category: 'accountings', is_configurable: true, roles: ['admin'] },
{ name: 'notify_privacy_policy_changed', category: 'app_management', is_configurable: false },
{ name: 'notify_admin_import_complete', category: 'app_management', is_configurable: false },
{ name: 'notify_admin_refund_created', category: 'wallet', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admins_role_update', category: 'users_accounts', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_user_role_update', category: 'users_accounts', is_configurable: false },
{ name: 'notify_admin_objects_stripe_sync', category: 'payments', is_configurable: false },
{ name: 'notify_user_when_payment_schedule_ready', category: 'payments', is_configurable: false },
{ name: 'notify_admin_payment_schedule_failed', category: 'payments', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_member_payment_schedule_failed', category: 'payments', is_configurable: false },
{ name: 'notify_admin_payment_schedule_check_deadline', category: 'payments', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_payment_schedule_transfer_deadline', category: 'payments', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_payment_schedule_error', category: 'payments', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_member_payment_schedule_error', category: 'payments', is_configurable: false },
{ name: 'notify_admin_payment_schedule_gateway_canceled', category: 'payments', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_member_payment_schedule_gateway_canceled', category: 'payments', is_configurable: false },
{ name: 'notify_admin_user_supporting_document_files_created', category: 'supporting_documents', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_user_supporting_document_files_updated', category: 'supporting_documents', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_user_is_validated', category: 'users_accounts', is_configurable: false },
{ name: 'notify_user_is_invalidated', category: 'users_accounts', is_configurable: false },
{ name: 'notify_user_supporting_document_refusal', category: 'supporting_documents', is_configurable: false },
{ name: 'notify_admin_user_supporting_document_refusal', category: 'supporting_documents', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_order_is_paid', category: 'shop', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_user_order_is_ready', category: 'shop', is_configurable: false },
{ name: 'notify_user_order_is_canceled', category: 'shop', is_configurable: false },
{ name: 'notify_user_order_is_refunded', category: 'shop', is_configurable: false },
{ name: 'notify_admin_low_stock_threshold', category: 'shop', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_training_auto_cancelled', category: 'trainings', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_member_training_auto_cancelled', category: 'trainings', is_configurable: false },
{ name: 'notify_member_training_authorization_expired', category: 'trainings', is_configurable: false },
{ name: 'notify_member_training_invalidated', category: 'trainings', is_configurable: false },
{ name: 'notify_member_reservation_limit_reached', category: 'agenda', is_configurable: false },
{ name: 'notify_admin_user_child_supporting_document_refusal', category: 'supporting_documents', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_user_child_supporting_document_refusal', category: 'supporting_documents', is_configurable: false },
{ name: 'notify_admin_child_created', category: 'users_accounts', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_user_child_is_validated', category: 'users_accounts', is_configurable: false },
{ name: 'notify_user_child_is_invalidated', category: 'users_accounts', is_configurable: false },
{ name: 'notify_admin_user_child_supporting_document_files_updated', category: 'supporting_documents', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_admin_user_child_supporting_document_files_created', category: 'supporting_documents', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_member_reservation_validated', category: 'agenda', is_configurable: false },
{ name: 'notify_admin_reservation_validated', category: 'agenda', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_member_pre_booked_reservation', category: 'agenda', is_configurable: false },
{ name: 'notify_admin_member_pre_booked_reservation', category: 'agenda', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_member_reservation_invalidated', category: 'agenda', is_configurable: false },
{ name: 'notify_admin_reservation_invalidated', category: 'agenda', is_configurable: true, roles: ['admin', 'manager'] },
{ name: 'notify_user_when_child_age_will_be_18', category: 'users_accounts', is_configurable: false }
].freeze
NOTIFICATIONS_TYPES.each do |notification_type_attrs|
notification_type = NotificationType.find_by(name: notification_type_attrs[:name])

View File

@ -2039,7 +2039,8 @@ CREATE TABLE public.open_id_connect_providers (
client__end_session_endpoint character varying,
profile_url character varying,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL
updated_at timestamp without time zone NOT NULL,
extra_authorize_params jsonb DEFAULT '{}'::jsonb
);
@ -4354,7 +4355,8 @@ CREATE TABLE public.users (
current_sign_in_ip inet,
last_sign_in_ip inet,
validated_at timestamp without time zone,
mapped_from_sso character varying
mapped_from_sso character varying,
supporting_documents_reminder_sent_at timestamp(6) without time zone
);
@ -9261,6 +9263,8 @@ INSERT INTO "schema_migrations" (version) VALUES
('20230728090257'),
('20230825101952'),
('20230828073428'),
('20230831103208');
('20230831103208'),
('20230901090637'),
('20230907124230');

View File

@ -1,6 +1,6 @@
{
"name": "fab-manager",
"version": "6.0.13",
"version": "6.0.14",
"description": "Fab-manager is the FabLab management solution. It provides a comprehensive, web-based, open-source tool to simplify your administrative tasks and your marker's projects.",
"keywords": [
"fablab",

View File

@ -607,3 +607,11 @@ notification_type_72:
is_configurable: false
created_at: 2023-02-16 10:42:39.143888000 Z
updated_at: 2023-02-16 10:42:39.143888000 Z
notification_type_73:
id: 73
name: notify_user_supporting_document_reminder
category: supporting_documents
is_configurable: false
created_at: 2023-02-02 08:25:33.439078000 Z
updated_at: 2023-02-02 08:25:33.439078000 Z

View File

@ -39,6 +39,21 @@ class NotificationsTest < ActionDispatch::IntegrationTest
assert_equal (Notification.where(receiver_id: @admin.id).count - 1), notifications_total
end
test 'polling endpoint' do
@admin = User.find_by(username: 'admin')
login_as(@admin, scope: :user)
get '/api/notifications/polling', params: { last_poll: Notification.order(:created_at).pick(:created_at) }
# Check response format & status
assert_equal 200, response.status, response.body
assert_match Mime[:json].to_s, response.content_type
# Check the list items are ok
notifications_total = json_response(response.body)[:totals][:total]
assert_not_equal notifications.count, 0
end
test 'Last unread returns last 3 unread notifications' do
@member = User.find(4)
login_as(@member, scope: :user)

View File

@ -0,0 +1,83 @@
# frozen_string_literal: true
require 'test_helper'
require 'minitest/autorun'
#require 'sidekiq/testing'
class SupportingDocumentsReminderWorkerTest < ActiveSupport::TestCase
include ActionMailer::TestHelper
setup do
@worker = SupportingDocumentsReminderWorker.new
group = groups(:group_1)
@users = User.where(group_id: group.id).members
@supporting_document_type_1 = SupportingDocumentType.create!(name: "doc1", groups: [group])
@supporting_document_type_2 = SupportingDocumentType.create!(name: "doc2", groups: [group])
end
test 'do nothing if it concerns another group' do
group = Group.create!(name: 'test', slug: 'test')
SupportingDocumentType.destroy_all
supporting_document_type = SupportingDocumentType.create!(name: "doc3", groups: [group])
@users.each do |user|
assert_nil user.supporting_documents_reminder_sent_at
end
assert_enqueued_emails 0 do
@worker.perform
end
@users.reload.each do |user|
assert_nil user.supporting_documents_reminder_sent_at
end
end
test 'notify every users who did not upload supporting document files' do
@users.each do |user|
assert_nil user.supporting_documents_reminder_sent_at
end
assert_enqueued_emails @users.length do
@worker.perform
end
@users.reload.each do |user|
assert user.supporting_documents_reminder_sent_at
end
assert_enqueued_emails 0 do
@worker.perform
end
end
test 'notify users even if they have uploaded 1 document of the 2' do
@users.each do |user|
user.supporting_document_files.create!(supporting_document_type: @supporting_document_type_1,
attachment: fixture_file_upload('document.pdf'))
end
assert_enqueued_emails @users.length do
@worker.perform
end
end
test 'do not notify users if they have uploaded all documents' do
@users.each do |user|
user.supporting_document_files.create!(supporting_document_type: @supporting_document_type_1,
attachment: fixture_file_upload('document.pdf'))
user.supporting_document_files.create!(supporting_document_type: @supporting_document_type_2,
attachment: fixture_file_upload('document.pdf'))
end
assert_enqueued_emails 0 do
@worker.perform
end
end
test 'do not notify users if they were created too recently' do
@users.update_all(created_at: 2.minutes.ago)
assert_enqueued_emails 0 do
@worker.perform
end
end
end