diff --git a/app/entreprises/app_relations_entreprises.py b/app/entreprises/app_relations_entreprises.py index 68ff9986..e3c0d1da 100644 --- a/app/entreprises/app_relations_entreprises.py +++ b/app/entreprises/app_relations_entreprises.py @@ -34,7 +34,7 @@ from flask_login import current_user from app.entreprises.models import ( Entreprise, - EntrepriseContact, + EntrepriseCorrespondant, EntrepriseOffre, EntrepriseOffreDepartement, 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 """ 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): files = [] 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}/*"): file = [os.path.basename(dir), os.path.basename(_file)] files.append(file) - return [offre, files, offre_depts] + return [offre, files, offre_depts, correspondant] return None -def send_email_notifications_entreprise( - subject, entreprise: Entreprise, contact: EntrepriseContact -): +def send_email_notifications_entreprise(subject, entreprise: Entreprise): txt = [ "Une entreprise est en attente de validation", "Entreprise:", @@ -116,14 +117,6 @@ def send_email_notifications_entreprise( f"\tcode postal: {entreprise.codepostal}", f"\tville: {entreprise.ville}", 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) email.send_email( @@ -135,34 +128,42 @@ def send_email_notifications_entreprise( return txt -def verif_contact_data(contact_data): +def verif_correspondant_data(correspondant_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 + Verifie les données d'une ligne Excel (correspondant) + correspondant_data[0]: nom + correspondant_data[1]: prenom + correspondant_data[2]: telephone + correspondant_data[3]: mail + correspondant_data[4]: poste + correspondant_data[5]: service + correspondant_data[6]: entreprise_id """ # 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 # 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: 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=entreprise.id + # correspondant possède le meme nom et prénom dans la meme entreprise + correspondant = EntrepriseCorrespondant.query.filter_by( + nom=correspondant_data[0].strip(), + prenom=correspondant_data[1].strip(), + entreprise_id=entreprise.id, ).first() - if contact is not None: + if correspondant is not None: 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 True @@ -174,23 +175,23 @@ def verif_entreprise_data(entreprise_data): """ if EntreprisePreferences.get_check_siret(): for data in entreprise_data: # champs obligatoires - if data == "": + if data.strip() == "": return False else: for data in entreprise_data[1:]: # champs obligatoires - if data == "": + if data.strip() == "": return False 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: return False try: req = requests.get( f"https://entreprise.data.gouv.fr/api/sirene/v1/siret/{siret}" ) + if req.status_code != 200: + return False except requests.ConnectionError: - print("no internet") - if req.status_code != 200: return False entreprise = Entreprise.query.filter_by(siret=siret).first() if entreprise is not None: diff --git a/app/entreprises/forms.py b/app/entreprises/forms.py index 0540c080..654b2618 100644 --- a/app/entreprises/forms.py +++ b/app/entreprises/forms.py @@ -40,11 +40,17 @@ from wtforms import ( SelectMultipleField, DateField, BooleanField, + FieldList, + FormField, ) from wtforms.validators import ValidationError, DataRequired, Email, Optional 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.auth.models import User @@ -66,7 +72,7 @@ def _build_string_field(label, required=True, render_kw=None): class EntrepriseCreationForm(FlaskForm): siret = _build_string_field( "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 (*)") adresse = _build_string_field("Adresse de l'entreprise (*)") @@ -74,15 +80,18 @@ class EntrepriseCreationForm(FlaskForm): ville = _build_string_field("Ville de l'entreprise (*)") pays = _build_string_field("Pays de l'entreprise", required=False) - 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) + nom_correspondant = _build_string_field("Nom du correspondant", required=False) + prenom_correspondant = _build_string_field( + "Prénom du correspondant", required=False + ) + telephone = _build_string_field("Téléphone du correspondant", required=False) mail = StringField( - "Mail du contact (*)", + "Mail du correspondant", validators=[Optional(), Email(message="Adresse e-mail invalide")], ) - poste = _build_string_field("Poste du contact", required=False) - service = _build_string_field("Service du contact", required=False) + poste = _build_string_field("Poste du correspondant", required=False) + service = _build_string_field("Service du correspondant", required=False) + submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE) def validate(self): @@ -90,29 +99,46 @@ class EntrepriseCreationForm(FlaskForm): 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 + 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( + "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): if EntreprisePreferences.get_check_siret(): - siret = siret.data.strip() - if re.match("^\d{14}$", siret) is None: + siret_data = siret.data.replace(" ", "") + self.siret.data = siret_data + if re.match("^\d{14}$", siret_data) is None: raise ValidationError("Format incorrect") try: 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}" ) + if req.status_code != 200: + raise ValidationError("SIRET inexistant") except requests.ConnectionError: - print("no internet") - if req.status_code != 200: - raise ValidationError("SIRET inexistant") - entreprise = Entreprise.query.filter_by(siret=siret).first() + raise ValidationError("Impossible de vérifier l'existance du SIRET") + entreprise = Entreprise.query.filter_by(siret=siret_data).first() if entreprise is not None: lien = f'ici' raise ValidationError( @@ -144,6 +170,7 @@ class MultiCheckboxField(SelectMultipleField): class OffreCreationForm(FlaskForm): + hidden_entreprise_id = HiddenField() intitule = _build_string_field("Intitulé (*)") description = TextAreaField( "Description (*)", validators=[DataRequired(message=CHAMP_REQUIS)] @@ -159,17 +186,44 @@ class OffreCreationForm(FlaskForm): duree = _build_string_field("Durée (*)") depts = MultiCheckboxField("Départements", validators=[Optional()], coerce=int) 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) def __init__(self, *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 = [ (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): + hidden_entreprise_id = HiddenField() intitule = _build_string_field("Intitulé (*)") description = TextAreaField( "Description (*)", validators=[DataRequired(message=CHAMP_REQUIS)] @@ -185,27 +239,79 @@ class OffreModificationForm(FlaskForm): duree = _build_string_field("Durée (*)") depts = MultiCheckboxField("Départements", validators=[Optional()], coerce=int) expiration_date = DateField("Date expiration", validators=[Optional()]) + correspondant = SelectField("Correspondant à contacté", validators=[Optional()]) submit = SubmitField("Modifier", render_kw=SUBMIT_MARGE) def __init__(self, *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 = [ (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): - hidden_entreprise_id = HiddenField() - nom = _build_string_field("Nom (*)") - prenom = _build_string_field("Prénom (*)") - telephone = _build_string_field("Téléphone (*)", required=False) + if len(self.depts.data) < 1: + self.depts.errors.append("Choisir au moins un département") + validate = 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 (*)", validators=[Optional(), Email(message="Adresse e-mail invalide")], + render_kw={"class": "form-control"}, ) - poste = _build_string_field("Poste", required=False) - service = _build_string_field("Service", required=False) + poste = _build_string_field( + "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) def validate(self): @@ -213,28 +319,37 @@ class ContactCreationForm(FlaskForm): 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("") - 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 - + correspondant_list = [] + for entry in self.correspondants.entries: + if entry.nom.data.strip() and entry.prenom.data.strip(): + if ( + entry.nom.data.strip(), + entry.prenom.data.strip(), + ) in correspondant_list: + entry.nom.errors.append( + "Vous avez saisi 2 fois le même nom et prenom" + ) + 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 return validate -class ContactModificationForm(FlaskForm): - hidden_contact_id = HiddenField() +class CorrespondantModificationForm(FlaskForm): + hidden_correspondant_id = HiddenField() hidden_entreprise_id = HiddenField() nom = _build_string_field("Nom (*)") prenom = _build_string_field("Prénom (*)") @@ -245,21 +360,29 @@ class ContactModificationForm(FlaskForm): ) poste = _build_string_field("Poste", required=False) service = _build_string_field("Service", required=False) + # depts = MultiCheckboxField("Départements", validators=[Optional()], coerce=int) 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): 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, + correspondant = EntrepriseCorrespondant.query.filter( + EntrepriseCorrespondant.id != self.hidden_correspondant_id.data, + EntrepriseCorrespondant.entreprise_id == self.hidden_entreprise_id.data, + EntrepriseCorrespondant.nom == self.nom.data, + EntrepriseCorrespondant.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)") + if correspondant is not None: + self.nom.errors.append("Ce correspondant existe déjà (même nom et prénom)") self.prenom.errors.append("") validate = False @@ -273,7 +396,59 @@ class ContactModificationForm(FlaskForm): 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( "Étudiant (*)", render_kw={"placeholder": "Tapez le nom de l'étudiant"}, @@ -289,6 +464,7 @@ class HistoriqueCreationForm(FlaskForm): date_fin = DateField( "Date fin (*)", validators=[DataRequired(message=CHAMP_REQUIS)] ) + notes = TextAreaField("Notes") submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE) def validate(self): @@ -319,25 +495,87 @@ class HistoriqueCreationForm(FlaskForm): 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): - responsable = _build_string_field( - "Responsable de formation (*)", - render_kw={"placeholder": "Tapez le nom du responsable de formation"}, + responsables = FieldList( + _build_string_field( + "Responsable (*)", + render_kw={ + "placeholder": "Tapez le nom du responsable de formation", + "class": "form-control", + }, + ), + min_entries=1, ) submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE) - def validate_responsable(self, responsable): - responsable_data = responsable.data.upper().strip() - stm = text( - "SELECT id, UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')')) FROM \"user\" WHERE UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')'))=:responsable_data" - ) - responsable = ( - User.query.from_statement(stm) - .params(responsable_data=responsable_data) - .first() - ) - if responsable is None: - raise ValidationError("Champ incorrect (selectionnez dans la liste)") + def validate(self): + 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( + "SELECT id, UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')')) FROM \"user\" WHERE UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')'))=:responsable_data" + ) + responsable = ( + User.query.from_statement(stm) + .params(responsable_data=responsable_data) + .first() + ) + if responsable is None: + entry.errors.append("Champ incorrect (selectionnez dans la liste)") + validate = False + + return validate class AjoutFichierForm(FlaskForm): diff --git a/app/entreprises/models.py b/app/entreprises/models.py index dd0b4ba4..90fcf335 100644 --- a/app/entreprises/models.py +++ b/app/entreprises/models.py @@ -11,8 +11,8 @@ class Entreprise(db.Model): ville = db.Column(db.Text) pays = db.Column(db.Text, default="FRANCE") visible = db.Column(db.Boolean, default=False) - contacts = db.relationship( - "EntrepriseContact", + correspondants = db.relationship( + "EntrepriseCorrespondant", backref="entreprise", lazy="dynamic", cascade="all, delete-orphan", @@ -35,12 +35,22 @@ class Entreprise(db.Model): } -class EntrepriseContact(db.Model): - __tablename__ = "are_contacts" +# class EntrepriseSite(db.Model): +# __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) entreprise_id = db.Column( 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) prenom = 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): __tablename__ = "are_offres" id = db.Column(db.Integer, primary_key=True) @@ -75,6 +96,9 @@ class EntrepriseOffre(db.Model): duree = db.Column(db.Text) expiration_date = db.Column(db.Date) expired = db.Column(db.Boolean, default=False) + correspondant_id = db.Column( + db.Integer, db.ForeignKey("are_correspondants.id", ondelete="cascade") + ) def to_dict(self): return { @@ -95,8 +119,8 @@ class EntrepriseLog(db.Model): text = db.Column(db.Text) -class EntrepriseEtudiant(db.Model): - __tablename__ = "are_etudiants" +class EntrepriseStageApprentissage(db.Model): + __tablename__ = "are_stages_apprentissages" id = db.Column(db.Integer, primary_key=True) entreprise_id = db.Column( db.Integer, db.ForeignKey("are_entreprises.id", ondelete="cascade") @@ -107,6 +131,7 @@ class EntrepriseEtudiant(db.Model): date_fin = db.Column(db.Date) formation_text = db.Column(db.Text) formation_scodoc = db.Column(db.Integer) + notes = db.Column(db.Text) class EntrepriseEnvoiOffre(db.Model): @@ -136,6 +161,15 @@ class EntrepriseOffreDepartement(db.Model): 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): __tablename__ = "are_preferences" id = db.Column(db.Integer, primary_key=True) diff --git a/app/entreprises/routes.py b/app/entreprises/routes.py index d1633e71..7fe2e727 100644 --- a/app/entreprises/routes.py +++ b/app/entreprises/routes.py @@ -12,14 +12,17 @@ from app.decorators import permission_required from app.entreprises import LOGS_LEN from app.entreprises.forms import ( + CorrespondantsCreationForm, EntrepriseCreationForm, EntrepriseModificationForm, SuppressionConfirmationForm, OffreCreationForm, OffreModificationForm, + CorrespondantModificationForm, ContactCreationForm, ContactModificationForm, - HistoriqueCreationForm, + StageApprentissageCreationForm, + StageApprentissageModificationForm, EnvoiOffreForm, AjoutFichierForm, ValidationConfirmationForm, @@ -30,9 +33,10 @@ from app.entreprises import bp from app.entreprises.models import ( Entreprise, EntrepriseOffre, - EntrepriseContact, + EntrepriseCorrespondant, EntrepriseLog, - EntrepriseEtudiant, + EntrepriseContact, + EntrepriseStageApprentissage, EntrepriseEnvoiOffre, EntrepriseOffreDepartement, EntreprisePreferences, @@ -96,22 +100,22 @@ def validation(): ) -@bp.route("/contacts", methods=["GET"]) +@bp.route("/correspondants", methods=["GET"]) @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 = ( - db.session.query(EntrepriseContact, Entreprise) - .join(Entreprise, EntrepriseContact.entreprise_id == Entreprise.id) + correspondants = ( + db.session.query(EntrepriseCorrespondant, Entreprise) + .join(Entreprise, EntrepriseCorrespondant.entreprise_id == Entreprise.id) .filter_by(visible=True) ) logs = EntrepriseLog.query.order_by(EntrepriseLog.date.desc()).limit(LOGS_LEN).all() return render_template( - "entreprises/contacts.html", - title="Contacts", - contacts=contacts, + "entreprises/correspondants.html", + title="Correspondants", + correspondants=correspondants, 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 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. """ 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) if offre_with_files is not None: offres_with_files.append(offre_with_files) - contacts = entreprise.contacts[:] + correspondants = entreprise.correspondants[:] logs = ( EntrepriseLog.query.order_by(EntrepriseLog.date.desc()) .filter_by(object=id) .limit(LOGS_LEN) .all() ) - historique = ( - db.session.query(EntrepriseEtudiant, Identite) - .order_by(EntrepriseEtudiant.date_debut.desc()) - .filter(EntrepriseEtudiant.entreprise_id == id) - .join(Identite, Identite.id == EntrepriseEtudiant.etudid) + stages_apprentissages = ( + db.session.query(EntrepriseStageApprentissage, Identite) + .order_by(EntrepriseStageApprentissage.date_debut.desc()) + .filter(EntrepriseStageApprentissage.entreprise_id == id) + .join(Identite, Identite.id == EntrepriseStageApprentissage.etudid) .all() ) return render_template( "entreprises/fiche_entreprise.html", title="Fiche entreprise", entreprise=entreprise, - contacts=contacts, + correspondants=correspondants, offres=offres_with_files, logs=logs, - historique=historique, + stages_apprentissages=stages_apprentissages, ) -@bp.route("/logs/", methods=["GET"]) +@bp.route("/fiche_entreprise//logs", methods=["GET"]) @permission_required(Permission.RelationsEntreprisesView) 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( description=f"fiche entreprise (validation) {id} inconnue" ) - contacts = entreprise.contacts + correspondants = entreprise.correspondants return render_template( "entreprises/fiche_entreprise_validation.html", title="Validation fiche entreprise", entreprise=entreprise, - contacts=contacts, + correspondants=correspondants, ) @@ -221,6 +225,9 @@ def offres_recues(): ) offres_recues_with_files = [] for offre in offres_recues: + correspondant = EntrepriseCorrespondant.query.filter_by( + id=offre[1].correspondant_id + ).first() files = [] path = os.path.join( Config.SCODOC_VAR_DIR, @@ -235,7 +242,7 @@ def offres_recues(): for file in glob.glob(f"{dir}/*"): file = [os.path.basename(dir), os.path.basename(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( "entreprises/offres_recues.html", title="Offres reçues", @@ -287,23 +294,24 @@ def add_entreprise(): ) db.session.add(entreprise) db.session.commit() - db.session.refresh(entreprise) - contact = EntrepriseContact( - entreprise_id=entreprise.id, - nom=form.nom_contact.data.strip(), - prenom=form.prenom_contact.data.strip(), - telephone=form.telephone.data.strip(), - mail=form.mail.data.strip(), - poste=form.poste.data.strip(), - service=form.service.data.strip(), - ) - db.session.add(contact) + if form.nom_correspondant.data.strip(): + db.session.refresh(entreprise) + correspondant = EntrepriseCorrespondant( + entreprise_id=entreprise.id, + nom=form.nom_correspondant.data.strip(), + prenom=form.prenom_correspondant.data.strip(), + telephone=form.telephone.data.strip(), + mail=form.mail.data.strip(), + poste=form.poste.data.strip(), + service=form.service.data.strip(), + ) + db.session.add(correspondant) if current_user.has_permission(Permission.RelationsEntreprisesValidate, None): entreprise.visible = True nom_entreprise = f"{entreprise.nom}" log = EntrepriseLog( 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.commit() @@ -314,18 +322,18 @@ def add_entreprise(): db.session.commit() if EntreprisePreferences.get_email_notifications(): 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.") return redirect(url_for("entreprises.index")) return render_template( "entreprises/ajout_entreprise.html", - title="Ajout entreprise avec contact", + title="Ajout entreprise avec correspondant", form=form, ) -@bp.route("/edit_entreprise/", methods=["GET", "POST"]) +@bp.route("/fiche_entreprise/edit_entreprise/", methods=["GET", "POST"]) @permission_required(Permission.RelationsEntreprisesChange) def edit_entreprise(id): """ @@ -396,7 +404,7 @@ def edit_entreprise(id): ) -@bp.route("/delete_entreprise/", methods=["GET", "POST"]) +@bp.route("/fiche_entreprise/delete_entreprise/", methods=["GET", "POST"]) @permission_required(Permission.RelationsEntreprisesChange) def delete_entreprise(id): """ @@ -432,7 +440,9 @@ def delete_entreprise(id): ) -@bp.route("/validate_entreprise/", methods=["GET", "POST"]) +@bp.route( + "/fiche_entreprise_validation//validate_entreprise", methods=["GET", "POST"] +) @permission_required(Permission.RelationsEntreprisesValidate) def validate_entreprise(id): """ @@ -447,7 +457,7 @@ def validate_entreprise(id): nom_entreprise = f"{entreprise.nom}" log = EntrepriseLog( 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.commit() @@ -460,7 +470,10 @@ def validate_entreprise(id): ) -@bp.route("/delete_validation_entreprise/", methods=["GET", "POST"]) +@bp.route( + "/fiche_entreprise_validation//delete_validation_entreprise", + methods=["GET", "POST"], +) @permission_required(Permission.RelationsEntreprisesValidate) def delete_validation_entreprise(id): """ @@ -482,7 +495,7 @@ def delete_validation_entreprise(id): ) -@bp.route("/add_offre/", methods=["GET", "POST"]) +@bp.route("/fiche_entreprise//add_offre", methods=["GET", "POST"]) @permission_required(Permission.RelationsEntreprisesChange) def add_offre(id): """ @@ -491,7 +504,7 @@ def add_offre(id): entreprise = Entreprise.query.filter_by(id=id, visible=True).first_or_404( description=f"entreprise {id} inconnue" ) - form = OffreCreationForm() + form = OffreCreationForm(hidden_entreprise_id=id) if form.validate_on_submit(): offre = EntrepriseOffre( entreprise_id=entreprise.id, @@ -501,6 +514,7 @@ def add_offre(id): missions=form.missions.data.strip(), duree=form.duree.data.strip(), expiration_date=form.expiration_date.data, + correspondant_id=form.correspondant.data, ) db.session.add(offre) db.session.commit() @@ -511,6 +525,19 @@ def add_offre(id): dept_id=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( authenticated_user=current_user.user_name, object=entreprise.id, @@ -527,7 +554,7 @@ def add_offre(id): ) -@bp.route("/edit_offre/", methods=["GET", "POST"]) +@bp.route("/fiche_entreprise/edit_offre/", methods=["GET", "POST"]) @permission_required(Permission.RelationsEntreprisesChange) def edit_offre(id): """ @@ -537,7 +564,9 @@ def edit_offre(id): description=f"offre {id} inconnue" ) 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] if form.validate_on_submit(): offre.intitule = form.intitule.data.strip() @@ -546,6 +575,7 @@ def edit_offre(id): offre.missions = form.missions.data.strip() offre.duree = form.duree.data.strip() offre.expiration_date = form.expiration_date.data + offre.correspondant_id = form.correspondant.data if offre_depts_list != form.depts.data: for dept in form.depts.data: if dept not in offre_depts_list: @@ -584,7 +614,7 @@ def edit_offre(id): ) -@bp.route("/delete_offre/", methods=["GET", "POST"]) +@bp.route("/fiche_entreprise/delete_offre/", methods=["GET", "POST"]) @permission_required(Permission.RelationsEntreprisesChange) def delete_offre(id): """ @@ -621,7 +651,7 @@ def delete_offre(id): ) -@bp.route("/delete_offre_recue/", methods=["GET", "POST"]) +@bp.route("/offres_recues/delete_offre_recue/", methods=["GET", "POST"]) @permission_required(Permission.RelationsEntreprisesView) def delete_offre_recue(id): """ @@ -635,7 +665,7 @@ def delete_offre_recue(id): return redirect(url_for("entreprises.offres_recues")) -@bp.route("/expired/", methods=["GET", "POST"]) +@bp.route("/fiche_entreprise/expired/", methods=["GET", "POST"]) @permission_required(Permission.RelationsEntreprisesChange) def expired(id): """ @@ -653,36 +683,153 @@ def expired(id): return redirect(url_for("entreprises.fiche_entreprise", id=offre.entreprise_id)) -@bp.route("/add_contact/", methods=["GET", "POST"]) +@bp.route("/fiche_entreprise//add_correspondant", methods=["GET", "POST"]) @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( description=f"entreprise {id} inconnue" ) - form = ContactCreationForm(hidden_entreprise_id=entreprise.id) + form = CorrespondantsCreationForm(hidden_entreprise_id=entreprise.id) if form.validate_on_submit(): - contact = EntrepriseContact( - entreprise_id=entreprise.id, - nom=form.nom.data.strip(), - prenom=form.prenom.data.strip(), - telephone=form.telephone.data.strip(), - mail=form.mail.data.strip(), - poste=form.poste.data.strip(), - service=form.service.data.strip(), - ) + for correspondant_entry in form.correspondants.entries: + correspondant = EntrepriseCorrespondant( + entreprise_id=entreprise.id, + nom=correspondant_entry.nom.data.strip(), + prenom=correspondant_entry.prenom.data.strip(), + telephone=correspondant_entry.telephone.data.strip(), + mail=correspondant_entry.mail.data.strip(), + poste=correspondant_entry.poste.data.strip(), + service=correspondant_entry.service.data.strip(), + ) + log = EntrepriseLog( + authenticated_user=current_user.user_name, + object=entreprise.id, + text="Création d'un correspondant", + ) + 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/", 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=entreprise.id, - text="Création d'un contact", + 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/", 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//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.commit() - flash("Le contact a été ajouté à la fiche entreprise.") - return redirect(url_for("entreprises.fiche_entreprise", id=entreprise.id)) + return redirect(url_for("entreprises.contacts", id=entreprise.id)) return render_template( "entreprises/form.html", title="Ajout contact", @@ -690,44 +837,38 @@ def add_contact(id): ) -@bp.route("/edit_contact/", methods=["GET", "POST"]) +@bp.route("/fiche_entreprise/edit_contact/", methods=["GET", "POST"]) @permission_required(Permission.RelationsEntreprisesChange) 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( description=f"contact {id} inconnu" ) - form = ContactModificationForm( - hidden_entreprise_id=contact.entreprise_id, - hidden_contact_id=contact.id, - ) + form = ContactModificationForm() if form.validate_on_submit(): - contact.nom = form.nom.data.strip() - contact.prenom = form.prenom.data.strip() - contact.telephone = form.telephone.data.strip() - 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", + 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" ) - 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() - flash("Le contact a été modifié.") - return redirect( - url_for("entreprises.fiche_entreprise", id=contact.entreprise.id) - ) + return redirect(url_for("entreprises.contacts", id=contact.entreprise)) elif request.method == "GET": - form.nom.data = contact.nom - form.prenom.data = contact.prenom - form.telephone.data = contact.telephone - form.mail.data = contact.mail - form.poste.data = contact.poste - form.service.data = contact.service + utilisateur = User.query.filter_by(id=contact.user).first() + form.date.data = contact.date.strftime("%Y-%m-%dT%H:%M") + form.utilisateur.data = ( + f"{utilisateur.nom} {utilisateur.prenom} ({utilisateur.user_name})" + ) + form.notes.data = contact.notes return render_template( "entreprises/form.html", title="Modification contact", @@ -735,57 +876,31 @@ def edit_contact(id): ) -@bp.route("/delete_contact/", methods=["GET", "POST"]) -@permission_required(Permission.RelationsEntreprisesChange) -def delete_contact(id): +@bp.route("/fiche_entreprise//contacts") +@permission_required(Permission.RelationsEntreprisesView) +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( - 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) - ) + contacts = EntrepriseContact.query.filter_by(entreprise=id).all() return render_template( - "entreprises/delete_confirmation.html", - title="Supression contact", - form=form, + "entreprises/contacts.html", + title="Liste des contacts", + contacts=contacts, + entreprise_id=id, ) -@bp.route("/add_historique/", methods=["GET", "POST"]) +@bp.route("/fiche_entreprise//add_stage_apprentissage", methods=["GET", "POST"]) @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 """ entreprise = Entreprise.query.filter_by(id=id, visible=True).first_or_404( description=f"entreprise {id} inconnue" ) - form = HistoriqueCreationForm() + form = StageApprentissageCreationForm() if form.validate_on_submit(): etudiant_nomcomplet = form.etudiant.data.upper().strip() stm = text( @@ -799,7 +914,7 @@ def add_historique(id): formation = etudiant.inscription_courante_date( form.date_debut.data, form.date_fin.data ) - historique = EntrepriseEtudiant( + stage_apprentissage = EntrepriseStageApprentissage( entreprise_id=entreprise.id, etudid=etudiant.id, type_offre=form.type_offre.data.strip(), @@ -809,19 +924,105 @@ def add_historique(id): formation_scodoc=formation.formsemestre.formsemestre_id if formation else None, + notes=form.notes.data.strip(), ) - db.session.add(historique) + db.session.add(stage_apprentissage) db.session.commit() flash("L'étudiant a été ajouté sur la fiche entreprise.") return redirect(url_for("entreprises.fiche_entreprise", id=entreprise.id)) return render_template( - "entreprises/ajout_historique.html", - title="Ajout historique", + "entreprises/ajout_stage_apprentissage.html", + title="Ajout stage / apprentissage", form=form, ) -@bp.route("/envoyer_offre/", methods=["GET", "POST"]) +@bp.route( + "/fiche_entreprise/edit_stage_apprentissage/", 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/", 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/", methods=["GET", "POST"]) @permission_required(Permission.RelationsEntreprisesSend) def envoyer_offre(id): """ @@ -832,23 +1033,25 @@ def envoyer_offre(id): ) form = EnvoiOffreForm() if form.validate_on_submit(): - responsable_data = form.responsable.data.upper().strip() - stm = text( - "SELECT id, UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')')) FROM \"user\" WHERE UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')'))=:responsable_data" - ) - responsable = ( - User.query.from_statement(stm) - .params(responsable_data=responsable_data) - .first() - ) - envoi_offre = EntrepriseEnvoiOffre( - sender_id=current_user.id, - receiver_id=responsable.id, - offre_id=offre.id, - ) - db.session.add(envoi_offre) - db.session.commit() - flash(f"L'offre a été envoyé à {responsable.get_nomplogin()}.") + for responsable in form.responsables.entries: + if responsable.data.strip(): + responsable_data = responsable.data.upper().strip() + stm = text( + "SELECT id, UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')')) FROM \"user\" WHERE UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')'))=:responsable_data" + ) + responsable = ( + User.query.from_statement(stm) + .params(responsable_data=responsable_data) + .first() + ) + envoi_offre = EntrepriseEnvoiOffre( + sender_id=current_user.id, + receiver_id=responsable.id, + offre_id=offre.id, + ) + db.session.add(envoi_offre) + db.session.commit() + flash(f"L'offre a été envoyé à {responsable.get_nomplogin()}.") return redirect(url_for("entreprises.fiche_entreprise", id=offre.entreprise_id)) return render_template( "entreprises/envoi_offre_form.html", @@ -924,10 +1127,11 @@ def export_entreprises(): filename = title return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE) 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) def get_import_entreprises_file_sample(): """ @@ -978,20 +1182,19 @@ def import_entreprises(): ligne += 1 if ( 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( - siret=entreprise_data[0], - nom=entreprise_data[1], - adresse=entreprise_data[2], - ville=entreprise_data[3], - codepostal=entreprise_data[4], - pays=entreprise_data[5], + siret=entreprise_data[0].replace(" ", ""), + nom=entreprise_data[1].strip(), + adresse=entreprise_data[2].strip(), + ville=entreprise_data[3].strip(), + codepostal=entreprise_data[4].strip(), + pays=entreprise_data[5].strip(), visible=True, ) entreprises_import.append(entreprise) - else: flash(f"Erreur lors de l'importation (ligne {ligne})") return render_template( @@ -1026,19 +1229,19 @@ def import_entreprises(): ) -@bp.route("/export_contacts") +@bp.route("/export_correspondants") @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 = ( - db.session.query(EntrepriseContact) - .join(Entreprise, EntrepriseContact.entreprise_id == Entreprise.id) + correspondants = ( + db.session.query(EntrepriseCorrespondant) + .join(Entreprise, EntrepriseCorrespondant.entreprise_id == Entreprise.id) .filter_by(visible=True) .all() ) - if contacts: + if correspondants: keys = [ "nom", "prenom", @@ -1049,20 +1252,24 @@ def export_contacts(): "entreprise_siret", ] titles = keys[:] - L = [[contact.to_dict().get(k, "") for k in keys] for contact in contacts] - title = "Contacts" + L = [ + [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) filename = title return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE) 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) -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 = [ "nom", @@ -1074,17 +1281,17 @@ def get_import_contacts_file_sample(): "entreprise_siret", ] titles = keys[:] - title = "ImportContacts" - xlsx = sco_excel.excel_simple_table(titles=titles, sheet_name="Contacts") + title = "ImportCorrespondants" + xlsx = sco_excel.excel_simple_table(titles=titles, sheet_name="Correspondants") filename = title 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) -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() if form.validate_on_submit(): @@ -1095,8 +1302,8 @@ def import_contacts(): file.save(file_path) data = sco_excel.excel_file_to_list(file_path) os.remove(file_path) - contacts_import = [] - contact_list = [] + correspondants_import = [] + correspondant_list = [] ligne = 0 titles = [ "nom", @@ -1110,57 +1317,72 @@ def import_contacts(): if data[1][0] != titles: flash("Veuillez utilisez la feuille excel à remplir") return render_template( - "entreprises/import_contacts.html", - title="Importation contacts", + "entreprises/import_correspondants.html", + title="Importation correspondants", form=form, ) - for contact_data in data[1][1:]: + for correspondant_data in data[1][1:]: ligne += 1 if ( - are.verif_contact_data(contact_data) - and (contact_data[0], contact_data[1], contact_data[6]) - not in contact_list - ): - contact_list.append((contact_data[0], contact_data[1], contact_data[6])) - 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], + are.verif_correspondant_data(correspondant_data) + and ( + correspondant_data[0].strip(), + correspondant_data[1].strip(), + correspondant_data[6].strip(), ) - 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: flash(f"Erreur lors de l'importation (ligne {ligne})") return render_template( - "entreprises/import_contacts.html", - title="Importation contacts", + "entreprises/import_correspondants.html", + title="Importation correspondants", form=form, ) - if len(contacts_import) > 0: - for contact in contacts_import: - db.session.add(contact) + if len(correspondants_import) > 0: + for correspondant in correspondants_import: + db.session.add(correspondant) log = EntrepriseLog( 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.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( - "entreprises/import_contacts.html", - title="Importation Contacts", + "entreprises/import_correspondants.html", + title="Importation correspondants", form=form, - contacts_import=contacts_import, + correspondants_import=correspondants_import, ) else: - flash('Feuille "Contacts" vide') + flash('Feuille "Correspondants" vide') return render_template( - "entreprises/import_contacts.html", - title="Importation contacts", + "entreprises/import_correspondants.html", + title="Importation correspondants", form=form, ) @@ -1198,7 +1420,7 @@ def get_offre_file(entreprise_id, offre_id, filedir, filename): abort(404, description=f"fichier {filename} inconnu") -@bp.route("/add_offre_file/", methods=["GET", "POST"]) +@bp.route("/fiche_entreprise/add_offre_file/", methods=["GET", "POST"]) @permission_required(Permission.RelationsEntreprisesChange) def add_offre_file(offre_id): """ @@ -1230,7 +1452,10 @@ def add_offre_file(offre_id): ) -@bp.route("/delete_offre_file//", methods=["GET", "POST"]) +@bp.route( + "/fiche_entreprise/delete_offre_file//", + methods=["GET", "POST"], +) @permission_required(Permission.RelationsEntreprisesChange) def delete_offre_file(offre_id, filedir): """ diff --git a/app/scodoc/sco_apogee_csv.py b/app/scodoc/sco_apogee_csv.py index d9c176eb..7fe9501d 100644 --- a/app/scodoc/sco_apogee_csv.py +++ b/app/scodoc/sco_apogee_csv.py @@ -396,7 +396,7 @@ class ApoEtud(dict): # Element etape (annuel ou non): if sco_formsemestre.sem_has_etape(sem, code) or ( - code in sem["elt_annee_apo"].split(",") + code in {x.strip() for x in sem["elt_annee_apo"].split(",")} ): export_res_etape = self.export_res_etape if (not export_res_etape) and cur_sem: @@ -412,7 +412,7 @@ class ApoEtud(dict): return VOID_APO_RES # Element semestre: - if code in sem["elt_sem_apo"].split(","): + if code in {x.strip() for x in sem["elt_sem_apo"].split(",")}: if self.export_res_sem: return self.comp_elt_semestre(nt, decision, etudid) else: @@ -421,7 +421,9 @@ class ApoEtud(dict): # Elements UE decisions_ue = nt.get_etud_decision_ues(etudid) for ue in nt.get_ues_stat_dict(): - if ue["code_apogee"] and code in ue["code_apogee"].split(","): + if ue["code_apogee"] and code in { + x.strip() for x in ue["code_apogee"].split(",") + }: if self.export_res_ues: if decisions_ue and ue["ue_id"] in decisions_ue: ue_status = nt.get_etud_ue_status(etudid, ue["ue_id"]) @@ -442,9 +444,10 @@ class ApoEtud(dict): modimpls = nt.get_modimpls_dict() module_code_found = False for modimpl in modimpls: - if modimpl["module"]["code_apogee"] and code in modimpl["module"][ - "code_apogee" - ].split(","): + module = modimpl["module"] + if module["code_apogee"] and code in { + x.strip() for x in module["code_apogee"].split(",") + }: n = nt.get_etud_mod_moy(modimpl["moduleimpl_id"], etudid) if n != "NI" and self.export_res_modules: return dict(N=self.fmt_note(n), B=20, J="", R="") @@ -949,8 +952,9 @@ class ApoData(object): return maq_elems, sem_elems def get_codes_by_sem(self): - """Pour chaque semestre associé, donne l'ensemble des codes Apogée qui s'y trouvent - (dans le semestre, les UE et les modules) + """Pour chaque semestre associé, donne l'ensemble des codes de cette maquette Apogée + qui s'y trouvent (dans le semestre, les UE ou les modules). + Return: { formsemestre_id : { 'code1', 'code2', ... }} """ codes_by_sem = {} for sem in self.sems_etape: @@ -961,8 +965,8 @@ class ApoData(object): # associé à l'étape, l'année ou les semestre: if ( sco_formsemestre.sem_has_etape(sem, code) - or (code in sem["elt_sem_apo"].split(",")) - or (code in sem["elt_annee_apo"].split(",")) + or (code in {x.strip() for x in sem["elt_sem_apo"].split(",")}) + or (code in {x.strip() for x in sem["elt_annee_apo"].split(",")}) ): s.add(code) continue @@ -970,17 +974,20 @@ class ApoData(object): formsemestre = FormSemestre.query.get_or_404(sem["formsemestre_id"]) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) for ue in nt.get_ues_stat_dict(): - if ue["code_apogee"] and code in ue["code_apogee"].split(","): - s.add(code) - continue + if ue["code_apogee"]: + codes = {x.strip() for x in ue["code_apogee"].split(",")} + if code in codes: + s.add(code) + continue # associé à un module: modimpls = nt.get_modimpls_dict() for modimpl in modimpls: - if modimpl["module"]["code_apogee"] and code in modimpl["module"][ - "code_apogee" - ].split(","): - s.add(code) - continue + module = modimpl["module"] + if module["code_apogee"]: + codes = {x.strip() for x in module["code_apogee"].split(",")} + if code in codes: + s.add(code) + continue # log('codes_by_sem=%s' % pprint.pformat(codes_by_sem)) return codes_by_sem diff --git a/app/scodoc/sco_portal_apogee.py b/app/scodoc/sco_portal_apogee.py index 836be2ed..8c77b91c 100644 --- a/app/scodoc/sco_portal_apogee.py +++ b/app/scodoc/sco_portal_apogee.py @@ -37,12 +37,20 @@ import xml.dom.minidom import app.scodoc.sco_utils as scu from app import log +from app.scodoc import sco_cache from app.scodoc.sco_exceptions import ScoValueError from app.scodoc import sco_preferences SCO_CACHE_ETAPE_FILENAME = os.path.join(scu.SCO_TMP_DIR, "last_etapes.xml") +class ApoInscritsEtapeCache(sco_cache.ScoDocCache): + """Cache liste des inscrits à une étape Apogée""" + + timeout = 10 * 60 # 10 minutes + prefix = "APOINSCRETAP" + + def has_portal(): "True if we are connected to a portal" return get_portal_url() @@ -139,14 +147,20 @@ get_maquette_url = _PI.get_maquette_url get_portal_api_version = _PI.get_portal_api_version -def get_inscrits_etape(code_etape, anneeapogee=None, ntrials=2): +def get_inscrits_etape(code_etape, anneeapogee=None, ntrials=4, use_cache=True): """Liste des inscrits à une étape Apogée Result = list of dicts ntrials: try several time the same request, useful for some bad web services + use_cache: use (redis) cache """ log("get_inscrits_etape: code=%s anneeapogee=%s" % (code_etape, anneeapogee)) if anneeapogee is None: anneeapogee = str(time.localtime()[0]) + if use_cache: + obj = ApoInscritsEtapeCache.get((code_etape, anneeapogee)) + if obj: + log("get_inscrits_etape: using cached data") + return obj etud_url = get_etud_url() api_ver = get_portal_api_version() @@ -189,6 +203,8 @@ def get_inscrits_etape(code_etape, anneeapogee=None, ntrials=2): return False # ??? pas d'annee d'inscription dans la réponse etuds = [e for e in etuds if check_inscription(e)] + if use_cache and etuds: + ApoInscritsEtapeCache.set((code_etape, anneeapogee), etuds) return etuds diff --git a/app/static/css/entreprises.css b/app/static/css/entreprises.css index fac9d11b..1f4e5f23 100644 --- a/app/static/css/entreprises.css +++ b/app/static/css/entreprises.css @@ -15,6 +15,10 @@ } +.form-error { + color: #a94442; +} + .nav-entreprise>ul>li>a:hover { color: red; } @@ -50,23 +54,23 @@ margin-bottom: -5px; } -.entreprise, .contact, .offre { +.entreprise, .correspondant, .offre { border: solid 2px; border-radius: 10px; padding: 10px; margin-bottom: 10px; } -.contacts-et-offres { +.correspondants-et-offres { display: flex; justify-content: space-between; } -.contacts-et-offres > div { +.correspondants-et-offres > div { flex: 1 0 0; } -.contacts-et-offres > div:nth-child(2) { +.correspondants-et-offres > div:nth-child(2) { margin-left: 20px; } diff --git a/app/templates/entreprises/_contact.html b/app/templates/entreprises/_contact.html deleted file mode 100644 index e2f61aa9..00000000 --- a/app/templates/entreprises/_contact.html +++ /dev/null @@ -1,26 +0,0 @@ -{# -*- mode: jinja-html -*- #} -
-
- Nom : {{ contact.nom }}
- Prénom : {{ contact.prenom }}
- {% if contact.telephone %} - Téléphone : {{ contact.telephone }}
- {% endif %} - {% if contact.mail %} - Mail : {{ contact.mail }}
- {% endif %} - {% if contact.poste %} - Poste : {{ contact.poste }}
- {% endif %} - {% if contact.service %} - Service : {{ contact.service }}
- {% endif %} -
- - {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} - - {% endif %} -
\ No newline at end of file diff --git a/app/templates/entreprises/_correspondant.html b/app/templates/entreprises/_correspondant.html new file mode 100644 index 00000000..0def6b89 --- /dev/null +++ b/app/templates/entreprises/_correspondant.html @@ -0,0 +1,26 @@ +{# -*- mode: jinja-html -*- #} +
+
+ Nom : {{ correspondant.nom }}
+ Prénom : {{ correspondant.prenom }}
+ {% if correspondant.telephone %} + Téléphone : {{ correspondant.telephone }}
+ {% endif %} + {% if correspondant.mail %} + Mail : {{ correspondant.mail }}
+ {% endif %} + {% if correspondant.poste %} + Poste : {{ correspondant.poste }}
+ {% endif %} + {% if correspondant.service %} + Service : {{ correspondant.service }}
+ {% endif %} +
+ + {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} + + {% endif %} +
\ No newline at end of file diff --git a/app/templates/entreprises/_offre.html b/app/templates/entreprises/_offre.html index cff6f03a..56541178 100644 --- a/app/templates/entreprises/_offre.html +++ b/app/templates/entreprises/_offre.html @@ -1,6 +1,7 @@ {# -*- mode: jinja-html -*- #}
+ Ajouté le {{ offre[0].date_ajout.strftime('%d/%m/%y') }} à {{ offre[0].date_ajout.strftime('%Hh%M') }}
Intitulé : {{ offre[0].intitule }}
Description : {{ offre[0].description }}
Type de l'offre : {{ offre[0].type_offre }}
@@ -9,6 +10,16 @@ {% if offre[2] %} Département(s) : {% for offre_dept in offre[2] %}
{{ offre_dept.dept_id|get_dept_acronym }}
{% endfor %}
{% 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 }})
+ {% else %} + ({{ offre[3].mail }}{{offre[3].telephone}})
+ {% endif %} + {% endif %} + {% for fichier in offre[1] %} {{ fichier[1] }} {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} @@ -16,6 +27,7 @@ {% endif %}
{% endfor %} + {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} Ajoutez un fichier {% endif %} @@ -26,9 +38,11 @@ Modifier l'offre Supprimer l'offre {% endif %} + {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesSend, None) %} Envoyer l'offre {% endif %} + {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} {% if not offre[0].expired %} Rendre expirée diff --git a/app/templates/entreprises/ajout_correspondants.html b/app/templates/entreprises/ajout_correspondants.html new file mode 100644 index 00000000..f7d5e5b1 --- /dev/null +++ b/app/templates/entreprises/ajout_correspondants.html @@ -0,0 +1,56 @@ +{# -*- mode: jinja-html -*- #} +{% extends 'base.html' %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block styles %} +{{super()}} +{% endblock %} + +{% block app_content %} +

