mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2024-11-28 09:24:24 +01:00
(feat) optional external id
This commit is contained in:
parent
e246480049
commit
4b84963d7f
@ -1,5 +1,6 @@
|
|||||||
# Changelog Fab-manager
|
# Changelog Fab-manager
|
||||||
|
|
||||||
|
- Optional external identifier for users
|
||||||
- Accounting data is now built each night and saved in database
|
- Accounting data is now built each night and saved in database
|
||||||
- OpenAPI endpoint to fetch accounting data
|
- OpenAPI endpoint to fetch accounting data
|
||||||
- Fix a bug: providing an array of attributes to filter OpenApi data, results in error
|
- Fix a bug: providing an array of attributes to filter OpenApi data, results in error
|
||||||
|
@ -232,6 +232,7 @@ class API::MembersController < API::ApiController
|
|||||||
|
|
||||||
elsif current_user.admin? || current_user.manager?
|
elsif current_user.admin? || current_user.manager?
|
||||||
params.require(:user).permit(:username, :email, :password, :password_confirmation, :is_allow_contact, :is_allow_newsletter, :group_id,
|
params.require(:user).permit(:username, :email, :password, :password_confirmation, :is_allow_contact, :is_allow_newsletter, :group_id,
|
||||||
|
:external_id,
|
||||||
tag_ids: [],
|
tag_ids: [],
|
||||||
profile_attributes: [:id, :first_name, :last_name, :phone, :interest, :software_mastered, :website, :job,
|
profile_attributes: [:id, :first_name, :last_name, :phone, :interest, :software_mastered, :website, :job,
|
||||||
:facebook, :twitter, :google_plus, :viadeo, :linkedin, :instagram, :youtube, :vimeo,
|
:facebook, :twitter, :google_plus, :viadeo, :linkedin, :instagram, :youtube, :vimeo,
|
||||||
|
@ -25,6 +25,7 @@ class OpenAPI::V1::UsersDoc < OpenAPI::V1::BaseDoc
|
|||||||
"id": 1746,
|
"id": 1746,
|
||||||
"email": "xxxxxxx@xxxx.com",
|
"email": "xxxxxxx@xxxx.com",
|
||||||
"created_at": "2016-05-04T17:21:48.403+02:00",
|
"created_at": "2016-05-04T17:21:48.403+02:00",
|
||||||
|
"external_id": "J5821-4"
|
||||||
"full_name": "xxxx xxxx",
|
"full_name": "xxxx xxxx",
|
||||||
"group": {
|
"group": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
@ -36,6 +37,7 @@ class OpenAPI::V1::UsersDoc < OpenAPI::V1::BaseDoc
|
|||||||
"id": 1745,
|
"id": 1745,
|
||||||
"email": "xxxxxxx@gmail.com",
|
"email": "xxxxxxx@gmail.com",
|
||||||
"created_at": "2016-05-03T15:21:13.125+02:00",
|
"created_at": "2016-05-03T15:21:13.125+02:00",
|
||||||
|
"external_id": "J5846-4"
|
||||||
"full_name": "xxxxx xxxxx",
|
"full_name": "xxxxx xxxxx",
|
||||||
"group": {
|
"group": {
|
||||||
"id": 2,
|
"id": 2,
|
||||||
@ -47,6 +49,7 @@ class OpenAPI::V1::UsersDoc < OpenAPI::V1::BaseDoc
|
|||||||
"id": 1744,
|
"id": 1744,
|
||||||
"email": "xxxxxxx@gmail.com",
|
"email": "xxxxxxx@gmail.com",
|
||||||
"created_at": "2016-05-03T13:51:03.223+02:00",
|
"created_at": "2016-05-03T13:51:03.223+02:00",
|
||||||
|
"external_id": "J5900-1"
|
||||||
"full_name": "xxxxxxx xxxx",
|
"full_name": "xxxxxxx xxxx",
|
||||||
"group": {
|
"group": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
@ -58,6 +61,7 @@ class OpenAPI::V1::UsersDoc < OpenAPI::V1::BaseDoc
|
|||||||
"id": 1743,
|
"id": 1743,
|
||||||
"email": "xxxxxxxx@setecastronomy.eu",
|
"email": "xxxxxxxx@setecastronomy.eu",
|
||||||
"created_at": "2016-05-03T12:24:38.724+02:00",
|
"created_at": "2016-05-03T12:24:38.724+02:00",
|
||||||
|
"external_id": "P4172-4"
|
||||||
"full_name": "xxx xxxxxxx",
|
"full_name": "xxx xxxxxxx",
|
||||||
"group": {
|
"group": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
@ -75,6 +79,7 @@ class OpenAPI::V1::UsersDoc < OpenAPI::V1::BaseDoc
|
|||||||
"id": 1746,
|
"id": 1746,
|
||||||
"email": "xxxxxxxxxxxx",
|
"email": "xxxxxxxxxxxx",
|
||||||
"created_at": "2016-05-04T17:21:48.403+02:00",
|
"created_at": "2016-05-04T17:21:48.403+02:00",
|
||||||
|
"external_id": "J5500-4"
|
||||||
"full_name": "xxxx xxxxxx",
|
"full_name": "xxxx xxxxxx",
|
||||||
"group": {
|
"group": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
@ -86,6 +91,7 @@ class OpenAPI::V1::UsersDoc < OpenAPI::V1::BaseDoc
|
|||||||
"id": 1745,
|
"id": 1745,
|
||||||
"email": "xxxxxxxxx@gmail.com",
|
"email": "xxxxxxxxx@gmail.com",
|
||||||
"created_at": "2016-05-03T15:21:13.125+02:00",
|
"created_at": "2016-05-03T15:21:13.125+02:00",
|
||||||
|
"external_id": null,
|
||||||
"full_name": "xxxxx xxxxxx",
|
"full_name": "xxxxx xxxxxx",
|
||||||
"group": {
|
"group": {
|
||||||
"id": 2,
|
"id": 2,
|
||||||
|
@ -114,7 +114,7 @@ export const DataMappingForm = <TFieldValues extends FieldValues, TContext exten
|
|||||||
* Return a className based on the current mapping-item status
|
* Return a className based on the current mapping-item status
|
||||||
*/
|
*/
|
||||||
const itemStatus = (index: number): string => {
|
const itemStatus = (index: number): string => {
|
||||||
if (currentFormValues[index]?.id) {
|
if (currentFormValues && currentFormValues[index]?.id) {
|
||||||
if (currentFormValues[index]._destroy) return 'destroyed-item';
|
if (currentFormValues[index]._destroy) return 'destroyed-item';
|
||||||
return 'saved-item';
|
return 'saved-item';
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ declare const Application: IApplication;
|
|||||||
|
|
||||||
interface ProfileFormOptionProps {
|
interface ProfileFormOptionProps {
|
||||||
user: User,
|
user: User,
|
||||||
|
operator: User,
|
||||||
activeProvider: ActiveProviderResponse,
|
activeProvider: ActiveProviderResponse,
|
||||||
onError: (message: string) => void,
|
onError: (message: string) => void,
|
||||||
onSuccess: (user: User) => void,
|
onSuccess: (user: User) => void,
|
||||||
@ -27,7 +28,7 @@ interface ProfileFormOptionProps {
|
|||||||
* (*) This component handle the first case.
|
* (*) This component handle the first case.
|
||||||
* It also deals with duplicate email addresses in database
|
* It also deals with duplicate email addresses in database
|
||||||
*/
|
*/
|
||||||
export const ProfileFormOption: React.FC<ProfileFormOptionProps> = ({ user, activeProvider, onError, onSuccess }) => {
|
export const ProfileFormOption: React.FC<ProfileFormOptionProps> = ({ user, operator, activeProvider, onError, onSuccess }) => {
|
||||||
const { t } = useTranslation('logged');
|
const { t } = useTranslation('logged');
|
||||||
|
|
||||||
const userLib = new UserLib(user);
|
const userLib = new UserLib(user);
|
||||||
@ -60,6 +61,7 @@ export const ProfileFormOption: React.FC<ProfileFormOptionProps> = ({ user, acti
|
|||||||
<UserProfileForm onError={onError}
|
<UserProfileForm onError={onError}
|
||||||
action="update"
|
action="update"
|
||||||
user={user}
|
user={user}
|
||||||
|
operator={operator}
|
||||||
onSuccess={onSuccess}
|
onSuccess={onSuccess}
|
||||||
size="small"
|
size="small"
|
||||||
showGroupInput
|
showGroupInput
|
||||||
|
@ -3,7 +3,7 @@ import * as React from 'react';
|
|||||||
import { react2angular } from 'react2angular';
|
import { react2angular } from 'react2angular';
|
||||||
import { useForm, useWatch, ValidateResult } from 'react-hook-form';
|
import { useForm, useWatch, ValidateResult } from 'react-hook-form';
|
||||||
import { isNil as _isNil } from 'lodash';
|
import { isNil as _isNil } from 'lodash';
|
||||||
import { User, UserFieldMapping } from '../../models/user';
|
import { User, UserFieldMapping, UserFieldsReservedForPrivileged } from '../../models/user';
|
||||||
import { IApplication } from '../../models/application';
|
import { IApplication } from '../../models/application';
|
||||||
import { Loader } from '../base/loader';
|
import { Loader } from '../base/loader';
|
||||||
import { FormInput } from '../form/form-input';
|
import { FormInput } from '../form/form-input';
|
||||||
@ -39,6 +39,7 @@ interface UserProfileFormProps {
|
|||||||
action: 'create' | 'update',
|
action: 'create' | 'update',
|
||||||
size?: 'small' | 'large',
|
size?: 'small' | 'large',
|
||||||
user: User,
|
user: User,
|
||||||
|
operator: User,
|
||||||
className?: string,
|
className?: string,
|
||||||
onError: (message: string) => void,
|
onError: (message: string) => void,
|
||||||
onSuccess: (user: User) => void,
|
onSuccess: (user: User) => void,
|
||||||
@ -51,7 +52,7 @@ interface UserProfileFormProps {
|
|||||||
/**
|
/**
|
||||||
* Form component to create or update a user
|
* Form component to create or update a user
|
||||||
*/
|
*/
|
||||||
export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size, user, className, onError, onSuccess, showGroupInput, showTermsAndConditionsInput, showTrainingsInput, showTagsInput }) => {
|
export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size, user, operator, className, onError, onSuccess, showGroupInput, showTermsAndConditionsInput, showTrainingsInput, showTagsInput }) => {
|
||||||
const { t } = useTranslation('shared');
|
const { t } = useTranslation('shared');
|
||||||
|
|
||||||
// regular expression to validate the input fields
|
// regular expression to validate the input fields
|
||||||
@ -66,7 +67,7 @@ export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size,
|
|||||||
const [groups, setGroups] = useState<SelectOption<number>[]>([]);
|
const [groups, setGroups] = useState<SelectOption<number>[]>([]);
|
||||||
const [termsAndConditions, setTermsAndConditions] = useState<CustomAsset>(null);
|
const [termsAndConditions, setTermsAndConditions] = useState<CustomAsset>(null);
|
||||||
const [profileCustomFields, setProfileCustomFields] = useState<ProfileCustomField[]>([]);
|
const [profileCustomFields, setProfileCustomFields] = useState<ProfileCustomField[]>([]);
|
||||||
const [requiredFieldsSettings, setRequiredFieldsSettings] = useState<Map<SettingName, string>>(new Map());
|
const [fieldsSettings, setFieldsSettings] = useState<Map<SettingName, string>>(new Map());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
AuthProviderAPI.active().then(data => {
|
AuthProviderAPI.active().then(data => {
|
||||||
@ -94,8 +95,8 @@ export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size,
|
|||||||
});
|
});
|
||||||
setValue('invoicing_profile_attributes.user_profile_custom_fields_attributes', userProfileCustomFields);
|
setValue('invoicing_profile_attributes.user_profile_custom_fields_attributes', userProfileCustomFields);
|
||||||
}).catch(error => onError(error));
|
}).catch(error => onError(error));
|
||||||
SettingAPI.query(['phone_required', 'address_required'])
|
SettingAPI.query(['phone_required', 'address_required', 'external_id'])
|
||||||
.then(settings => setRequiredFieldsSettings(settings))
|
.then(settings => setFieldsSettings(settings))
|
||||||
.catch(error => onError(error));
|
.catch(error => onError(error));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -150,6 +151,10 @@ export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size,
|
|||||||
* Check if the given field path should be disabled
|
* Check if the given field path should be disabled
|
||||||
*/
|
*/
|
||||||
const isDisabled = function (id: string) {
|
const isDisabled = function (id: string) {
|
||||||
|
// some fields may be reserved in edition for priviledged users
|
||||||
|
if (UserFieldsReservedForPrivileged.includes(id) && !(new UserLib(operator).isPrivileged(user))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// if the current provider is the local database, then all fields are enabled
|
// if the current provider is the local database, then all fields are enabled
|
||||||
if (isLocalDatabaseProvider) {
|
if (isLocalDatabaseProvider) {
|
||||||
return false;
|
return false;
|
||||||
@ -209,7 +214,7 @@ export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size,
|
|||||||
value: phoneRegex,
|
value: phoneRegex,
|
||||||
message: t('app.shared.user_profile_form.phone_number_invalid')
|
message: t('app.shared.user_profile_form.phone_number_invalid')
|
||||||
},
|
},
|
||||||
required: requiredFieldsSettings.get('phone_required') === 'true'
|
required: fieldsSettings.get('phone_required') === 'true'
|
||||||
}}
|
}}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
formState={formState}
|
formState={formState}
|
||||||
@ -222,7 +227,7 @@ export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size,
|
|||||||
<FormInput id="invoicing_profile_attributes.address_attributes.address"
|
<FormInput id="invoicing_profile_attributes.address_attributes.address"
|
||||||
register={register}
|
register={register}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
rules={{ required: requiredFieldsSettings.get('address_required') === 'true' }}
|
rules={{ required: fieldsSettings.get('address_required') === 'true' }}
|
||||||
label={t('app.shared.user_profile_form.address')} />
|
label={t('app.shared.user_profile_form.address')} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -234,6 +239,11 @@ export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size,
|
|||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
formState={formState}
|
formState={formState}
|
||||||
label={t('app.shared.user_profile_form.pseudonym')} />
|
label={t('app.shared.user_profile_form.pseudonym')} />
|
||||||
|
{fieldsSettings.get('external_id') === 'true' && <FormInput id="external_id"
|
||||||
|
register={register}
|
||||||
|
disabled={isDisabled}
|
||||||
|
formState={formState}
|
||||||
|
label={t('app.shared.user_profile_form.external_id')} />}
|
||||||
<FormInput id="email"
|
<FormInput id="email"
|
||||||
register={register}
|
register={register}
|
||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
@ -398,4 +408,4 @@ const UserProfileFormWrapper: React.FC<UserProfileFormProps> = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Application.Components.component('userProfileForm', react2angular(UserProfileFormWrapper, ['action', 'size', 'user', 'className', 'onError', 'onSuccess', 'showGroupInput', 'showTermsAndConditionsInput', 'showTagsInput', 'showTrainingsInput']));
|
Application.Components.component('userProfileForm', react2angular(UserProfileFormWrapper, ['action', 'size', 'user', 'operator', 'className', 'onError', 'onSuccess', 'showGroupInput', 'showTermsAndConditionsInput', 'showTagsInput', 'showTrainingsInput']));
|
||||||
|
@ -164,6 +164,7 @@ export const accountSettings = [
|
|||||||
'phone_required',
|
'phone_required',
|
||||||
'confirmation_required',
|
'confirmation_required',
|
||||||
'address_required',
|
'address_required',
|
||||||
|
'external_id',
|
||||||
'user_change_group',
|
'user_change_group',
|
||||||
'user_validation_required',
|
'user_validation_required',
|
||||||
'user_validation_required_list'
|
'user_validation_required_list'
|
||||||
|
@ -12,6 +12,7 @@ type ProfileAttributesSocial = {
|
|||||||
export interface User {
|
export interface User {
|
||||||
id: number,
|
id: number,
|
||||||
username?: string,
|
username?: string,
|
||||||
|
external_id?: string,
|
||||||
email: string,
|
email: string,
|
||||||
group_id?: number,
|
group_id?: number,
|
||||||
role?: UserRole
|
role?: UserRole
|
||||||
@ -130,3 +131,5 @@ export const UserFieldMapping = Object.assign({
|
|||||||
is_allow_newsletter: 'user.is_allow_newsletter',
|
is_allow_newsletter: 'user.is_allow_newsletter',
|
||||||
group_id: 'user.group_id'
|
group_id: 'user.group_id'
|
||||||
}, ...socialMappings);
|
}, ...socialMappings);
|
||||||
|
|
||||||
|
export const UserFieldsReservedForPrivileged = ['external_id'];
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
<section class="panel panel-default bg-light m-lg">
|
<section class="panel panel-default bg-light m-lg">
|
||||||
<div class="panel-body m-r">
|
<div class="panel-body m-r">
|
||||||
<user-profile-form user="user"
|
<user-profile-form user="user"
|
||||||
|
operator="currentUser"
|
||||||
action="'update'"
|
action="'update'"
|
||||||
on-error="onError"
|
on-error="onError"
|
||||||
on-success="onUserSuccess"
|
on-success="onUserSuccess"
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
<div class="panel-body m-r">
|
<div class="panel-body m-r">
|
||||||
|
|
||||||
<user-profile-form user="user"
|
<user-profile-form user="user"
|
||||||
|
operator="currentUser"
|
||||||
action="'create'"
|
action="'create'"
|
||||||
on-error="onError"
|
on-error="onError"
|
||||||
on-success="onUserSuccess"
|
on-success="onUserSuccess"
|
||||||
|
@ -126,6 +126,19 @@
|
|||||||
</boolean-setting>
|
</boolean-setting>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<h3 class="m-l" translate>{{ 'app.admin.settings.external_id' }}</h3>
|
||||||
|
<p class="alert alert-warning m-h-md" translate>
|
||||||
|
{{ 'app.admin.settings.external_id_info_html' }}
|
||||||
|
</p>
|
||||||
|
<div class="col-md-10 col-md-offset-1">
|
||||||
|
<boolean-setting name="'external_id'"
|
||||||
|
label="'app.admin.settings.enable_external_id' | translate"
|
||||||
|
on-success="onSuccess"
|
||||||
|
on-error="onError">
|
||||||
|
</boolean-setting>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<h3 class="m-l" translate>{{ 'app.admin.settings.account.organization' }}</h3>
|
<h3 class="m-l" translate>{{ 'app.admin.settings.account.organization' }}</h3>
|
||||||
|
@ -108,7 +108,7 @@
|
|||||||
</section>
|
</section>
|
||||||
<section class="panel panel-default bg-light m">
|
<section class="panel panel-default bg-light m">
|
||||||
<div class="panel-body m-r">
|
<div class="panel-body m-r">
|
||||||
<user-profile-form user="user" action="'update'" on-error="onError" on-success="onSuccess" />
|
<user-profile-form user="user" operator="user" action="'update'" on-error="onError" on-success="onSuccess" />
|
||||||
</div> <!-- ./panel-body -->
|
</div> <!-- ./panel-body -->
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
<profile-form-option on-error="onError"
|
<profile-form-option on-error="onError"
|
||||||
on-success="onSuccess"
|
on-success="onSuccess"
|
||||||
user="user"
|
user="user"
|
||||||
|
operator="currentUser"
|
||||||
active-provider="activeProvider" />
|
active-provider="activeProvider" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
44
app/models/concerns/user_ressources_concern.rb
Normal file
44
app/models/concerns/user_ressources_concern.rb
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Add resources-related functionalities to the user model (eg. Reservation, Subscrtion, Project, etc.)
|
||||||
|
module UserRessourcesConcern
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
def training_machine?(machine)
|
||||||
|
return true if admin? || manager?
|
||||||
|
|
||||||
|
trainings.map(&:machines).flatten.uniq.include?(machine)
|
||||||
|
end
|
||||||
|
|
||||||
|
def packs?(item)
|
||||||
|
return true if admin?
|
||||||
|
|
||||||
|
PrepaidPackService.user_packs(self, item).count.positive?
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_training_reservation_by_machine(machine)
|
||||||
|
reservations.where(reservable_type: 'Training', reservable_id: machine.trainings.map(&:id))
|
||||||
|
.includes(:slots)
|
||||||
|
.where('slots.start_at>= ?', DateTime.current)
|
||||||
|
.order('slots.start_at': :asc)
|
||||||
|
.references(:slots)
|
||||||
|
.limit(1)
|
||||||
|
.first
|
||||||
|
end
|
||||||
|
|
||||||
|
def subscribed_plan
|
||||||
|
return nil if subscription.nil? || subscription.expired_at < DateTime.current
|
||||||
|
|
||||||
|
subscription.plan
|
||||||
|
end
|
||||||
|
|
||||||
|
def subscription
|
||||||
|
subscriptions.order(:created_at).last
|
||||||
|
end
|
||||||
|
|
||||||
|
def all_projects
|
||||||
|
my_projects.to_a.concat projects
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
72
app/models/concerns/user_role_concern.rb
Normal file
72
app/models/concerns/user_role_concern.rb
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Add role-based functionalities to the user model
|
||||||
|
module UserRoleConcern
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
def admin?
|
||||||
|
has_role? :admin
|
||||||
|
end
|
||||||
|
|
||||||
|
def member?
|
||||||
|
has_role? :member
|
||||||
|
end
|
||||||
|
|
||||||
|
def manager?
|
||||||
|
has_role? :manager
|
||||||
|
end
|
||||||
|
|
||||||
|
def partner?
|
||||||
|
has_role? :partner
|
||||||
|
end
|
||||||
|
|
||||||
|
def privileged?
|
||||||
|
admin? || manager?
|
||||||
|
end
|
||||||
|
|
||||||
|
def role
|
||||||
|
if admin?
|
||||||
|
'admin'
|
||||||
|
elsif manager?
|
||||||
|
'manager'
|
||||||
|
elsif member?
|
||||||
|
'member'
|
||||||
|
else
|
||||||
|
'other'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class_methods do
|
||||||
|
def admins
|
||||||
|
User.with_role(:admin)
|
||||||
|
end
|
||||||
|
|
||||||
|
def members
|
||||||
|
User.with_role(:member)
|
||||||
|
end
|
||||||
|
|
||||||
|
def partners
|
||||||
|
User.with_role(:partner)
|
||||||
|
end
|
||||||
|
|
||||||
|
def managers
|
||||||
|
User.with_role(:manager)
|
||||||
|
end
|
||||||
|
|
||||||
|
def admins_and_managers
|
||||||
|
User.with_any_role(:admin, :manager)
|
||||||
|
end
|
||||||
|
|
||||||
|
def online_payers
|
||||||
|
User.with_any_role(:admin, :manager, :member)
|
||||||
|
end
|
||||||
|
|
||||||
|
def adminsys
|
||||||
|
return if Rails.application.secrets.adminsys_email.blank?
|
||||||
|
|
||||||
|
User.find_by('lower(email) = ?', Rails.application.secrets.adminsys_email&.downcase)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -157,7 +157,8 @@ class Setting < ApplicationRecord
|
|||||||
store_module
|
store_module
|
||||||
store_withdrawal_instructions
|
store_withdrawal_instructions
|
||||||
store_hidden
|
store_hidden
|
||||||
advanced_accounting] }
|
advanced_accounting
|
||||||
|
external_id] }
|
||||||
# WARNING: when adding a new key, you may also want to add it in:
|
# WARNING: when adding a new key, you may also want to add it in:
|
||||||
# - config/locales/en.yml#settings
|
# - config/locales/en.yml#settings
|
||||||
# - app/frontend/src/javascript/models/setting.ts#SettingName
|
# - app/frontend/src/javascript/models/setting.ts#SettingName
|
||||||
|
@ -7,6 +7,8 @@ class User < ApplicationRecord
|
|||||||
include NotifyWith::NotificationAttachedObject
|
include NotifyWith::NotificationAttachedObject
|
||||||
|
|
||||||
include SingleSignOnConcern
|
include SingleSignOnConcern
|
||||||
|
include UserRoleConcern
|
||||||
|
include UserRessourcesConcern
|
||||||
# Include default devise modules. Others available are:
|
# Include default devise modules. Others available are:
|
||||||
# :lockable, :timeoutable and :omniauthable
|
# :lockable, :timeoutable and :omniauthable
|
||||||
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable,
|
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable,
|
||||||
@ -55,6 +57,7 @@ class User < ApplicationRecord
|
|||||||
email&.downcase!
|
email&.downcase!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
before_validation :set_external_id_nil
|
||||||
before_create :assign_default_role
|
before_create :assign_default_role
|
||||||
after_create :init_dependencies
|
after_create :init_dependencies
|
||||||
after_update :update_invoicing_profile, if: :invoicing_data_was_modified?
|
after_update :update_invoicing_profile, if: :invoicing_data_was_modified?
|
||||||
@ -79,6 +82,7 @@ class User < ApplicationRecord
|
|||||||
validate :cgu_must_accept, if: :new_record?
|
validate :cgu_must_accept, if: :new_record?
|
||||||
|
|
||||||
validates :username, presence: true, uniqueness: true, length: { maximum: 30 }
|
validates :username, presence: true, uniqueness: true, length: { maximum: 30 }
|
||||||
|
validates :external_id, uniqueness: true, allow_blank: true
|
||||||
validate :password_complexity
|
validate :password_complexity
|
||||||
|
|
||||||
scope :active, -> { where(is_active: true) }
|
scope :active, -> { where(is_active: true) }
|
||||||
@ -96,104 +100,6 @@ class User < ApplicationRecord
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.admins
|
|
||||||
User.with_role(:admin)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.members
|
|
||||||
User.with_role(:member)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.partners
|
|
||||||
User.with_role(:partner)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.managers
|
|
||||||
User.with_role(:manager)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.admins_and_managers
|
|
||||||
User.with_any_role(:admin, :manager)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.online_payers
|
|
||||||
User.with_any_role(:admin, :manager, :member)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.adminsys
|
|
||||||
return if Rails.application.secrets.adminsys_email.blank?
|
|
||||||
|
|
||||||
User.find_by('lower(email) = ?', Rails.application.secrets.adminsys_email&.downcase)
|
|
||||||
end
|
|
||||||
|
|
||||||
def training_machine?(machine)
|
|
||||||
return true if admin? || manager?
|
|
||||||
|
|
||||||
trainings.map(&:machines).flatten.uniq.include?(machine)
|
|
||||||
end
|
|
||||||
|
|
||||||
def packs?(item)
|
|
||||||
return true if admin?
|
|
||||||
|
|
||||||
PrepaidPackService.user_packs(self, item).count.positive?
|
|
||||||
end
|
|
||||||
|
|
||||||
def next_training_reservation_by_machine(machine)
|
|
||||||
reservations.where(reservable_type: 'Training', reservable_id: machine.trainings.map(&:id))
|
|
||||||
.includes(:slots)
|
|
||||||
.where('slots.start_at>= ?', DateTime.current)
|
|
||||||
.order('slots.start_at': :asc)
|
|
||||||
.references(:slots)
|
|
||||||
.limit(1)
|
|
||||||
.first
|
|
||||||
end
|
|
||||||
|
|
||||||
def subscribed_plan
|
|
||||||
return nil if subscription.nil? || subscription.expired_at < DateTime.current
|
|
||||||
|
|
||||||
subscription.plan
|
|
||||||
end
|
|
||||||
|
|
||||||
def subscription
|
|
||||||
subscriptions.order(:created_at).last
|
|
||||||
end
|
|
||||||
|
|
||||||
def admin?
|
|
||||||
has_role? :admin
|
|
||||||
end
|
|
||||||
|
|
||||||
def member?
|
|
||||||
has_role? :member
|
|
||||||
end
|
|
||||||
|
|
||||||
def manager?
|
|
||||||
has_role? :manager
|
|
||||||
end
|
|
||||||
|
|
||||||
def partner?
|
|
||||||
has_role? :partner
|
|
||||||
end
|
|
||||||
|
|
||||||
def privileged?
|
|
||||||
admin? || manager?
|
|
||||||
end
|
|
||||||
|
|
||||||
def role
|
|
||||||
if admin?
|
|
||||||
'admin'
|
|
||||||
elsif manager?
|
|
||||||
'manager'
|
|
||||||
elsif member?
|
|
||||||
'member'
|
|
||||||
else
|
|
||||||
'other'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def all_projects
|
|
||||||
my_projects.to_a.concat projects
|
|
||||||
end
|
|
||||||
|
|
||||||
def generate_subscription_invoice(operator_profile_id)
|
def generate_subscription_invoice(operator_profile_id)
|
||||||
return unless subscription
|
return unless subscription
|
||||||
|
|
||||||
@ -267,6 +173,10 @@ class User < ApplicationRecord
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def set_external_id_nil
|
||||||
|
self.external_id = nil if external_id.blank?
|
||||||
|
end
|
||||||
|
|
||||||
def assign_default_role
|
def assign_default_role
|
||||||
add_role(:member) if roles.blank?
|
add_role(:member) if roles.blank?
|
||||||
end
|
end
|
||||||
@ -353,6 +263,6 @@ class User < ApplicationRecord
|
|||||||
def password_complexity
|
def password_complexity
|
||||||
return if password.blank? || SecurePassword.is_secured?(password)
|
return if password.blank? || SecurePassword.is_secured?(password)
|
||||||
|
|
||||||
errors.add I18n.t("app.public.common.password_is_too_weak"), I18n.t("app.public.common.password_is_too_weak_explanations")
|
errors.add I18n.t('app.public.common.password_is_too_weak'), I18n.t('app.public.common.password_is_too_weak_explanations')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -42,7 +42,8 @@ class SettingPolicy < ApplicationPolicy
|
|||||||
payment_gateway payzen_endpoint payzen_public_key public_agenda_module renew_pack_threshold statistics_module
|
payment_gateway payzen_endpoint payzen_public_key public_agenda_module renew_pack_threshold statistics_module
|
||||||
pack_only_for_subscription overlapping_categories public_registrations facebook twitter viadeo linkedin instagram
|
pack_only_for_subscription overlapping_categories public_registrations facebook twitter viadeo linkedin instagram
|
||||||
youtube vimeo dailymotion github echosciences pinterest lastfm flickr machines_module user_change_group
|
youtube vimeo dailymotion github echosciences pinterest lastfm flickr machines_module user_change_group
|
||||||
user_validation_required user_validation_required_list store_module store_withdrawal_instructions store_hidden]
|
user_validation_required user_validation_required_list store_module store_withdrawal_instructions store_hidden
|
||||||
|
external_id]
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -27,13 +27,13 @@ class Members::ImportService
|
|||||||
log << user.errors.to_hash unless user.errors.to_hash.empty?
|
log << user.errors.to_hash unless user.errors.to_hash.empty?
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
log << e.to_s
|
log << e.to_s
|
||||||
puts e
|
Rails.logger.error e
|
||||||
puts e.backtrace
|
Rails.logger.debug e.backtrace
|
||||||
end
|
end
|
||||||
rescue ArgumentError => e
|
rescue ArgumentError => e
|
||||||
log << e.to_s
|
log << e.to_s
|
||||||
puts e
|
Rails.logger.error e
|
||||||
puts e.backtrace
|
Rails.logger.debug e.backtrace
|
||||||
end
|
end
|
||||||
log
|
log
|
||||||
end
|
end
|
||||||
@ -52,6 +52,7 @@ class Members::ImportService
|
|||||||
res.merge! hashify(row, 'id')
|
res.merge! hashify(row, 'id')
|
||||||
res.merge! hashify(row, 'username')
|
res.merge! hashify(row, 'username')
|
||||||
res.merge! hashify(row, 'email')
|
res.merge! hashify(row, 'email')
|
||||||
|
res.merge! hashify(row, 'external_id')
|
||||||
res.merge! hashify(row, 'password', value: password)
|
res.merge! hashify(row, 'password', value: password)
|
||||||
res.merge! hashify(row, 'password', key: :password_confirmation, value: password)
|
res.merge! hashify(row, 'password', key: :password_confirmation, value: password)
|
||||||
res.merge! hashify(row, 'allow_contact', value: row['allow_contact'] == 'yes', key: :is_allow_contact)
|
res.merge! hashify(row, 'allow_contact', value: row['allow_contact'] == 'yes', key: :is_allow_contact)
|
||||||
@ -93,26 +94,22 @@ class Members::ImportService
|
|||||||
res.merge! hashify(row, 'softwares', key: :software_mastered)
|
res.merge! hashify(row, 'softwares', key: :software_mastered)
|
||||||
res.merge! hashify(row, 'website')
|
res.merge! hashify(row, 'website')
|
||||||
res.merge! hashify(row, 'job')
|
res.merge! hashify(row, 'job')
|
||||||
res.merge! hashify(row, 'facebook')
|
res.merge! social_networks(row)
|
||||||
res.merge! hashify(row, 'twitter')
|
|
||||||
res.merge! hashify(row, 'googleplus', key: :google_plus)
|
|
||||||
res.merge! hashify(row, 'viadeo')
|
|
||||||
res.merge! hashify(row, 'linkedin')
|
|
||||||
res.merge! hashify(row, 'instagram')
|
|
||||||
res.merge! hashify(row, 'youtube')
|
|
||||||
res.merge! hashify(row, 'vimeo')
|
|
||||||
res.merge! hashify(row, 'dailymotion')
|
|
||||||
res.merge! hashify(row, 'github')
|
|
||||||
res.merge! hashify(row, 'echosciences')
|
|
||||||
res.merge! hashify(row, 'pinterest')
|
|
||||||
res.merge! hashify(row, 'lastfm')
|
|
||||||
res.merge! hashify(row, 'flickr')
|
|
||||||
|
|
||||||
res[:id] = user.profile.id if user&.profile
|
res[:id] = user.profile.id if user&.profile
|
||||||
|
|
||||||
res
|
res
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def social_networks(row)
|
||||||
|
res = {}
|
||||||
|
networks = %w[facebook twitter viadeo linkedin instagram youtube vimeo dailymotion github echosciences pinterest lastfm flickr]
|
||||||
|
networks.each do |network|
|
||||||
|
res.merge! hashify(row, network)
|
||||||
|
end
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
def invoicing_profile(row, user)
|
def invoicing_profile(row, user)
|
||||||
res = {}
|
res = {}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
json.extract! member, :id, :username, :email, :group_id
|
json.extract! member, :id, :username, :email, :group_id, :external_id
|
||||||
json.role member.roles.first.name
|
json.role member.roles.first.name
|
||||||
json.name member.profile.full_name
|
json.name member.profile.full_name
|
||||||
json.need_completion member.need_completion?
|
json.need_completion member.need_completion?
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
json.extract! user, :id, :email, :created_at
|
# frozen_string_literal: true
|
||||||
|
|
||||||
if user.association(:profile).loaded?
|
json.extract! user, :id, :email, :created_at, :external_id
|
||||||
json.full_name user.profile.full_name
|
|
||||||
end
|
json.full_name user.profile.full_name if user.association(:profile).loaded?
|
||||||
|
|
||||||
if user.association(:group).loaded?
|
if user.association(:group).loaded?
|
||||||
json.group do
|
json.group do
|
||||||
|
@ -1614,6 +1614,9 @@ en:
|
|||||||
address: "Address"
|
address: "Address"
|
||||||
address_required_info_html: "You can define if the address should be required to register a new user on Fab-manager.<br/><strong>Please note</strong> that, depending on your country, the regulations may requires addresses for the invoices to be valid."
|
address_required_info_html: "You can define if the address should be required to register a new user on Fab-manager.<br/><strong>Please note</strong> that, depending on your country, the regulations may requires addresses for the invoices to be valid."
|
||||||
address_is_required: "Address is required"
|
address_is_required: "Address is required"
|
||||||
|
external_id: "External identifier"
|
||||||
|
external_id_info_html: "You can set up an external identifier for your users which cannot be modified by the user himself."
|
||||||
|
enable_external_id: "Enable the external ID"
|
||||||
captcha: "Captcha"
|
captcha: "Captcha"
|
||||||
captcha_info_html: "You can setup a protection against robots, to prevent them creating members accounts. This protection is using Google reCAPTCHA. Sign up for <a href='http://www.google.com/recaptcha/admin' target='_blank'>an API key pair</a> to start using the captcha."
|
captcha_info_html: "You can setup a protection against robots, to prevent them creating members accounts. This protection is using Google reCAPTCHA. Sign up for <a href='http://www.google.com/recaptcha/admin' target='_blank'>an API key pair</a> to start using the captcha."
|
||||||
site_key: "Site key"
|
site_key: "Site key"
|
||||||
|
@ -68,6 +68,7 @@ en:
|
|||||||
declare_organization: "I declare to be an organization"
|
declare_organization: "I declare to be an organization"
|
||||||
declare_organization_help: "If you declare to be an organization, your invoices will be issued in the name of the organization."
|
declare_organization_help: "If you declare to be an organization, your invoices will be issued in the name of the organization."
|
||||||
pseudonym: "Nickname"
|
pseudonym: "Nickname"
|
||||||
|
external_id: "External identifier"
|
||||||
first_name: "First name"
|
first_name: "First name"
|
||||||
surname: "Surname"
|
surname: "Surname"
|
||||||
email_address: "Email address"
|
email_address: "Email address"
|
||||||
|
@ -624,3 +624,4 @@ en:
|
|||||||
store_withdrawal_instructions: "Withdrawal instructions"
|
store_withdrawal_instructions: "Withdrawal instructions"
|
||||||
store_hidden: "Store hidden to the public"
|
store_hidden: "Store hidden to the public"
|
||||||
advanced_accounting: "Advanced accounting"
|
advanced_accounting: "Advanced accounting"
|
||||||
|
external_id: "external identifier"
|
||||||
|
9
db/migrate/20221206100225_add_external_id_to_user.rb
Normal file
9
db/migrate/20221206100225_add_external_id_to_user.rb
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# From this migration users can be identified by an unique external ID
|
||||||
|
class AddExternalIdToUser < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
add_column :users, :external_id, :string, null: true
|
||||||
|
add_index :users, :external_id, unique: true, where: '(external_id IS NOT NULL)', name: 'unique_not_null_external_id'
|
||||||
|
end
|
||||||
|
end
|
22
db/schema.rb
22
db/schema.rb
@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 2022_11_22_123605) do
|
ActiveRecord::Schema.define(version: 2022_12_06_100225) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "fuzzystrmatch"
|
enable_extension "fuzzystrmatch"
|
||||||
@ -19,8 +19,8 @@ ActiveRecord::Schema.define(version: 2022_11_22_123605) do
|
|||||||
enable_extension "unaccent"
|
enable_extension "unaccent"
|
||||||
|
|
||||||
create_table "abuses", id: :serial, force: :cascade do |t|
|
create_table "abuses", id: :serial, force: :cascade do |t|
|
||||||
t.string "signaled_type"
|
|
||||||
t.integer "signaled_id"
|
t.integer "signaled_id"
|
||||||
|
t.string "signaled_type"
|
||||||
t.string "first_name"
|
t.string "first_name"
|
||||||
t.string "last_name"
|
t.string "last_name"
|
||||||
t.string "email"
|
t.string "email"
|
||||||
@ -68,8 +68,8 @@ ActiveRecord::Schema.define(version: 2022_11_22_123605) do
|
|||||||
t.string "locality"
|
t.string "locality"
|
||||||
t.string "country"
|
t.string "country"
|
||||||
t.string "postal_code"
|
t.string "postal_code"
|
||||||
t.string "placeable_type"
|
|
||||||
t.integer "placeable_id"
|
t.integer "placeable_id"
|
||||||
|
t.string "placeable_type"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
end
|
end
|
||||||
@ -93,8 +93,8 @@ ActiveRecord::Schema.define(version: 2022_11_22_123605) do
|
|||||||
end
|
end
|
||||||
|
|
||||||
create_table "assets", id: :serial, force: :cascade do |t|
|
create_table "assets", id: :serial, force: :cascade do |t|
|
||||||
t.string "viewable_type"
|
|
||||||
t.integer "viewable_id"
|
t.integer "viewable_id"
|
||||||
|
t.string "viewable_type"
|
||||||
t.string "attachment"
|
t.string "attachment"
|
||||||
t.string "type"
|
t.string "type"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
@ -176,8 +176,8 @@ ActiveRecord::Schema.define(version: 2022_11_22_123605) do
|
|||||||
end
|
end
|
||||||
|
|
||||||
create_table "credits", id: :serial, force: :cascade do |t|
|
create_table "credits", id: :serial, force: :cascade do |t|
|
||||||
t.string "creditable_type"
|
|
||||||
t.integer "creditable_id"
|
t.integer "creditable_id"
|
||||||
|
t.string "creditable_type"
|
||||||
t.integer "plan_id"
|
t.integer "plan_id"
|
||||||
t.integer "hours"
|
t.integer "hours"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
@ -406,15 +406,15 @@ ActiveRecord::Schema.define(version: 2022_11_22_123605) do
|
|||||||
|
|
||||||
create_table "notifications", id: :serial, force: :cascade do |t|
|
create_table "notifications", id: :serial, force: :cascade do |t|
|
||||||
t.integer "receiver_id"
|
t.integer "receiver_id"
|
||||||
t.string "attached_object_type"
|
|
||||||
t.integer "attached_object_id"
|
t.integer "attached_object_id"
|
||||||
|
t.string "attached_object_type"
|
||||||
t.integer "notification_type_id"
|
t.integer "notification_type_id"
|
||||||
t.boolean "is_read", default: false
|
t.boolean "is_read", default: false
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.string "receiver_type"
|
t.string "receiver_type"
|
||||||
t.boolean "is_send", default: false
|
t.boolean "is_send", default: false
|
||||||
t.jsonb "meta_data", default: "{}"
|
t.jsonb "meta_data", default: {}
|
||||||
t.index ["notification_type_id"], name: "index_notifications_on_notification_type_id"
|
t.index ["notification_type_id"], name: "index_notifications_on_notification_type_id"
|
||||||
t.index ["receiver_id"], name: "index_notifications_on_receiver_id"
|
t.index ["receiver_id"], name: "index_notifications_on_receiver_id"
|
||||||
end
|
end
|
||||||
@ -654,8 +654,8 @@ ActiveRecord::Schema.define(version: 2022_11_22_123605) do
|
|||||||
create_table "prices", id: :serial, force: :cascade do |t|
|
create_table "prices", id: :serial, force: :cascade do |t|
|
||||||
t.integer "group_id"
|
t.integer "group_id"
|
||||||
t.integer "plan_id"
|
t.integer "plan_id"
|
||||||
t.string "priceable_type"
|
|
||||||
t.integer "priceable_id"
|
t.integer "priceable_id"
|
||||||
|
t.string "priceable_type"
|
||||||
t.integer "amount"
|
t.integer "amount"
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
@ -855,8 +855,8 @@ ActiveRecord::Schema.define(version: 2022_11_22_123605) do
|
|||||||
t.text "message"
|
t.text "message"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.string "reservable_type"
|
|
||||||
t.integer "reservable_id"
|
t.integer "reservable_id"
|
||||||
|
t.string "reservable_type"
|
||||||
t.integer "nb_reserve_places"
|
t.integer "nb_reserve_places"
|
||||||
t.integer "statistic_profile_id"
|
t.integer "statistic_profile_id"
|
||||||
t.index ["reservable_type", "reservable_id"], name: "index_reservations_on_reservable_type_and_reservable_id"
|
t.index ["reservable_type", "reservable_id"], name: "index_reservations_on_reservable_type_and_reservable_id"
|
||||||
@ -865,8 +865,8 @@ ActiveRecord::Schema.define(version: 2022_11_22_123605) do
|
|||||||
|
|
||||||
create_table "roles", id: :serial, force: :cascade do |t|
|
create_table "roles", id: :serial, force: :cascade do |t|
|
||||||
t.string "name"
|
t.string "name"
|
||||||
t.string "resource_type"
|
|
||||||
t.integer "resource_id"
|
t.integer "resource_id"
|
||||||
|
t.string "resource_type"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.index ["name", "resource_type", "resource_id"], name: "index_roles_on_name_and_resource_type_and_resource_id"
|
t.index ["name", "resource_type", "resource_id"], name: "index_roles_on_name_and_resource_type_and_resource_id"
|
||||||
@ -1150,9 +1150,11 @@ ActiveRecord::Schema.define(version: 2022_11_22_123605) do
|
|||||||
t.inet "last_sign_in_ip"
|
t.inet "last_sign_in_ip"
|
||||||
t.string "mapped_from_sso"
|
t.string "mapped_from_sso"
|
||||||
t.datetime "validated_at"
|
t.datetime "validated_at"
|
||||||
|
t.string "external_id"
|
||||||
t.index ["auth_token"], name: "index_users_on_auth_token"
|
t.index ["auth_token"], name: "index_users_on_auth_token"
|
||||||
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
|
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
|
||||||
t.index ["email"], name: "index_users_on_email", unique: true
|
t.index ["email"], name: "index_users_on_email", unique: true
|
||||||
|
t.index ["external_id"], name: "unique_not_null_external_id", unique: true, where: "(external_id IS NOT NULL)"
|
||||||
t.index ["group_id"], name: "index_users_on_group_id"
|
t.index ["group_id"], name: "index_users_on_group_id"
|
||||||
t.index ["provider"], name: "index_users_on_provider"
|
t.index ["provider"], name: "index_users_on_provider"
|
||||||
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
|
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
|
||||||
|
@ -997,6 +997,8 @@ Setting.set('advanced_accounting', false) unless Setting.find_by(name: 'advanced
|
|||||||
|
|
||||||
Setting.set('accounting_VAT_code', '4457') unless Setting.find_by(name: 'accounting_VAT_code').try(:value)
|
Setting.set('accounting_VAT_code', '4457') unless Setting.find_by(name: 'accounting_VAT_code').try(:value)
|
||||||
|
|
||||||
|
Setting.set('external_id', false) unless Setting.find_by(name: 'external_id').try(:value)
|
||||||
|
|
||||||
if StatisticCustomAggregation.count.zero?
|
if StatisticCustomAggregation.count.zero?
|
||||||
# available reservations hours for machines
|
# available reservations hours for machines
|
||||||
machine_hours = StatisticType.find_by(key: 'hour', statistic_index_id: 2)
|
machine_hours = StatisticType.find_by(key: 'hour', statistic_index_id: 2)
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
id;gender;first_name;last_name;username;email;password;birthdate;address;phone;group;tags;trainings;website;job;interests;softwares;allow_contact;allow_newsletter;organization_name;organization_address;facebook;twitter;googleplus;viadeo;linkedin;instagram;youtube;vimeo;dailymotion;github;echosciences;pinterest;lastfm;flickr
|
id;gender;first_name;last_name;username;email;password;external_id;birthdate;address;phone;group;tags;trainings;website;job;interests;softwares;allow_contact;allow_newsletter;organization_name;organization_address;facebook;twitter;viadeo;linkedin;instagram;youtube;vimeo;dailymotion;github;echosciences;pinterest;lastfm;flickr
|
||||||
;male;jean;dupont;jdupont;jean.dupont@gmail.com;;1970-01-01;12 bvd Libération - 75000 Paris;0123456789;standard;1,2;1;http://www.example.com;Charpentier;Ping-pong;AutoCAD;yes;no;;;http://www.facebook.com/jdupont;;;;;;;;;http://github.com/example;;;;
|
;male;jean;dupont;jdupont;jean.dupont@gmail.com;;JD84401;1970-01-01;12 bvd Libération - 75000 Paris;123456789;standard;1,2;1;http://www.example.com;Charpentier;Ping-pong;AutoCAD;yes;no;;;http://www.facebook.com/jdupont;;;;;;;;http://github.com/example;;;;
|
||||||
43;;;;;;newpassword
|
43;;;;;;newP@ssword5;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
10
test/fixtures/users.yml
vendored
10
test/fixtures/users.yml
vendored
@ -29,6 +29,7 @@ user_1:
|
|||||||
auth_token:
|
auth_token:
|
||||||
merged_at:
|
merged_at:
|
||||||
is_allow_newsletter: true
|
is_allow_newsletter: true
|
||||||
|
external_id: J5821-4
|
||||||
|
|
||||||
user_2:
|
user_2:
|
||||||
id: 2
|
id: 2
|
||||||
@ -61,6 +62,7 @@ user_2:
|
|||||||
auth_token:
|
auth_token:
|
||||||
merged_at:
|
merged_at:
|
||||||
is_allow_newsletter: true
|
is_allow_newsletter: true
|
||||||
|
external_id: J5846-4
|
||||||
|
|
||||||
user_3:
|
user_3:
|
||||||
id: 3
|
id: 3
|
||||||
@ -93,6 +95,7 @@ user_3:
|
|||||||
auth_token:
|
auth_token:
|
||||||
merged_at:
|
merged_at:
|
||||||
is_allow_newsletter: false
|
is_allow_newsletter: false
|
||||||
|
external_id: J5900-1
|
||||||
|
|
||||||
user_4:
|
user_4:
|
||||||
id: 4
|
id: 4
|
||||||
@ -125,6 +128,7 @@ user_4:
|
|||||||
auth_token:
|
auth_token:
|
||||||
merged_at:
|
merged_at:
|
||||||
is_allow_newsletter: false
|
is_allow_newsletter: false
|
||||||
|
external_id: P4172-4
|
||||||
|
|
||||||
user_5:
|
user_5:
|
||||||
id: 5
|
id: 5
|
||||||
@ -157,6 +161,7 @@ user_5:
|
|||||||
auth_token:
|
auth_token:
|
||||||
merged_at:
|
merged_at:
|
||||||
is_allow_newsletter: true
|
is_allow_newsletter: true
|
||||||
|
external_id: J5500-4
|
||||||
|
|
||||||
user_6:
|
user_6:
|
||||||
id: 6
|
id: 6
|
||||||
@ -189,6 +194,7 @@ user_6:
|
|||||||
auth_token:
|
auth_token:
|
||||||
merged_at:
|
merged_at:
|
||||||
is_allow_newsletter: true
|
is_allow_newsletter: true
|
||||||
|
external_id:
|
||||||
|
|
||||||
user_7:
|
user_7:
|
||||||
id: 7
|
id: 7
|
||||||
@ -221,6 +227,7 @@ user_7:
|
|||||||
auth_token:
|
auth_token:
|
||||||
merged_at:
|
merged_at:
|
||||||
is_allow_newsletter: false
|
is_allow_newsletter: false
|
||||||
|
external_id:
|
||||||
|
|
||||||
user_8:
|
user_8:
|
||||||
id: 8
|
id: 8
|
||||||
@ -253,6 +260,7 @@ user_8:
|
|||||||
auth_token:
|
auth_token:
|
||||||
merged_at:
|
merged_at:
|
||||||
is_allow_newsletter: false
|
is_allow_newsletter: false
|
||||||
|
external_id:
|
||||||
|
|
||||||
user_9:
|
user_9:
|
||||||
id: 9
|
id: 9
|
||||||
@ -285,6 +293,7 @@ user_9:
|
|||||||
auth_token:
|
auth_token:
|
||||||
merged_at:
|
merged_at:
|
||||||
is_allow_newsletter: true
|
is_allow_newsletter: true
|
||||||
|
external_id:
|
||||||
|
|
||||||
user_10:
|
user_10:
|
||||||
id: 10
|
id: 10
|
||||||
@ -317,3 +326,4 @@ user_10:
|
|||||||
auth_token:
|
auth_token:
|
||||||
merged_at:
|
merged_at:
|
||||||
is_allow_newsletter: true
|
is_allow_newsletter: true
|
||||||
|
external_id:
|
||||||
|
@ -12,6 +12,10 @@ class OpenApi::UsersTest < ActionDispatch::IntegrationTest
|
|||||||
test 'list all users' do
|
test 'list all users' do
|
||||||
get '/open_api/v1/users', headers: open_api_headers(@token)
|
get '/open_api/v1/users', headers: open_api_headers(@token)
|
||||||
assert_response :success
|
assert_response :success
|
||||||
|
assert_equal Mime[:json], response.content_type
|
||||||
|
|
||||||
|
users = json_response(response.body)
|
||||||
|
assert_not_nil(users[:users].detect { |u| u[:external_id] == 'J5821-4' })
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'list all users with pagination' do
|
test 'list all users with pagination' do
|
||||||
|
Loading…
Reference in New Issue
Block a user