Merge branch 'entreprises' of https://scodoc.org/git/arthur.zhu/ScoDoc into entreprises

This commit is contained in:
Emmanuel Viennet 2022-04-18 17:34:42 +02:00
commit d4d84fcb1e
22 changed files with 1388 additions and 552 deletions

View File

@ -34,7 +34,7 @@ from flask_login import current_user
from app.entreprises.models import ( from app.entreprises.models import (
Entreprise, Entreprise,
EntrepriseContact, EntrepriseCorrespondant,
EntrepriseOffre, EntrepriseOffre,
EntrepriseOffreDepartement, EntrepriseOffreDepartement,
EntreprisePreferences, EntreprisePreferences,
@ -85,6 +85,9 @@ def get_offre_files_and_depts(offre: EntrepriseOffre, depts: list):
Retourne l'offre, les fichiers attachés a l'offre et les département liés Retourne l'offre, les fichiers attachés a l'offre et les département liés
""" """
offre_depts = EntrepriseOffreDepartement.query.filter_by(offre_id=offre.id).all() offre_depts = EntrepriseOffreDepartement.query.filter_by(offre_id=offre.id).all()
correspondant = EntrepriseCorrespondant.query.filter_by(
id=offre.correspondant_id
).first()
if not offre_depts or check_offre_depts(depts, offre_depts): if not offre_depts or check_offre_depts(depts, offre_depts):
files = [] files = []
path = os.path.join( path = os.path.join(
@ -100,13 +103,11 @@ def get_offre_files_and_depts(offre: EntrepriseOffre, depts: list):
for _file in glob.glob(f"{dir}/*"): for _file in glob.glob(f"{dir}/*"):
file = [os.path.basename(dir), os.path.basename(_file)] file = [os.path.basename(dir), os.path.basename(_file)]
files.append(file) files.append(file)
return [offre, files, offre_depts] return [offre, files, offre_depts, correspondant]
return None return None
def send_email_notifications_entreprise( def send_email_notifications_entreprise(subject, entreprise: Entreprise):
subject, entreprise: Entreprise, contact: EntrepriseContact
):
txt = [ txt = [
"Une entreprise est en attente de validation", "Une entreprise est en attente de validation",
"Entreprise:", "Entreprise:",
@ -116,14 +117,6 @@ def send_email_notifications_entreprise(
f"\tcode postal: {entreprise.codepostal}", f"\tcode postal: {entreprise.codepostal}",
f"\tville: {entreprise.ville}", f"\tville: {entreprise.ville}",
f"\tpays: {entreprise.pays}", f"\tpays: {entreprise.pays}",
"",
"Contact:",
f"nom: {contact.nom}",
f"prenom: {contact.prenom}",
f"telephone: {contact.telephone}",
f"mail: {contact.mail}",
f"poste: {contact.poste}",
f"service: {contact.service}",
] ]
txt = "\n".join(txt) txt = "\n".join(txt)
email.send_email( email.send_email(
@ -135,34 +128,42 @@ def send_email_notifications_entreprise(
return txt return txt
def verif_contact_data(contact_data): def verif_correspondant_data(correspondant_data):
""" """
Verifie les données d'une ligne Excel (contact) Verifie les données d'une ligne Excel (correspondant)
contact_data[0]: nom correspondant_data[0]: nom
contact_data[1]: prenom correspondant_data[1]: prenom
contact_data[2]: telephone correspondant_data[2]: telephone
contact_data[3]: mail correspondant_data[3]: mail
contact_data[4]: poste correspondant_data[4]: poste
contact_data[5]: service correspondant_data[5]: service
contact_data[6]: entreprise_id correspondant_data[6]: entreprise_id
""" """
# champs obligatoires # champs obligatoires
if contact_data[0] == "" or contact_data[1] == "" or contact_data[6] == "": if (
correspondant_data[0].strip() == ""
or correspondant_data[1].strip() == ""
or correspondant_data[6].strip() == ""
):
return False return False
# entreprise_id existant # entreprise_id existant
entreprise = Entreprise.query.filter_by(siret=contact_data[6]).first() entreprise = Entreprise.query.filter_by(siret=correspondant_data[6].strip()).first()
if entreprise is None: if entreprise is None:
return False return False
# contact possède le meme nom et prénom dans la meme entreprise # correspondant possède le meme nom et prénom dans la meme entreprise
contact = EntrepriseContact.query.filter_by( correspondant = EntrepriseCorrespondant.query.filter_by(
nom=contact_data[0], prenom=contact_data[1], entreprise_id=entreprise.id nom=correspondant_data[0].strip(),
prenom=correspondant_data[1].strip(),
entreprise_id=entreprise.id,
).first() ).first()
if contact is not None: if correspondant is not None:
return False return False
if contact_data[2] == "" and contact_data[3] == "": # 1 moyen de contact if (
correspondant_data[2].strip() == "" and correspondant_data[3].strip() == ""
): # 1 moyen de contact
return False return False
return True return True
@ -174,24 +175,24 @@ def verif_entreprise_data(entreprise_data):
""" """
if EntreprisePreferences.get_check_siret(): if EntreprisePreferences.get_check_siret():
for data in entreprise_data: # champs obligatoires for data in entreprise_data: # champs obligatoires
if data == "": if data.strip() == "":
return False return False
else: else:
for data in entreprise_data[1:]: # champs obligatoires for data in entreprise_data[1:]: # champs obligatoires
if data == "": if data.strip() == "":
return False return False
if EntreprisePreferences.get_check_siret(): if EntreprisePreferences.get_check_siret():
siret = entreprise_data[0].strip() # vérification sur le siret siret = entreprise_data[0].replace(" ", "") # vérification sur le siret
if re.match("^\d{14}$", siret) is None: if re.match("^\d{14}$", siret) is None:
return False return False
try: try:
req = requests.get( req = requests.get(
f"https://entreprise.data.gouv.fr/api/sirene/v1/siret/{siret}" f"https://entreprise.data.gouv.fr/api/sirene/v1/siret/{siret}"
) )
except requests.ConnectionError:
print("no internet")
if req.status_code != 200: if req.status_code != 200:
return False return False
except requests.ConnectionError:
return False
entreprise = Entreprise.query.filter_by(siret=siret).first() entreprise = Entreprise.query.filter_by(siret=siret).first()
if entreprise is not None: if entreprise is not None:
return False return False

View File

@ -40,11 +40,17 @@ from wtforms import (
SelectMultipleField, SelectMultipleField,
DateField, DateField,
BooleanField, BooleanField,
FieldList,
FormField,
) )
from wtforms.validators import ValidationError, DataRequired, Email, Optional from wtforms.validators import ValidationError, DataRequired, Email, Optional
from wtforms.widgets import ListWidget, CheckboxInput from wtforms.widgets import ListWidget, CheckboxInput
from app.entreprises.models import Entreprise, EntrepriseContact, EntreprisePreferences from app.entreprises.models import (
Entreprise,
EntrepriseCorrespondant,
EntreprisePreferences,
)
from app.models import Identite, Departement from app.models import Identite, Departement
from app.auth.models import User from app.auth.models import User
@ -66,7 +72,7 @@ def _build_string_field(label, required=True, render_kw=None):
class EntrepriseCreationForm(FlaskForm): class EntrepriseCreationForm(FlaskForm):
siret = _build_string_field( siret = _build_string_field(
"SIRET (*)", "SIRET (*)",
render_kw={"placeholder": "Numéro composé de 14 chiffres", "maxlength": "14"}, render_kw={"placeholder": "Numéro composé de 14 chiffres"},
) )
nom_entreprise = _build_string_field("Nom de l'entreprise (*)") nom_entreprise = _build_string_field("Nom de l'entreprise (*)")
adresse = _build_string_field("Adresse de l'entreprise (*)") adresse = _build_string_field("Adresse de l'entreprise (*)")
@ -74,15 +80,18 @@ class EntrepriseCreationForm(FlaskForm):
ville = _build_string_field("Ville de l'entreprise (*)") ville = _build_string_field("Ville de l'entreprise (*)")
pays = _build_string_field("Pays de l'entreprise", required=False) pays = _build_string_field("Pays de l'entreprise", required=False)
nom_contact = _build_string_field("Nom du contact (*)") nom_correspondant = _build_string_field("Nom du correspondant", required=False)
prenom_contact = _build_string_field("Prénom du contact (*)") prenom_correspondant = _build_string_field(
telephone = _build_string_field("Téléphone du contact (*)", required=False) "Prénom du correspondant", required=False
)
telephone = _build_string_field("Téléphone du correspondant", required=False)
mail = StringField( mail = StringField(
"Mail du contact (*)", "Mail du correspondant",
validators=[Optional(), Email(message="Adresse e-mail invalide")], validators=[Optional(), Email(message="Adresse e-mail invalide")],
) )
poste = _build_string_field("Poste du contact", required=False) poste = _build_string_field("Poste du correspondant", required=False)
service = _build_string_field("Service du contact", required=False) service = _build_string_field("Service du correspondant", required=False)
submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE) submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE)
def validate(self): def validate(self):
@ -90,29 +99,46 @@ class EntrepriseCreationForm(FlaskForm):
if not FlaskForm.validate(self): if not FlaskForm.validate(self):
validate = False validate = False
if not self.telephone.data and not self.mail.data: if (
self.nom_correspondant.data.strip()
or self.prenom_correspondant.data.strip()
or self.telephone.data.strip()
or self.mail.data.strip()
or self.poste.data.strip()
or self.service.data.strip()
):
if not self.nom_correspondant.data.strip():
self.nom_correspondant.errors.append("Ce champ est requis")
validate = False
if not self.prenom_correspondant.data.strip():
self.prenom_correspondant.errors.append("Ce champ est requis")
validate = False
if not self.telephone.data.strip() and not self.mail.data.strip():
self.telephone.errors.append( self.telephone.errors.append(
"Saisir un moyen de contact (mail ou téléphone)" "Saisir un moyen de contact (mail ou téléphone)"
) )
self.mail.errors.append("Saisir un moyen de contact (mail ou téléphone)") self.mail.errors.append(
"Saisir un moyen de contact (mail ou téléphone)"
)
validate = False validate = False
return validate return validate
def validate_siret(self, siret): def validate_siret(self, siret):
if EntreprisePreferences.get_check_siret(): if EntreprisePreferences.get_check_siret():
siret = siret.data.strip() siret_data = siret.data.replace(" ", "")
if re.match("^\d{14}$", siret) is None: self.siret.data = siret_data
if re.match("^\d{14}$", siret_data) is None:
raise ValidationError("Format incorrect") raise ValidationError("Format incorrect")
try: try:
req = requests.get( req = requests.get(
f"https://entreprise.data.gouv.fr/api/sirene/v1/siret/{siret}" f"https://entreprise.data.gouv.fr/api/sirene/v1/siret/{siret_data}"
) )
except requests.ConnectionError:
print("no internet")
if req.status_code != 200: if req.status_code != 200:
raise ValidationError("SIRET inexistant") raise ValidationError("SIRET inexistant")
entreprise = Entreprise.query.filter_by(siret=siret).first() except requests.ConnectionError:
raise ValidationError("Impossible de vérifier l'existance du SIRET")
entreprise = Entreprise.query.filter_by(siret=siret_data).first()
if entreprise is not None: if entreprise is not None:
lien = f'<a href="/ScoDoc/entreprises/fiche_entreprise/{entreprise.id}">ici</a>' lien = f'<a href="/ScoDoc/entreprises/fiche_entreprise/{entreprise.id}">ici</a>'
raise ValidationError( raise ValidationError(
@ -144,6 +170,7 @@ class MultiCheckboxField(SelectMultipleField):
class OffreCreationForm(FlaskForm): class OffreCreationForm(FlaskForm):
hidden_entreprise_id = HiddenField()
intitule = _build_string_field("Intitulé (*)") intitule = _build_string_field("Intitulé (*)")
description = TextAreaField( description = TextAreaField(
"Description (*)", validators=[DataRequired(message=CHAMP_REQUIS)] "Description (*)", validators=[DataRequired(message=CHAMP_REQUIS)]
@ -159,17 +186,44 @@ class OffreCreationForm(FlaskForm):
duree = _build_string_field("Durée (*)") duree = _build_string_field("Durée (*)")
depts = MultiCheckboxField("Départements", validators=[Optional()], coerce=int) depts = MultiCheckboxField("Départements", validators=[Optional()], coerce=int)
expiration_date = DateField("Date expiration", validators=[Optional()]) expiration_date = DateField("Date expiration", validators=[Optional()])
correspondant = SelectField("Correspondant à contacté", validators=[Optional()])
fichier = FileField(
"Fichier (*)",
validators=[
Optional(),
FileAllowed(["pdf", "docx"], "Fichier .pdf ou .docx uniquement"),
],
)
submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE) submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.correspondant.choices = [
(correspondant.id, f"{correspondant.nom} {correspondant.prenom}")
for correspondant in EntrepriseCorrespondant.query.filter_by(
entreprise_id=self.hidden_entreprise_id.data
)
]
self.depts.choices = [ self.depts.choices = [
(dept.id, dept.acronym) for dept in Departement.query.all() (dept.id, dept.acronym) for dept in Departement.query.all()
] ]
def validate(self):
validate = True
if not FlaskForm.validate(self):
validate = False
if len(self.depts.data) < 1:
self.depts.errors.append("Choisir au moins un département")
validate = False
return validate
class OffreModificationForm(FlaskForm): class OffreModificationForm(FlaskForm):
hidden_entreprise_id = HiddenField()
intitule = _build_string_field("Intitulé (*)") intitule = _build_string_field("Intitulé (*)")
description = TextAreaField( description = TextAreaField(
"Description (*)", validators=[DataRequired(message=CHAMP_REQUIS)] "Description (*)", validators=[DataRequired(message=CHAMP_REQUIS)]
@ -185,27 +239,79 @@ class OffreModificationForm(FlaskForm):
duree = _build_string_field("Durée (*)") duree = _build_string_field("Durée (*)")
depts = MultiCheckboxField("Départements", validators=[Optional()], coerce=int) depts = MultiCheckboxField("Départements", validators=[Optional()], coerce=int)
expiration_date = DateField("Date expiration", validators=[Optional()]) expiration_date = DateField("Date expiration", validators=[Optional()])
correspondant = SelectField("Correspondant à contacté", validators=[Optional()])
submit = SubmitField("Modifier", render_kw=SUBMIT_MARGE) submit = SubmitField("Modifier", render_kw=SUBMIT_MARGE)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.correspondant.choices = [
(correspondant.id, f"{correspondant.nom} {correspondant.prenom}")
for correspondant in EntrepriseCorrespondant.query.filter_by(
entreprise_id=self.hidden_entreprise_id.data
)
]
self.depts.choices = [ self.depts.choices = [
(dept.id, dept.acronym) for dept in Departement.query.all() (dept.id, dept.acronym) for dept in Departement.query.all()
] ]
def validate(self):
validate = True
if not FlaskForm.validate(self):
validate = False
class ContactCreationForm(FlaskForm): if len(self.depts.data) < 1:
hidden_entreprise_id = HiddenField() self.depts.errors.append("Choisir au moins un département")
nom = _build_string_field("Nom (*)") validate = False
prenom = _build_string_field("Prénom (*)")
telephone = _build_string_field("Téléphone (*)", required=False) return validate
class CorrespondantCreationForm(FlaskForm):
nom = _build_string_field("Nom (*)", render_kw={"class": "form-control"})
prenom = _build_string_field("Prénom (*)", render_kw={"class": "form-control"})
telephone = _build_string_field(
"Téléphone (*)", required=False, render_kw={"class": "form-control"}
)
mail = StringField( mail = StringField(
"Mail (*)", "Mail (*)",
validators=[Optional(), Email(message="Adresse e-mail invalide")], validators=[Optional(), Email(message="Adresse e-mail invalide")],
render_kw={"class": "form-control"},
) )
poste = _build_string_field("Poste", required=False) poste = _build_string_field(
service = _build_string_field("Service", required=False) "Poste", required=False, render_kw={"class": "form-control"}
)
service = _build_string_field(
"Service", required=False, render_kw={"class": "form-control"}
)
# depts = MultiCheckboxField("Départements", validators=[Optional()], coerce=int)
# def __init__(self, *args, **kwargs):
# super().__init__(*args, **kwargs)
# self.depts.choices = [
# (dept.id, dept.acronym) for dept in Departement.query.all()
# ]
def validate(self):
validate = True
if not FlaskForm.validate(self):
validate = False
if not self.telephone.data and not self.mail.data:
self.telephone.errors.append(
"Saisir un moyen de contact (mail ou téléphone)"
)
self.mail.errors.append("Saisir un moyen de contact (mail ou téléphone)")
validate = False
return validate
class CorrespondantsCreationForm(FlaskForm):
hidden_entreprise_id = HiddenField()
correspondants = FieldList(FormField(CorrespondantCreationForm), min_entries=1)
submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE) submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE)
def validate(self): def validate(self):
@ -213,28 +319,37 @@ class ContactCreationForm(FlaskForm):
if not FlaskForm.validate(self): if not FlaskForm.validate(self):
validate = False validate = False
contact = EntrepriseContact.query.filter_by( correspondant_list = []
entreprise_id=self.hidden_entreprise_id.data, for entry in self.correspondants.entries:
nom=self.nom.data, if entry.nom.data.strip() and entry.prenom.data.strip():
prenom=self.prenom.data, if (
).first() entry.nom.data.strip(),
if contact is not None: entry.prenom.data.strip(),
self.nom.errors.append("Ce contact existe déjà (même nom et prénom)") ) in correspondant_list:
self.prenom.errors.append("") entry.nom.errors.append(
validate = False "Vous avez saisi 2 fois le même nom et prenom"
if not self.telephone.data and not self.mail.data:
self.telephone.errors.append(
"Saisir un moyen de contact (mail ou téléphone)"
) )
self.mail.errors.append("Saisir un moyen de contact (mail ou téléphone)") entry.prenom.errors.append("")
validate = False
correspondant_list.append(
(entry.nom.data.strip(), entry.prenom.data.strip())
)
correspondant = EntrepriseCorrespondant.query.filter_by(
entreprise_id=self.hidden_entreprise_id.data,
nom=entry.nom.data,
prenom=entry.prenom.data,
).first()
if correspondant is not None:
entry.nom.errors.append(
"Ce correspondant existe déjà (même nom et prénom)"
)
entry.prenom.errors.append("")
validate = False validate = False
return validate return validate
class ContactModificationForm(FlaskForm): class CorrespondantModificationForm(FlaskForm):
hidden_contact_id = HiddenField() hidden_correspondant_id = HiddenField()
hidden_entreprise_id = HiddenField() hidden_entreprise_id = HiddenField()
nom = _build_string_field("Nom (*)") nom = _build_string_field("Nom (*)")
prenom = _build_string_field("Prénom (*)") prenom = _build_string_field("Prénom (*)")
@ -245,21 +360,29 @@ class ContactModificationForm(FlaskForm):
) )
poste = _build_string_field("Poste", required=False) poste = _build_string_field("Poste", required=False)
service = _build_string_field("Service", required=False) service = _build_string_field("Service", required=False)
# depts = MultiCheckboxField("Départements", validators=[Optional()], coerce=int)
submit = SubmitField("Modifier", render_kw=SUBMIT_MARGE) submit = SubmitField("Modifier", render_kw=SUBMIT_MARGE)
# def __init__(self, *args, **kwargs):
# super().__init__(*args, **kwargs)
# self.depts.choices = [
# (dept.id, dept.acronym) for dept in Departement.query.all()
# ]
def validate(self): def validate(self):
validate = True validate = True
if not FlaskForm.validate(self): if not FlaskForm.validate(self):
validate = False validate = False
contact = EntrepriseContact.query.filter( correspondant = EntrepriseCorrespondant.query.filter(
EntrepriseContact.id != self.hidden_contact_id.data, EntrepriseCorrespondant.id != self.hidden_correspondant_id.data,
EntrepriseContact.entreprise_id == self.hidden_entreprise_id.data, EntrepriseCorrespondant.entreprise_id == self.hidden_entreprise_id.data,
EntrepriseContact.nom == self.nom.data, EntrepriseCorrespondant.nom == self.nom.data,
EntrepriseContact.prenom == self.prenom.data, EntrepriseCorrespondant.prenom == self.prenom.data,
).first() ).first()
if contact is not None: if correspondant is not None:
self.nom.errors.append("Ce contact existe déjà (même nom et prénom)") self.nom.errors.append("Ce correspondant existe déjà (même nom et prénom)")
self.prenom.errors.append("") self.prenom.errors.append("")
validate = False validate = False
@ -273,7 +396,59 @@ class ContactModificationForm(FlaskForm):
return validate return validate
class HistoriqueCreationForm(FlaskForm): class ContactCreationForm(FlaskForm):
date = _build_string_field(
"Date (*)",
render_kw={"type": "datetime-local"},
)
utilisateur = _build_string_field(
"Utilisateur (*)",
render_kw={"placeholder": "Tapez le nom de l'utilisateur"},
)
notes = TextAreaField("Notes (*)", validators=[DataRequired(message=CHAMP_REQUIS)])
submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE)
def validate_utilisateur(self, utilisateur):
utilisateur_data = self.utilisateur.data.upper().strip()
stm = text(
"SELECT id, UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')')) FROM \"user\" WHERE UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')'))=:utilisateur_data"
)
utilisateur = (
User.query.from_statement(stm)
.params(utilisateur_data=utilisateur_data)
.first()
)
if utilisateur is None:
raise ValidationError("Champ incorrect (selectionnez dans la liste)")
class ContactModificationForm(FlaskForm):
date = _build_string_field(
"Date (*)",
render_kw={"type": "datetime-local"},
)
utilisateur = _build_string_field(
"Utilisateur (*)",
render_kw={"placeholder": "Tapez le nom de l'utilisateur"},
)
notes = TextAreaField("Notes (*)", validators=[DataRequired(message=CHAMP_REQUIS)])
submit = SubmitField("Modifier", render_kw=SUBMIT_MARGE)
def validate_utilisateur(self, utilisateur):
utilisateur_data = self.utilisateur.data.upper().strip()
stm = text(
"SELECT id, UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')')) FROM \"user\" WHERE UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')'))=:utilisateur_data"
)
utilisateur = (
User.query.from_statement(stm)
.params(utilisateur_data=utilisateur_data)
.first()
)
if utilisateur is None:
raise ValidationError("Champ incorrect (selectionnez dans la liste)")
class StageApprentissageCreationForm(FlaskForm):
etudiant = _build_string_field( etudiant = _build_string_field(
"Étudiant (*)", "Étudiant (*)",
render_kw={"placeholder": "Tapez le nom de l'étudiant"}, render_kw={"placeholder": "Tapez le nom de l'étudiant"},
@ -289,6 +464,7 @@ class HistoriqueCreationForm(FlaskForm):
date_fin = DateField( date_fin = DateField(
"Date fin (*)", validators=[DataRequired(message=CHAMP_REQUIS)] "Date fin (*)", validators=[DataRequired(message=CHAMP_REQUIS)]
) )
notes = TextAreaField("Notes")
submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE) submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE)
def validate(self): def validate(self):
@ -319,15 +495,74 @@ class HistoriqueCreationForm(FlaskForm):
raise ValidationError("Champ incorrect (selectionnez dans la liste)") raise ValidationError("Champ incorrect (selectionnez dans la liste)")
class StageApprentissageModificationForm(FlaskForm):
etudiant = _build_string_field(
"Étudiant (*)",
render_kw={"placeholder": "Tapez le nom de l'étudiant"},
)
type_offre = SelectField(
"Type de l'offre (*)",
choices=[("Stage"), ("Alternance")],
validators=[DataRequired(message=CHAMP_REQUIS)],
)
date_debut = DateField(
"Date début (*)", validators=[DataRequired(message=CHAMP_REQUIS)]
)
date_fin = DateField(
"Date fin (*)", validators=[DataRequired(message=CHAMP_REQUIS)]
)
notes = TextAreaField("Notes")
submit = SubmitField("Modifier", render_kw=SUBMIT_MARGE)
def validate(self):
validate = True
if not FlaskForm.validate(self):
validate = False
if (
self.date_debut.data
and self.date_fin.data
and self.date_debut.data > self.date_fin.data
):
self.date_debut.errors.append("Les dates sont incompatibles")
self.date_fin.errors.append("Les dates sont incompatibles")
validate = False
return validate
def validate_etudiant(self, etudiant):
etudiant_data = etudiant.data.upper().strip()
stm = text(
"SELECT id, CONCAT(nom, ' ', prenom) as nom_prenom FROM Identite WHERE CONCAT(nom, ' ', prenom)=:nom_prenom"
)
etudiant = (
Identite.query.from_statement(stm).params(nom_prenom=etudiant_data).first()
)
if etudiant is None:
raise ValidationError("Champ incorrect (selectionnez dans la liste)")
class EnvoiOffreForm(FlaskForm): class EnvoiOffreForm(FlaskForm):
responsable = _build_string_field( responsables = FieldList(
"Responsable de formation (*)", _build_string_field(
render_kw={"placeholder": "Tapez le nom du responsable de formation"}, "Responsable (*)",
render_kw={
"placeholder": "Tapez le nom du responsable de formation",
"class": "form-control",
},
),
min_entries=1,
) )
submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE) submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE)
def validate_responsable(self, responsable): def validate(self):
responsable_data = responsable.data.upper().strip() validate = True
if not FlaskForm.validate(self):
validate = False
for entry in self.responsables.entries:
if entry.data:
responsable_data = entry.data.upper().strip()
stm = text( stm = text(
"SELECT id, UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')')) FROM \"user\" WHERE UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')'))=:responsable_data" "SELECT id, UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')')) FROM \"user\" WHERE UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')'))=:responsable_data"
) )
@ -337,7 +572,10 @@ class EnvoiOffreForm(FlaskForm):
.first() .first()
) )
if responsable is None: if responsable is None:
raise ValidationError("Champ incorrect (selectionnez dans la liste)") entry.errors.append("Champ incorrect (selectionnez dans la liste)")
validate = False
return validate
class AjoutFichierForm(FlaskForm): class AjoutFichierForm(FlaskForm):

