mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-01 21:52:19 +01:00
(feat) add supporting document for child
This commit is contained in:
parent
b473a7cd35
commit
ecc4fde47f
@ -50,6 +50,9 @@ class API::ChildrenController < API::APIController
|
||||
end
|
||||
|
||||
def child_params
|
||||
params.require(:child).permit(:first_name, :last_name, :email, :phone, :birthday, :user_id)
|
||||
params.require(:child).permit(:first_name, :last_name, :email, :phone, :birthday, :user_id,
|
||||
supporting_document_files_attributes: %i[id supportable_id supportable_type
|
||||
supporting_document_type_id
|
||||
attachment _destroy])
|
||||
end
|
||||
end
|
||||
|
@ -48,6 +48,6 @@ class API::SupportingDocumentFilesController < API::APIController
|
||||
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def supporting_document_file_params
|
||||
params.required(:supporting_document_file).permit(:supporting_document_type_id, :attachment, :user_id)
|
||||
params.required(:supporting_document_file).permit(:supporting_document_type_id, :attachment, :supportable_id, :supportable_type)
|
||||
end
|
||||
end
|
||||
|
@ -27,6 +27,7 @@ class API::SupportingDocumentRefusalsController < API::APIController
|
||||
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def supporting_document_refusal_params
|
||||
params.required(:supporting_document_refusal).permit(:message, :operator_id, :user_id, supporting_document_type_ids: [])
|
||||
params.required(:supporting_document_refusal).permit(:message, :operator_id, :supportable_id, :supportable_type,
|
||||
supporting_document_type_ids: [])
|
||||
end
|
||||
end
|
||||
|
@ -45,6 +45,6 @@ class API::SupportingDocumentTypesController < API::APIController
|
||||
end
|
||||
|
||||
def supporting_document_type_params
|
||||
params.require(:supporting_document_type).permit(:name, group_ids: [])
|
||||
params.require(:supporting_document_type).permit(:name, :document_type, group_ids: [])
|
||||
end
|
||||
end
|
||||
|
@ -15,12 +15,22 @@ export default class ChildAPI {
|
||||
}
|
||||
|
||||
static async create (child: Child): Promise<Child> {
|
||||
const res: AxiosResponse<Child> = await apiClient.post('/api/children', { child });
|
||||
const data = ApiLib.serializeAttachments(child, 'child', ['supporting_document_files_attributes']);
|
||||
const res: AxiosResponse<Child> = await apiClient.post('/api/children', data, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
return res?.data;
|
||||
}
|
||||
|
||||
static async update (child: Child): Promise<Child> {
|
||||
const res: AxiosResponse<Child> = await apiClient.patch(`/api/children/${child.id}`, { child });
|
||||
const data = ApiLib.serializeAttachments(child, 'child', ['supporting_document_files_attributes']);
|
||||
const res: AxiosResponse<Child> = await apiClient.put(`/api/children/${child.id}`, data, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
return res?.data;
|
||||
}
|
||||
|
||||
|
@ -1,33 +1,34 @@
|
||||
import React from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useForm, useWatch } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import moment from 'moment';
|
||||
import { Child } from '../../models/child';
|
||||
import { TDateISODate } from '../../typings/date-iso';
|
||||
import { FormInput } from '../form/form-input';
|
||||
import { FabButton } from '../base/fab-button';
|
||||
import { FormFileUpload } from '../form/form-file-upload';
|
||||
import { FileType } from '../../models/file';
|
||||
import { SupportingDocumentType } from '../../models/supporting-document-type';
|
||||
|
||||
interface ChildFormProps {
|
||||
child: Child;
|
||||
onChange: (field: string, value: string | TDateISODate) => void;
|
||||
onSubmit: (data: Child) => void;
|
||||
supportingDocumentsTypes: Array<SupportingDocumentType>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A form for creating or editing a child.
|
||||
*/
|
||||
export const ChildForm: React.FC<ChildFormProps> = ({ child, onChange, onSubmit }) => {
|
||||
export const ChildForm: React.FC<ChildFormProps> = ({ child, onSubmit, supportingDocumentsTypes }) => {
|
||||
const { t } = useTranslation('public');
|
||||
|
||||
const { register, formState, handleSubmit } = useForm<Child>({
|
||||
const { register, formState, handleSubmit, setValue, control } = useForm<Child>({
|
||||
defaultValues: child
|
||||
});
|
||||
const output = useWatch<Child>({ control }); // eslint-disable-line
|
||||
|
||||
/**
|
||||
* Handle the change of a child form field
|
||||
*/
|
||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
onChange(event.target.id, event.target.value);
|
||||
const getSupportingDocumentsTypeName = (id: number): string => {
|
||||
const supportingDocumentType = supportingDocumentsTypes.find((supportingDocumentType) => supportingDocumentType.id === id);
|
||||
return supportingDocumentType ? supportingDocumentType.name : '';
|
||||
};
|
||||
|
||||
return (
|
||||
@ -41,14 +42,12 @@ export const ChildForm: React.FC<ChildFormProps> = ({ child, onChange, onSubmit
|
||||
rules={{ required: true }}
|
||||
formState={formState}
|
||||
label={t('app.public.child_form.first_name')}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<FormInput id="last_name"
|
||||
register={register}
|
||||
rules={{ required: true }}
|
||||
formState={formState}
|
||||
label={t('app.public.child_form.last_name')}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<FormInput id="birthday"
|
||||
register={register}
|
||||
@ -57,22 +56,31 @@ export const ChildForm: React.FC<ChildFormProps> = ({ child, onChange, onSubmit
|
||||
label={t('app.public.child_form.birthday')}
|
||||
type="date"
|
||||
max={moment().subtract(18, 'year').format('YYYY-MM-DD')}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<FormInput id="phone"
|
||||
register={register}
|
||||
formState={formState}
|
||||
label={t('app.public.child_form.phone')}
|
||||
onChange={handleChange}
|
||||
type="tel"
|
||||
/>
|
||||
<FormInput id="email"
|
||||
register={register}
|
||||
rules={{ required: true }}
|
||||
formState={formState}
|
||||
label={t('app.public.child_form.email')}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
{output.supporting_document_files_attributes.map((sf, index) => {
|
||||
return (
|
||||
<FormFileUpload key={index}
|
||||
defaultFile={sf as FileType}
|
||||
id={`supporting_document_files_attributes.${index}`}
|
||||
accept="application/pdf"
|
||||
setValue={setValue}
|
||||
label={getSupportingDocumentsTypeName(sf.supporting_document_type_id)}
|
||||
showRemoveButton={false}
|
||||
register={register}
|
||||
formState={formState} />
|
||||
);
|
||||
})}
|
||||
|
||||
<div className="actions">
|
||||
<FabButton type="button" onClick={handleSubmit(onSubmit)}>
|
||||
|
@ -1,11 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FabModal, ModalSize } from '../base/fab-modal';
|
||||
import { Child } from '../../models/child';
|
||||
import { TDateISODate } from '../../typings/date-iso';
|
||||
import ChildAPI from '../../api/child';
|
||||
import { ChildForm } from './child-form';
|
||||
import { SupportingDocumentType } from '../../models/supporting-document-type';
|
||||
|
||||
interface ChildModalProps {
|
||||
child?: Child;
|
||||
@ -13,23 +12,19 @@ interface ChildModalProps {
|
||||
toggleModal: () => void;
|
||||
onSuccess: (child: Child) => void;
|
||||
onError: (error: string) => void;
|
||||
supportingDocumentsTypes: Array<SupportingDocumentType>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A modal for creating or editing a child.
|
||||
*/
|
||||
export const ChildModal: React.FC<ChildModalProps> = ({ child, isOpen, toggleModal, onSuccess, onError }) => {
|
||||
export const ChildModal: React.FC<ChildModalProps> = ({ child, isOpen, toggleModal, onSuccess, onError, supportingDocumentsTypes }) => {
|
||||
const { t } = useTranslation('public');
|
||||
const [data, setData] = useState<Child>(child);
|
||||
|
||||
useEffect(() => {
|
||||
setData(child);
|
||||
}, [child]);
|
||||
|
||||
/**
|
||||
* Save the child to the API
|
||||
*/
|
||||
const handleSaveChild = async (): Promise<void> => {
|
||||
const handleSaveChild = async (data: Child): Promise<void> => {
|
||||
try {
|
||||
if (child?.id) {
|
||||
await ChildAPI.update(data);
|
||||
@ -43,25 +38,14 @@ export const ChildModal: React.FC<ChildModalProps> = ({ child, isOpen, toggleMod
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the change of a child form field
|
||||
*/
|
||||
const handleChildChanged = (field: string, value: string | TDateISODate): void => {
|
||||
setData({
|
||||
...data,
|
||||
[field]: value
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<FabModal title={t(`app.public.child_modal.${child?.id ? 'edit' : 'new'}_child`)}
|
||||
width={ModalSize.large}
|
||||
isOpen={isOpen}
|
||||
toggleModal={toggleModal}
|
||||
closeButton={true}
|
||||
confirmButton={false}
|
||||
onConfirm={handleSaveChild} >
|
||||
<ChildForm child={child} onChange={handleChildChanged} onSubmit={handleSaveChild} />
|
||||
confirmButton={false} >
|
||||
<ChildForm child={child} onSubmit={handleSaveChild} supportingDocumentsTypes={supportingDocumentsTypes}/>
|
||||
</FabModal>
|
||||
);
|
||||
};
|
||||
|
@ -9,6 +9,8 @@ import { IApplication } from '../../models/application';
|
||||
import { ChildModal } from './child-modal';
|
||||
import { ChildItem } from './child-item';
|
||||
import { FabButton } from '../base/fab-button';
|
||||
import { SupportingDocumentType } from '../../models/supporting-document-type';
|
||||
import SupportingDocumentTypeAPI from '../../api/supporting-document-type';
|
||||
|
||||
declare const Application: IApplication;
|
||||
|
||||
@ -27,9 +29,13 @@ export const ChildrenList: React.FC<ChildrenListProps> = ({ currentUser, onError
|
||||
const [children, setChildren] = useState<Array<Child>>([]);
|
||||
const [isOpenChildModal, setIsOpenChildModal] = useState<boolean>(false);
|
||||
const [child, setChild] = useState<Child>();
|
||||
const [supportingDocumentsTypes, setSupportingDocumentsTypes] = useState<Array<SupportingDocumentType>>([]);
|
||||
|
||||
useEffect(() => {
|
||||
ChildAPI.index({ user_id: currentUser.id }).then(setChildren);
|
||||
SupportingDocumentTypeAPI.index({ document_type: 'Child' }).then(tData => {
|
||||
setSupportingDocumentsTypes(tData);
|
||||
});
|
||||
}, [currentUser]);
|
||||
|
||||
/**
|
||||
@ -37,7 +43,12 @@ export const ChildrenList: React.FC<ChildrenListProps> = ({ currentUser, onError
|
||||
*/
|
||||
const addChild = () => {
|
||||
setIsOpenChildModal(true);
|
||||
setChild({ user_id: currentUser.id } as Child);
|
||||
setChild({
|
||||
user_id: currentUser.id,
|
||||
supporting_document_files_attributes: supportingDocumentsTypes.map(t => {
|
||||
return { supporting_document_type_id: t.id };
|
||||
})
|
||||
} as Child);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -45,7 +56,13 @@ export const ChildrenList: React.FC<ChildrenListProps> = ({ currentUser, onError
|
||||
*/
|
||||
const editChild = (child: Child) => {
|
||||
setIsOpenChildModal(true);
|
||||
setChild(child);
|
||||
setChild({
|
||||
...child,
|
||||
supporting_document_files_attributes: supportingDocumentsTypes.map(t => {
|
||||
const file = child.supporting_document_files_attributes.find(f => f.supporting_document_type_id === t.id);
|
||||
return file || { supporting_document_type_id: t.id };
|
||||
})
|
||||
} as Child);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -78,7 +95,7 @@ export const ChildrenList: React.FC<ChildrenListProps> = ({ currentUser, onError
|
||||
<ChildItem key={child.id} child={child} onEdit={editChild} onDelete={deleteChild} />
|
||||
))}
|
||||
</div>
|
||||
<ChildModal child={child} isOpen={isOpenChildModal} toggleModal={() => setIsOpenChildModal(false)} onSuccess={handleSaveChildSuccess} onError={onError} />
|
||||
<ChildModal child={child} isOpen={isOpenChildModal} toggleModal={() => setIsOpenChildModal(false)} onSuccess={handleSaveChildSuccess} onError={onError} supportingDocumentsTypes={supportingDocumentsTypes} />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
@ -19,12 +19,13 @@ type FormFileUploadProps<TFieldValues> = FormComponent<TFieldValues> & AbstractF
|
||||
accept?: string,
|
||||
onFileChange?: (value: FileType) => void,
|
||||
onFileRemove?: () => void,
|
||||
showRemoveButton?: boolean,
|
||||
}
|
||||
|
||||
/**
|
||||
* This component allows to upload file, in forms managed by react-hook-form.
|
||||
*/
|
||||
export const FormFileUpload = <TFieldValues extends FieldValues>({ id, label, register, defaultFile, className, rules, disabled, error, warning, formState, onFileChange, onFileRemove, accept, setValue }: FormFileUploadProps<TFieldValues>) => {
|
||||
export const FormFileUpload = <TFieldValues extends FieldValues>({ id, label, register, defaultFile, className, rules, disabled, error, warning, formState, onFileChange, onFileRemove, accept, setValue, showRemoveButton = true }: FormFileUploadProps<TFieldValues>) => {
|
||||
const { t } = useTranslation('shared');
|
||||
|
||||
const [file, setFile] = useState<FileType>(defaultFile);
|
||||
@ -100,7 +101,7 @@ export const FormFileUpload = <TFieldValues extends FieldValues>({ id, label, re
|
||||
id={`${id}[attachment_files]`}
|
||||
onChange={onFileSelected}
|
||||
placeholder={placeholder()}/>
|
||||
{hasFile() &&
|
||||
{showRemoveButton && hasFile() &&
|
||||
<FabButton onClick={onRemoveFile} icon={<Trash size={20} weight="fill" />} className="is-main" />
|
||||
}
|
||||
</div>
|
||||
|
@ -49,7 +49,7 @@ export const SupportingDocumentsFiles: React.FC<SupportingDocumentsFilesProps> =
|
||||
SupportingDocumentTypeAPI.index({ group_id: currentUser.group_id }).then(tData => {
|
||||
setSupportingDocumentsTypes(tData);
|
||||
});
|
||||
SupportingDocumentFileAPI.index({ user_id: currentUser.id }).then(fData => {
|
||||
SupportingDocumentFileAPI.index({ supportable_id: currentUser.id, supportable_type: 'User' }).then(fData => {
|
||||
setSupportingDocumentsFiles(fData);
|
||||
});
|
||||
}, []);
|
||||
@ -106,7 +106,8 @@ export const SupportingDocumentsFiles: React.FC<SupportingDocumentsFilesProps> =
|
||||
for (const proofOfIdentityTypeId of Object.keys(files)) {
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append('supporting_document_file[user_id]', currentUser.id.toString());
|
||||
formData.append('supporting_document_file[supportable_id]', currentUser.id.toString());
|
||||
formData.append('supporting_document_file[supportable_type]', 'User');
|
||||
formData.append('supporting_document_file[supporting_document_type_id]', proofOfIdentityTypeId);
|
||||
formData.append('supporting_document_file[attachment]', files[proofOfIdentityTypeId]);
|
||||
const proofOfIdentityFile = getSupportingDocumentsFileByType(parseInt(proofOfIdentityTypeId, 10));
|
||||
@ -117,7 +118,7 @@ export const SupportingDocumentsFiles: React.FC<SupportingDocumentsFilesProps> =
|
||||
}
|
||||
}
|
||||
if (Object.keys(files).length > 0) {
|
||||
SupportingDocumentFileAPI.index({ user_id: currentUser.id }).then(fData => {
|
||||
SupportingDocumentFileAPI.index({ supportable_id: currentUser.id, supportable_type: 'User' }).then(fData => {
|
||||
setSupportingDocumentsFiles(fData);
|
||||
setFiles({});
|
||||
onSuccess(t('app.logged.dashboard.supporting_documents_files.file_successfully_uploaded'));
|
||||
|
@ -27,7 +27,8 @@ export const SupportingDocumentsRefusalModal: React.FC<SupportingDocumentsRefusa
|
||||
const [data, setData] = useState<SupportingDocumentRefusal>({
|
||||
id: null,
|
||||
operator_id: operator.id,
|
||||
user_id: member.id,
|
||||
supportable_id: member.id,
|
||||
supportable_type: 'User',
|
||||
supporting_document_type_ids: [],
|
||||
message: ''
|
||||
});
|
||||
|
@ -63,6 +63,7 @@ export const SupportingDocumentsTypeForm: React.FC<SupportingDocumentsTypeFormPr
|
||||
{t('app.admin.settings.account.supporting_documents_type_form.type_form_info')}
|
||||
</div>
|
||||
<form name="supportingDocumentTypeForm">
|
||||
{supportingDocumentType?.document_type === 'User' &&
|
||||
<div className="field">
|
||||
<Select defaultValue={groupsValues()}
|
||||
placeholder={t('app.admin.settings.account.supporting_documents_type_form.select_group')}
|
||||
@ -70,6 +71,7 @@ export const SupportingDocumentsTypeForm: React.FC<SupportingDocumentsTypeFormPr
|
||||
options={buildOptions()}
|
||||
isMulti />
|
||||
</div>
|
||||
}
|
||||
<div className="field">
|
||||
<FabInput id="supporting_document_type_name"
|
||||
icon={<i className="fa fa-edit" />}
|
||||
|
@ -14,18 +14,19 @@ interface SupportingDocumentsTypeModalProps {
|
||||
onError: (message: string) => void,
|
||||
groups: Array<Group>,
|
||||
proofOfIdentityType?: SupportingDocumentType,
|
||||
documentType: 'User' | 'Child',
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal dialog to create/edit a supporting documents type
|
||||
*/
|
||||
export const SupportingDocumentsTypeModal: React.FC<SupportingDocumentsTypeModalProps> = ({ isOpen, toggleModal, onSuccess, onError, proofOfIdentityType, groups }) => {
|
||||
export const SupportingDocumentsTypeModal: React.FC<SupportingDocumentsTypeModalProps> = ({ isOpen, toggleModal, onSuccess, onError, proofOfIdentityType, groups, documentType }) => {
|
||||
const { t } = useTranslation('admin');
|
||||
|
||||
const [data, setData] = useState<SupportingDocumentType>({ id: proofOfIdentityType?.id, group_ids: proofOfIdentityType?.group_ids || [], name: proofOfIdentityType?.name || '' });
|
||||
const [data, setData] = useState<SupportingDocumentType>({ id: proofOfIdentityType?.id, group_ids: proofOfIdentityType?.group_ids || [], name: proofOfIdentityType?.name || '', document_type: documentType });
|
||||
|
||||
useEffect(() => {
|
||||
setData({ id: proofOfIdentityType?.id, group_ids: proofOfIdentityType?.group_ids || [], name: proofOfIdentityType?.name || '' });
|
||||
setData({ id: proofOfIdentityType?.id, group_ids: proofOfIdentityType?.group_ids || [], name: proofOfIdentityType?.name || '', document_type: documentType });
|
||||
}, [proofOfIdentityType]);
|
||||
|
||||
/**
|
||||
@ -63,7 +64,7 @@ export const SupportingDocumentsTypeModal: React.FC<SupportingDocumentsTypeModal
|
||||
* Check if the form is valid (not empty)
|
||||
*/
|
||||
const isPreventedSaveType = (): boolean => {
|
||||
return !data.name || data.group_ids.length === 0;
|
||||
return !data.name || (documentType === 'User' && data.group_ids.length === 0);
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -21,12 +21,13 @@ declare const Application: IApplication;
|
||||
interface SupportingDocumentsTypesListProps {
|
||||
onSuccess: (message: string) => void,
|
||||
onError: (message: string) => void,
|
||||
documentType: 'User' | 'Child',
|
||||
}
|
||||
|
||||
/**
|
||||
* This component shows a list of all types of supporting documents (e.g. student ID, Kbis extract, etc.)
|
||||
*/
|
||||
const SupportingDocumentsTypesList: React.FC<SupportingDocumentsTypesListProps> = ({ onSuccess, onError }) => {
|
||||
const SupportingDocumentsTypesList: React.FC<SupportingDocumentsTypesListProps> = ({ onSuccess, onError, documentType }) => {
|
||||
const { t } = useTranslation('admin');
|
||||
|
||||
// list of displayed supporting documents type
|
||||
@ -48,7 +49,7 @@ const SupportingDocumentsTypesList: React.FC<SupportingDocumentsTypesListProps>
|
||||
useEffect(() => {
|
||||
GroupAPI.index({ disabled: false }).then(data => {
|
||||
setGroups(data);
|
||||
SupportingDocumentTypeAPI.index().then(pData => {
|
||||
SupportingDocumentTypeAPI.index({ document_type: documentType }).then(pData => {
|
||||
setSupportingDocumentsTypes(pData);
|
||||
});
|
||||
});
|
||||
@ -91,7 +92,7 @@ const SupportingDocumentsTypesList: React.FC<SupportingDocumentsTypesListProps>
|
||||
*/
|
||||
const onSaveTypeSuccess = (message: string): void => {
|
||||
setModalIsOpen(false);
|
||||
SupportingDocumentTypeAPI.index().then(pData => {
|
||||
SupportingDocumentTypeAPI.index({ document_type: documentType }).then(pData => {
|
||||
setSupportingDocumentsTypes(orderTypes(pData, supportingDocumentsTypeOrder));
|
||||
onSuccess(message);
|
||||
}).catch((error) => {
|
||||
@ -121,7 +122,7 @@ const SupportingDocumentsTypesList: React.FC<SupportingDocumentsTypesListProps>
|
||||
*/
|
||||
const onDestroySuccess = (message: string): void => {
|
||||
setDestroyModalIsOpen(false);
|
||||
SupportingDocumentTypeAPI.index().then(pData => {
|
||||
SupportingDocumentTypeAPI.index({ document_type: documentType }).then(pData => {
|
||||
setSupportingDocumentsTypes(pData);
|
||||
setSupportingDocumentsTypes(orderTypes(pData, supportingDocumentsTypeOrder));
|
||||
onSuccess(message);
|
||||
@ -190,6 +191,7 @@ const SupportingDocumentsTypesList: React.FC<SupportingDocumentsTypesListProps>
|
||||
window.location.href = '/#!/admin/members?tabs=1';
|
||||
};
|
||||
|
||||
if (documentType === 'User') {
|
||||
return (
|
||||
<FabPanel className="supporting-documents-types-list" header={<div>
|
||||
<span>{t('app.admin.settings.account.supporting_documents_types_list.add_supporting_documents_types')}</span>
|
||||
@ -211,6 +213,7 @@ const SupportingDocumentsTypesList: React.FC<SupportingDocumentsTypesListProps>
|
||||
<SupportingDocumentsTypeModal isOpen={modalIsOpen}
|
||||
groups={groups}
|
||||
proofOfIdentityType={supportingDocumentsType}
|
||||
documentType={documentType}
|
||||
toggleModal={toggleCreateAndEditModal}
|
||||
onSuccess={onSaveTypeSuccess}
|
||||
onError={onError} />
|
||||
@ -267,6 +270,71 @@ const SupportingDocumentsTypesList: React.FC<SupportingDocumentsTypesListProps>
|
||||
</div>
|
||||
</FabPanel>
|
||||
);
|
||||
} else if (documentType === 'Child') {
|
||||
return (
|
||||
<div className="supporting-documents-types-list">
|
||||
<div className="types-list">
|
||||
<div className="title">
|
||||
<h3>{t('app.admin.settings.account.supporting_documents_types_list.supporting_documents_type_title')}</h3>
|
||||
<FabButton onClick={addType}>{t('app.admin.settings.account.supporting_documents_types_list.add_type')}</FabButton>
|
||||
</div>
|
||||
|
||||
<SupportingDocumentsTypeModal isOpen={modalIsOpen}
|
||||
groups={groups}
|
||||
proofOfIdentityType={supportingDocumentsType}
|
||||
documentType={documentType}
|
||||
toggleModal={toggleCreateAndEditModal}
|
||||
onSuccess={onSaveTypeSuccess}
|
||||
onError={onError} />
|
||||
<DeleteSupportingDocumentsTypeModal isOpen={destroyModalIsOpen}
|
||||
proofOfIdentityTypeId={supportingDocumentsTypeId}
|
||||
toggleModal={toggleDestroyModal}
|
||||
onSuccess={onDestroySuccess}
|
||||
onError={onError}/>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="name">
|
||||
<a onClick={setTypeOrder('name')}>
|
||||
{t('app.admin.settings.account.supporting_documents_types_list.name')}
|
||||
<i className={orderClassName('name')} />
|
||||
</a>
|
||||
</th>
|
||||
<th className="actions"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{supportingDocumentsTypes.map(poit => {
|
||||
return (
|
||||
<tr key={poit.id}>
|
||||
<td>{poit.name}</td>
|
||||
<td>
|
||||
<div className="buttons">
|
||||
<FabButton className="edit-btn" onClick={editType(poit)}>
|
||||
<i className="fa fa-edit" />
|
||||
</FabButton>
|
||||
<FabButton className="delete-btn" onClick={destroyType(poit.id)}>
|
||||
<i className="fa fa-trash" />
|
||||
</FabButton>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
{!hasTypes() && (
|
||||
<p className="no-types-info">
|
||||
<HtmlTranslate trKey="app.admin.settings.account.supporting_documents_types_list.no_types" />
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const SupportingDocumentsTypesListWrapper: React.FC<SupportingDocumentsTypesListProps> = (props) => {
|
||||
@ -277,4 +345,4 @@ const SupportingDocumentsTypesListWrapper: React.FC<SupportingDocumentsTypesList
|
||||
);
|
||||
};
|
||||
|
||||
Application.Components.component('supportingDocumentsTypesList', react2angular(SupportingDocumentsTypesListWrapper, ['onSuccess', 'onError']));
|
||||
Application.Components.component('supportingDocumentsTypesList', react2angular(SupportingDocumentsTypesListWrapper, ['onSuccess', 'onError', 'documentType']));
|
||||
|
@ -39,7 +39,7 @@ const SupportingDocumentsValidation: React.FC<SupportingDocumentsValidationProps
|
||||
SupportingDocumentTypeAPI.index({ group_id: member.group_id }).then(tData => {
|
||||
setDocumentsTypes(tData);
|
||||
});
|
||||
SupportingDocumentFileAPI.index({ user_id: member.id }).then(fData => {
|
||||
SupportingDocumentFileAPI.index({ supportable_id: member.id, supportable_type: 'User' }).then(fData => {
|
||||
setDocumentsFiles(fData);
|
||||
});
|
||||
}, []);
|
||||
|
@ -35,6 +35,9 @@ export default class ApiLib {
|
||||
if (file?.is_main) {
|
||||
data.set(`${name}[${attr}][${i}][is_main]`, file.is_main.toString());
|
||||
}
|
||||
if (file?.supporting_document_type_id) {
|
||||
data.set(`${name}[${attr}][${i}][supporting_document_type_id]`, file.supporting_document_type_id.toString());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (object[attr]?.attachment_files && object[attr]?.attachment_files[0]) {
|
||||
|
@ -12,5 +12,15 @@ export interface Child {
|
||||
email?: string,
|
||||
phone?: string,
|
||||
birthday: TDateISODate,
|
||||
user_id: number
|
||||
user_id: number,
|
||||
supporting_document_files_attributes?: Array<{
|
||||
id?: number,
|
||||
supportable_id?: number,
|
||||
supportable_type?: 'User' | 'Child',
|
||||
supporting_document_type_id: number,
|
||||
attachment?: File,
|
||||
attachment_name?: string,
|
||||
attachment_url?: string,
|
||||
_destroy?: boolean
|
||||
}>,
|
||||
}
|
||||
|
@ -1,12 +1,14 @@
|
||||
import { ApiFilter } from './api';
|
||||
|
||||
export interface SupportingDocumentFileIndexFilter extends ApiFilter {
|
||||
user_id: number,
|
||||
supportable_id: number,
|
||||
supportable_type?: 'User' | 'Child',
|
||||
}
|
||||
|
||||
export interface SupportingDocumentFile {
|
||||
id?: number,
|
||||
attachment?: string,
|
||||
user_id?: number,
|
||||
supportable_id?: number,
|
||||
supportable_type?: 'User' | 'Child',
|
||||
supporting_document_type_id: number,
|
||||
}
|
||||
|
@ -1,13 +1,15 @@
|
||||
import { ApiFilter } from './api';
|
||||
|
||||
export interface SupportingDocumentRefusalIndexFilter extends ApiFilter {
|
||||
user_id: number,
|
||||
supportable_id: number,
|
||||
supportable_type: 'User' | 'Child',
|
||||
}
|
||||
|
||||
export interface SupportingDocumentRefusal {
|
||||
id: number,
|
||||
message: string,
|
||||
user_id: number,
|
||||
supportable_id: number,
|
||||
supportable_type: 'User' | 'Child',
|
||||
operator_id: number,
|
||||
supporting_document_type_ids: Array<number>,
|
||||
}
|
||||
|
@ -2,10 +2,12 @@ import { ApiFilter } from './api';
|
||||
|
||||
export interface SupportingDocumentTypeIndexfilter extends ApiFilter {
|
||||
group_id?: number,
|
||||
document_type?: 'User' | 'Child'
|
||||
}
|
||||
|
||||
export interface SupportingDocumentType {
|
||||
id: number,
|
||||
name: string,
|
||||
group_ids: Array<number>
|
||||
group_ids: Array<number>,
|
||||
document_type: 'User' | 'Child'
|
||||
}
|
||||
|
@ -62,6 +62,12 @@
|
||||
on-error="onError">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<supporting-documents-types-list on-success="onSuccess" on-error="onError" document-type="'Child'" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<h3 class="m-l" translate>{{ 'app.admin.settings.captcha' }}</h3>
|
||||
<p class="alert alert-warning m-h-md" ng-bind-html="'app.admin.settings.captcha_info_html' | translate"></p>
|
||||
@ -167,4 +173,4 @@
|
||||
</div>
|
||||
|
||||
|
||||
<supporting-documents-types-list on-success="onSuccess" on-error="onError"/>
|
||||
<supporting-documents-types-list on-success="onSuccess" on-error="onError" document-type="'User'" />
|
||||
|
@ -4,9 +4,13 @@
|
||||
class Child < ApplicationRecord
|
||||
belongs_to :user
|
||||
|
||||
has_many :supporting_document_files, as: :supportable, dependent: :destroy
|
||||
accepts_nested_attributes_for :supporting_document_files, allow_destroy: true, reject_if: :all_blank
|
||||
has_many :supporting_document_refusals, as: :supportable, dependent: :destroy
|
||||
|
||||
validates :first_name, presence: true
|
||||
validates :last_name, presence: true
|
||||
validates :email, presence: true, format: { with: Devise.email_regexp }
|
||||
# validates :email, presence: true, format: { with: Devise.email_regexp }
|
||||
validate :validate_age
|
||||
|
||||
# birthday should less than 18 years ago
|
||||
|
@ -6,7 +6,7 @@ class SupportingDocumentFile < ApplicationRecord
|
||||
mount_uploader :attachment, SupportingDocumentFileUploader
|
||||
|
||||
belongs_to :supporting_document_type
|
||||
belongs_to :user
|
||||
belongs_to :supportable, polymorphic: true
|
||||
|
||||
validates :attachment, file_size: { maximum: ENV.fetch('MAX_SUPPORTING_DOCUMENT_FILE_SIZE', 5.megabytes).to_i }
|
||||
end
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
# An admin can mark an uploaded document as refused, this will notify the member
|
||||
class SupportingDocumentRefusal < ApplicationRecord
|
||||
belongs_to :user
|
||||
belongs_to :supportable, polymorphic: true
|
||||
belongs_to :operator, class_name: 'User', inverse_of: :supporting_document_refusals
|
||||
has_many :supporting_document_refusals_types, dependent: :destroy
|
||||
has_many :supporting_document_types, through: :supporting_document_refusals_types
|
||||
|
@ -8,4 +8,6 @@ class SupportingDocumentType < ApplicationRecord
|
||||
|
||||
has_many :supporting_document_refusals_types, dependent: :destroy
|
||||
has_many :supporting_document_refusals, through: :supporting_document_refusals_types
|
||||
|
||||
validates :document_type, presence: true, inclusion: { in: %w[User Child] }
|
||||
end
|
||||
|
@ -47,8 +47,8 @@ class User < ApplicationRecord
|
||||
|
||||
has_many :accounting_periods, foreign_key: 'closed_by', dependent: :nullify, inverse_of: :user
|
||||
|
||||
has_many :supporting_document_files, dependent: :destroy
|
||||
has_many :supporting_document_refusals, dependent: :destroy
|
||||
has_many :supporting_document_files, as: :supportable, dependent: :destroy
|
||||
has_many :supporting_document_refusals, as: :supportable, dependent: :destroy
|
||||
|
||||
has_many :notifications, as: :receiver, dependent: :destroy
|
||||
has_many :notification_preferences, dependent: :destroy
|
||||
|
@ -6,15 +6,11 @@ class SupportingDocumentFilePolicy < ApplicationPolicy
|
||||
user.privileged?
|
||||
end
|
||||
|
||||
def create?
|
||||
user.privileged? or record.user_id == user.id
|
||||
%w[create update download].each do |action|
|
||||
define_method "#{action}?" do
|
||||
user.privileged? ||
|
||||
(record.supportable_type == 'User' && record.supportable_id.to_i == user.id) ||
|
||||
(record.supportable_type == 'Child' && user.children.exists?(id: record.supportable_id.to_i))
|
||||
end
|
||||
|
||||
def update?
|
||||
user.privileged? or record.user_id == user.id
|
||||
end
|
||||
|
||||
def download?
|
||||
user.privileged? or record.user_id == user.id
|
||||
end
|
||||
end
|
||||
|
@ -4,23 +4,32 @@
|
||||
class SupportingDocumentFileService
|
||||
def self.list(operator, filters = {})
|
||||
files = []
|
||||
if filters[:user_id].present? && (operator.privileged? || filters[:user_id].to_i == operator.id)
|
||||
files = SupportingDocumentFile.where(user_id: filters[:user_id])
|
||||
if filters[:supportable_id].present? && can_list?(operator, filters[:supportable_id], filters[:supportable_type])
|
||||
files = SupportingDocumentFile.where(supportable_id: filters[:supportable_id], supportable_type: filters[:supportable_type])
|
||||
end
|
||||
files
|
||||
end
|
||||
|
||||
def self.can_list?(operator, supportable_id, supportable_type)
|
||||
operator.privileged? ||
|
||||
(supportable_type == 'User' && supportable_id.to_i == operator.id) ||
|
||||
(supportable_type == 'Child' && operator.children.exists?(id: supportable_id.to_i))
|
||||
end
|
||||
|
||||
def self.create(supporting_document_file)
|
||||
saved = supporting_document_file.save
|
||||
|
||||
if saved
|
||||
user = User.find(supporting_document_file.user_id)
|
||||
all_files_are_upload = true
|
||||
if supporting_document_file.supportable_type == 'User'
|
||||
user = supporting_document_file.supportable
|
||||
user.group.supporting_document_types.each do |type|
|
||||
file = type.supporting_document_files.find_by(user_id: supporting_document_file.user_id)
|
||||
file = type.supporting_document_files.find_by(supportable_id: supporting_document_file.supportable_id,
|
||||
supportable_type: supporting_document_file.supportable_type)
|
||||
all_files_are_upload = false unless file
|
||||
end
|
||||
if all_files_are_upload
|
||||
end
|
||||
if all_files_are_upload && (supporting_document_file.supportable_type == 'User')
|
||||
NotificationCenter.call type: 'notify_admin_user_supporting_document_files_created',
|
||||
receiver: User.admins_and_managers,
|
||||
attached_object: user
|
||||
@ -32,13 +41,16 @@ class SupportingDocumentFileService
|
||||
def self.update(supporting_document_file, params)
|
||||
updated = supporting_document_file.update(params)
|
||||
if updated
|
||||
user = supporting_document_file.user
|
||||
all_files_are_upload = true
|
||||
if supporting_document_file.supportable_type == 'User'
|
||||
user = supporting_document_file.supportable
|
||||
user.group.supporting_document_types.each do |type|
|
||||
file = type.supporting_document_files.find_by(user_id: supporting_document_file.user_id)
|
||||
file = type.supporting_document_files.find_by(supportable_id: supporting_document_file.supportable_id,
|
||||
supportable_type: supporting_document_file.supportable_type)
|
||||
all_files_are_upload = false unless file
|
||||
end
|
||||
if all_files_are_upload
|
||||
end
|
||||
if all_files_are_upload && (supporting_document_file.supportable_type == 'User')
|
||||
NotificationCenter.call type: 'notify_admin_user_supporting_document_files_updated',
|
||||
receiver: User.admins_and_managers,
|
||||
attached_object: supporting_document_file
|
||||
|
@ -4,19 +4,22 @@
|
||||
class SupportingDocumentRefusalService
|
||||
def self.list(filters = {})
|
||||
refusals = []
|
||||
refusals = SupportingDocumentRefusal.where(user_id: filters[:user_id]) if filters[:user_id].present?
|
||||
if filters[:supportable_id].present?
|
||||
refusals = SupportingDocumentRefusal.where(supportable_id: filters[:supportable_id],
|
||||
supportable_type: filters[:supportable_type])
|
||||
end
|
||||
refusals
|
||||
end
|
||||
|
||||
def self.create(supporting_document_refusal)
|
||||
saved = supporting_document_refusal.save
|
||||
|
||||
if saved
|
||||
if saved && supporting_document_refusal.supportable_type == 'User'
|
||||
NotificationCenter.call type: 'notify_admin_user_supporting_document_refusal',
|
||||
receiver: User.admins_and_managers,
|
||||
attached_object: supporting_document_refusal
|
||||
NotificationCenter.call type: 'notify_user_supporting_document_refusal',
|
||||
receiver: supporting_document_refusal.user,
|
||||
receiver: supporting_document_refusal.supportable,
|
||||
attached_object: supporting_document_refusal
|
||||
end
|
||||
saved
|
||||
|
@ -9,7 +9,7 @@ class SupportingDocumentTypeService
|
||||
|
||||
group.supporting_document_types.includes(:groups)
|
||||
else
|
||||
SupportingDocumentType.all
|
||||
SupportingDocumentType.where(document_type: filters[:document_type] || 'User')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,3 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
json.extract! child, :id, :first_name, :last_name, :email, :birthday, :phone, :user_id
|
||||
json.supporting_document_files_attributes child.supporting_document_files do |f|
|
||||
json.id f.id
|
||||
json.supportable_id f.supportable_id
|
||||
json.supportable_type f.supportable_type
|
||||
json.supporting_document_type_id f.supporting_document_type_id
|
||||
json.attachment f.attachment.file.filename
|
||||
json.attachment_name f.attachment_identifier
|
||||
json.attachment_url f.attachment_url
|
||||
end
|
||||
|
@ -2,4 +2,4 @@
|
||||
|
||||
json.title notification.notification_type
|
||||
json.description t('.supporting_document_files_uploaded',
|
||||
NAME: notification.attached_object&.user&.profile&.full_name || t('api.notifications.deleted_user'))
|
||||
NAME: notification.attached_object&.supportable&.profile&.full_name || t('api.notifications.deleted_user'))
|
||||
|
@ -2,4 +2,4 @@
|
||||
|
||||
json.title notification.notification_type
|
||||
json.description t('.refusal',
|
||||
NAME: notification.attached_object&.user&.profile&.full_name || t('api.notifications.deleted_user'))
|
||||
NAME: notification.attached_object&.supportable&.profile&.full_name || t('api.notifications.deleted_user'))
|
||||
|
@ -1,4 +1,4 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
json.extract! supporting_document_file, :id, :user_id, :supporting_document_type_id
|
||||
json.extract! supporting_document_file, :id, :supportable_id, :supportable_type, :supporting_document_type_id
|
||||
json.attachment supporting_document_file.attachment.file.filename
|
||||
|
@ -1,3 +1,3 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
json.extract! supporting_document_refusal, :id, :user_id, :operator_id, :supporting_document_type_ids, :message
|
||||
json.extract! supporting_document_refusal, :id, :supportable_id, :supportable_type, :operator_id, :supporting_document_type_ids, :message
|
||||
|
@ -1,3 +1,3 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
json.extract! supporting_document_type, :id, :name, :group_ids
|
||||
json.extract! supporting_document_type, :id, :name, :group_ids, :document_type
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<p>
|
||||
<%= t('.body.user_update_supporting_document_file',
|
||||
NAME: @attached_object&.user&.profile&.full_name || t('api.notifications.deleted_user')) %>
|
||||
NAME: @attached_object&.supportable&.profile&.full_name || t('api.notifications.deleted_user')) %>
|
||||
</p>
|
||||
<ul>
|
||||
<li><%= @attached_object.supporting_document_type.name %></li>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<p>
|
||||
<%= t('.body.user_supporting_document_files_refusal',
|
||||
NAME: @attached_object&.user&.profile&.full_name || t('api.notifications.deleted_user'),
|
||||
NAME: @attached_object&.supportable&.profile&.full_name || t('api.notifications.deleted_user'),
|
||||
OPERATOR: @attached_object&.operator&.profile&.full_name || t('api.notifications.deleted_user')) %>
|
||||
</p>
|
||||
<ul>
|
||||
|
@ -0,0 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# add document_type to supporting_document_type
|
||||
class AddDucumentTypeToSupportingDocumentType < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :supporting_document_types, :document_type, :string, default: 'User'
|
||||
end
|
||||
end
|
@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# change user_id to supportable from supporting_document_file
|
||||
class ChangeUserIdToSupportableFromSupportingDocumentFile < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
rename_column :supporting_document_files, :user_id, :supportable_id
|
||||
add_column :supporting_document_files, :supportable_type, :string, default: 'User'
|
||||
end
|
||||
end
|
@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# change user_id to supportable from supporting_document_refusal
|
||||
class ChangeUserIdToSupportableFromSupportingDocumentRefusal < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
rename_column :supporting_document_refusals, :user_id, :supportable_id
|
||||
add_column :supporting_document_refusals, :supportable_type, :string, default: 'User'
|
||||
end
|
||||
end
|
@ -3746,10 +3746,11 @@ ALTER SEQUENCE public.subscriptions_id_seq OWNED BY public.subscriptions.id;
|
||||
CREATE TABLE public.supporting_document_files (
|
||||
id bigint NOT NULL,
|
||||
supporting_document_type_id bigint,
|
||||
user_id bigint,
|
||||
supportable_id bigint,
|
||||
attachment 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,
|
||||
supportable_type character varying DEFAULT 'User'::character varying
|
||||
);
|
||||
|
||||
|
||||
@ -3778,11 +3779,12 @@ ALTER SEQUENCE public.supporting_document_files_id_seq OWNED BY public.supportin
|
||||
|
||||
CREATE TABLE public.supporting_document_refusals (
|
||||
id bigint NOT NULL,
|
||||
user_id bigint,
|
||||
supportable_id bigint,
|
||||
operator_id integer,
|
||||
message text,
|
||||
created_at timestamp without time zone NOT NULL,
|
||||
updated_at timestamp without time zone NOT NULL
|
||||
updated_at timestamp without time zone NOT NULL,
|
||||
supportable_type character varying DEFAULT 'User'::character varying
|
||||
);
|
||||
|
||||
|
||||
@ -3823,7 +3825,8 @@ CREATE TABLE public.supporting_document_types (
|
||||
id bigint NOT NULL,
|
||||
name 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,
|
||||
document_type character varying DEFAULT 'User'::character varying
|
||||
);
|
||||
|
||||
|
||||
@ -7320,6 +7323,13 @@ CREATE INDEX index_subscriptions_on_plan_id ON public.subscriptions USING btree
|
||||
CREATE INDEX index_subscriptions_on_statistic_profile_id ON public.subscriptions USING btree (statistic_profile_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_supporting_document_files_on_supportable_id; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE INDEX index_supporting_document_files_on_supportable_id ON public.supporting_document_files USING btree (supportable_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_supporting_document_files_on_supporting_document_type_id; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
@ -7328,17 +7338,10 @@ CREATE INDEX index_supporting_document_files_on_supporting_document_type_id ON p
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_supporting_document_files_on_user_id; Type: INDEX; Schema: public; Owner: -
|
||||
-- Name: index_supporting_document_refusals_on_supportable_id; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE INDEX index_supporting_document_files_on_user_id ON public.supporting_document_files USING btree (user_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_supporting_document_refusals_on_user_id; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE INDEX index_supporting_document_refusals_on_user_id ON public.supporting_document_refusals USING btree (user_id);
|
||||
CREATE INDEX index_supporting_document_refusals_on_supportable_id ON public.supporting_document_refusals USING btree (supportable_id);
|
||||
|
||||
|
||||
--
|
||||
@ -8144,7 +8147,7 @@ ALTER TABLE ONLY public.orders
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.supporting_document_refusals
|
||||
ADD CONSTRAINT fk_rails_91d424352e FOREIGN KEY (user_id) REFERENCES public.users(id);
|
||||
ADD CONSTRAINT fk_rails_91d424352e FOREIGN KEY (supportable_id) REFERENCES public.users(id);
|
||||
|
||||
|
||||
--
|
||||
@ -8929,6 +8932,9 @@ INSERT INTO "schema_migrations" (version) VALUES
|
||||
('20230509161557'),
|
||||
('20230510141305'),
|
||||
('20230511080650'),
|
||||
('20230511081018');
|
||||
('20230511081018'),
|
||||
('20230524080448'),
|
||||
('20230524083558'),
|
||||
('20230524110215');
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user