{{ title }}

+
+
+
+

+ (*) champs requis +

+
+ {{ form.hidden_tag() }} + {% for subfield in form.correspondants %} + {% for subsubfield in subfield %} + {% if subsubfield.errors %} + {% for error in subsubfield.errors %} +

{{ error }}

+ {% endfor %} + {% endif %} + {% endfor %} + {% endfor %} + {{ form.correspondants }} +
+ + +
+
+
+
+ + +{% endblock %} \ No newline at end of file diff --git a/app/templates/entreprises/ajout_entreprise.html b/app/templates/entreprises/ajout_entreprise.html index 0eead428..83dff9fd 100644 --- a/app/templates/entreprises/ajout_entreprise.html +++ b/app/templates/entreprises/ajout_entreprise.html @@ -3,7 +3,7 @@ {% import 'bootstrap/wtf.html' as wtf %} {% block app_content %} -

Ajout entreprise avec contact

+

Ajout entreprise


@@ -16,45 +16,43 @@
{% endblock %} \ No newline at end of file diff --git a/app/templates/entreprises/ajout_historique.html b/app/templates/entreprises/ajout_stage_apprentissage.html similarity index 97% rename from app/templates/entreprises/ajout_historique.html rename to app/templates/entreprises/ajout_stage_apprentissage.html index 678d3a72..8055850f 100644 --- a/app/templates/entreprises/ajout_historique.html +++ b/app/templates/entreprises/ajout_stage_apprentissage.html @@ -9,7 +9,7 @@ {% endblock %} {% block app_content %} -