View File

@ -11,8 +11,8 @@ class Entreprise(db.Model):
ville = db.Column(db.Text) ville = db.Column(db.Text)
pays = db.Column(db.Text, default="FRANCE") pays = db.Column(db.Text, default="FRANCE")
visible = db.Column(db.Boolean, default=False) visible = db.Column(db.Boolean, default=False)
contacts = db.relationship( correspondants = db.relationship(
"EntrepriseContact", "EntrepriseCorrespondant",
backref="entreprise", backref="entreprise",
lazy="dynamic", lazy="dynamic",
cascade="all, delete-orphan", cascade="all, delete-orphan",
@ -35,12 +35,22 @@ class Entreprise(db.Model):
} }
class EntrepriseContact(db.Model): # class EntrepriseSite(db.Model):
__tablename__ = "are_contacts" # __tablename__ = "are_sites"
# id = db.Column(db.Integer, primary_key=True)
# entreprise_id = db.Column(
# db.Integer, db.ForeignKey("are_entreprises.id", ondelete="cascade")
# )
# nom = db.Column(db.Text)
class EntrepriseCorrespondant(db.Model):
__tablename__ = "are_correspondants"
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
entreprise_id = db.Column( entreprise_id = db.Column(
db.Integer, db.ForeignKey("are_entreprises.id", ondelete="cascade") db.Integer, db.ForeignKey("are_entreprises.id", ondelete="cascade")
) )
# site_id = db.Column(db.Integer, db.ForeignKey("are_sites.id", ondelete="cascade"))
nom = db.Column(db.Text) nom = db.Column(db.Text)
prenom = db.Column(db.Text) prenom = db.Column(db.Text)
telephone = db.Column(db.Text) telephone = db.Column(db.Text)
@ -61,6 +71,17 @@ class EntrepriseContact(db.Model):
} }
class EntrepriseContact(db.Model):
__tablename__ = "are_contacts"
id = db.Column(db.Integer, primary_key=True)
date = db.Column(db.DateTime(timezone=True))
user = db.Column(db.Integer, db.ForeignKey("user.id", ondelete="cascade"))
entreprise = db.Column(
db.Integer, db.ForeignKey("are_entreprises.id", ondelete="cascade")
)
notes = db.Column(db.Text)
class EntrepriseOffre(db.Model): class EntrepriseOffre(db.Model):
__tablename__ = "are_offres" __tablename__ = "are_offres"
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
@ -75,6 +96,9 @@ class EntrepriseOffre(db.Model):
duree = db.Column(db.Text) duree = db.Column(db.Text)
expiration_date = db.Column(db.Date) expiration_date = db.Column(db.Date)
expired = db.Column(db.Boolean, default=False) expired = db.Column(db.Boolean, default=False)
correspondant_id = db.Column(
db.Integer, db.ForeignKey("are_correspondants.id", ondelete="cascade")
)
def to_dict(self): def to_dict(self):
return { return {
@ -95,8 +119,8 @@ class EntrepriseLog(db.Model):
text = db.Column(db.Text) text = db.Column(db.Text)
class EntrepriseEtudiant(db.Model): class EntrepriseStageApprentissage(db.Model):
__tablename__ = "are_etudiants" __tablename__ = "are_stages_apprentissages"
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
entreprise_id = db.Column( entreprise_id = db.Column(
db.Integer, db.ForeignKey("are_entreprises.id", ondelete="cascade") db.Integer, db.ForeignKey("are_entreprises.id", ondelete="cascade")
@ -107,6 +131,7 @@ class EntrepriseEtudiant(db.Model):
date_fin = db.Column(db.Date) date_fin = db.Column(db.Date)
formation_text = db.Column(db.Text) formation_text = db.Column(db.Text)
formation_scodoc = db.Column(db.Integer) formation_scodoc = db.Column(db.Integer)
notes = db.Column(db.Text)
class EntrepriseEnvoiOffre(db.Model): class EntrepriseEnvoiOffre(db.Model):
@ -136,6 +161,15 @@ class EntrepriseOffreDepartement(db.Model):
dept_id = db.Column(db.Integer, db.ForeignKey("departement.id", ondelete="cascade")) dept_id = db.Column(db.Integer, db.ForeignKey("departement.id", ondelete="cascade"))
# class EntrepriseCorrespondantDepartement(db.Model):
# __tablename__ = "are_correspondant_departement"
# id = db.Column(db.Integer, primary_key=True)
# correspondant_id = db.Column(
# db.Integer, db.ForeignKey("are_correspondants.id", ondelete="cascade")
# )
# dept_id = db.Column(db.Integer, db.ForeignKey("departement.id", ondelete="cascade"))
class EntreprisePreferences(db.Model): class EntreprisePreferences(db.Model):
__tablename__ = "are_preferences" __tablename__ = "are_preferences"
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)

View File

@ -12,14 +12,17 @@ from app.decorators import permission_required
from app.entreprises import LOGS_LEN from app.entreprises import LOGS_LEN
from app.entreprises.forms import ( from app.entreprises.forms import (
CorrespondantsCreationForm,
EntrepriseCreationForm, EntrepriseCreationForm,
EntrepriseModificationForm, EntrepriseModificationForm,
SuppressionConfirmationForm, SuppressionConfirmationForm,
OffreCreationForm, OffreCreationForm,
OffreModificationForm, OffreModificationForm,
CorrespondantModificationForm,
ContactCreationForm, ContactCreationForm,
ContactModificationForm, ContactModificationForm,
HistoriqueCreationForm, StageApprentissageCreationForm,
StageApprentissageModificationForm,
EnvoiOffreForm, EnvoiOffreForm,
AjoutFichierForm, AjoutFichierForm,
ValidationConfirmationForm, ValidationConfirmationForm,
@ -30,9 +33,10 @@ from app.entreprises import bp
from app.entreprises.models import ( from app.entreprises.models import (
Entreprise, Entreprise,
EntrepriseOffre, EntrepriseOffre,
EntrepriseContact, EntrepriseCorrespondant,
EntrepriseLog, EntrepriseLog,
EntrepriseEtudiant, EntrepriseContact,
EntrepriseStageApprentissage,
EntrepriseEnvoiOffre, EntrepriseEnvoiOffre,
EntrepriseOffreDepartement, EntrepriseOffreDepartement,
EntreprisePreferences, EntreprisePreferences,
@ -96,22 +100,22 @@ def validation():
) )
@bp.route("/contacts", methods=["GET"]) @bp.route("/correspondants", methods=["GET"])
@permission_required(Permission.RelationsEntreprisesView) @permission_required(Permission.RelationsEntreprisesView)
def contacts(): def correspondants():
""" """
Permet d'afficher une page avec la liste des contacts des entreprises visibles et une liste des dernières opérations Permet d'afficher une page avec la liste des correspondants des entreprises visibles et une liste des dernières opérations
""" """
contacts = ( correspondants = (
db.session.query(EntrepriseContact, Entreprise) db.session.query(EntrepriseCorrespondant, Entreprise)
.join(Entreprise, EntrepriseContact.entreprise_id == Entreprise.id) .join(Entreprise, EntrepriseCorrespondant.entreprise_id == Entreprise.id)
.filter_by(visible=True) .filter_by(visible=True)
) )
logs = EntrepriseLog.query.order_by(EntrepriseLog.date.desc()).limit(LOGS_LEN).all() logs = EntrepriseLog.query.order_by(EntrepriseLog.date.desc()).limit(LOGS_LEN).all()
return render_template( return render_template(
"entreprises/contacts.html", "entreprises/correspondants.html",
title="Contacts", title="Correspondants",
contacts=contacts, correspondants=correspondants,
logs=logs, logs=logs,
) )
@ -122,7 +126,7 @@ def fiche_entreprise(id):
""" """
Permet d'afficher la fiche entreprise d'une entreprise avec une liste des dernières opérations et Permet d'afficher la fiche entreprise d'une entreprise avec une liste des dernières opérations et
l'historique des étudiants ayant réaliser un stage ou une alternance dans cette entreprise. l'historique des étudiants ayant réaliser un stage ou une alternance dans cette entreprise.
La fiche entreprise comporte les informations de l'entreprise, les contacts de l'entreprise et La fiche entreprise comporte les informations de l'entreprise, les correspondants de l'entreprise et
les offres de l'entreprise. les offres de l'entreprise.
""" """
entreprise = Entreprise.query.filter_by(id=id, visible=True).first_or_404( entreprise = Entreprise.query.filter_by(id=id, visible=True).first_or_404(
@ -141,32 +145,32 @@ def fiche_entreprise(id):
offre_with_files = are.get_offre_files_and_depts(offre, depts) offre_with_files = are.get_offre_files_and_depts(offre, depts)
if offre_with_files is not None: if offre_with_files is not None:
offres_with_files.append(offre_with_files) offres_with_files.append(offre_with_files)
contacts = entreprise.contacts[:] correspondants = entreprise.correspondants[:]
logs = ( logs = (
EntrepriseLog.query.order_by(EntrepriseLog.date.desc()) EntrepriseLog.query.order_by(EntrepriseLog.date.desc())
.filter_by(object=id) .filter_by(object=id)
.limit(LOGS_LEN) .limit(LOGS_LEN)
.all() .all()
) )
historique = ( stages_apprentissages = (
db.session.query(EntrepriseEtudiant, Identite) db.session.query(EntrepriseStageApprentissage, Identite)
.order_by(EntrepriseEtudiant.date_debut.desc()) .order_by(EntrepriseStageApprentissage.date_debut.desc())
.filter(EntrepriseEtudiant.entreprise_id == id) .filter(EntrepriseStageApprentissage.entreprise_id == id)
.join(Identite, Identite.id == EntrepriseEtudiant.etudid) .join(Identite, Identite.id == EntrepriseStageApprentissage.etudid)
.all() .all()
) )
return render_template( return render_template(
"entreprises/fiche_entreprise.html", "entreprises/fiche_entreprise.html",
title="Fiche entreprise", title="Fiche entreprise",
entreprise=entreprise, entreprise=entreprise,
contacts=contacts, correspondants=correspondants,
offres=offres_with_files, offres=offres_with_files,
logs=logs, logs=logs,
historique=historique, stages_apprentissages=stages_apprentissages,
) )
@bp.route("/logs/<int:id>", methods=["GET"]) @bp.route("/fiche_entreprise/<int:id>/logs", methods=["GET"])
@permission_required(Permission.RelationsEntreprisesView) @permission_required(Permission.RelationsEntreprisesView)
def logs_entreprise(id): def logs_entreprise(id):
""" """
@ -198,12 +202,12 @@ def fiche_entreprise_validation(id):
entreprise = Entreprise.query.filter_by(id=id, visible=False).first_or_404( entreprise = Entreprise.query.filter_by(id=id, visible=False).first_or_404(
description=f"fiche entreprise (validation) {id} inconnue" description=f"fiche entreprise (validation) {id} inconnue"
) )
contacts = entreprise.contacts correspondants = entreprise.correspondants
return render_template( return render_template(
"entreprises/fiche_entreprise_validation.html", "entreprises/fiche_entreprise_validation.html",
title="Validation fiche entreprise", title="Validation fiche entreprise",
entreprise=entreprise, entreprise=entreprise,
contacts=contacts, correspondants=correspondants,
) )
@ -221,6 +225,9 @@ def offres_recues():
) )
offres_recues_with_files = [] offres_recues_with_files = []
for offre in offres_recues: for offre in offres_recues:
correspondant = EntrepriseCorrespondant.query.filter_by(
id=offre[1].correspondant_id
).first()
files = [] files = []
path = os.path.join( path = os.path.join(
Config.SCODOC_VAR_DIR, Config.SCODOC_VAR_DIR,
@ -235,7 +242,7 @@ def offres_recues():
for file in glob.glob(f"{dir}/*"): for file in glob.glob(f"{dir}/*"):
file = [os.path.basename(dir), os.path.basename(file)] file = [os.path.basename(dir), os.path.basename(file)]
files.append(file) files.append(file)
offres_recues_with_files.append([offre[0], offre[1], files]) offres_recues_with_files.append([offre[0], offre[1], files, correspondant])
return render_template( return render_template(
"entreprises/offres_recues.html", "entreprises/offres_recues.html",
title="Offres reçues", title="Offres reçues",
@ -287,23 +294,24 @@ def add_entreprise():
) )
db.session.add(entreprise) db.session.add(entreprise)
db.session.commit() db.session.commit()
if form.nom_correspondant.data.strip():
db.session.refresh(entreprise) db.session.refresh(entreprise)
contact = EntrepriseContact( correspondant = EntrepriseCorrespondant(
entreprise_id=entreprise.id, entreprise_id=entreprise.id,
nom=form.nom_contact.data.strip(), nom=form.nom_correspondant.data.strip(),
prenom=form.prenom_contact.data.strip(), prenom=form.prenom_correspondant.data.strip(),
telephone=form.telephone.data.strip(), telephone=form.telephone.data.strip(),
mail=form.mail.data.strip(), mail=form.mail.data.strip(),
poste=form.poste.data.strip(), poste=form.poste.data.strip(),
service=form.service.data.strip(), service=form.service.data.strip(),
) )
db.session.add(contact) db.session.add(correspondant)
if current_user.has_permission(Permission.RelationsEntreprisesValidate, None): if current_user.has_permission(Permission.RelationsEntreprisesValidate, None):
entreprise.visible = True entreprise.visible = True
nom_entreprise = f"<a href=/ScoDoc/entreprises/fiche_entreprise/{entreprise.id}>{entreprise.nom}</a>" nom_entreprise = f"<a href=/ScoDoc/entreprises/fiche_entreprise/{entreprise.id}>{entreprise.nom}</a>"
log = EntrepriseLog( log = EntrepriseLog(
authenticated_user=current_user.user_name, authenticated_user=current_user.user_name,
text=f"{nom_entreprise} - Création de la fiche entreprise ({entreprise.nom}) avec un contact", text=f"{nom_entreprise} - Création de la fiche entreprise ({entreprise.nom})",
) )
db.session.add(log) db.session.add(log)
db.session.commit() db.session.commit()
@ -314,18 +322,18 @@ def add_entreprise():
db.session.commit() db.session.commit()
if EntreprisePreferences.get_email_notifications(): if EntreprisePreferences.get_email_notifications():
are.send_email_notifications_entreprise( are.send_email_notifications_entreprise(
"entreprise en attente de validation", entreprise, contact "entreprise en attente de validation", entreprise
) )
flash("L'entreprise a été ajouté à la liste pour la validation.") flash("L'entreprise a été ajouté à la liste pour la validation.")
return redirect(url_for("entreprises.index")) return redirect(url_for("entreprises.index"))
return render_template( return render_template(
"entreprises/ajout_entreprise.html", "entreprises/ajout_entreprise.html",
title="Ajout entreprise avec contact", title="Ajout entreprise avec correspondant",
form=form, form=form,
) )
@bp.route("/edit_entreprise/<int:id>", methods=["GET", "POST"]) @bp.route("/fiche_entreprise/edit_entreprise/<int:id>", methods=["GET", "POST"])
@permission_required(Permission.RelationsEntreprisesChange) @permission_required(Permission.RelationsEntreprisesChange)
def edit_entreprise(id): def edit_entreprise(id):
""" """
@ -396,7 +404,7 @@ def edit_entreprise(id):
) )
@bp.route("/delete_entreprise/<int:id>", methods=["GET", "POST"]) @bp.route("/fiche_entreprise/delete_entreprise/<int:id>", methods=["GET", "POST"])
@permission_required(Permission.RelationsEntreprisesChange) @permission_required(Permission.RelationsEntreprisesChange)
def delete_entreprise(id): def delete_entreprise(id):
""" """
@ -432,7 +440,9 @@ def delete_entreprise(id):
) )
@bp.route("/validate_entreprise/<int:id>", methods=["GET", "POST"]) @bp.route(
"/fiche_entreprise_validation/<int:id>/validate_entreprise", methods=["GET", "POST"]
)
@permission_required(Permission.RelationsEntreprisesValidate) @permission_required(Permission.RelationsEntreprisesValidate)
def validate_entreprise(id): def validate_entreprise(id):
""" """
@ -447,7 +457,7 @@ def validate_entreprise(id):
nom_entreprise = f"<a href=/ScoDoc/entreprises/fiche_entreprise/{entreprise.id}>{entreprise.nom}</a>" nom_entreprise = f"<a href=/ScoDoc/entreprises/fiche_entreprise/{entreprise.id}>{entreprise.nom}</a>"
log = EntrepriseLog( log = EntrepriseLog(
authenticated_user=current_user.user_name, authenticated_user=current_user.user_name,
text=f"{nom_entreprise} - Validation de la fiche entreprise ({entreprise.nom}) avec un contact", text=f"{nom_entreprise} - Validation de la fiche entreprise ({entreprise.nom}) avec un correspondant",
) )
db.session.add(log) db.session.add(log)
db.session.commit() db.session.commit()
@ -460,7 +470,10 @@ def validate_entreprise(id):
) )
@bp.route("/delete_validation_entreprise/<int:id>", methods=["GET", "POST"]) @bp.route(
"/fiche_entreprise_validation/<int:id>/delete_validation_entreprise",
methods=["GET", "POST"],
)
@permission_required(Permission.RelationsEntreprisesValidate) @permission_required(Permission.RelationsEntreprisesValidate)
def delete_validation_entreprise(id): def delete_validation_entreprise(id):
""" """
@ -482,7 +495,7 @@ def delete_validation_entreprise(id):
) )
@bp.route("/add_offre/<int:id>", methods=["GET", "POST"]) @bp.route("/fiche_entreprise/<int:id>/add_offre", methods=["GET", "POST"])
@permission_required(Permission.RelationsEntreprisesChange) @permission_required(Permission.RelationsEntreprisesChange)
def add_offre(id): def add_offre(id):
""" """
@ -491,7 +504,7 @@ def add_offre(id):
entreprise = Entreprise.query.filter_by(id=id, visible=True).first_or_404( entreprise = Entreprise.query.filter_by(id=id, visible=True).first_or_404(
description=f"entreprise {id} inconnue" description=f"entreprise {id} inconnue"
) )
form = OffreCreationForm() form = OffreCreationForm(hidden_entreprise_id=id)
if form.validate_on_submit(): if form.validate_on_submit():
offre = EntrepriseOffre( offre = EntrepriseOffre(
entreprise_id=entreprise.id, entreprise_id=entreprise.id,
@ -501,6 +514,7 @@ def add_offre(id):
missions=form.missions.data.strip(), missions=form.missions.data.strip(),
duree=form.duree.data.strip(), duree=form.duree.data.strip(),
expiration_date=form.expiration_date.data, expiration_date=form.expiration_date.data,
correspondant_id=form.correspondant.data,
) )
db.session.add(offre) db.session.add(offre)
db.session.commit() db.session.commit()
@ -511,6 +525,19 @@ def add_offre(id):
dept_id=dept, dept_id=dept,
) )
db.session.add(offre_dept) db.session.add(offre_dept)
if form.fichier.data:
date = f"{datetime.now().strftime('%Y-%m-%d-%H-%M-%S')}"
path = os.path.join(
Config.SCODOC_VAR_DIR,
"entreprises",
f"{offre.entreprise_id}",
f"{offre.id}",
f"{date}",
)
os.makedirs(path)
file = form.fichier.data
filename = secure_filename(file.filename)
file.save(os.path.join(path, filename))
log = EntrepriseLog( log = EntrepriseLog(
authenticated_user=current_user.user_name, authenticated_user=current_user.user_name,
object=entreprise.id, object=entreprise.id,
@ -527,7 +554,7 @@ def add_offre(id):
) )
@bp.route("/edit_offre/<int:id>", methods=["GET", "POST"]) @bp.route("/fiche_entreprise/edit_offre/<int:id>", methods=["GET", "POST"])
@permission_required(Permission.RelationsEntreprisesChange) @permission_required(Permission.RelationsEntreprisesChange)
def edit_offre(id): def edit_offre(id):
""" """
@ -537,7 +564,9 @@ def edit_offre(id):
description=f"offre {id} inconnue" description=f"offre {id} inconnue"
) )
offre_depts = EntrepriseOffreDepartement.query.filter_by(offre_id=offre.id).all() offre_depts = EntrepriseOffreDepartement.query.filter_by(offre_id=offre.id).all()
form = OffreModificationForm() form = OffreModificationForm(
hidden_entreprise_id=offre.entreprise_id, correspondant=offre.correspondant_id
)
offre_depts_list = [(offre_dept.dept_id) for offre_dept in offre_depts] offre_depts_list = [(offre_dept.dept_id) for offre_dept in offre_depts]
if form.validate_on_submit(): if form.validate_on_submit():
offre.intitule = form.intitule.data.strip() offre.intitule = form.intitule.data.strip()
@ -546,6 +575,7 @@ def edit_offre(id):
offre.missions = form.missions.data.strip() offre.missions = form.missions.data.strip()
offre.duree = form.duree.data.strip() offre.duree = form.duree.data.strip()
offre.expiration_date = form.expiration_date.data offre.expiration_date = form.expiration_date.data
offre.correspondant_id = form.correspondant.data
if offre_depts_list != form.depts.data: if offre_depts_list != form.depts.data:
for dept in form.depts.data: for dept in form.depts.data:
if dept not in offre_depts_list: if dept not in offre_depts_list:
@ -584,7 +614,7 @@ def edit_offre(id):
) )
@bp.route("/delete_offre/<int:id>", methods=["GET", "POST"]) @bp.route("/fiche_entreprise/delete_offre/<int:id>", methods=["GET", "POST"])
@permission_required(Permission.RelationsEntreprisesChange) @permission_required(Permission.RelationsEntreprisesChange)
def delete_offre(id): def delete_offre(id):
""" """
@ -621,7 +651,7 @@ def delete_offre(id):
) )
@bp.route("/delete_offre_recue/<int:id>", methods=["GET", "POST"]) @bp.route("/offres_recues/delete_offre_recue/<int:id>", methods=["GET", "POST"])
@permission_required(Permission.RelationsEntreprisesView) @permission_required(Permission.RelationsEntreprisesView)
def delete_offre_recue(id): def delete_offre_recue(id):
""" """
@ -635,7 +665,7 @@ def delete_offre_recue(id):
return redirect(url_for("entreprises.offres_recues")) return redirect(url_for("entreprises.offres_recues"))
@bp.route("/expired/<int:id>", methods=["GET", "POST"]) @bp.route("/fiche_entreprise/expired/<int:id>", methods=["GET", "POST"])
@permission_required(Permission.RelationsEntreprisesChange) @permission_required(Permission.RelationsEntreprisesChange)
def expired(id): def expired(id):
""" """
@ -653,36 +683,153 @@ def expired(id):
return redirect(url_for("entreprises.fiche_entreprise", id=offre.entreprise_id)) return redirect(url_for("entreprises.fiche_entreprise", id=offre.entreprise_id))
@bp.route("/add_contact/<int:id>", methods=["GET", "POST"]) @bp.route("/fiche_entreprise/<int:id>/add_correspondant", methods=["GET", "POST"])
@permission_required(Permission.RelationsEntreprisesChange) @permission_required(Permission.RelationsEntreprisesChange)
def add_contact(id): def add_correspondant(id):
""" """
Permet d'ajouter un contact a une entreprise Permet d'ajouter un correspondant a une entreprise
""" """
entreprise = Entreprise.query.filter_by(id=id, visible=True).first_or_404( entreprise = Entreprise.query.filter_by(id=id, visible=True).first_or_404(
description=f"entreprise {id} inconnue" description=f"entreprise {id} inconnue"
) )
form = ContactCreationForm(hidden_entreprise_id=entreprise.id) form = CorrespondantsCreationForm(hidden_entreprise_id=entreprise.id)
if form.validate_on_submit(): if form.validate_on_submit():
contact = EntrepriseContact( for correspondant_entry in form.correspondants.entries:
correspondant = EntrepriseCorrespondant(
entreprise_id=entreprise.id, entreprise_id=entreprise.id,
nom=form.nom.data.strip(), nom=correspondant_entry.nom.data.strip(),
prenom=form.prenom.data.strip(), prenom=correspondant_entry.prenom.data.strip(),
telephone=form.telephone.data.strip(), telephone=correspondant_entry.telephone.data.strip(),
mail=form.mail.data.strip(), mail=correspondant_entry.mail.data.strip(),
poste=form.poste.data.strip(), poste=correspondant_entry.poste.data.strip(),
service=form.service.data.strip(), service=correspondant_entry.service.data.strip(),
) )
log = EntrepriseLog( log = EntrepriseLog(
authenticated_user=current_user.user_name, authenticated_user=current_user.user_name,
object=entreprise.id, object=entreprise.id,
text="Création d'un contact", text="Création d'un correspondant",
) )
db.session.add(log) db.session.add(log)
db.session.add(correspondant)
db.session.commit()
flash("Le correspondant a été ajouté à la fiche entreprise.")
return redirect(url_for("entreprises.fiche_entreprise", id=entreprise.id))
return render_template(
"entreprises/ajout_correspondants.html",
title="Ajout correspondant",
form=form,
)
@bp.route("/fiche_entreprise/edit_correspondant/<int:id>", methods=["GET", "POST"])
@permission_required(Permission.RelationsEntreprisesChange)
def edit_correspondant(id):
"""
Permet de modifier un correspondant
"""
correspondant = EntrepriseCorrespondant.query.filter_by(id=id).first_or_404(
description=f"correspondant {id} inconnu"
)
form = CorrespondantModificationForm(
hidden_entreprise_id=correspondant.entreprise_id,
hidden_correspondant_id=correspondant.id,
)
if form.validate_on_submit():
correspondant.nom = form.nom.data.strip()
correspondant.prenom = form.prenom.data.strip()
correspondant.telephone = form.telephone.data.strip()
correspondant.mail = form.mail.data.strip()
correspondant.poste = form.poste.data.strip()
correspondant.service = form.service.data.strip()
log = EntrepriseLog(
authenticated_user=current_user.user_name,
object=correspondant.entreprise_id,
text="Modification d'un correspondant",
)
db.session.add(log)
db.session.commit()
flash("Le correspondant a été modifié.")
return redirect(
url_for("entreprises.fiche_entreprise", id=correspondant.entreprise.id)
)
elif request.method == "GET":
form.nom.data = correspondant.nom
form.prenom.data = correspondant.prenom
form.telephone.data = correspondant.telephone
form.mail.data = correspondant.mail
form.poste.data = correspondant.poste
form.service.data = correspondant.service
return render_template(
"entreprises/form.html",
title="Modification correspondant",
form=form,
)
@bp.route("/fiche_entreprise/delete_correspondant/<int:id>", methods=["GET", "POST"])
@permission_required(Permission.RelationsEntreprisesChange)
def delete_correspondant(id):
"""
Permet de supprimer un correspondant
"""
correspondant = EntrepriseCorrespondant.query.filter_by(id=id).first_or_404(
description=f"correspondant {id} inconnu"
)
form = SuppressionConfirmationForm()
if form.validate_on_submit():
db.session.delete(correspondant)
log = EntrepriseLog(
authenticated_user=current_user.user_name,
object=correspondant.entreprise_id,
text="Suppression d'un correspondant",
)
db.session.add(log)
db.session.commit()
flash("Le correspondant a été supprimé de la fiche entreprise.")
return redirect(
url_for("entreprises.fiche_entreprise", id=correspondant.entreprise_id)
)
return render_template(
"entreprises/delete_confirmation.html",
title="Supression correspondant",
form=form,
)
@bp.route("/fiche_entreprise/<int:id>/add_contact", methods=["GET", "POST"])
@permission_required(Permission.RelationsEntreprisesChange)
def add_contact(id):
"""
Permet d'ajouter un contact avec une entreprise
"""
entreprise = Entreprise.query.filter_by(id=id, visible=True).first_or_404(
description=f"entreprise {id} inconnue"
)
form = ContactCreationForm(
date=f"{datetime.now().strftime('%Y-%m-%dT%H:%M')}",
utilisateur=f"{current_user.nom} {current_user.prenom} ({current_user.user_name})"
if current_user.nom and current_user.prenom
else "",
)
if form.validate_on_submit():
utilisateur_data = form.utilisateur.data.upper().strip()
stm = text(
"SELECT id, UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')')) FROM \"user\" WHERE UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')'))=:utilisateur_data"
)
utilisateur = (
User.query.from_statement(stm)
.params(utilisateur_data=utilisateur_data)
.first()
)
contact = EntrepriseContact(
date=form.date.data,
user=utilisateur.id,
entreprise=entreprise.id,
notes=form.notes.data.strip(),
)
db.session.add(contact) db.session.add(contact)
db.session.commit() db.session.commit()
flash("Le contact a été ajouté à la fiche entreprise.") return redirect(url_for("entreprises.contacts", id=entreprise.id))
return redirect(url_for("entreprises.fiche_entreprise", id=entreprise.id))
return render_template( return render_template(
"entreprises/form.html", "entreprises/form.html",
title="Ajout contact", title="Ajout contact",
@ -690,44 +837,38 @@ def add_contact(id):
) )
@bp.route("/edit_contact/<int:id>", methods=["GET", "POST"]) @bp.route("/fiche_entreprise/edit_contact/<int:id>", methods=["GET", "POST"])
@permission_required(Permission.RelationsEntreprisesChange) @permission_required(Permission.RelationsEntreprisesChange)
def edit_contact(id): def edit_contact(id):
""" """
Permet de modifier un contact Permet d'editer un contact avec une entreprise
""" """
contact = EntrepriseContact.query.filter_by(id=id).first_or_404( contact = EntrepriseContact.query.filter_by(id=id).first_or_404(
description=f"contact {id} inconnu" description=f"contact {id} inconnu"
) )
form = ContactModificationForm( form = ContactModificationForm()
hidden_entreprise_id=contact.entreprise_id,
hidden_contact_id=contact.id,
)
if form.validate_on_submit(): if form.validate_on_submit():
contact.nom = form.nom.data.strip() utilisateur_data = form.utilisateur.data.upper().strip()
contact.prenom = form.prenom.data.strip() stm = text(
contact.telephone = form.telephone.data.strip() "SELECT id, UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')')) FROM \"user\" WHERE UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')'))=:utilisateur_data"
contact.mail = form.mail.data.strip()
contact.poste = form.poste.data.strip()
contact.service = form.service.data.strip()
log = EntrepriseLog(
authenticated_user=current_user.user_name,
object=contact.entreprise_id,
text="Modification d'un contact",
) )
db.session.add(log) utilisateur = (
User.query.from_statement(stm)
.params(utilisateur_data=utilisateur_data)
.first()
)
contact.date = form.date.data
contact.user = utilisateur.id
contact.notes = form.notes.data
db.session.commit() db.session.commit()
flash("Le contact a été modifié.") return redirect(url_for("entreprises.contacts", id=contact.entreprise))
return redirect(
url_for("entreprises.fiche_entreprise", id=contact.entreprise.id)
)
elif request.method == "GET": elif request.method == "GET":
form.nom.data = contact.nom utilisateur = User.query.filter_by(id=contact.user).first()
form.prenom.data = contact.prenom form.date.data = contact.date.strftime("%Y-%m-%dT%H:%M")
form.telephone.data = contact.telephone form.utilisateur.data = (
form.mail.data = contact.mail f"{utilisateur.nom} {utilisateur.prenom} ({utilisateur.user_name})"
form.poste.data = contact.poste )
form.service.data = contact.service form.notes.data = contact.notes
return render_template( return render_template(
"entreprises/form.html", "entreprises/form.html",
title="Modification contact", title="Modification contact",
@ -735,57 +876,31 @@ def edit_contact(id):
) )
@bp.route("/delete_contact/<int:id>", methods=["GET", "POST"]) @bp.route("/fiche_entreprise/<int:id>/contacts")
@permission_required(Permission.RelationsEntreprisesChange) @permission_required(Permission.RelationsEntreprisesView)
def delete_contact(id): def contacts(id):
""" """
Permet de supprimer un contact Permet d'afficher une page avec la liste des contacts d'une entreprise
""" """
contact = EntrepriseContact.query.filter_by(id=id).first_or_404( contacts = EntrepriseContact.query.filter_by(entreprise=id).all()
description=f"contact {id} inconnu"
)
form = SuppressionConfirmationForm()
if form.validate_on_submit():
contact_count = EntrepriseContact.query.filter_by(
entreprise_id=contact.entreprise_id
).count()
if contact_count == 1:
flash(
"Le contact n'a pas été supprimé de la fiche entreprise. (1 contact minimum)"
)
return redirect(
url_for("entreprises.fiche_entreprise", id=contact.entreprise_id)
)
else:
db.session.delete(contact)
log = EntrepriseLog(
authenticated_user=current_user.user_name,
object=contact.entreprise_id,
text="Suppression d'un contact",
)
db.session.add(log)
db.session.commit()
flash("Le contact a été supprimé de la fiche entreprise.")
return redirect(
url_for("entreprises.fiche_entreprise", id=contact.entreprise_id)
)
return render_template( return render_template(
"entreprises/delete_confirmation.html", "entreprises/contacts.html",
title="Supression contact", title="Liste des contacts",
form=form, contacts=contacts,
entreprise_id=id,
) )
@bp.route("/add_historique/<int:id>", methods=["GET", "POST"]) @bp.route("/fiche_entreprise/<int:id>/add_stage_apprentissage", methods=["GET", "POST"])
@permission_required(Permission.RelationsEntreprisesChange) @permission_required(Permission.RelationsEntreprisesChange)
def add_historique(id): def add_stage_apprentissage(id):
""" """
Permet d'ajouter un étudiant ayant réalisé un stage ou une alternance sur la fiche entreprise de l'entreprise Permet d'ajouter un étudiant ayant réalisé un stage ou une alternance sur la fiche entreprise de l'entreprise
""" """
entreprise = Entreprise.query.filter_by(id=id, visible=True).first_or_404( entreprise = Entreprise.query.filter_by(id=id, visible=True).first_or_404(
description=f"entreprise {id} inconnue" description=f"entreprise {id} inconnue"
) )
form = HistoriqueCreationForm() form = StageApprentissageCreationForm()
if form.validate_on_submit(): if form.validate_on_submit():
etudiant_nomcomplet = form.etudiant.data.upper().strip() etudiant_nomcomplet = form.etudiant.data.upper().strip()
stm = text( stm = text(
@ -799,7 +914,7 @@ def add_historique(id):
formation = etudiant.inscription_courante_date( formation = etudiant.inscription_courante_date(
form.date_debut.data, form.date_fin.data form.date_debut.data, form.date_fin.data
) )
historique = EntrepriseEtudiant( stage_apprentissage = EntrepriseStageApprentissage(
entreprise_id=entreprise.id, entreprise_id=entreprise.id,
etudid=etudiant.id, etudid=etudiant.id,
type_offre=form.type_offre.data.strip(), type_offre=form.type_offre.data.strip(),
@ -809,19 +924,105 @@ def add_historique(id):
formation_scodoc=formation.formsemestre.formsemestre_id formation_scodoc=formation.formsemestre.formsemestre_id
if formation if formation
else None, else None,
notes=form.notes.data.strip(),
) )
db.session.add(historique) db.session.add(stage_apprentissage)
db.session.commit() db.session.commit()
flash("L'étudiant a été ajouté sur la fiche entreprise.") flash("L'étudiant a été ajouté sur la fiche entreprise.")
return redirect(url_for("entreprises.fiche_entreprise", id=entreprise.id)) return redirect(url_for("entreprises.fiche_entreprise", id=entreprise.id))
return render_template( return render_template(
"entreprises/ajout_historique.html", "entreprises/ajout_stage_apprentissage.html",
title="Ajout historique", title="Ajout stage / apprentissage",
form=form, form=form,
) )
@bp.route("/envoyer_offre/<int:id>", methods=["GET", "POST"]) @bp.route(
"/fiche_entreprise/edit_stage_apprentissage/<int:id>", methods=["GET", "POST"]
)
@permission_required(Permission.RelationsEntreprisesChange)
def edit_stage_apprentissage(id):
"""
Permet de modifier un étudiant ayant réalisé un stage ou une alternance sur la fiche entreprise de l'entreprise
"""
stage_apprentissage = EntrepriseStageApprentissage.query.filter_by(
id=id
).first_or_404(description=f"stage_apprentissage {id} inconnue")
etudiant = Identite.query.filter_by(id=stage_apprentissage.etudid).first_or_404(
description=f"etudiant {id} inconnue"
)
form = StageApprentissageModificationForm()
if form.validate_on_submit():
etudiant_nomcomplet = form.etudiant.data.upper().strip()
stm = text(
"SELECT id, CONCAT(nom, ' ', prenom) as nom_prenom FROM Identite WHERE CONCAT(nom, ' ', prenom)=:nom_prenom"
)
etudiant = (
Identite.query.from_statement(stm)
.params(nom_prenom=etudiant_nomcomplet)
.first()
)
formation = etudiant.inscription_courante_date(
form.date_debut.data, form.date_fin.data
)
stage_apprentissage.etudid = etudiant.id
stage_apprentissage.type_offre = form.type_offre.data.strip()
stage_apprentissage.date_debut = form.date_debut.data
stage_apprentissage.date_fin = form.date_fin.data
stage_apprentissage.formation_text = (
formation.formsemestre.titre if formation else None,
)
stage_apprentissage.formation_scodoc = (
formation.formsemestre.formsemestre_id if formation else None,
)
stage_apprentissage.notes = form.notes.data.strip()
db.session.commit()
return redirect(
url_for(
"entreprises.fiche_entreprise", id=stage_apprentissage.entreprise_id
)
)
elif request.method == "GET":
form.etudiant.data = f"{sco_etud.format_nom(etudiant.nom)} {sco_etud.format_prenom(etudiant.prenom)}"
form.type_offre.data = stage_apprentissage.type_offre
form.date_debut.data = stage_apprentissage.date_debut
form.date_fin.data = stage_apprentissage.date_fin
form.notes.data = stage_apprentissage.notes
return render_template(
"entreprises/ajout_stage_apprentissage.html",
title="Modification stage / apprentissage",
form=form,
)
@bp.route(
"/fiche_entreprise/delete_stage_apprentissage/<int:id>", methods=["GET", "POST"]
)
@permission_required(Permission.RelationsEntreprisesChange)
def delete_stage_apprentissage(id):
"""
Permet de supprimer un étudiant ayant réalisé un stage ou une alternance sur la fiche entreprise de l'entreprise
"""
stage_apprentissage = EntrepriseStageApprentissage.query.filter_by(
id=id
).first_or_404(description=f"stage_apprentissage {id} inconnu")
form = SuppressionConfirmationForm()
if form.validate_on_submit():
db.session.delete(stage_apprentissage)
db.session.commit()
return redirect(
url_for(
"entreprises.fiche_entreprise", id=stage_apprentissage.entreprise_id
)
)
return render_template(
"entreprises/delete_confirmation.html",
title="Supression stage/apprentissage",
form=form,
)
@bp.route("/fiche_entreprise/envoyer_offre/<int:id>", methods=["GET", "POST"])
@permission_required(Permission.RelationsEntreprisesSend) @permission_required(Permission.RelationsEntreprisesSend)
def envoyer_offre(id): def envoyer_offre(id):
""" """
@ -832,7 +1033,9 @@ def envoyer_offre(id):
) )
form = EnvoiOffreForm() form = EnvoiOffreForm()
if form.validate_on_submit(): if form.validate_on_submit():
responsable_data = form.responsable.data.upper().strip() for responsable in form.responsables.entries:
if responsable.data.strip():
responsable_data = responsable.data.upper().strip()
stm = text( stm = text(
"SELECT id, UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')')) FROM \"user\" WHERE UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')'))=:responsable_data" "SELECT id, UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')')) FROM \"user\" WHERE UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')'))=:responsable_data"
) )
@ -924,10 +1127,11 @@ def export_entreprises():
filename = title filename = title
return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE) return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE)
else: else:
abort(404) flash("Aucune entreprise dans la base.")
return redirect(url_for("entreprises.index"))
@bp.route("/get_import_entreprises_file_sample") @bp.route("/import_entreprises/get_import_entreprises_file_sample")
@permission_required(Permission.RelationsEntreprisesExport) @permission_required(Permission.RelationsEntreprisesExport)
def get_import_entreprises_file_sample(): def get_import_entreprises_file_sample():
""" """
@ -978,20 +1182,19 @@ def import_entreprises():
ligne += 1 ligne += 1
if ( if (
are.verif_entreprise_data(entreprise_data) are.verif_entreprise_data(entreprise_data)
and entreprise_data[0] not in siret_list and entreprise_data[0].replace(" ", "") not in siret_list
): ):
siret_list.append(entreprise_data[0]) siret_list.append(entreprise_data[0].replace(" ", ""))
entreprise = Entreprise( entreprise = Entreprise(
siret=entreprise_data[0], siret=entreprise_data[0].replace(" ", ""),
nom=entreprise_data[1], nom=entreprise_data[1].strip(),
adresse=entreprise_data[2], adresse=entreprise_data[2].strip(),
ville=entreprise_data[3], ville=entreprise_data[3].strip(),
codepostal=entreprise_data[4], codepostal=entreprise_data[4].strip(),
pays=entreprise_data[5], pays=entreprise_data[5].strip(),
visible=True, visible=True,
) )
entreprises_import.append(entreprise) entreprises_import.append(entreprise)
else: else:
flash(f"Erreur lors de l'importation (ligne {ligne})") flash(f"Erreur lors de l'importation (ligne {ligne})")
return render_template( return render_template(
@ -1026,19 +1229,19 @@ def import_entreprises():
) )
@bp.route("/export_contacts") @bp.route("/export_correspondants")
@permission_required(Permission.RelationsEntreprisesExport) @permission_required(Permission.RelationsEntreprisesExport)
def export_contacts(): def export_correspondants():
""" """
Permet d'exporter la liste des contacts sous format excel (.xlsx) Permet d'exporter la liste des correspondants sous format excel (.xlsx)
""" """
contacts = ( correspondants = (
db.session.query(EntrepriseContact) db.session.query(EntrepriseCorrespondant)
.join(Entreprise, EntrepriseContact.entreprise_id == Entreprise.id) .join(Entreprise, EntrepriseCorrespondant.entreprise_id == Entreprise.id)
.filter_by(visible=True) .filter_by(visible=True)
.all() .all()
) )
if contacts: if correspondants:
keys = [ keys = [
"nom", "nom",
"prenom", "prenom",
@ -1049,20 +1252,24 @@ def export_contacts():
"entreprise_siret", "entreprise_siret",
] ]
titles = keys[:] titles = keys[:]
L = [[contact.to_dict().get(k, "") for k in keys] for contact in contacts] L = [
title = "Contacts" [correspondant.to_dict().get(k, "") for k in keys]
for correspondant in correspondants
]
title = "Correspondants"
xlsx = sco_excel.excel_simple_table(titles=titles, lines=L, sheet_name=title) xlsx = sco_excel.excel_simple_table(titles=titles, lines=L, sheet_name=title)
filename = title filename = title
return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE) return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE)
else: else:
abort(404) flash("Aucun correspondant dans la base.")
return redirect(url_for("entreprises.correspondants"))
@bp.route("/get_import_contacts_file_sample") @bp.route("/import_correspondants/get_import_correspondants_file_sample")
@permission_required(Permission.RelationsEntreprisesExport) @permission_required(Permission.RelationsEntreprisesExport)
def get_import_contacts_file_sample(): def get_import_correspondants_file_sample():
""" """
Permet de récupérer un fichier exemple vide pour pouvoir importer des contacts Permet de récupérer un fichier exemple vide pour pouvoir importer des correspondants
""" """
keys = [ keys = [
"nom", "nom",
@ -1074,17 +1281,17 @@ def get_import_contacts_file_sample():
"entreprise_siret", "entreprise_siret",
] ]
titles = keys[:] titles = keys[:]
title = "ImportContacts" title = "ImportCorrespondants"
xlsx = sco_excel.excel_simple_table(titles=titles, sheet_name="Contacts") xlsx = sco_excel.excel_simple_table(titles=titles, sheet_name="Correspondants")
filename = title filename = title
return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE) return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE)
@bp.route("/import_contacts", methods=["GET", "POST"]) @bp.route("/import_correspondants", methods=["GET", "POST"])
@permission_required(Permission.RelationsEntreprisesExport) @permission_required(Permission.RelationsEntreprisesExport)
def import_contacts(): def import_correspondants():
""" """
Permet d'importer des contacts a l'aide d'un fichier excel (.xlsx) Permet d'importer des correspondants a l'aide d'un fichier excel (.xlsx)
""" """
form = ImportForm() form = ImportForm()
if form.validate_on_submit(): if form.validate_on_submit():
@ -1095,8 +1302,8 @@ def import_contacts():
file.save(file_path) file.save(file_path)
data = sco_excel.excel_file_to_list(file_path) data = sco_excel.excel_file_to_list(file_path)
os.remove(file_path) os.remove(file_path)
contacts_import = [] correspondants_import = []
contact_list = [] correspondant_list = []
ligne = 0 ligne = 0
titles = [ titles = [
"nom", "nom",
@ -1110,57 +1317,72 @@ def import_contacts():
if data[1][0] != titles: if data[1][0] != titles:
flash("Veuillez utilisez la feuille excel à remplir") flash("Veuillez utilisez la feuille excel à remplir")
return render_template( return render_template(
"entreprises/import_contacts.html", "entreprises/import_correspondants.html",
title="Importation contacts", title="Importation correspondants",
form=form, form=form,
) )
for contact_data in data[1][1:]: for correspondant_data in data[1][1:]:
ligne += 1 ligne += 1
if ( if (
are.verif_contact_data(contact_data) are.verif_correspondant_data(correspondant_data)
and (contact_data[0], contact_data[1], contact_data[6]) and (
not in contact_list correspondant_data[0].strip(),
): correspondant_data[1].strip(),
contact_list.append((contact_data[0], contact_data[1], contact_data[6])) correspondant_data[6].strip(),
contact = EntrepriseContact(
nom=contact_data[0],
prenom=contact_data[1],
telephone=contact_data[2],
mail=contact_data[3],
poste=contact_data[4],
service=contact_data[5],
entreprise_id=contact_data[6],
) )
contacts_import.append(contact) not in correspondant_list
):
correspondant_list.append(
(
correspondant_data[0].strip(),
correspondant_data[1].strip(),
correspondant_data[6].strip(),
)
)
entreprise = Entreprise.query.filter_by(
siret=correspondant_data[6].strip()
).first()
correspondant = EntrepriseCorrespondant(
nom=correspondant_data[0].strip(),
prenom=correspondant_data[1].strip(),
telephone=correspondant_data[2].strip(),
mail=correspondant_data[3].strip(),
poste=correspondant_data[4].strip(),
service=correspondant_data[5].strip(),
entreprise_id=entreprise.id,
)
correspondants_import.append(correspondant)
else: else:
flash(f"Erreur lors de l'importation (ligne {ligne})") flash(f"Erreur lors de l'importation (ligne {ligne})")
return render_template( return render_template(
"entreprises/import_contacts.html", "entreprises/import_correspondants.html",
title="Importation contacts", title="Importation correspondants",
form=form, form=form,
) )
if len(contacts_import) > 0: if len(correspondants_import) > 0:
for contact in contacts_import: for correspondant in correspondants_import:
db.session.add(contact) db.session.add(correspondant)
log = EntrepriseLog( log = EntrepriseLog(
authenticated_user=current_user.user_name, authenticated_user=current_user.user_name,
text=f"Importation de {len(contacts_import)} contact(s)", text=f"Importation de {len(correspondants_import)} correspondant(s)",
) )
db.session.add(log) db.session.add(log)
db.session.commit() db.session.commit()
flash(f"Importation réussie de {len(contacts_import)} contact(s)") flash(
f"Importation réussie de {len(correspondants_import)} correspondant(s)"
)
return render_template( return render_template(
"entreprises/import_contacts.html", "entreprises/import_correspondants.html",
title="Importation Contacts", title="Importation correspondants",
form=form, form=form,
contacts_import=contacts_import, correspondants_import=correspondants_import,
) )
else: else:
flash('Feuille "Contacts" vide') flash('Feuille "Correspondants" vide')
return render_template( return render_template(
"entreprises/import_contacts.html", "entreprises/import_correspondants.html",
title="Importation contacts", title="Importation correspondants",
form=form, form=form,
) )
@ -1198,7 +1420,7 @@ def get_offre_file(entreprise_id, offre_id, filedir, filename):
abort(404, description=f"fichier {filename} inconnu") abort(404, description=f"fichier {filename} inconnu")
@bp.route("/add_offre_file/<int:offre_id>", methods=["GET", "POST"]) @bp.route("/fiche_entreprise/add_offre_file/<int:offre_id>", methods=["GET", "POST"])
@permission_required(Permission.RelationsEntreprisesChange) @permission_required(Permission.RelationsEntreprisesChange)
def add_offre_file(offre_id): def add_offre_file(offre_id):
""" """
@ -1230,7 +1452,10 @@ def add_offre_file(offre_id):
) )
@bp.route("/delete_offre_file/<int:offre_id>/<string:filedir>", methods=["GET", "POST"]) @bp.route(
"/fiche_entreprise/delete_offre_file/<int:offre_id>/<string:filedir>",
methods=["GET", "POST"],
)
@permission_required(Permission.RelationsEntreprisesChange) @permission_required(Permission.RelationsEntreprisesChange)
def delete_offre_file(offre_id, filedir): def delete_offre_file(offre_id, filedir):
""" """

