diff --git a/recruitment/forms.py b/recruitment/forms.py index 642246ddb..4881db315 100644 --- a/recruitment/forms.py +++ b/recruitment/forms.py @@ -50,6 +50,8 @@ from recruitment.models import ( RecruitmentSurvey, RejectedCandidate, RejectReason, + Resume, + Skill, SkillZone, SkillZoneCandidate, Stage, @@ -257,6 +259,7 @@ class RecruitmentCreationForm(ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + reload_queryset(self.fields) if not self.instance.pk: self.fields["recruitment_managers"] = HorillaMultiSelectField( @@ -271,11 +274,31 @@ class RecruitmentCreationForm(ModelForm): label="Employee", ) + skill_choices = [("", _("---Choose Skills---"))] + list( + self.fields["skills"].queryset.values_list("id", "title") + ) + self.fields["skills"].choices = skill_choices + self.fields["skills"].choices += [("create", _("Create new skill "))] + + # def create_option(self, *args,**kwargs): + # option = super().create_option(*args,**kwargs) + + # if option.get('value') == "create": + # option['attrs']['class'] = 'text-danger' + + # return option + def clean(self): if isinstance(self.fields["recruitment_managers"], HorillaMultiSelectField): ids = self.data.getlist("recruitment_managers") if ids: self.errors.pop("recruitment_managers", None) + open_positions = self.cleaned_data.get("open_positions") + is_published = self.cleaned_data.get("is_published") + if is_published and not open_positions: + raise forms.ValidationError( + _("Job position is required if the recruitment is publishing.") + ) super().clean() @@ -423,6 +446,9 @@ class CandidateCreationForm(ModelForm): return super().clean() +from horilla.horilla_middlewares import _thread_locals + + class ApplicationForm(RegistrationForm): """ Form for create Candidate @@ -468,8 +494,23 @@ class ApplicationForm(RegistrationForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + request = getattr(_thread_locals, "request", None) + self.fields["recruitment_id"].widget.attrs = {"data-widget": "ajax-widget"} self.fields["job_position_id"].widget.attrs = {"data-widget": "ajax-widget"} + if request and request.user.has_perm("recruitment.add_candidate"): + self.fields["profile"].required = False + + def clean(self, *args, **kwargs): + name = self.cleaned_data["name"] + request = getattr(_thread_locals, "request", None) + + if request and request.user.has_perm("recruitment.add_candidate"): + profile_pic_url = f"https://ui-avatars.com/api/?name={name}" + self.cleaned_data["profile"] = profile_pic_url + + super().clean() + return self.cleaned_data class RecruitmentDropDownForm(DropDownForm): @@ -665,7 +706,9 @@ class QuestionForm(ModelForm): required=False, label=_("Recruitment"), ) - options = forms.CharField(widget=forms.TextInput, label=_("Options"), required=True) + options = forms.CharField( + widget=forms.TextInput, label=_("Options"), required=False + ) class Meta: """ @@ -697,7 +740,7 @@ class QuestionForm(ModelForm): cleaned_data = super().clean() recruitment = self.cleaned_data["recruitment"] question_type = self.cleaned_data["type"] - options = self.cleaned_data["options"] + options = self.cleaned_data.get("options") if not recruitment.exists(): # or jobs.exists()): raise ValidationError( {"recruitment": _("Choose any recruitment to apply this question")} @@ -740,7 +783,7 @@ class QuestionForm(ModelForm): } ), label=_("Options"), - required=True, + required=False, initial=initial, ) @@ -834,7 +877,20 @@ class AddQuestionForm(Form): return table_html -exclude_fields = ["id", "profile", "portfolio", "resume", "sequence", "schedule_date"] +exclude_fields = [ + "id", + "profile", + "portfolio", + "resume", + "sequence", + "schedule_date", + "created_at", + "created_by", + "modified_by", + "is_active", + "last_updated", + "horilla_history", +] class CandidateExportForm(forms.Form): @@ -1160,3 +1216,24 @@ class ScheduleInterviewForm(ModelForm): context = {"form": self} table_html = render_to_string("common_form.html", context) return table_html + + +class SkillsForm(ModelForm): + class Meta: + model = Skill + fields = ["title"] + + +class ResumeForm(ModelForm): + class Meta: + model = Resume + fields = ["file", "recruitment_id"] + widgets = {"recruitment_id": forms.HiddenInput()} + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields["file"].widget.attrs.update( + { + "onchange": "submitForm($(this))", + } + ) diff --git a/recruitment/models.py b/recruitment/models.py index 62500bdb9..d3a8240f4 100644 --- a/recruitment/models.py +++ b/recruitment/models.py @@ -80,6 +80,18 @@ class SurveyTemplate(HorillaModel): return self.title +class Skill(HorillaModel): + title = models.CharField(max_length=100) + + def __str__(self): + return self.title + + def save(self, *args, **kwargs): + title = self.title + self.title = title.capitalize() + super().save(*args, **kwargs) + + class Recruitment(HorillaModel): """ Recruitment model @@ -136,6 +148,7 @@ class Recruitment(HorillaModel): ) start_date = models.DateField(default=django.utils.timezone.now) end_date = models.DateField(blank=True, null=True) + skills = models.ManyToManyField(Skill, blank=True) objects = HorillaCompanyManager() default = models.manager.Manager() @@ -175,14 +188,20 @@ class Recruitment(HorillaModel): def clean(self): if self.title is None: raise ValidationError({"title": _("This field is required")}) + if self.is_published: + if self.vacancy <= 0: + raise ValidationError( + _( + "Vacancy must be greater than zero if the recruitment is publishing." + ) + ) + if self.end_date is not None and ( self.start_date is not None and self.start_date > self.end_date ): raise ValidationError( {"end_date": _("End date cannot be less than start date.")} ) - # if not self.is_event_based and self.job_position_id is None: - # raise ValidationError({"job_position_id": _("This field is required")}) return super().clean() def save(self, *args, **kwargs): @@ -888,3 +907,19 @@ class InterviewSchedule(HorillaModel): def __str__(self) -> str: return f"{self.candidate_id} -Interview." + + +class Resume(models.Model): + file = models.FileField( + upload_to="recruitment/resume", + validators=[ + validate_pdf, + ], + ) + recruitment_id = models.ForeignKey( + Recruitment, on_delete=models.CASCADE, related_name="resume" + ) + is_candidate = models.BooleanField(default=False) + + def __str__(self): + return f"{self.recruitment_id} - Resume {self.pk}" diff --git a/recruitment/templates/candidate/candidate_nav.html b/recruitment/templates/candidate/candidate_nav.html index 73997817c..92124b5e6 100644 --- a/recruitment/templates/candidate/candidate_nav.html +++ b/recruitment/templates/candidate/candidate_nav.html @@ -6,39 +6,12 @@ aria-labelledby="candidateExport" aria-hidden="true" > -