Ajout historique

+

{{ title }}


diff --git a/app/templates/entreprises/contacts.html b/app/templates/entreprises/contacts.html index fa1609ae..2896627e 100644 --- a/app/templates/entreprises/contacts.html +++ b/app/templates/entreprises/contacts.html @@ -9,64 +9,51 @@ {% endblock %} {% block app_content %} - {% include 'entreprises/nav.html' %} - - {% if logs %} -
-

Dernières opérations Voir tout

-
    - {% for log in logs %} -
  • {{ log.date.strftime('%d %b %Hh%M') }}{{ log.text|safe }} par {{ log.authenticated_user|get_nomcomplet_by_username }}
  • - {% endfor %} -
-
- {% endif %} - -
- {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) %} - Importer des contacts - {% endif %} - {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) and contacts %} - Exporter la liste des contacts - {% endif %} -
- -
+

Liste des contacts

+ {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} + Ajouter contact + {% endif %} - - - - - - - + + + + {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} + + {% endif %} - {% for contact in contacts %} + {% for contact in contacts %} - - - - - - - + + + + {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} + + {% endif %} - {% endfor %} + {% endfor %} - - - - - - - + + + + {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} + + {% endif %}
NomPrenomTelephoneMailPosteServiceEntrepriseDateUtilisateurNotesAction
{{ contact[0].nom }}{{ contact[0].prenom }}{{ contact[0].telephone }}{{ contact[0].mail }}{{ contact[0].poste}}{{ contact[0].service}}{{ contact[1].nom }}{{ contact.date.strftime('%d/%m/%Y %Hh%M') }}{{ contact.user|get_nomcomplet_by_id }}{{ contact.notes }} + +
NomPrenomTelephoneMailPosteServiceEntrepriseDateUtilisateurNotesAction
diff --git a/app/templates/entreprises/correspondants.html b/app/templates/entreprises/correspondants.html new file mode 100644 index 00000000..047b57aa --- /dev/null +++ b/app/templates/entreprises/correspondants.html @@ -0,0 +1,102 @@ +{# -*- mode: jinja-html -*- #} +{% extends 'base.html' %} + +{% block styles %} +{{super()}} + + + +{% endblock %} + +{% block app_content %} + {% include 'entreprises/nav.html' %} + + {% if logs %} +
+

Dernières opérations Voir tout

+
    + {% for log in logs %} +
  • {{ log.date.strftime('%d %b %Hh%M') }}{{ log.text|safe }} par {{ log.authenticated_user|get_nomcomplet_by_username }}
  • + {% endfor %} +
+
+ {% endif %} + +
+ {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) %} + Importer des correspondants + {% endif %} + {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesExport, None) and correspondants %} + Exporter la liste des correspondants + {% endif %} +
+ +
+