View File

@ -15,6 +15,10 @@
} }
.form-error {
color: #a94442;
}
.nav-entreprise>ul>li>a:hover { .nav-entreprise>ul>li>a:hover {
color: red; color: red;
} }
@ -50,23 +54,23 @@
margin-bottom: -5px; margin-bottom: -5px;
} }
.entreprise, .contact, .offre { .entreprise, .correspondant, .offre {
border: solid 2px; border: solid 2px;
border-radius: 10px; border-radius: 10px;
padding: 10px; padding: 10px;
margin-bottom: 10px; margin-bottom: 10px;
} }
.contacts-et-offres { .correspondants-et-offres {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
.contacts-et-offres > div { .correspondants-et-offres > div {
flex: 1 0 0; flex: 1 0 0;
} }
.contacts-et-offres > div:nth-child(2) { .correspondants-et-offres > div:nth-child(2) {
margin-left: 20px; margin-left: 20px;
} }

View File

@ -1,26 +0,0 @@
{# -*- mode: jinja-html -*- #}
<div class="contact">
<div>
Nom : {{ contact.nom }}<br>
Prénom : {{ contact.prenom }}<br>
{% if contact.telephone %}
Téléphone : {{ contact.telephone }}<br>
{% endif %}
{% if contact.mail %}
Mail : {{ contact.mail }}<br>
{% endif %}
{% if contact.poste %}
Poste : {{ contact.poste }}<br>
{% endif %}
{% if contact.service %}
Service : {{ contact.service }}<br>
{% endif %}
</div>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<div class="parent-btn">
<a class="btn btn-primary" href="{{ url_for('entreprises.edit_contact', id=contact.id) }}">Modifier contact</a>
<a class="btn btn-danger" href="{{ url_for('entreprises.delete_contact', id=contact.id) }}">Supprimer contact</a>
</div>
{% endif %}
</div>

View File

@ -0,0 +1,26 @@
{# -*- mode: jinja-html -*- #}
<div class="correspondant">
<div>
Nom : {{ correspondant.nom }}<br>
Prénom : {{ correspondant.prenom }}<br>
{% if correspondant.telephone %}
Téléphone : {{ correspondant.telephone }}<br>
{% endif %}
{% if correspondant.mail %}
Mail : {{ correspondant.mail }}<br>
{% endif %}
{% if correspondant.poste %}
Poste : {{ correspondant.poste }}<br>
{% endif %}
{% if correspondant.service %}
Service : {{ correspondant.service }}<br>
{% endif %}
</div>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<div class="parent-btn">
<a class="btn btn-primary" href="{{ url_for('entreprises.edit_correspondant', id=correspondant.id) }}">Modifier correspondant</a>
<a class="btn btn-danger" href="{{ url_for('entreprises.delete_correspondant', id=correspondant.id) }}">Supprimer correspondant</a>
</div>
{% endif %}
</div>

View File

@ -1,6 +1,7 @@
{# -*- mode: jinja-html -*- #} {# -*- mode: jinja-html -*- #}
<div class="offre"> <div class="offre">
<div> <div>
Ajouté le {{ offre[0].date_ajout.strftime('%d/%m/%y') }} à {{ offre[0].date_ajout.strftime('%Hh%M') }}<br>
Intitulé : {{ offre[0].intitule }}<br> Intitulé : {{ offre[0].intitule }}<br>
Description : {{ offre[0].description }}<br> Description : {{ offre[0].description }}<br>
Type de l'offre : {{ offre[0].type_offre }}<br> Type de l'offre : {{ offre[0].type_offre }}<br>
@ -9,6 +10,16 @@
{% if offre[2] %} {% if offre[2] %}
Département(s) : {% for offre_dept in offre[2] %} <div class="offre-depts">{{ offre_dept.dept_id|get_dept_acronym }}</div> {% endfor %}<br> Département(s) : {% for offre_dept in offre[2] %} <div class="offre-depts">{{ offre_dept.dept_id|get_dept_acronym }}</div> {% endfor %}<br>
{% endif %} {% endif %}
{% if offre[0].correspondant_id %}
Contacté {{ offre[3].nom }} {{ offre[3].prenom }}
{% if offre[3].mail and offre[3].telephone %}
({{ offre[3].mail }} - {{ offre[3].telephone }})<br>
{% else %}
({{ offre[3].mail }}{{offre[3].telephone}})<br>
{% endif %}
{% endif %}
{% for fichier in offre[1] %} {% for fichier in offre[1] %}
<a href="{{ url_for('entreprises.get_offre_file', entreprise_id=entreprise.id, offre_id=offre[0].id, filedir=fichier[0], filename=fichier[1] )}}">{{ fichier[1] }}</a> <a href="{{ url_for('entreprises.get_offre_file', entreprise_id=entreprise.id, offre_id=offre[0].id, filedir=fichier[0], filename=fichier[1] )}}">{{ fichier[1] }}</a>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
@ -16,6 +27,7 @@
{% endif %} {% endif %}
<br> <br>
{% endfor %} {% endfor %}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<a href="{{ url_for('entreprises.add_offre_file', offre_id=offre[0].id) }}">Ajoutez un fichier</a> <a href="{{ url_for('entreprises.add_offre_file', offre_id=offre[0].id) }}">Ajoutez un fichier</a>
{% endif %} {% endif %}
@ -26,9 +38,11 @@
<a class="btn btn-primary" href="{{ url_for('entreprises.edit_offre', id=offre[0].id) }}">Modifier l'offre</a> <a class="btn btn-primary" href="{{ url_for('entreprises.edit_offre', id=offre[0].id) }}">Modifier l'offre</a>
<a class="btn btn-danger" href="{{ url_for('entreprises.delete_offre', id=offre[0].id) }}">Supprimer l'offre</a> <a class="btn btn-danger" href="{{ url_for('entreprises.delete_offre', id=offre[0].id) }}">Supprimer l'offre</a>
{% endif %} {% endif %}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesSend, None) %} {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesSend, None) %}
<a class="btn btn-primary" href="{{ url_for('entreprises.envoyer_offre', id=offre[0].id) }}">Envoyer l'offre</a> <a class="btn btn-primary" href="{{ url_for('entreprises.envoyer_offre', id=offre[0].id) }}">Envoyer l'offre</a>
{% endif %} {% endif %}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
{% if not offre[0].expired %} {% if not offre[0].expired %}
<a class="btn btn-danger" href="{{ url_for('entreprises.expired', id=offre[0].id) }}">Rendre expirée</a> <a class="btn btn-danger" href="{{ url_for('entreprises.expired', id=offre[0].id) }}">Rendre expirée</a>

View File

@ -0,0 +1,56 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block styles %}
{{super()}}
{% endblock %}
{% block app_content %}
<h1>{{ title }}</h1>
<br>
<div class="row">
<div class="col-md-4">
<p>
(*) champs requis
</p>
<form method="POST" action="" novalidate>
{{ form.hidden_tag() }}
{% for subfield in form.correspondants %}
{% for subsubfield in subfield %}
{% if subsubfield.errors %}
{% for error in subsubfield.errors %}
<p class="help-block form-error">{{ error }}</p>
{% endfor %}
{% endif %}
{% endfor %}
{% endfor %}
{{ form.correspondants }}
<div style="margin-bottom: 10px;">
<button class="btn btn-default" id="add-correspondant-field">Ajouter un correspondant</button>
<input class="btn btn-default" type="submit" value="Envoyer">
</div>
</form>
</div>
</div>
<script>
window.onload = function(e) {
let addCorrespondantFieldBtn = document.getElementById('add-correspondant-field');
addCorrespondantFieldBtn.addEventListener('click', function(e){
e.preventDefault();
let allCorrepondantsFieldWrapper = document.getElementById('correspondants');
let allCorrepondantsField = allCorrepondantsFieldWrapper.getElementsByTagName('input');
let correspondantInputIds = []
let csrf_token = document.getElementById('csrf_token').value;
for(let i = 0; i < allCorrepondantsField.length; i++) {
correspondantInputIds.push(parseInt(allCorrepondantsField[i].name.split('-')[1]));
}
let newFieldName = `correspondants-${Math.max(...correspondantInputIds) + 1}`;
allCorrepondantsFieldWrapper.insertAdjacentHTML('beforeend',`
<li><label for="${newFieldName}">Correspondants-${Math.max(...correspondantInputIds) + 1}</label> <table id="${newFieldName}"><tr><th><label for="${newFieldName}-nom">Nom (*)</label></th><td><input class="form-control" id="${newFieldName}-nom" name="${newFieldName}-nom" required type="text" value=""></td></tr><tr><th><label for="${newFieldName}-prenom">Prénom (*)</label></th><td><input class="form-control" id="${newFieldName}-prenom" name="${newFieldName}-prenom" required type="text" value=""></td></tr><tr><th><label for="${newFieldName}-telephone">Téléphone (*)</label></th><td><input class="form-control" id="${newFieldName}-telephone" name="${newFieldName}-telephone" type="text" value=""></td></tr><tr><th><label for="${newFieldName}-mail">Mail (*)</label></th><td><input class="form-control" id="${newFieldName}-mail" name="${newFieldName}-mail" type="text" value=""></td></tr><tr><th><label for="${newFieldName}-poste">Poste</label></th><td><input class="form-control" id="${newFieldName}-poste" name="${newFieldName}-poste" type="text" value=""></td></tr><tr><th><label for="${newFieldName}-service">Service</label></th><td><input class="form-control" id="${newFieldName}-service" name="${newFieldName}-service" type="text" value=""></td></tr></table><input id="${newFieldName}-csrf_token" name="${newFieldName}-csrf_token" type="hidden" value=${csrf_token}></li>
`);
});
}
</script>
{% endblock %}

View File

@ -3,7 +3,7 @@
{% import 'bootstrap/wtf.html' as wtf %} {% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %} {% block app_content %}
<h1>Ajout entreprise avec contact</h1> <h1>Ajout entreprise</h1>
<br> <br>
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-4">
@ -16,12 +16,18 @@
</div> </div>
<script> <script>
window.onload = function(e){ {# ajout margin-bottom sur le champ pays #}
var champ_pays = document.getElementById("pays")
if (champ_pays !== null) {
var closest_form_group = champ_pays.closest(".form-group")
closest_form_group.style.marginBottom = "50px"
}
document.getElementById("siret").addEventListener("keyup", autocomplete); document.getElementById("siret").addEventListener("keyup", autocomplete);
function autocomplete() { function autocomplete() {
var input = document.getElementById("siret").value; var input = document.getElementById("siret").value.replaceAll(" ", "")
if(input.length == 14) { if(input.length >= 14) {
fetch("https://entreprise.data.gouv.fr/api/sirene/v1/siret/" + input) fetch("https://entreprise.data.gouv.fr/api/sirene/v1/siret/" + input)
.then(response => { .then(response => {
if(response.ok) if(response.ok)
@ -48,13 +54,5 @@
document.getElementById("codepostal").value = '' document.getElementById("codepostal").value = ''
document.getElementById("ville").value = '' document.getElementById("ville").value = ''
} }
}
{# ajout margin-bottom sur le champ pays #}
var champ_pays = document.getElementById("pays")
if (champ_pays !== null) {
var closest_form_group = champ_pays.closest(".form-group")
closest_form_group.style.marginBottom = "50px"
}
</script> </script>
{% endblock %} {% endblock %}

View File

@ -9,7 +9,7 @@
{% endblock %} {% endblock %}
{% block app_content %} {% block app_content %}
<h1>Ajout historique</h1> <h1>{{ title }}</h1>
<br> <br>
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-4">

View File

@ -9,64 +9,51 @@
{% endblock %} {% endblock %}
{% block app_content %} {% block app_content %}
{% include 'entreprises/nav.html' %} <div class="container" style="margin-bottom: 10px;">
{% if logs %}
<div class="container">
<h3>Dernières opérations <a href="{{ url_for('entreprises.logs') }}">Voir tout</a></h3>
<ul>
{% for log in logs %}
<li><span style="margin-right: 10px;">{{ log.date.strftime('%d %b %Hh%M') }}</span><span>{{ log.text|safe }} par {{ log.authenticated_user|get_nomcomplet_by_username }}</span></li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="container boutons">
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) %}
<a class="btn btn-default" href="{{ url_for('entreprises.import_contacts') }}">Importer des contacts</a>
{% endif %}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) and contacts %}
<a class="btn btn-default" href="{{ url_for('entreprises.export_contacts') }}">Exporter la liste des contacts</a>
{% endif %}
</div>
<div class="container" style="margin-bottom: 10px;">
<h1>Liste des contacts</h1> <h1>Liste des contacts</h1>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<a class="btn btn-primary" style="margin-bottom:10px;" href="{{ url_for('entreprises.add_contact', id=entreprise_id) }}">Ajouter contact</a>
{% endif %}
<table id="table-contacts"> <table id="table-contacts">
<thead> <thead>
<tr> <tr>
<td data-priority="1">Nom</td> <td data-priority="">Date</td>
<td data-priority="3">Prenom</td> <td data-priority="">Utilisateur</td>
<td data-priority="4">Telephone</td> <td data-priority="">Notes</td>
<td data-priority="5">Mail</td> {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td data-priority="6">Poste</td> <td data-priority="">Action</td>
<td data-priority="7">Service</td> {% endif %}
<td data-priority="2">Entreprise</td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for contact in contacts %} {% for contact in contacts %}
<tr> <tr>
<td>{{ contact[0].nom }}</td> <td>{{ contact.date.strftime('%d/%m/%Y %Hh%M') }}</td>
<td>{{ contact[0].prenom }}</td> <td>{{ contact.user|get_nomcomplet_by_id }}</td>
<td>{{ contact[0].telephone }}</td> <td>{{ contact.notes }}</td>
<td>{{ contact[0].mail }}</td> {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td>{{ contact[0].poste}}</td> <td>
<td>{{ contact[0].service}}</td> <div class="btn-group">
<td><a href="{{ url_for('entreprises.fiche_entreprise', id=contact[1].id) }}">{{ contact[1].nom }}</a></td> <a class="btn btn-default dropdown-toggle" data-toggle="dropdown" href="#">Action
<span class="caret"></span>
</a>
<ul class="dropdown-menu pull-left">
<li><a href="{{ url_for('entreprises.edit_contact', id=contact.id) }}">Modifier</a></li>
</ul>
</div>
</td>
{% endif %}
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<td>Nom</td> <td>Date</td>
<td>Prenom</td> <td>Utilisateur</td>
<td>Telephone</td> <td>Notes</td>
<td>Mail</td> {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td>Poste</td> <td>Action</td>
<td>Service</td> {% endif %}
<td>Entreprise</td>
</tr> </tr>
</tfoot> </tfoot>
</table> </table>

View File

@ -0,0 +1,102 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% block styles %}
{{super()}}
<script src="/ScoDoc/static/jQuery/jquery-1.12.4.min.js"></script>
<link rel="stylesheet" type="text/css" href="/ScoDoc/static/DataTables/datatables.min.css">
<script type="text/javascript" charset="utf8" src="/ScoDoc/static/DataTables/datatables.min.js"></script>
{% endblock %}
{% block app_content %}
{% include 'entreprises/nav.html' %}
{% if logs %}
<div class="container">
<h3>Dernières opérations <a href="{{ url_for('entreprises.logs') }}">Voir tout</a></h3>
<ul>
{% for log in logs %}
<li><span style="margin-right: 10px;">{{ log.date.strftime('%d %b %Hh%M') }}</span><span>{{ log.text|safe }} par {{ log.authenticated_user|get_nomcomplet_by_username }}</span></li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="container boutons">
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) %}
<a class="btn btn-default" href="{{ url_for('entreprises.import_correspondants') }}">Importer des correspondants</a>
{% endif %}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) and correspondants %}
<a class="btn btn-default" href="{{ url_for('entreprises.export_correspondants') }}">Exporter la liste des correspondants</a>
{% endif %}
</div>
<div class="container" style="margin-bottom: 10px;">
<h1>Liste des correspondants</h1>
<table id="table-correspondants">
<thead>
<tr>
<td data-priority="1">Nom</td>
<td data-priority="3">Prenom</td>
<td data-priority="4">Téléphone</td>
<td data-priority="5">Mail</td>
<td data-priority="6">Poste</td>
<td data-priority="7">Service</td>
<td data-priority="2">Entreprise</td>
</tr>
</thead>
<tbody>
{% for correspondant in correspondants %}
<tr>
<td>{{ correspondant[0].nom }}</td>
<td>{{ correspondant[0].prenom }}</td>
<td>{{ correspondant[0].telephone }}</td>
<td>{{ correspondant[0].mail }}</td>
<td>{{ correspondant[0].poste}}</td>
<td>{{ correspondant[0].service}}</td>
<td><a href="{{ url_for('entreprises.fiche_entreprise', id=correspondant[1].id) }}">{{ correspondant[1].nom }}</a></td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td>Nom</td>
<td>Prenom</td>
<td>Téléphone</td>
<td>Mail</td>
<td>Poste</td>
<td>Service</td>
<td>Entreprise</td>
</tr>
</tfoot>
</table>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let table = new DataTable('#table-correspondants',
{
"autoWidth": false,
"responsive": {
"details": true
},
"pageLength": 10,
"language": {
"emptyTable": "Aucune donnée disponible dans le tableau",
"info": "Affichage de _START_ à _END_ sur _TOTAL_ entrées",
"infoEmpty": "Affichage de 0 à 0 sur 0 entrées",
"infoFiltered": "(filtrées depuis un total de _MAX_ entrées)",
"lengthMenu": "Afficher _MENU_ entrées",
"loadingRecords": "Chargement...",
"processing": "Traitement...",
"search": "Rechercher:",
"zeroRecords": "Aucune entrée correspondante trouvée",
"paginate": {
"next": "Suivante",
"previous": "Précédente"
}
}
});
});
</script>
{% endblock %}

