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

This commit is contained in:
Emmanuel Viennet 2022-02-16 17:53:50 +01:00
commit 4d50830bd0
29 changed files with 1871 additions and 505 deletions

View File

@ -4,10 +4,11 @@
from flask import Blueprint
from app.scodoc import sco_etud
from app.auth.models import User
from app.models import Departement
bp = Blueprint("entreprises", __name__)
LOGS_LEN = 10
LOGS_LEN = 5
@bp.app_template_filter()
@ -21,9 +22,21 @@ def format_nom(s):
@bp.app_template_filter()
def get_nomcomplet(s):
def get_nomcomplet_by_username(s):
user = User.query.filter_by(user_name=s).first()
return user.get_nomcomplet()
@bp.app_template_filter()
def get_nomcomplet_by_id(id):
user = User.query.filter_by(id=id).first()
return user.get_nomcomplet()
@bp.app_template_filter()
def get_dept_acronym(id):
dept = Departement.query.filter_by(id=id).first()
return dept.acronym
from app.entreprises import routes

View File

@ -0,0 +1,153 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
#
##############################################################################
import os
from config import Config
import re
import requests
import glob
from flask_login import current_user
from app.entreprises.models import (
Entreprise,
EntrepriseContact,
EntrepriseOffre,
EntrepriseOffreDepartement,
)
from app.models import Departement
from app.scodoc.sco_permissions import Permission
def get_depts():
"""
Retourne une liste contenant les l'id des départements des roles de l'utilisateur courant
"""
depts = []
for role in current_user.user_roles:
dept_id = get_dept_id_by_acronym(role.dept)
if dept_id is not None:
depts.append(dept_id)
return depts
def get_dept_id_by_acronym(acronym):
"""
Retourne l'id d'un departement a l'aide de son acronym
"""
dept = Departement.query.filter_by(acronym=acronym).first()
if dept is not None:
return dept.id
return None
def check_offre_depts(depts, offre_depts):
"""
Retourne vrai si l'utilisateur a le droit de visibilité sur l'offre
"""
if current_user.has_permission(Permission.RelationsEntreprisesChange, None):
return True
for offre_dept in offre_depts:
if offre_dept.dept_id in depts:
return True
return False
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
"""
offre_depts = EntrepriseOffreDepartement.query.filter_by(offre_id=offre.id).all()
if not offre_depts or check_offre_depts(depts, offre_depts):
files = []
path = os.path.join(
Config.SCODOC_VAR_DIR,
"entreprises",
f"{offre.entreprise_id}",
f"{offre.id}",
)
if os.path.exists(path):
for dir in glob.glob(
f"{path}/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]"
):
for _file in glob.glob(f"{dir}/*"):
file = [os.path.basename(dir), os.path.basename(_file)]
files.append(file)
return [offre, files, offre_depts]
return None
def verif_contact_data(contact_data):
"""
Verifie les données d'une ligne Excel (contact)
contact_data[0]: nom
contact_data[1]: prenom
contact_data[2]: telephone
contact_data[3]: mail
contact_data[4]: poste
contact_data[5]: service
contact_data[6]: entreprise_id
"""
# champs obligatoires
if contact_data[0] == "" or contact_data[1] == "" or contact_data[6] == "":
return False
# entreprise_id existant
entreprise = Entreprise.query.filter_by(id=contact_data[6]).first()
if entreprise is None:
return False
# contact possède le meme nom et prénom dans la meme entreprise
contact = EntrepriseContact.query.filter_by(
nom=contact_data[0], prenom=contact_data[1], entreprise_id=contact_data[6]
).first()
if contact is not None:
return False
if contact_data[2] == "" and contact_data[3] == "": # 1 moyen de contact
return False
return True
def verif_entreprise_data(entreprise_data):
"""
Verifie les données d'une ligne Excel (entreprise)
"""
for data in entreprise_data: # champs obligatoires
if data == "":
return False
siret = entreprise_data[0].strip() # vérification sur le siret
if re.match("^\d{14}$", siret) is None:
return False
req = requests.get(f"https://entreprise.data.gouv.fr/api/sirene/v1/siret/{siret}")
if req.status_code != 200:
return False
entreprise = Entreprise.query.filter_by(siret=siret).first()
if entreprise is not None:
return False
return True

View File

@ -31,70 +31,76 @@ from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileAllowed, FileRequired
from markupsafe import Markup
from sqlalchemy import text
from wtforms import StringField, SubmitField, TextAreaField, SelectField, HiddenField
from wtforms.fields import EmailField, DateField
from wtforms.validators import ValidationError, DataRequired, Email
from wtforms import (
StringField,
SubmitField,
TextAreaField,
SelectField,
HiddenField,
SelectMultipleField,
DateField,
)
from wtforms.validators import ValidationError, DataRequired, Email, Optional
from wtforms.widgets import ListWidget, CheckboxInput
from app.entreprises.models import Entreprise, EntrepriseContact
from app.models import Identite
from app.models import Identite, Departement
from app.auth.models import User
CHAMP_REQUIS = "Ce champ est requis"
SUBMIT_MARGE = {"style": "margin-bottom: 10px;"}
def _build_string_field(label, required=True, render_kw=None):
if required:
return StringField(
label,
validators=[DataRequired(message=CHAMP_REQUIS)],
render_kw=render_kw,
)
else:
return StringField(label, validators=[Optional()], render_kw=render_kw)
class EntrepriseCreationForm(FlaskForm):
siret = StringField(
siret = _build_string_field(
"SIRET",
validators=[DataRequired(message=CHAMP_REQUIS)],
render_kw={"placeholder": "Numéro composé de 14 chiffres", "maxlength": "14"},
)
nom_entreprise = StringField(
"Nom de l'entreprise",
validators=[DataRequired(message=CHAMP_REQUIS)],
)
adresse = StringField(
"Adresse de l'entreprise",
validators=[DataRequired(message=CHAMP_REQUIS)],
)
codepostal = StringField(
"Code postal de l'entreprise",
validators=[DataRequired(message=CHAMP_REQUIS)],
)
ville = StringField(
"Ville de l'entreprise",
validators=[DataRequired(message=CHAMP_REQUIS)],
)
pays = StringField(
"Pays de l'entreprise",
validators=[DataRequired(message=CHAMP_REQUIS)],
render_kw={"style": "margin-bottom: 50px;"},
)
nom_entreprise = _build_string_field("Nom de l'entreprise")
adresse = _build_string_field("Adresse de l'entreprise")
codepostal = _build_string_field("Code postal de l'entreprise")
ville = _build_string_field("Ville de l'entreprise")
pays = _build_string_field("Pays de l'entreprise")
nom_contact = StringField(
"Nom du contact", validators=[DataRequired(message=CHAMP_REQUIS)]
)
prenom_contact = StringField(
"Prénom du contact",
validators=[DataRequired(message=CHAMP_REQUIS)],
)
telephone = StringField(
"Téléphone du contact",
validators=[DataRequired(message=CHAMP_REQUIS)],
)
mail = EmailField(
nom_contact = _build_string_field("Nom du contact")
prenom_contact = _build_string_field("Prénom du contact")
telephone = _build_string_field("Téléphone du contact", required=False)
mail = StringField(
"Mail du contact",
validators=[
DataRequired(message=CHAMP_REQUIS),
Email(message="Adresse e-mail invalide"),
],
validators=[Optional(), Email(message="Adresse e-mail invalide")],
)
poste = StringField("Poste du contact", validators=[])
service = StringField("Service du contact", validators=[])
submit = SubmitField("Envoyer", render_kw={"style": "margin-bottom: 10px;"})
poste = _build_string_field("Poste du contact", required=False)
service = _build_string_field("Service du contact", required=False)
submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE)
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
def validate_siret(self, siret):
siret = siret.data.strip()
if re.match("^\d{14}$", siret) == None:
if re.match("^\d{14}$", siret) is None:
raise ValidationError("Format incorrect")
req = requests.get(
f"https://entreprise.data.gouv.fr/api/sirene/v1/siret/{siret}"
@ -110,22 +116,22 @@ class EntrepriseCreationForm(FlaskForm):
class EntrepriseModificationForm(FlaskForm):
siret = StringField("SIRET", validators=[], render_kw={"disabled": ""})
nom = StringField(
"Nom de l'entreprise",
validators=[DataRequired(message=CHAMP_REQUIS)],
)
adresse = StringField("Adresse", validators=[DataRequired(message=CHAMP_REQUIS)])
codepostal = StringField(
"Code postal", validators=[DataRequired(message=CHAMP_REQUIS)]
)
ville = StringField("Ville", validators=[DataRequired(message=CHAMP_REQUIS)])
pays = StringField("Pays", validators=[DataRequired(message=CHAMP_REQUIS)])
submit = SubmitField("Modifier", render_kw={"style": "margin-bottom: 10px;"})
siret = StringField("SIRET", render_kw={"disabled": ""})
nom = _build_string_field("Nom de l'entreprise")
adresse = _build_string_field("Adresse")
codepostal = _build_string_field("Code postal")
ville = _build_string_field("Ville")
pays = _build_string_field("Pays")
submit = SubmitField("Modifier", render_kw=SUBMIT_MARGE)
class MultiCheckboxField(SelectMultipleField):
widget = ListWidget(prefix_label=False)
option_widget = CheckboxInput()
class OffreCreationForm(FlaskForm):
intitule = StringField("Intitulé", validators=[DataRequired(message=CHAMP_REQUIS)])
intitule = _build_string_field("Intitulé")
description = TextAreaField(
"Description", validators=[DataRequired(message=CHAMP_REQUIS)]
)
@ -134,15 +140,24 @@ class OffreCreationForm(FlaskForm):
choices=[("Stage"), ("Alternance")],
validators=[DataRequired(message=CHAMP_REQUIS)],
)
missions = TextAreaField(
"Missions", validators=[DataRequired(message=CHAMP_REQUIS)]
missions = _build_string_field("Missions")
duree = _build_string_field("Durée")
depts = MultiCheckboxField("Départements", validators=[Optional()], coerce=int)
expiration_date = DateField(
"Date expiration", validators=[DataRequired(message=CHAMP_REQUIS)]
)
duree = StringField("Durée", validators=[DataRequired(message=CHAMP_REQUIS)])
submit = SubmitField("Envoyer", render_kw={"style": "margin-bottom: 10px;"})
submit = SubmitField("Envoyer", 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()
]
class OffreModificationForm(FlaskForm):
intitule = StringField("Intitulé", validators=[DataRequired(message=CHAMP_REQUIS)])
intitule = _build_string_field("Intitulé")
description = TextAreaField(
"Description", validators=[DataRequired(message=CHAMP_REQUIS)]
)
@ -151,73 +166,104 @@ class OffreModificationForm(FlaskForm):
choices=[("Stage"), ("Alternance")],
validators=[DataRequired(message=CHAMP_REQUIS)],
)
missions = TextAreaField(
"Missions", validators=[DataRequired(message=CHAMP_REQUIS)]
missions = _build_string_field("Missions")
duree = _build_string_field("Durée")
depts = MultiCheckboxField("Départements", validators=[Optional()], coerce=int)
expiration_date = DateField(
"Date expiration", validators=[DataRequired(message=CHAMP_REQUIS)]
)
duree = StringField("Durée", validators=[DataRequired(message=CHAMP_REQUIS)])
submit = SubmitField("Modifier", render_kw={"style": "margin-bottom: 10px;"})
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()
]
class ContactCreationForm(FlaskForm):
hidden_entreprise_id = HiddenField()
nom = StringField("Nom", validators=[DataRequired(message=CHAMP_REQUIS)])
prenom = StringField("Prénom", validators=[DataRequired(message=CHAMP_REQUIS)])
telephone = StringField(
"Téléphone", validators=[DataRequired(message=CHAMP_REQUIS)]
)
mail = EmailField(
nom = _build_string_field("Nom")
prenom = _build_string_field("Prénom")
telephone = _build_string_field("Téléphone", required=False)
mail = StringField(
"Mail",
validators=[
DataRequired(message=CHAMP_REQUIS),
Email(message="Adresse e-mail invalide"),
],
validators=[Optional(), Email(message="Adresse e-mail invalide")],
)
poste = StringField("Poste", validators=[])
service = StringField("Service", validators=[])
submit = SubmitField("Envoyer", render_kw={"style": "margin-bottom: 10px;"})
poste = _build_string_field("Poste", required=False)
service = _build_string_field("Service", required=False)
submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE)
def validate(self):
rv = FlaskForm.validate(self)
if not rv:
return False
validate = True
if not FlaskForm.validate(self):
validate = False
contact = EntrepriseContact.query.filter_by(
entreprise_id=self.hidden_entreprise_id.data,
nom=self.nom.data,
prenom=self.prenom.data,
).first()
if contact is not None:
self.nom.errors.append("Ce contact existe déjà (même nom et prénom)")
self.prenom.errors.append("")
return False
validate = False
return True
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 ContactModificationForm(FlaskForm):
nom = StringField("Nom", validators=[DataRequired(message=CHAMP_REQUIS)])
prenom = StringField("Prénom", validators=[DataRequired(message=CHAMP_REQUIS)])
telephone = StringField(
"Téléphone", validators=[DataRequired(message=CHAMP_REQUIS)]
)
mail = EmailField(
hidden_contact_id = HiddenField()
hidden_entreprise_id = HiddenField()
nom = _build_string_field("Nom")
prenom = _build_string_field("Prénom")
telephone = _build_string_field("Téléphone", required=False)
mail = StringField(
"Mail",
validators=[
DataRequired(message=CHAMP_REQUIS),
Email(message="Adresse e-mail invalide"),
],
validators=[Optional(), Email(message="Adresse e-mail invalide")],
)
poste = StringField("Poste", validators=[])
service = StringField("Service", validators=[])
submit = SubmitField("Modifier", render_kw={"style": "margin-bottom: 10px;"})
poste = _build_string_field("Poste", required=False)
service = _build_string_field("Service", required=False)
submit = SubmitField("Modifier", render_kw=SUBMIT_MARGE)
def validate(self):
validate = True
if not FlaskForm.validate(self):
validate = False
contact = EntrepriseContact.query.filter(
EntrepriseContact.id != self.hidden_contact_id.data,
EntrepriseContact.entreprise_id == self.hidden_entreprise_id.data,
EntrepriseContact.nom == self.nom.data,
EntrepriseContact.prenom == self.prenom.data,
).first()
if contact is not None:
self.nom.errors.append("Ce contact existe déjà (même nom et prénom)")
self.prenom.errors.append("")
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 HistoriqueCreationForm(FlaskForm):
etudiant = StringField(
etudiant = _build_string_field(
"Étudiant",
validators=[DataRequired(message=CHAMP_REQUIS)],
render_kw={"placeholder": "Tapez le nom de l'étudiant puis selectionnez"},
render_kw={"placeholder": "Tapez le nom de l'étudiant"},
)
type_offre = SelectField(
"Type de l'offre",
@ -228,18 +274,23 @@ class HistoriqueCreationForm(FlaskForm):
"Date début", validators=[DataRequired(message=CHAMP_REQUIS)]
)
date_fin = DateField("Date fin", validators=[DataRequired(message=CHAMP_REQUIS)])
submit = SubmitField("Envoyer", render_kw={"style": "margin-bottom: 10px;"})
submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE)
def validate(self):
rv = FlaskForm.validate(self)
if not rv:
return False
validate = True
if not FlaskForm.validate(self):
validate = False
if self.date_debut.data > self.date_fin.data:
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")
return False
return True
validate = False
return validate
def validate_etudiant(self, etudiant):
etudiant_data = etudiant.data.upper().strip()
@ -254,11 +305,11 @@ class HistoriqueCreationForm(FlaskForm):
class EnvoiOffreForm(FlaskForm):
responsable = StringField(
responsable = _build_string_field(
"Responsable de formation",
validators=[DataRequired(message=CHAMP_REQUIS)],
render_kw={"placeholder": "Tapez le nom du responsable de formation"},
)
submit = SubmitField("Envoyer", render_kw={"style": "margin-bottom: 10px;"})
submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE)
def validate_responsable(self, responsable):
responsable_data = responsable.data.upper().strip()
@ -282,8 +333,23 @@ class AjoutFichierForm(FlaskForm):
FileAllowed(["pdf", "docx"], "Fichier .pdf ou .docx uniquement"),
],
)
submit = SubmitField("Envoyer", render_kw={"style": "margin-bottom: 10px;"})
submit = SubmitField("Ajouter", render_kw=SUBMIT_MARGE)
class SuppressionConfirmationForm(FlaskForm):
submit = SubmitField("Supprimer", render_kw={"style": "margin-bottom: 10px;"})
submit = SubmitField("Supprimer", render_kw=SUBMIT_MARGE)
class ValidationConfirmationForm(FlaskForm):
submit = SubmitField("Valider", render_kw=SUBMIT_MARGE)
class ImportForm(FlaskForm):
fichier = FileField(
"Fichier",
validators=[
FileRequired(message=CHAMP_REQUIS),
FileAllowed(["xlsx"], "Fichier .xlsx uniquement"),
],
)
submit = SubmitField("Importer", render_kw=SUBMIT_MARGE)

View File

@ -2,7 +2,7 @@ from app import db
class Entreprise(db.Model):
__tablename__ = "entreprises"
__tablename__ = "are_entreprises"
id = db.Column(db.Integer, primary_key=True)
siret = db.Column(db.Text)
nom = db.Column(db.Text)
@ -10,6 +10,7 @@ class Entreprise(db.Model):
codepostal = db.Column(db.Text)
ville = db.Column(db.Text)
pays = db.Column(db.Text)
visible = db.Column(db.Boolean, default=False)
contacts = db.relationship(
"EntrepriseContact",
backref="entreprise",
@ -28,17 +29,17 @@ class Entreprise(db.Model):
"siret": self.siret,
"nom": self.nom,
"adresse": self.adresse,
"codepostal": self.codepostal,
"code_postal": self.codepostal,
"ville": self.ville,
"pays": self.pays,
}
class EntrepriseContact(db.Model):
__tablename__ = "entreprise_contact"
__tablename__ = "are_entreprise_contact"
id = db.Column(db.Integer, primary_key=True)
entreprise_id = db.Column(
db.Integer, db.ForeignKey("entreprises.id", ondelete="cascade")
db.Integer, db.ForeignKey("are_entreprises.id", ondelete="cascade")
)
nom = db.Column(db.Text)
prenom = db.Column(db.Text)
@ -55,31 +56,15 @@ class EntrepriseContact(db.Model):
"mail": self.mail,
"poste": self.poste,
"service": self.service,
}
def to_dict_export(self):
entreprise = Entreprise.query.get(self.entreprise_id)
return {
"nom": self.nom,
"prenom": self.prenom,
"telephone": self.telephone,
"mail": self.mail,
"poste": self.poste,
"service": self.service,
"siret": entreprise.siret,
"nom_entreprise": entreprise.nom,
"adresse_entreprise": entreprise.adresse,
"codepostal": entreprise.codepostal,
"ville": entreprise.ville,
"pays": entreprise.pays,
"entreprise_id": self.entreprise_id,
}
class EntrepriseOffre(db.Model):
__tablename__ = "entreprise_offre"
__tablename__ = "are_entreprise_offre"
id = db.Column(db.Integer, primary_key=True)
entreprise_id = db.Column(
db.Integer, db.ForeignKey("entreprises.id", ondelete="cascade")
db.Integer, db.ForeignKey("are_entreprises.id", ondelete="cascade")
)
date_ajout = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
intitule = db.Column(db.Text)
@ -87,6 +72,7 @@ class EntrepriseOffre(db.Model):
type_offre = db.Column(db.Text)
missions = db.Column(db.Text)
duree = db.Column(db.Text)
expiration_date = db.Column(db.Date)
def to_dict(self):
return {
@ -99,7 +85,7 @@ class EntrepriseOffre(db.Model):
class EntrepriseLog(db.Model):
__tablename__ = "entreprise_log"
__tablename__ = "are_entreprise_log"
id = db.Column(db.Integer, primary_key=True)
date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
authenticated_user = db.Column(db.Text)
@ -108,9 +94,11 @@ class EntrepriseLog(db.Model):
class EntrepriseEtudiant(db.Model):
__tablename__ = "entreprise_etudiant"
__tablename__ = "are_entreprise_etudiant"
id = db.Column(db.Integer, primary_key=True)
entreprise_id = db.Column(db.Integer, db.ForeignKey("entreprises.id"))
entreprise_id = db.Column(
db.Integer, db.ForeignKey("are_entreprises.id", ondelete="cascade")
)
etudid = db.Column(db.Integer)
type_offre = db.Column(db.Text)
date_debut = db.Column(db.Date)
@ -120,18 +108,33 @@ class EntrepriseEtudiant(db.Model):
class EntrepriseEnvoiOffre(db.Model):
__tablename__ = "entreprise_envoi_offre"
__tablename__ = "are_entreprise_envoi_offre"
id = db.Column(db.Integer, primary_key=True)
sender_id = db.Column(db.Integer, db.ForeignKey("user.id"))
receiver_id = db.Column(db.Integer, db.ForeignKey("user.id"))
offre_id = db.Column(db.Integer, db.ForeignKey("entreprise_offre.id"))
sender_id = db.Column(db.Integer, db.ForeignKey("user.id", ondelete="cascade"))
receiver_id = db.Column(db.Integer, db.ForeignKey("user.id", ondelete="cascade"))
offre_id = db.Column(
db.Integer, db.ForeignKey("are_entreprise_offre.id", ondelete="cascade")
)
date_envoi = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
class EntrepriseEnvoiOffreEtudiant(db.Model):
__tablename__ = "entreprise_envoi_offre_etudiant"
__tablename__ = "are_entreprise_envoi_offre_etudiant"
id = db.Column(db.Integer, primary_key=True)
sender_id = db.Column(db.Integer, db.ForeignKey("user.id"))
receiver_id = db.Column(db.Integer, db.ForeignKey("identite.id"))
offre_id = db.Column(db.Integer, db.ForeignKey("entreprise_offre.id"))
sender_id = db.Column(db.Integer, db.ForeignKey("user.id", ondelete="cascade"))
receiver_id = db.Column(
db.Integer, db.ForeignKey("identite.id", ondelete="cascade")
)
offre_id = db.Column(
db.Integer, db.ForeignKey("are_entreprise_offre.id", ondelete="cascade")
)
date_envoi = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
class EntrepriseOffreDepartement(db.Model):
__tablename__ = "are_entreprise_offre_departement"
id = db.Column(db.Integer, primary_key=True)
offre_id = db.Column(
db.Integer, db.ForeignKey("are_entreprise_offre.id", ondelete="cascade")
)
dept_id = db.Column(db.Integer, db.ForeignKey("departement.id", ondelete="cascade"))

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
.nav-entreprise>ul {
padding-left: 0;
}
.nav-entreprise li{
list-style: none;
display: inline-block;
padding: 10px;
}
.nav-entreprise>ul>li>a {
text-decoration: none;
color: black;
padding: 15px;
}
.nav-entreprise>ul>li>a:hover {
color: red;
}
.boutons .btn {
margin-top: 5px;
margin-bottom: 5px;
}
.btn-inverse {
color: #ffffff;
text-shadow: 0 -1px 0 rgb(0 0 0 / 25%);
background-color: #363636;
}
.btn-inverse:hover {
color: #ffffff;
background-color: #222222;
*background-color: #151515;
}
.fiche-entreprise .btn {
margin-top: 5px;
margin-bottom: 5px;
}
.offre .btn {
margin-top: 5px;
margin-bottom: 5px;
}
.parent-btn {
margin-bottom: -5px;
}
.entreprise, .contact, .offre {
border: solid 2px;
border-radius: 10px;
padding: 10px;
margin-bottom: 10px;
}
.contacts-et-offres {
display: flex;
justify-content: space-between;
}
.contacts-et-offres > div {
flex: 1 0 0;
}
.contacts-et-offres > div:nth-child(2) {
margin-left: 20px;
}
#depts {
list-style: none;
padding-left: 10px;
}
.offre-depts {
display: inline-block;
border: black solid 2px;
border-radius: 5px;
padding: 1px;
}
.offre-recue {
display: flex;
justify-content: space-between;
}

View File

@ -4,6 +4,7 @@
{% block styles %}
{{super()}}
<link rel="stylesheet" href="/ScoDoc/static/css/scodoc.css">
<link rel="stylesheet" href="/ScoDoc/static/css/entreprises.css">
{% endblock %}
{% block title %}
@ -35,6 +36,9 @@
url_for('scolar.index_html', scodoc_dept=g.scodoc_dept)
}}">Dept. {{ g.scodoc_dept }}</a></li>
{% endif %}
{% if not current_user.is_anonymous and current_user.has_permission(current_user.Permission.RelationsEntreprisesView, None) %}
<li><a href="{{ url_for('entreprises.index') }}">Entreprises</a></li>
{% endif %}
</ul>
<ul class="nav navbar-nav navbar-right">
{% if current_user.is_anonymous %}

View File

@ -1,20 +1,26 @@
{# -*- mode: jinja-html -*- #}
<div class="contact">
<div>
<p>
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 %}
</p>
</div>
<div style="margin-bottom: 10px;">
{% 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

@ -1,21 +1,34 @@
{# -*- mode: jinja-html -*- #}
<div class="offre">
<div>
<p>
Intitulé : {{ offre[0].intitule }}<br>
Description : {{ offre[0].description }}<br>
Type de l'offre : {{ offre[0].type_offre }}<br>
Missions : {{ offre[0].missions }}<br>
Durée : {{ offre[0].duree }}<br>
{% 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>
{% endif %}
{% 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.delete_offre_file', offre_id=offre[0].id, filedir=fichier[0] )}}" style="margin-left: 5px;"><img title="Supprimer fichier" alt="supprimer" width="10" height="9" border="0" src="/ScoDoc/static/icons/delete_small_img.png" /></a><br>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<a href="{{ url_for('entreprises.delete_offre_file', offre_id=offre[0].id, filedir=fichier[0] )}}" style="margin-left: 5px;"><img title="Supprimer fichier" alt="supprimer" width="10" height="9" border="0" src="/ScoDoc/static/icons/delete_small_img.png" /></a>
{% endif %}
<br>
{% endfor %}
{% 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>
</p>
{% endif %}
</div>
<div style="margin-bottom: 10px;">
<div class="parent-btn">
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<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>
{% endif %}
{% 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>
{% endif %}
</div>
</div>

View File

@ -3,7 +3,7 @@
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>{{ title }}</h1>
<h1>Ajout entreprise avec contact</h1>
<br>
<div class="row">
<div class="col-md-4">
@ -13,6 +13,7 @@
{{ wtf.quick_form(form, novalidate=True) }}
</div>
</div>
<script>
window.onload = function(e){
document.getElementById("siret").addEventListener("keyup", autocomplete);
@ -50,5 +51,12 @@
document.getElementById("pays").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>
{% endblock %}

View File

@ -9,13 +9,14 @@
{% endblock %}
{% block app_content %}
<h1>{{ title }}</h1>
<h1>Ajout historique</h1>
<br>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form, novalidate=True) }}
</div>
</div>
<script>
window.onload = function(e) {
var etudiants_options = {

View File

@ -2,19 +2,31 @@
{% extends 'base.html' %}
{% block app_content %}
{% include 'entreprises/nav.html' %}
{% if logs %}
<div class="container">
<h3>Dernières opérations</h3>
<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 }}</span></li>
<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.items %}
<a class="btn btn-default" href="{{ url_for('entreprises.export_contacts') }}">Exporter la liste des contacts</a>
{% endif %}
</div>
<div class="container">
<h1>Liste des contacts</h1>
{% if contacts %}
{% if contacts.items %}
<div class="table-responsive">
<table class="table table-bordered table-hover">
<tr>
@ -26,7 +38,7 @@
<th>Service</th>
<th>Entreprise</th>
</tr>
{% for contact in contacts %}
{% for contact in contacts.items %}
<tr class="table-row active">
<th>{{ contact[0].nom }}</th>
<th>{{ contact[0].prenom }}</th>
@ -38,15 +50,32 @@
</tr>
{% endfor %}
</table>
</div>
<div class="text-center">
<a href="{{ url_for('entreprises.contacts', page=contacts.prev_num) }}" class="btn btn-default {% if contacts.page == 1 %}disabled{% endif %}">
&laquo;
</a>
{% for page_num in contacts.iter_pages(left_edge=1, right_edge=1, left_current=1, right_current=2) %}
{% if page_num %}
{% if contacts.page == page_num %}
<a href="{{ url_for('entreprises.contacts', page=page_num) }}" class="btn btn-inverse">{{ page_num }}</a>
{% else %}
<a href="{{ url_for('entreprises.contacts', page=page_num) }}" class="btn btn-default">{{ page_num }}</a>
{% endif %}
{% else %}
...
{% endif %}
{% endfor %}
<a href="{{ url_for('entreprises.contacts', page=contacts.next_num) }}" class="btn btn-default {% if contacts.page == contacts.pages %}disabled{% endif %}">
&raquo;
</a>
</div>
<p class="text-center">
Page {{ contacts.page }} sur {{ contacts.pages }}
</p>
{% else %}
<div>Aucun contact présent dans la base</div>
</div>
{% endif %}
<div>
{% if contacts %}
<a class="btn btn-default" href="{{ url_for('entreprises.export_contacts') }}">Exporter la liste des contacts</a>
<a class="btn btn-default" href="{{ url_for('entreprises.export_contacts_bis') }}">Exporter la liste des contacts avec leur entreprise</a>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -5,7 +5,7 @@
{% block app_content %}
<h1>{{ title }}</h1>
<br>
<div style="color:red">Cliquez sur le bouton supprimer pour confirmer votre supression</div>
<div style="color:red;">Cliquez sur le bouton Supprimer pour confirmer votre supression</div>
<br>
<div class="row">
<div class="col-md-4">

View File

@ -2,21 +2,38 @@
{% extends 'base.html' %}
{% block app_content %}
{% include 'entreprises/nav.html' %}
{% if logs %}
<div class="container">
<h3>Dernières opérations</h3>
<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 }}</span></li>
<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.RelationsEntreprisesChange, None) %}
<a class="btn btn-default" href="{{ url_for('entreprises.add_entreprise') }}">Ajouter une entreprise</a>
{% endif %}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) %}
<a class="btn btn-default" href="{{ url_for('entreprises.import_entreprises') }}">Importer des entreprises</a>
{% endif %}
{% if entreprises %}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) and entreprises.items%}
<a class="btn btn-default" href="{{ url_for('entreprises.export_entreprises') }}">Exporter la liste des entreprises</a>
{% endif %}
{% endif %}
</div>
<div class="container">
<h1>Liste des entreprises</h1>
{% if entreprises %}
{% if entreprises.items %}
<div class="table-responsive">
<table class="table table-bordered table-hover">
<table class="table table-bordered table-hover" style="margin-bottom:60px;">
<tr>
<th>SIRET</th>
<th>Nom</th>
@ -24,9 +41,11 @@
<th>Code postal</th>
<th>Ville</th>
<th>Pays</th>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<th>Action</th>
{% endif %}
</tr>
{% for entreprise in entreprises %}
{% for entreprise in entreprises.items %}
<tr class="table-row active">
<th><a href="{{ url_for('entreprises.fiche_entreprise', id=entreprise.id) }}">{{ entreprise.siret }}</a></th>
<th>{{ entreprise.nom }}</th>
@ -34,6 +53,7 @@
<th>{{ entreprise.codepostal }}</th>
<th>{{ entreprise.ville }}</th>
<th>{{ entreprise.pays }}</th>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<th>
<div class="btn-group">
<a class="btn btn-default dropdown-toggle" data-toggle="dropdown" href="#">Action
@ -45,19 +65,37 @@
</ul>
</div>
</th>
{% endif %}
</tr>
{% endfor %}
</table>
</div>
<div class="text-center">
<a href="{{ url_for('entreprises.index', page=entreprises.prev_num) }}" class="btn btn-default {% if entreprises.page == 1 %}disabled{% endif %}">
&laquo;
</a>
{% for page_num in entreprises.iter_pages(left_edge=1, right_edge=1, left_current=1, right_current=2) %}
{% if page_num %}
{% if entreprises.page == page_num %}
<a href="{{ url_for('entreprises.index', page=page_num) }}" class="btn btn-inverse">{{ page_num }}</a>
{% else %}
<a href="{{ url_for('entreprises.index', page=page_num) }}" class="btn btn-default">{{ page_num }}</a>
{% endif %}
{% else %}
...
{% endif %}
{% endfor %}
<a href="{{ url_for('entreprises.index', page=entreprises.next_num) }}" class="btn btn-default {% if entreprises.page == entreprises.pages %}disabled{% endif %}">
&raquo;
</a>
</div>
<p class="text-center">
Page {{ entreprises.page }} sur {{ entreprises.pages }}
</p>
{% else %}
<div>Aucune entreprise présent dans la base</div>
<br>
</div>
{% endif %}
<div style="margin-bottom: 20px;">
<a class="btn btn-default" href="{{ url_for('entreprises.add_entreprise') }}">Ajouter une entreprise</a>
{% if entreprises %}
<a class="btn btn-default" href="{{ url_for('entreprises.export_entreprises') }}">Exporter la liste des entreprises</a>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,51 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% block app_content %}
{% include 'entreprises/nav.html' %}
{% if logs %}
<div class="container">
<h3>Dernières opérations</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">
<h1>Liste des entreprises à valider</h1>
{% if entreprises %}
<div class="table-responsive">
<table class="table table-bordered table-hover">
<tr>
<th>SIRET</th>
<th>Nom</th>
<th>Adresse</th>
<th>Code postal</th>
<th>Ville</th>
<th>Pays</th>
<th>Action</th>
</tr>
{% for entreprise in entreprises %}
<tr class="table-row active">
<th><a href="{{ url_for('entreprises.fiche_entreprise_validation', id=entreprise.id) }}">{{ entreprise.siret }}</a></th>
<th>{{ entreprise.nom }}</th>
<th>{{ entreprise.adresse }}</th>
<th>{{ entreprise.codepostal }}</th>
<th>{{ entreprise.ville }}</th>
<th>{{ entreprise.pays }}</th>
<th>
<a class="btn btn-default" href="{{ url_for('entreprises.fiche_entreprise_validation', id=entreprise.id) }}">Voir</a>
</th>
</tr>
{% endfor %}
</table>
</div>
{% else %}
<div>Aucune entreprise à valider</div>
{% endif %}
</div>
{% endblock %}

View File

@ -9,13 +9,14 @@
{% endblock %}
{% block app_content %}
<h1>{{ title }}</h1>
<h1>Envoyer une offre</h1>
<br>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form, novalidate=True) }}
</div>
</div>
<script>
window.onload = function(e) {
var responsables_options = {

View File

@ -4,17 +4,18 @@
{% block app_content %}
{% if logs %}
<div class="container">
<h3>Dernières opérations sur cette fiche</h3>
<h3>Dernières opérations sur cette fiche <a href="{{ url_for('entreprises.logs_entreprise', id=entreprise.id) }}">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 }}</span>
<span>{{ log.text|safe }} par {{ log.authenticated_user|get_nomcomplet_by_username }}</span>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if historique %}
<div class="container">
<h3>Historique</h3>
@ -32,38 +33,22 @@
</ul>
</div>
{% endif %}
<div class="container">
<div class="container fiche-entreprise">
<h2>Fiche entreprise - {{ entreprise.nom }} ({{ entreprise.siret }})</h2>
<div class="entreprise">
<div>
<p>
SIRET : {{ entreprise.siret }}<br>
Nom : {{ entreprise.nom }}<br>
Adresse : {{ entreprise.adresse }}<br>
Code postal : {{ entreprise.codepostal }}<br>
Ville : {{ entreprise.ville }}<br>
Pays : {{ entreprise.pays }}
</p>
</div>
</div>
{% if contacts %}
<div>
{% for contact in contacts %}
Contact {{loop.index}}
{% include 'entreprises/_contact.html' %}
{% endfor %}
</div>
{% endif %}
{% if offres %}
<div>
{% for offre in offres %}
Offre {{loop.index}} (ajouté le {{offre[0].date_ajout.strftime('%d/%m/%Y') }})
{% include 'entreprises/_offre.html' %}
{% endfor %}
</div>
{% endif %}
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
<div>
<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>
@ -71,6 +56,29 @@
<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_historique', id=entreprise.id) }}">Ajouter
historique</a>
{% endif %}
<a class="btn btn-primary" href="{{ url_for('entreprises.offres_expirees', id=entreprise.id) }}">Voir les offres expirées</a>
<div>
<div class="contacts-et-offres">
{% if contacts %}
<div>
<h3>Contacts</h3>
{% for contact in contacts %}
{% include 'entreprises/_contact.html' %}
{% endfor %}
</div>
{% endif %}
{% if offres %}
<div>
<h3>Offres - <a href="{{ url_for('entreprises.offres_expirees', id=entreprise.id) }}">Voir les offres expirées</a></h3>
{% for offre in offres %}
{% include 'entreprises/_offre.html' %}
{% endfor %}
</div>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,50 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% block app_content %}
<div class="container">
<h2>Fiche entreprise - {{ entreprise.nom }} ({{ entreprise.siret }})</h2>
<div class="entreprise">
<div>
SIRET : {{ entreprise.siret }}<br>
Nom : {{ entreprise.nom }}<br>
Adresse : {{ entreprise.adresse }}<br>
Code postal : {{ entreprise.codepostal }}<br>
Ville : {{ entreprise.ville }}<br>
Pays : {{ entreprise.pays }}
</div>
</div>
{% if contacts %}
<div>
{% for contact in contacts %}
<div>
<h3>Contact</h3>
<div class="contact">
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>
</div>
{% endfor %}
</div>
{% endif %}
<div>
<a class="btn btn-success" href="{{ url_for('entreprises.validate_entreprise', id=entreprise.id) }}">Valider</a>
<a class="btn btn-danger" href="{{ url_for('entreprises.delete_validation_entreprise', id=entreprise.id) }}">Supprimer</a>
</div>
</div>
{% endblock %}

View File

@ -4,8 +4,6 @@
{% block styles %}
{{super()}}
<link type="text/css" rel="stylesheet" href="/ScoDoc/static/css/autosuggest_inquisitor.css" />
<script src="/ScoDoc/static/libjs/AutoSuggest.js"></script>
{% endblock %}
{% block app_content %}
@ -16,4 +14,13 @@
{{ wtf.quick_form(form, novalidate=True) }}
</div>
</div>
<script>
{# pour les formulaires offre #}
var champ_depts = document.getElementById("depts")
if (champ_depts !== null) {
var closest_form_control = champ_depts.closest(".form-control")
closest_form_control.classList.remove("form-control")
}
</script>
{% endblock %}

View File

@ -0,0 +1,59 @@
{# -*- 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">
{{ 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_id</td><td>integer</td><td>l'id 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,49 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block styles %}
{{super()}}
{% endblock %}
{% block app_content %}
<h1>Importation entreprises</h1>
<br>
<div>
<a href="{{ url_for('entreprises.get_import_entreprises_file_sample') }}">Obtenir la feuille excel à remplir</a>
</div>
<br>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form, novalidate=True) }}
</div>
</div>
{% if not entreprises_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>siret</td><td>text</td><td>siret de l'entreprise</td></tr>
<tr><td>nom</td><td>text</td><td>nom de l'entreprise</td></tr>
<tr><td>adresse</td><td>text</td><td>adresse de l'entreprise</td></tr>
<tr><td>ville</td><td>text</td><td>ville de l'entreprise</td></tr>
<tr><td>code_postal</td><td>text</td><td>code postal de l'entreprise</td></tr>
<tr><td>pays</td><td>text</td><td>pays de l'entreprise</td></tr>
</table>
{% endif %}
{% if entreprises_import %}
<br><div>Importation de {{ entreprises_import|length }} entreprise(s)</div>
{% for entreprise in entreprises_import %}
<div class="entreprise">
<div>
SIRET : {{ entreprise.siret }}<br>
Nom : {{ entreprise.nom }}<br>
Adresse : {{ entreprise.adresse }}<br>
Code postal : {{ entreprise.codepostal }}<br>
Ville : {{ entreprise.ville }}<br>
Pays : {{ entreprise.pays }}
</div>
</div>
{% endfor %}
{% endif %}
{% endblock %}

View File

@ -0,0 +1,43 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% block app_content %}
<div class="container">
<h3>Dernières opérations</h3>
{% if logs.items %}
<ul>
{% for log in logs.items %}
<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 class="text-center">
<a href="{{ url_for('entreprises.logs', page=logs.prev_num) }}" class="btn btn-default {% if logs.page == 1 %}disabled{% endif %}">
&laquo;
</a>
{% for page_num in logs.iter_pages(left_edge=1, right_edge=1, left_current=1, right_current=2) %}
{% if page_num %}
{% if logs.page == page_num %}
<a href="{{ url_for('entreprises.logs', page=page_num) }}" class="btn btn-inverse">{{ page_num }}</a>
{% else %}
<a href="{{ url_for('entreprises.logs', page=page_num) }}" class="btn btn-default">{{ page_num }}</a>
{% endif %}
{% else %}
...
{% endif %}
{% endfor %}
<a href="{{ url_for('entreprises.logs', page=logs.next_num) }}" class="btn btn-default {% if logs.page == logs.pages %}disabled{% endif %}">
&raquo;
</a>
</div>
<p class="text-center">
Page {{ logs.page }} sur {{ logs.pages }}
</p>
{% else %}
<div>Aucune opération</div>
{% endif %}
</div>
{% endblock %}

View File

@ -0,0 +1,42 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% block app_content %}
<div class="container">
<h3>Dernières opérations - {{ entreprise.nom }}</h3>
{% if logs.items %}
<ul>
{% for log in logs.items %}
<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 class="text-center">
<a href="{{ url_for('entreprises.logs_entreprise', id=entreprise.id, page=logs.prev_num) }}" class="btn btn-default {% if logs.page == 1 %}disabled{% endif %}">
&laquo;
</a>
{% for page_num in logs.iter_pages(left_edge=1, right_edge=1, left_current=1, right_current=2) %}
{% if page_num %}
{% if logs.page == page_num %}
<a href="{{ url_for('entreprises.logs_entreprise', id=entreprise.id, page=page_num) }}" class="btn btn-inverse">{{ page_num }}</a>
{% else %}
<a href="{{ url_for('entreprises.logs_entreprise', id=entreprise.id, page=page_num) }}" class="btn btn-default">{{ page_num }}</a>
{% endif %}
{% else %}
...
{% endif %}
{% endfor %}
<a href="{{ url_for('entreprises.logs_entreprise', id=entreprise.id, page=logs.next_num) }}" class="btn btn-default {% if logs.page == logs.pages %}disabled{% endif %}">
&raquo;
</a>
</div>
<p class="text-center">
Page {{ logs.page }} sur {{ logs.pages }}
</p>
{% else %}
<div>Aucune opération</div>
{% endif %}
</div>
{% endblock %}

View File

@ -0,0 +1,11 @@
{# -*- mode: jinja-html -*- #}
<nav class="nav-entreprise">
<ul>
<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.offres_recues') }}">Offres reçues</a></li>
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesValidate, None) %}
<li><a href="{{ url_for('entreprises.validation') }}">Entreprises à valider</a></li>
{% endif %}
</ul>
</nav>

View File

@ -1,29 +0,0 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% block app_content %}
<div class="container">
<h1>{{ title }}</h1>
{% if offres_recus %}
<div class="table-responsive">
<div>
{% for offre in offres_recus %}
<div>
<p>
Date envoi : {{ offre[0].date_envoi.strftime('%d/%m/%Y %H:%M') }}<br>
Intitulé : {{ offre[1].intitule }}<br>
Description : {{ offre[1].description }}<br>
Type de l'offre : {{ offre[1].type_offre }}<br>
Missions : {{ offre[1].missions }}<br>
Durée : {{ offre[1].duree }}<br>
</p>
</div>
{% endfor %}
</div>
<br>
{% else %}
<div>Aucune offre reçue</div>
</div>
{% endif %}
</div>
{% endblock %}

View File

@ -0,0 +1,15 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% block app_content %}
<div class="container">
<h1>Offres expirées - {{ entreprise.nom }}</h1>
{% if offres_expirees %}
{% for offre in offres_expirees %}
{% include 'entreprises/_offre.html' %}
{% endfor %}
{% else %}
<div>Aucune offre expirées</div>
{% endif %}
</div>
{% endblock %}

View File

@ -0,0 +1,34 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% block app_content %}
{% include 'entreprises/nav.html' %}
<div class="container">
<h1>Offres reçues</h1>
{% if offres_recues %}
{% for offre in offres_recues %}
<div class="offre offre-recue">
<div>
Envoyé le {{ offre[0].date_envoi.strftime('%d %B %Y à %H:%M') }} par {{ offre[0].sender_id|get_nomcomplet_by_id }}<br>
Intitulé : {{ offre[1].intitule }}<br>
Description : {{ offre[1].description }}<br>
Type de l'offre : {{ offre[1].type_offre }}<br>
Missions : {{ offre[1].missions }}<br>
Durée : {{ offre[1].duree }}<br>
<a href="{{ url_for('entreprises.fiche_entreprise', id=offre[1].entreprise_id) }}">lien vers l'entreprise</a><br>
{% for fichier in offre[2] %}
<a href="{{ url_for('entreprises.get_offre_file', entreprise_id=offre[1].entreprise_id, offre_id=offre[1].id, filedir=fichier[0], filename=fichier[1]) }}">{{ fichier[1] }}</a><br>
{% endfor %}
</div>
<div>
<a href="{{ url_for('entreprises.delete_offre_recue', id=offre[0].id) }}" style="margin-left: 5px;"><img title="Supprimer fichier" alt="supprimer" width="16" height="16" border="0" src="/ScoDoc/static/icons/delete_small_img.png" /></a>
</div>
</div>
{% endfor %}
{% else %}
<div>Aucune offre reçue</div>
{% endif %}
</div>
{% endblock %}

View File

@ -0,0 +1,15 @@
{# -*- mode: jinja-html -*- #}
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>Validation entreprise</h1>
<br>
<div style="color:green;">Cliquez sur le bouton Valider pour confirmer votre validation</div>
<br>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,253 @@
"""tables module gestion relations entreprises
Revision ID: 593451ab68b3
Revises: c95d5a3bf0de
Create Date: 2022-02-04 17:06:02.519231
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = "593451ab68b3"
down_revision = "c95d5a3bf0de"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"are_entreprise_log",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column(
"date",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=True,
),
sa.Column("authenticated_user", sa.Text(), nullable=True),
sa.Column("object", sa.Integer(), nullable=True),
sa.Column("text", sa.Text(), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"are_entreprises",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("siret", sa.Text(), nullable=True),
sa.Column("nom", sa.Text(), nullable=True),
sa.Column("adresse", sa.Text(), nullable=True),
sa.Column("codepostal", sa.Text(), nullable=True),
sa.Column("ville", sa.Text(), nullable=True),
sa.Column("pays", sa.Text(), nullable=True),
sa.Column("visible", sa.Boolean(), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"are_entreprise_contact",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("entreprise_id", sa.Integer(), nullable=True),
sa.Column("nom", sa.Text(), nullable=True),
sa.Column("prenom", sa.Text(), nullable=True),
sa.Column("telephone", sa.Text(), nullable=True),
sa.Column("mail", sa.Text(), nullable=True),
sa.Column("poste", sa.Text(), nullable=True),
sa.Column("service", sa.Text(), nullable=True),
sa.ForeignKeyConstraint(
["entreprise_id"], ["are_entreprises.id"], ondelete="cascade"
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"are_entreprise_etudiant",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("entreprise_id", sa.Integer(), nullable=True),
sa.Column("etudid", sa.Integer(), nullable=True),
sa.Column("type_offre", sa.Text(), nullable=True),
sa.Column("date_debut", sa.Date(), nullable=True),
sa.Column("date_fin", sa.Date(), nullable=True),
sa.Column("formation_text", sa.Text(), nullable=True),
sa.Column("formation_scodoc", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
["entreprise_id"], ["are_entreprises.id"], ondelete="cascade"
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"are_entreprise_offre",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("entreprise_id", sa.Integer(), nullable=True),
sa.Column(
"date_ajout",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=True,
),
sa.Column("intitule", sa.Text(), nullable=True),
sa.Column("description", sa.Text(), nullable=True),
sa.Column("type_offre", sa.Text(), nullable=True),
sa.Column("missions", sa.Text(), nullable=True),
sa.Column("duree", sa.Text(), nullable=True),
sa.Column("expiration_date", sa.Date(), nullable=True),
sa.ForeignKeyConstraint(
["entreprise_id"], ["are_entreprises.id"], ondelete="cascade"
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"are_entreprise_envoi_offre",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("sender_id", sa.Integer(), nullable=True),
sa.Column("receiver_id", sa.Integer(), nullable=True),
sa.Column("offre_id", sa.Integer(), nullable=True),
sa.Column(
"date_envoi",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=True,
),
sa.ForeignKeyConstraint(
["offre_id"], ["are_entreprise_offre.id"], ondelete="cascade"
),
sa.ForeignKeyConstraint(["receiver_id"], ["user.id"], ondelete="cascade"),
sa.ForeignKeyConstraint(["sender_id"], ["user.id"], ondelete="cascade"),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"are_entreprise_envoi_offre_etudiant",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("sender_id", sa.Integer(), nullable=True),
sa.Column("receiver_id", sa.Integer(), nullable=True),
sa.Column("offre_id", sa.Integer(), nullable=True),
sa.Column(
"date_envoi",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=True,
),
sa.ForeignKeyConstraint(
["offre_id"], ["are_entreprise_offre.id"], ondelete="cascade"
),
sa.ForeignKeyConstraint(["receiver_id"], ["identite.id"], ondelete="cascade"),
sa.ForeignKeyConstraint(["sender_id"], ["user.id"], ondelete="cascade"),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"are_entreprise_offre_departement",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("offre_id", sa.Integer(), nullable=True),
sa.Column("dept_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(["dept_id"], ["departement.id"], ondelete="cascade"),
sa.ForeignKeyConstraint(
["offre_id"], ["are_entreprise_offre.id"], ondelete="cascade"
),
sa.PrimaryKeyConstraint("id"),
)
op.drop_table("entreprise_contact")
op.drop_table("entreprise_correspondant")
op.drop_index("ix_entreprises_dept_id", table_name="entreprises")
op.drop_table("entreprises")
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"entreprises",
sa.Column("id", sa.INTEGER(), autoincrement=True, nullable=False),
sa.Column("nom", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("adresse", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("ville", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("codepostal", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("pays", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("localisation", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("dept_id", sa.INTEGER(), autoincrement=False, nullable=True),
sa.Column(
"date_creation",
postgresql.TIMESTAMP(timezone=True),
server_default=sa.text("now()"),
autoincrement=False,
nullable=True,
),
sa.Column("secteur", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("privee", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("plus10salaries", sa.BOOLEAN(), autoincrement=False, nullable=True),
sa.Column("contact_origine", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("note", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("qualite_relation", sa.INTEGER(), autoincrement=False, nullable=True),
sa.ForeignKeyConstraint(
["dept_id"], ["departement.id"], name="entreprises_dept_id_fkey"
),
sa.PrimaryKeyConstraint("id", name="entreprises_pkey"),
)
op.create_table(
"entreprise_correspondant",
sa.Column(
"id",
sa.INTEGER(),
server_default=sa.text(
"nextval('entreprise_correspondant_id_seq'::regclass)"
),
autoincrement=True,
nullable=False,
),
sa.Column("entreprise_id", sa.INTEGER(), autoincrement=False, nullable=True),
sa.Column("nom", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("prenom", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("civilite", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("fonction", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("phone1", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("phone2", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("mobile", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("mail1", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("mail2", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("fax", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("note", sa.TEXT(), autoincrement=False, nullable=True),
sa.ForeignKeyConstraint(
["entreprise_id"],
["entreprises.id"],
name="entreprise_correspondant_entreprise_id_fkey",
),
sa.PrimaryKeyConstraint("id", name="entreprise_correspondant_pkey"),
postgresql_ignore_search_path=False,
)
op.create_table(
"entreprise_contact",
sa.Column("id", sa.INTEGER(), autoincrement=True, nullable=False),
sa.Column("entreprise_id", sa.INTEGER(), autoincrement=False, nullable=True),
sa.Column(
"entreprise_corresp_id", sa.INTEGER(), autoincrement=False, nullable=True
),
sa.Column("etudid", sa.INTEGER(), autoincrement=False, nullable=True),
sa.Column("type_contact", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column(
"date",
postgresql.TIMESTAMP(timezone=True),
autoincrement=False,
nullable=True,
),
sa.Column("enseignant", sa.TEXT(), autoincrement=False, nullable=True),
sa.Column("description", sa.TEXT(), autoincrement=False, nullable=True),
sa.ForeignKeyConstraint(
["entreprise_corresp_id"],
["entreprise_correspondant.id"],
name="entreprise_contact_entreprise_corresp_id_fkey",
),
sa.ForeignKeyConstraint(
["entreprise_id"],
["entreprises.id"],
name="entreprise_contact_entreprise_id_fkey",
),
sa.PrimaryKeyConstraint("id", name="entreprise_contact_pkey"),
)
op.create_index("ix_entreprises_dept_id", "entreprises", ["dept_id"], unique=False)
op.drop_table("are_entreprise_offre_departement")
op.drop_table("are_entreprise_envoi_offre_etudiant")
op.drop_table("are_entreprise_envoi_offre")
op.drop_table("are_entreprise_offre")
op.drop_table("are_entreprise_etudiant")
op.drop_table("are_entreprise_contact")
op.drop_table("are_entreprises")
op.drop_table("are_entreprise_log")
# ### end Alembic commands ###