Liste des correspondants

+ + + + + + + + + + + + + + {% for correspondant in correspondants %} + + + + + + + + + + {% endfor %} + + + + + + + + + + + + +
NomPrenomTéléphoneMailPosteServiceEntreprise
{{ correspondant[0].nom }}{{ correspondant[0].prenom }}{{ correspondant[0].telephone }}{{ correspondant[0].mail }}{{ correspondant[0].poste}}{{ correspondant[0].service}}{{ correspondant[1].nom }}
NomPrenomTéléphoneMailPosteServiceEntreprise
+
+ + +{% endblock %} \ No newline at end of file diff --git a/app/templates/entreprises/envoi_offre_form.html b/app/templates/entreprises/envoi_offre_form.html index 94708672..ca5ba131 100644 --- a/app/templates/entreprises/envoi_offre_form.html +++ b/app/templates/entreprises/envoi_offre_form.html @@ -16,7 +16,22 @@

(*) champs requis

- {{ wtf.quick_form(form, novalidate=True) }} +
+ {{ form.hidden_tag() }} + {{ form.responsables.label }}
+ {% for subfield in form.responsables %} + {% if subfield.errors %} + {% for error in subfield.errors %} +

{{ error }}

+ {% endfor %} + {% endif %} + {% endfor %} + {{ form.responsables }} +
+ + +
+
@@ -30,7 +45,28 @@ minchars: 2, 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',` +
  • + `); + var as_r = new bsn.AutoSuggest(newFieldName, responsables_options); + }); } {% endblock %} \ No newline at end of file diff --git a/app/templates/entreprises/fiche_entreprise.html b/app/templates/entreprises/fiche_entreprise.html index 2d530e70..b8d4b123 100644 --- a/app/templates/entreprises/fiche_entreprise.html +++ b/app/templates/entreprises/fiche_entreprise.html @@ -1,6 +1,13 @@ {# -*- mode: jinja-html -*- #} {% extends 'base.html' %} +{% block styles %} +{{super()}} + + + +{% endblock %} + {% block app_content %} {% if logs %}
    @@ -15,24 +22,6 @@
    {% endif %} - - {% if historique %} -
    -

    Historique

    -
      - {% for data in historique %} -
    • - {{ data[0].date_debut.strftime('%d/%m/%Y') }} - {{ - data[0].date_fin.strftime('%d/%m/%Y') }} - - {{ 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 %} - -
    • - {% endfor %} -
    -
    - {% endif %}

    Fiche entreprise - {{ entreprise.nom }} ({{ entreprise.siret }})

    @@ -53,20 +42,19 @@ Modifier Supprimer Ajouter offre - Ajouter contact - Ajouter - historique + Ajouter correspondant {% endif %} + Liste contacts Voir les offres expirées
    -
    - {% if contacts %} +
    + {% if correspondants %}
    -

    Contacts

    - {% for contact in contacts %} - {% include 'entreprises/_contact.html' %} +

    Correspondants

    + {% for correspondant in correspondants %} + {% include 'entreprises/_correspondant.html' %} {% endfor %}
    {% endif %} @@ -81,4 +69,95 @@ {% endif %}
    + +
    + {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} + Ajouter stage ou apprentissage + {% endif %} +

    Liste des stages et apprentissages réalisés au sein de l'entreprise

    + + + + + + + + + + + {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} + + {% endif %} + + + + {% for data in stages_apprentissages %} + + + + + + + + + {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} + + {% endif %} + + {% endfor %} + + + + + + + + + + + {% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %} + + {% endif %} + + +
    Date débutDate finDuréeTypeÉtudiantFormationNotesAction
    {{ data[0].date_debut.strftime('%d/%m/%Y') }}{{ data[0].date_fin.strftime('%d/%m/%Y') }}{{ (data[0].date_fin-data[0].date_debut).days//7 }} semaines{{ data[0].type_offre }}{{ data[1].nom|format_nom }} {{ data[1].prenom|format_prenom }}{% if data[0].formation_text %}{{ data[0].formation_text }}{% endif %}{{ data[0].notes }} + +
    Date débutDate finDuréeTypeÉtudiantFormationNotesAction
    +
    + + {% endblock %} \ No newline at end of file diff --git a/app/templates/entreprises/fiche_entreprise_validation.html b/app/templates/entreprises/fiche_entreprise_validation.html index 072461b4..2f243b24 100644 --- a/app/templates/entreprises/fiche_entreprise_validation.html +++ b/app/templates/entreprises/fiche_entreprise_validation.html @@ -16,25 +16,25 @@
    - {% if contacts %} + {% if correspondants %}
    - {% for contact in contacts %} + {% for correspondant in correspondants %}
    -

    Contact

    -
    - Nom : {{ contact.nom }}
    - Prénom : {{ contact.prenom }}
    - {% if contact.telephone %} - Téléphone : {{ contact.telephone }}
    +

    Correspondant

    +
    + Nom : {{ correspondant.nom }}
    + Prénom : {{ correspondant.prenom }}
    + {% if correspondant.telephone %} + Téléphone : {{ correspondant.telephone }}
    {% endif %} - {% if contact.mail %} - Mail : {{ contact.mail }}
    + {% if correspondant.mail %} + Mail : {{ correspondant.mail }}
    {% endif %} - {% if contact.poste %} - Poste : {{ contact.poste }}
    + {% if correspondant.poste %} + Poste : {{ correspondant.poste }}
    {% endif %} - {% if contact.service %} - Service : {{ contact.service }}
    + {% if correspondant.service %} + Service : {{ correspondant.service }}
    {% endif %}
    diff --git a/app/templates/entreprises/form.html b/app/templates/entreprises/form.html index 071f84fd..fe51a97b 100644 --- a/app/templates/entreprises/form.html +++ b/app/templates/entreprises/form.html @@ -4,6 +4,8 @@ {% block styles %} {{super()}} + + {% endblock %} {% block app_content %} @@ -25,5 +27,36 @@ var closest_form_control = champ_depts.closest(".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); {% endblock %} \ No newline at end of file diff --git a/app/templates/entreprises/import_contacts.html b/app/templates/entreprises/import_contacts.html deleted file mode 100644 index 292a0e9f..00000000 --- a/app/templates/entreprises/import_contacts.html +++ /dev/null @@ -1,62 +0,0 @@ -{# -*- mode: jinja-html -*- #} -{% extends 'base.html' %} -{% import 'bootstrap/wtf.html' as wtf %} - -{% block styles %} -{{super()}} -{% endblock %} - -{% block app_content %} -

    Importation contacts

    -
    - -
    -
    -
    -

    - (*) champs requis -

    - {{ wtf.quick_form(form, novalidate=True) }} -
    -
    - - {% if not contacts_import %} - - - - - - - - - -
    AttributTypeDescription
    nomtextnom du contact
    prenomtextprenom du contact
    telephonetexttelephone du contact
    mailtextmail du contact
    postetextposte du contact
    servicetextservice dans lequel travaille le contact
    entreprise_siretintegerSIRET de l'entreprise
    - {% endif %} - - {% if contacts_import %} -
    Importation de {{ contacts_import|length }} contact(s)
    - {% for contact in contacts_import %} -
    -
    - Nom : {{ contact.nom }}
    - Prénom : {{ contact.prenom }}
    - {% if contact.telephone %} - Téléphone : {{ contact.telephone }}
    - {% endif %} - {% if contact.mail %} - Mail : {{ contact.mail }}
    - {% endif %} - {% if contact.poste %} - Poste : {{ contact.poste }}
    - {% endif %} - {% if contact.service %} - Service : {{ contact.service }}
    - {% endif %} - lien vers l'entreprise -
    -
    - {% endfor %} - {% endif %} -{% endblock %} \ No newline at end of file diff --git a/app/templates/entreprises/import_correspondants.html b/app/templates/entreprises/import_correspondants.html new file mode 100644 index 00000000..cfbaf11a --- /dev/null +++ b/app/templates/entreprises/import_correspondants.html @@ -0,0 +1,62 @@ +{# -*- mode: jinja-html -*- #} +{% extends 'base.html' %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block styles %} +{{super()}} +{% endblock %} + +{% block app_content %} +

    Importation correspondants

    +
    + +
    +
    +
    +

    + (*) champs requis +

    + {{ wtf.quick_form(form, novalidate=True) }} +
    +
    + + {% if not correspondants_import %} + + + + + + + + + +
    AttributTypeDescription
    nomtextnom du correspondant
    prenomtextprenom du correspondant
    telephonetexttelephone du correspondant
    mailtextmail du correspondant
    postetextposte du correspondant
    servicetextservice dans lequel travaille le correspondant
    entreprise_siretintegerSIRET de l'entreprise
    + {% endif %} + + {% if correspondants_import %} +
    Importation de {{ correspondants_import|length }} correspondant(s)
    + {% for correspondant in correspondants_import %} +
    +
    + Nom : {{ correspondant.nom }}
    + Prénom : {{ correspondant.prenom }}
    + {% if correspondant.telephone %} + Téléphone : {{ correspondant.telephone }}
    + {% endif %} + {% if correspondant.mail %} + Mail : {{ correspondant.mail }}
    + {% endif %} + {% if correspondant.poste %} + Poste : {{ correspondant.poste }}
    + {% endif %} + {% if correspondant.service %} + Service : {{ correspondant.service }}
    + {% endif %} + lien vers l'entreprise +
    +
    + {% endfor %} + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/app/templates/entreprises/nav.html b/app/templates/entreprises/nav.html index e2421165..50bca601 100644 --- a/app/templates/entreprises/nav.html +++ b/app/templates/entreprises/nav.html @@ -2,7 +2,7 @@