View File

@ -16,7 +16,22 @@
<p> <p>
(*) champs requis (*) champs requis
</p> </p>
{{ wtf.quick_form(form, novalidate=True) }} <form method="POST" action="" novalidate>
{{ form.hidden_tag() }}
{{ form.responsables.label }}<br>
{% for subfield in form.responsables %}
{% if subfield.errors %}
{% for error in subfield.errors %}
<p class="help-block form-error">{{ error }}</p>
{% endfor %}
{% endif %}
{% endfor %}
{{ form.responsables }}
<div style="margin-bottom: 10px;">
<button class="btn btn-default" id="add-responsable-field">Ajouter un responsable</button>
<input class="btn btn-default" type="submit" value="Envoyer">
</div>
</form>
</div> </div>
</div> </div>
@ -30,7 +45,28 @@
minchars: 2, minchars: 2,
timeout: 60000 timeout: 60000
}; };
var as_responsables = new bsn.AutoSuggest('responsable', responsables_options);
let allResponsablesFieldWrapper = document.getElementById('responsables');
let allResponsablesField = allResponsablesFieldWrapper.getElementsByTagName('input');
for(let i = 0; i < allResponsablesField.length; i++) {
new bsn.AutoSuggest(allResponsablesField[i].id, responsables_options);
}
let addResponsableFieldBtn = document.getElementById('add-responsable-field');
addResponsableFieldBtn.addEventListener('click', function(e){
e.preventDefault();
let allResponsablesFieldWrapper = document.getElementById('responsables');
let allResponsablesField = allResponsablesFieldWrapper.getElementsByTagName('input');
let responsableInputIds = []
for(let i = 0; i < allResponsablesField.length; i++) {
responsableInputIds.push(parseInt(allResponsablesField[i].name.split('-')[1]));
}
let newFieldName = `responsables-${Math.max(...responsableInputIds) + 1}`;
allResponsablesFieldWrapper.insertAdjacentHTML('beforeend',`
<li><label for="${newFieldName}">Responsable (*)</label> <input class="form-control" id="${newFieldName}" name="${newFieldName}" type="text" value="" placeholder="Tapez le nom du responsable de formation"></li>
`);
var as_r = new bsn.AutoSuggest(newFieldName, responsables_options);
});
} }
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,6 +1,13 @@
{# -*- mode: jinja-html -*- #} {# -*- mode: jinja-html -*- #}
{% extends 'base.html' %} {% extends 'base.html' %}
{% block styles %}
{{super()}}
<script src="/ScoDoc/static/jQuery/jquery-1.12.4.min.js"></script>
<link rel="stylesheet" type="text/css" href="/ScoDoc/static/DataTables/datatables.min.css">
<script type="text/javascript" charset="utf8" src="/ScoDoc/static/DataTables/datatables.min.js"></script>
{% endblock %}
{% block app_content %} {% block app_content %}
{% if logs %} {% if logs %}
<div class="container"> <div class="container">
@ -16,24 +23,6 @@
</div> </div>
{% endif %} {% endif %}
{% if historique %}
<div class="container">
<h3>Historique</h3>
<ul>
{% for data in historique %}
<li>
<span style="margin-right: 10px;">{{ data[0].date_debut.strftime('%d/%m/%Y') }} - {{
data[0].date_fin.strftime('%d/%m/%Y') }}</span>
<span style="margin-right: 10px;">
{{ data[0].type_offre }} réalisé par {{ data[1].nom|format_nom }} {{ data[1].prenom|format_prenom }}
{% if data[0].formation_text %} en {{ data[0].formation_text }}{% endif %}
</span>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="container fiche-entreprise"> <div class="container fiche-entreprise">
<h2>Fiche entreprise - {{ entreprise.nom }} ({{ entreprise.siret }})</h2> <h2>Fiche entreprise - {{ entreprise.nom }} ({{ entreprise.siret }})</h2>
@ -53,20 +42,19 @@
<a class="btn btn-primary" href="{{ url_for('entreprises.edit_entreprise', id=entreprise.id) }}">Modifier</a> <a class="btn btn-primary" href="{{ url_for('entreprises.edit_entreprise', id=entreprise.id) }}">Modifier</a>
<a class="btn btn-danger" href="{{ url_for('entreprises.delete_entreprise', id=entreprise.id) }}">Supprimer</a> <a class="btn btn-danger" href="{{ url_for('entreprises.delete_entreprise', id=entreprise.id) }}">Supprimer</a>
<a class="btn btn-primary" href="{{ url_for('entreprises.add_offre', id=entreprise.id) }}">Ajouter offre</a> <a class="btn btn-primary" href="{{ url_for('entreprises.add_offre', id=entreprise.id) }}">Ajouter offre</a>
<a class="btn btn-primary" href="{{ url_for('entreprises.add_contact', id=entreprise.id) }}">Ajouter contact</a> <a class="btn btn-primary" href="{{ url_for('entreprises.add_correspondant', id=entreprise.id) }}">Ajouter correspondant</a>
<a class="btn btn-primary" href="{{ url_for('entreprises.add_historique', id=entreprise.id) }}">Ajouter
historique</a>
{% endif %} {% endif %}
<a class="btn btn-primary" href="{{ url_for('entreprises.contacts', id=entreprise.id) }}">Liste contacts</a>
<a class="btn btn-primary" href="{{ url_for('entreprises.offres_expirees', id=entreprise.id) }}">Voir les offres expirées</a> <a class="btn btn-primary" href="{{ url_for('entreprises.offres_expirees', id=entreprise.id) }}">Voir les offres expirées</a>
<div> <div>
<div class="contacts-et-offres"> <div class="correspondants-et-offres">
{% if contacts %} {% if correspondants %}
<div> <div>
<h3>Contacts</h3> <h3>Correspondants</h3>
{% for contact in contacts %} {% for correspondant in correspondants %}
{% include 'entreprises/_contact.html' %} {% include 'entreprises/_correspondant.html' %}
{% endfor %} {% endfor %}
</div> </div>
{% endif %} {% endif %}
@ -81,4 +69,95 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>
<div style="margin-bottom: 10px;">
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<a class="btn btn-primary" href="{{ url_for('entreprises.add_stage_apprentissage', id=entreprise.id) }}">Ajouter stage ou apprentissage</a>
{% endif %}
<h3>Liste des stages et apprentissages réalisés au sein de l'entreprise</h3>
<table id="table-stages-apprentissages">
<thead>
<tr>
<td data-priority="">Date début</td>
<td data-priority="">Date fin</td>
<td data-priority="">Durée</td>
<td data-priority="">Type</td>
<td data-priority="">Étudiant</td>
<td data-priority="">Formation</td>
<td data-priority="">Notes</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td data-priority="3">Action</td>
{% endif %}
</tr>
</thead>
<tbody>
{% for data in stages_apprentissages %}
<tr>
<td>{{ data[0].date_debut.strftime('%d/%m/%Y') }}</td>
<td>{{ data[0].date_fin.strftime('%d/%m/%Y') }}</td>
<td>{{ (data[0].date_fin-data[0].date_debut).days//7 }} semaines</td>
<td>{{ data[0].type_offre }}</td>
<td>{{ data[1].nom|format_nom }} {{ data[1].prenom|format_prenom }}</td>
<td>{% if data[0].formation_text %}{{ data[0].formation_text }}{% endif %}</td>
<td>{{ data[0].notes }}</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td>
<div class="btn-group">
<a class="btn btn-default dropdown-toggle" data-toggle="dropdown" href="#">Action
<span class="caret"></span>
</a>
<ul class="dropdown-menu pull-left">
<li><a href="{{ url_for('entreprises.edit_stage_apprentissage', id=data[0].id) }}">Modifier</a></li>
<li><a href="{{ url_for('entreprises.delete_stage_apprentissage', id=data[0].id) }}" style="color:red">Supprimer</a></li>
</ul>
</div>
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td>Date début</td>
<td>Date fin</td>
<td>Durée</td>
<td>Type</td>
<td>Étudiant</td>
<td>Formation</td>
<td>Notes</td>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<td>Action</td>
{% endif %}
</tr>
</tfoot>
</table>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
let table = new DataTable('#table-stages-apprentissages',
{
"autoWidth": false,
"responsive": {
"details": true
},
"pageLength": 10,
"language": {
"emptyTable": "Aucune donnée disponible dans le tableau",
"info": "Affichage de _START_ à _END_ sur _TOTAL_ entrées",
"infoEmpty": "Affichage de 0 à 0 sur 0 entrées",
"infoFiltered": "(filtrées depuis un total de _MAX_ entrées)",
"lengthMenu": "Afficher _MENU_ entrées",
"loadingRecords": "Chargement...",
"processing": "Traitement...",
"search": "Rechercher:",
"zeroRecords": "Aucune entrée correspondante trouvée",
"paginate": {
"next": "Suivante",
"previous": "Précédente"
}
}
});
});
</script>
{% endblock %} {% endblock %}

View File

@ -16,25 +16,25 @@
</div> </div>
</div> </div>
{% if contacts %} {% if correspondants %}
<div> <div>
{% for contact in contacts %} {% for correspondant in correspondants %}
<div> <div>
<h3>Contact</h3> <h3>Correspondant</h3>
<div class="contact"> <div class="correspondant">
Nom : {{ contact.nom }}<br> Nom : {{ correspondant.nom }}<br>
Prénom : {{ contact.prenom }}<br> Prénom : {{ correspondant.prenom }}<br>
{% if contact.telephone %} {% if correspondant.telephone %}
Téléphone : {{ contact.telephone }}<br> Téléphone : {{ correspondant.telephone }}<br>
{% endif %} {% endif %}
{% if contact.mail %} {% if correspondant.mail %}
Mail : {{ contact.mail }}<br> Mail : {{ correspondant.mail }}<br>
{% endif %} {% endif %}
{% if contact.poste %} {% if correspondant.poste %}
Poste : {{ contact.poste }}<br> Poste : {{ correspondant.poste }}<br>
{% endif %} {% endif %}
{% if contact.service %} {% if correspondant.service %}
Service : {{ contact.service }}<br> Service : {{ correspondant.service }}<br>
{% endif %} {% endif %}
</div> </div>
</div> </div>

View File

@ -4,6 +4,8 @@
{% block styles %} {% block styles %}
{{super()}} {{super()}}
<link type="text/css" rel="stylesheet" href="/ScoDoc/static/css/autosuggest_inquisitor.css" />
<script src="/ScoDoc/static/libjs/AutoSuggest.js"></script>
{% endblock %} {% endblock %}
{% block app_content %} {% block app_content %}
@ -25,5 +27,36 @@
var closest_form_control = champ_depts.closest(".form-control") var closest_form_control = champ_depts.closest(".form-control")
closest_form_control.classList.remove("form-control") closest_form_control.classList.remove("form-control")
} }
if(document.getElementById("expiration_date") !== null && document.getElementById("expiration_date").value === "")
expiration()
if(document.getElementById("type_offre") !== null)
document.getElementById("type_offre").addEventListener("change", expiration);
function expiration() {
var date = new Date()
var expiration = document.getElementById("expiration_date")
var type_offre = document.getElementById("type_offre").value
if (type_offre === "Alternance") {
expiration.value = `${date.getFullYear() + 1}-01-01`
} else {
if(date.getMonth() + 1 < 8)
expiration.value = `${date.getFullYear()}-08-01`
else
expiration.value = `${date.getFullYear() + 1}-08-01`
}
}
var responsables_options = {
script: "/ScoDoc/entreprises/responsables?",
varname: "term",
json: true,
noresults: "Valeur invalide !",
minchars: 2,
timeout: 60000
};
var as_utilisateurs = new bsn.AutoSuggest('utilisateur', responsables_options);
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,62 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block styles %}
{{super()}}
{% endblock %}
{% block app_content %}
<h1>Importation contacts</h1>
<br>
<div>
<a href="{{ url_for('entreprises.get_import_contacts_file_sample') }}">Obtenir la feuille excel à remplir</a>
</div>
<br>
<div class="row">
<div class="col-md-4">
<p>
(*) champs requis
</p>
{{ wtf.quick_form(form, novalidate=True) }}
</div>
</div>
{% if not contacts_import %}
<table class="table">
<thead><tr><td><b>Attribut</b></td><td><b>Type</b></td><td><b>Description</b></td></tr></thead>
<tr><td>nom</td><td>text</td><td>nom du contact</td></tr>
<tr><td>prenom</td><td>text</td><td>prenom du contact</td></tr>
<tr><td>telephone</td><td>text</td><td>telephone du contact</td></tr>
<tr><td>mail</td><td>text</td><td>mail du contact</td></tr>
<tr><td>poste</td><td>text</td><td>poste du contact</td></tr>
<tr><td>service</td><td>text</td><td>service dans lequel travaille le contact</td></tr>
<tr><td>entreprise_siret</td><td>integer</td><td>SIRET de l'entreprise</td></tr>
</table>
{% endif %}
{% if contacts_import %}
<br><div>Importation de {{ contacts_import|length }} contact(s)</div>
{% for contact in contacts_import %}
<div class="contact">
<div>
Nom : {{ contact.nom }}<br>
Prénom : {{ contact.prenom }}<br>
{% if contact.telephone %}
Téléphone : {{ contact.telephone }}<br>
{% endif %}
{% if contact.mail %}
Mail : {{ contact.mail }}<br>
{% endif %}
{% if contact.poste %}
Poste : {{ contact.poste }}<br>
{% endif %}
{% if contact.service %}
Service : {{ contact.service }}<br>
{% endif %}
<a href="{{ url_for('entreprises.fiche_entreprise', id=contact.entreprise_id )}}">lien vers l'entreprise</a>
</div>
</div>
{% endfor %}
{% endif %}
{% endblock %}

View File

@ -0,0 +1,62 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block styles %}
{{super()}}
{% endblock %}
{% block app_content %}
<h1>Importation correspondants</h1>
<br>
<div>
<a href="{{ url_for('entreprises.get_import_correspondants_file_sample') }}">Obtenir la feuille excel à remplir</a>
</div>
<br>
<div class="row">
<div class="col-md-4">
<p>
(*) champs requis
</p>
{{ wtf.quick_form(form, novalidate=True) }}
</div>
</div>
{% if not correspondants_import %}
<table class="table">
<thead><tr><td><b>Attribut</b></td><td><b>Type</b></td><td><b>Description</b></td></tr></thead>
<tr><td>nom</td><td>text</td><td>nom du correspondant</td></tr>
<tr><td>prenom</td><td>text</td><td>prenom du correspondant</td></tr>
<tr><td>telephone</td><td>text</td><td>telephone du correspondant</td></tr>
<tr><td>mail</td><td>text</td><td>mail du correspondant</td></tr>
<tr><td>poste</td><td>text</td><td>poste du correspondant</td></tr>
<tr><td>service</td><td>text</td><td>service dans lequel travaille le correspondant</td></tr>
<tr><td>entreprise_siret</td><td>integer</td><td>SIRET de l'entreprise</td></tr>
</table>
{% endif %}
{% if correspondants_import %}
<br><div>Importation de {{ correspondants_import|length }} correspondant(s)</div>
{% for correspondant in correspondants_import %}
<div class="correspondant">
<div>
Nom : {{ correspondant.nom }}<br>
Prénom : {{ correspondant.prenom }}<br>
{% if correspondant.telephone %}
Téléphone : {{ correspondant.telephone }}<br>
{% endif %}
{% if correspondant.mail %}
Mail : {{ correspondant.mail }}<br>
{% endif %}
{% if correspondant.poste %}
Poste : {{ correspondant.poste }}<br>
{% endif %}
{% if correspondant.service %}
Service : {{ correspondant.service }}<br>
{% endif %}
<a href="{{ url_for('entreprises.fiche_entreprise', id=correspondant.entreprise_id )}}">lien vers l'entreprise</a>
</div>
</div>
{% endfor %}
{% endif %}
{% endblock %}

View File

@ -2,7 +2,7 @@
<nav class="nav-entreprise"> <nav class="nav-entreprise">
<ul> <ul>
<li><a href="{{ url_for('entreprises.index') }}">Entreprises</a></li> <li><a href="{{ url_for('entreprises.index') }}">Entreprises</a></li>
<li><a href="{{ url_for('entreprises.contacts') }}">Contacts</a></li> <li><a href="{{ url_for('entreprises.correspondants') }}">Correspondants</a></li>
<li><a href="{{ url_for('entreprises.offres_recues') }}">Offres reçues</a></li> <li><a href="{{ url_for('entreprises.offres_recues') }}">Offres reçues</a></li>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesValidate, None) %} {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesValidate, None) %}
<li><a href="{{ url_for('entreprises.validation') }}">Entreprises à valider</a></li> <li><a href="{{ url_for('entreprises.validation') }}">Entreprises à valider</a></li>

View File

@ -10,12 +10,22 @@
{% for offre in offres_recues %} {% for offre in offres_recues %}
<div class="offre offre-recue"> <div class="offre offre-recue">
<div> <div>
Envoyé le {{ offre[0].date_envoi.strftime('%d %B %Y à %H:%M') }} par {{ offre[0].sender_id|get_nomcomplet_by_id }}<br> Envoyé le {{ offre[0].date_envoi.strftime('%d/%m/%Y') }} à {{ offre[0].date_envoi.strftime('%Hh%M') }} par {{ offre[0].sender_id|get_nomcomplet_by_id }}<br>
Intitulé : {{ offre[1].intitule }}<br> Intitulé : {{ offre[1].intitule }}<br>
Description : {{ offre[1].description }}<br> Description : {{ offre[1].description }}<br>
Type de l'offre : {{ offre[1].type_offre }}<br> Type de l'offre : {{ offre[1].type_offre }}<br>
Missions : {{ offre[1].missions }}<br> Missions : {{ offre[1].missions }}<br>
Durée : {{ offre[1].duree }}<br> Durée : {{ offre[1].duree }}<br>
{% if offre[1].correspondant_id %}
Contacté {{ offre[3].nom }} {{ offre[3].prenom }}
{% if offre[3].mail and offre[3].telephone %}
({{ offre[3].mail }} - {{ offre[3].telephone }})<br>
{% else %}
({{ offre[3].mail }}{{offre[3].telephone}})<br>
{% endif %}
{% endif %}
<a href="{{ url_for('entreprises.fiche_entreprise', id=offre[1].entreprise_id) }}">lien vers l'entreprise</a><br> <a href="{{ url_for('entreprises.fiche_entreprise', id=offre[1].entreprise_id) }}">lien vers l'entreprise</a><br>
{% for fichier in offre[2] %} {% for fichier in offre[2] %}

View File

@ -1,8 +1,8 @@
"""tables module gestion relations entreprises """tables module gestion relations entreprises
Revision ID: af05f03b81be Revision ID: e5043b68e6b9
Revises: b9aadc10227f Revises: b9aadc10227f
Create Date: 2022-03-01 17:12:32.927643 Create Date: 2022-04-04 09:14:54.605480
""" """
from alembic import op from alembic import op
@ -10,7 +10,7 @@ import sqlalchemy as sa
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = "af05f03b81be" revision = "e5043b68e6b9"
down_revision = "b9aadc10227f" down_revision = "b9aadc10227f"
branch_labels = None branch_labels = None
depends_on = None depends_on = None
@ -54,6 +54,19 @@ def upgrade():
op.create_table( op.create_table(
"are_contacts", "are_contacts",
sa.Column("id", sa.Integer(), nullable=False), sa.Column("id", sa.Integer(), nullable=False),
sa.Column("date", sa.DateTime(timezone=True), nullable=True),
sa.Column("user", sa.Integer(), nullable=True),
sa.Column("entreprise", sa.Integer(), nullable=True),
sa.Column("notes", sa.Text(), nullable=True),
sa.ForeignKeyConstraint(
["entreprise"], ["are_entreprises.id"], ondelete="cascade"
),
sa.ForeignKeyConstraint(["user"], ["user.id"], ondelete="cascade"),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"are_correspondants",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("entreprise_id", sa.Integer(), nullable=True), sa.Column("entreprise_id", sa.Integer(), nullable=True),
sa.Column("nom", sa.Text(), nullable=True), sa.Column("nom", sa.Text(), nullable=True),
sa.Column("prenom", sa.Text(), nullable=True), sa.Column("prenom", sa.Text(), nullable=True),
@ -67,7 +80,7 @@ def upgrade():
sa.PrimaryKeyConstraint("id"), sa.PrimaryKeyConstraint("id"),
) )
op.create_table( op.create_table(
"are_etudiants", "are_stages_apprentissages",
sa.Column("id", sa.Integer(), nullable=False), sa.Column("id", sa.Integer(), nullable=False),
sa.Column("entreprise_id", sa.Integer(), nullable=True), sa.Column("entreprise_id", sa.Integer(), nullable=True),
sa.Column("etudid", sa.Integer(), nullable=True), sa.Column("etudid", sa.Integer(), nullable=True),
@ -76,6 +89,7 @@ def upgrade():
sa.Column("date_fin", sa.Date(), nullable=True), sa.Column("date_fin", sa.Date(), nullable=True),
sa.Column("formation_text", sa.Text(), nullable=True), sa.Column("formation_text", sa.Text(), nullable=True),
sa.Column("formation_scodoc", sa.Integer(), nullable=True), sa.Column("formation_scodoc", sa.Integer(), nullable=True),
sa.Column("notes", sa.Text(), nullable=True),
sa.ForeignKeyConstraint( sa.ForeignKeyConstraint(
["entreprise_id"], ["are_entreprises.id"], ondelete="cascade" ["entreprise_id"], ["are_entreprises.id"], ondelete="cascade"
), ),
@ -98,6 +112,10 @@ def upgrade():
sa.Column("duree", sa.Text(), nullable=True), sa.Column("duree", sa.Text(), nullable=True),
sa.Column("expiration_date", sa.Date(), nullable=True), sa.Column("expiration_date", sa.Date(), nullable=True),
sa.Column("expired", sa.Boolean(), nullable=True), sa.Column("expired", sa.Boolean(), nullable=True),
sa.Column("correspondant_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
["correspondant_id"], ["are_correspondants.id"], ondelete="cascade"
),
sa.ForeignKeyConstraint( sa.ForeignKeyConstraint(
["entreprise_id"], ["are_entreprises.id"], ondelete="cascade" ["entreprise_id"], ["are_entreprises.id"], ondelete="cascade"
), ),
@ -146,9 +164,9 @@ def upgrade():
sa.ForeignKeyConstraint(["offre_id"], ["are_offres.id"], ondelete="cascade"), sa.ForeignKeyConstraint(["offre_id"], ["are_offres.id"], ondelete="cascade"),
sa.PrimaryKeyConstraint("id"), sa.PrimaryKeyConstraint("id"),
) )
op.drop_index("ix_entreprises_dept_id", table_name="entreprises")
op.drop_table("entreprise_contact") op.drop_table("entreprise_contact")
op.drop_table("entreprise_correspondant") op.drop_table("entreprise_correspondant")
op.drop_index("ix_entreprises_dept_id", table_name="entreprises")
op.drop_table("entreprises") op.drop_table("entreprises")
# ### end Alembic commands ### # ### end Alembic commands ###
@ -246,7 +264,8 @@ def downgrade():
op.drop_table("are_envoi_offre_etudiant") op.drop_table("are_envoi_offre_etudiant")
op.drop_table("are_envoi_offre") op.drop_table("are_envoi_offre")
op.drop_table("are_offres") op.drop_table("are_offres")
op.drop_table("are_etudiants") op.drop_table("are_stages_apprentissages")
op.drop_table("are_correspondants")
op.drop_table("are_contacts") op.drop_table("are_contacts")
op.drop_table("are_preferences") op.drop_table("are_preferences")
op.drop_table("are_logs") op.drop_table("are_logs")