From c652ec017189be7a40703cce14430dbd3934cbde Mon Sep 17 00:00:00 2001 From: Horilla Date: Fri, 1 Dec 2023 15:36:51 +0530 Subject: [PATCH] [IMP] Multi Company in Horilla --- asset/filters.py | 36 +-- asset/forms.py | 28 ++- asset/models.py | 13 +- .../category/asset_category_creation.html | 5 + .../category/asset_category_update.html | 7 + attendance/filters.py | 58 +---- attendance/forms.py | 10 + attendance/models.py | 21 +- .../own_attendance/attendances.html | 6 + base/context_processors.py | 92 ++++++++ base/forms.py | 7 +- base/horilla_company_manager.py | 41 ++++ base/methods.py | 11 + base/middleware.py | 97 ++++++++ base/models.py | 113 +++++---- base/templates/base/company/company.html | 127 +++++----- base/templates/base/company/company_form.html | 68 ++++++ base/templates/base/company/company_view.html | 128 +++++----- .../templates/base/department/department.html | 75 ++++-- .../base/department/department_form.html | 36 +++ .../base/department/department_view.html | 83 ++++--- .../base/job_position/job_position.html | 73 +++--- .../base/job_position/job_position_form.html | 34 +++ .../base/job_position/job_position_view.html | 220 ++++++++++-------- base/templates/base/job_role/job_role.html | 72 +++--- .../base/job_role/job_role_form.html | 33 +++ .../base/job_role/job_role_view.html | 194 ++++++++------- base/templates/base/shift/shift.html | 7 + base/templates/base/work_type/work_type.html | 54 +++-- .../base/work_type/work_type_form.html | 33 +++ .../base/work_type/work_type_view.html | 79 ++++--- base/thread_local_middleware.py | 17 ++ base/urls.py | 24 +- base/views.py | 131 ++++++++--- employee/forms.py | 2 + employee/models.py | 13 +- employee/views.py | 4 +- horilla/__init__.py | 2 + horilla/filters.py | 22 +- horilla/horilla_context_processors.py | 10 + horilla/horilla_middlewares.py | 9 + horilla/settings.py | 1 + horilla/urls.py | 2 +- .../multiselect_components/table.html | 12 + leave/filters.py | 38 +-- leave/forms.py | 15 +- leave/models.py | 30 ++- .../leave_allocation_request_group_by.html | 2 + onboarding/filters.py | 43 +--- onboarding/forms.py | 14 +- onboarding/models.py | 25 +- payroll/filters.py | 2 +- payroll/forms/component_forms.py | 11 +- payroll/forms/forms.py | 2 + payroll/forms/tax_forms.py | 2 + payroll/models/models.py | 13 +- pms/filters.py | 14 +- pms/forms.py | 51 ++-- pms/models.py | 26 ++- .../question_template_update.html | 5 + .../question_template_view.html | 4 + pms/templates/period/period_create.html | 7 + pms/templates/period/period_update.html | 7 + recruitment/filters.py | 35 +-- recruitment/forms.py | 5 +- recruitment/models.py | 12 +- .../form/recruitment_drop_down_form.html | 10 + templates/index.html | 71 ++++-- templates/settings.html | 16 +- templates/sidebar.html | 40 +++- 70 files changed, 1701 insertions(+), 909 deletions(-) create mode 100644 base/context_processors.py create mode 100644 base/horilla_company_manager.py create mode 100644 base/middleware.py create mode 100644 base/templates/base/company/company_form.html create mode 100644 base/templates/base/department/department_form.html create mode 100644 base/templates/base/job_position/job_position_form.html create mode 100644 base/templates/base/job_role/job_role_form.html create mode 100644 base/templates/base/work_type/work_type_form.html create mode 100644 base/thread_local_middleware.py create mode 100644 horilla/horilla_context_processors.py create mode 100644 horilla/horilla_middlewares.py diff --git a/asset/filters.py b/asset/filters.py index ace7f1ce1..e0da7cb05 100644 --- a/asset/filters.py +++ b/asset/filters.py @@ -7,6 +7,7 @@ import django_filters from django import forms from django_filters import FilterSet from .models import Asset, AssetAssignment, AssetCategory, AssetRequest +from base.methods import reload_queryset class CustomFilterSet(FilterSet): @@ -23,42 +24,40 @@ class CustomFilterSet(FilterSet): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - - for field_name, field in self.filters.items(): + reload_queryset(self.form.fields) + for field_name, field in self.form.fields.items(): filter_widget = self.filters[field_name] widget = filter_widget.field.widget if isinstance( - widget, (forms.NumberInput, forms.EmailInput, forms.TextInput) + widget, (forms.NumberInput, forms.EmailInput, forms.TextInput) ): - filter_widget.field.widget.attrs.update({"class": "oh-input w-100"}) + field.widget.attrs.update({"class": "oh-input w-100"}) elif isinstance(widget, (forms.Select,)): - filter_widget.field.widget.attrs.update( + field.widget.attrs.update( { "class": "oh-select oh-select-2", } ) elif isinstance(widget, (forms.Textarea)): - filter_widget.field.widget.attrs.update({"class": "oh-input w-100"}) + field.widget.attrs.update({"class": "oh-input w-100"}) elif isinstance( - widget, - ( - forms.CheckboxInput, - forms.CheckboxSelectMultiple, - ), + widget, + ( + forms.CheckboxInput, + forms.CheckboxSelectMultiple, + ), ): filter_widget.field.widget.attrs.update( {"class": "oh-switch__checkbox"} ) elif isinstance(widget, (forms.ModelChoiceField)): - filter_widget.field.widget.attrs.update( + field.widget.attrs.update( { "class": "oh-select oh-select-2 ", } ) elif isinstance(widget, (forms.DateField)): - filter_widget.field.widget.attrs.update( - {"type": "date", "class": "oh-input w-100"} - ) + field.widget.attrs.update({"type": "date", "class": "oh-input w-100"}) if isinstance(field, django_filters.CharFilter): field.lookup_expr = "icontains" @@ -217,8 +216,9 @@ class AssetRequestReGroup: ("asset_category_id", "Asset Category"), ("asset_request_date", "Request Date"), ("asset_request_status", "Status"), - ] - + ] + + class AssetAllocationReGroup: """ Class to keep the field name for group by option @@ -229,4 +229,4 @@ class AssetAllocationReGroup: ("assigned_to_employee_id", "Employee"), ("assigned_date", "Assigned Date"), ("return_date", "Return Date"), - ] + ] diff --git a/asset/forms.py b/asset/forms.py index cf1467222..ea211c214 100644 --- a/asset/forms.py +++ b/asset/forms.py @@ -1,4 +1,5 @@ """ +forms.py Asset Management Forms This module contains Django ModelForms for handling various aspects of asset management, @@ -7,11 +8,12 @@ including asset creation, allocation, return, category assignment, and batch han from datetime import date import uuid from django import forms -from django.forms import ModelForm +from base.forms import ModelForm from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ from employee.models import Employee -from .models import Asset, AssetRequest, AssetAssignment, AssetCategory, AssetLot +from asset.models import Asset, AssetRequest, AssetAssignment, AssetCategory, AssetLot +from base.methods import reload_queryset def set_date_field_initial(instance): @@ -84,7 +86,7 @@ class AssetForm(ModelForm): if instance: kwargs["initial"] = set_date_field_initial(instance) super(AssetForm, self).__init__(*args, **kwargs) - + reload_queryset(self.fields) self.fields["asset_category_id"].widget.attrs.update({"id": str(uuid.uuid4())}) self.fields["asset_lot_number_id"].widget.attrs.update( {"id": str(uuid.uuid4())} @@ -95,13 +97,13 @@ class AssetForm(ModelForm): instance = self.instance if instance.pk: if asset_in_use := instance.assetassignment_set.filter( - return_status__isnull=True + return_status__isnull=True ): raise ValidationError('Asset in use you can"t change the status') if ( - Asset.objects.filter(asset_tracking_id=self.data["asset_tracking_id"]) - .exclude(id=instance.pk) - .exists() + Asset.objects.filter(asset_tracking_id=self.data["asset_tracking_id"]) + .exclude(id=instance.pk) + .exists() ): raise ValidationError( {"asset_tracking_id": "Already asset with this tracking id exists."} @@ -191,6 +193,7 @@ class AssetRequestForm(ModelForm): *args, **kwargs, ) + reload_queryset(self.fields) if user is not None and user.has_perm("asset.add_assetrequest"): self.fields["requested_employee_id"].queryset = Employee.objects.all() self.fields["requested_employee_id"].initial = Employee.objects.filter( @@ -212,8 +215,10 @@ class AssetAllocationForm(ModelForm): def __init__(self, *args, **kwargs): super(AssetAllocationForm, self).__init__(*args, **kwargs) - self.fields['asset_id'].queryset = Asset.objects.filter(asset_status="Available") - + reload_queryset(self.fields) + self.fields["asset_id"].queryset = Asset.objects.filter( + asset_status="Available" + ) class Meta: """ @@ -278,6 +283,7 @@ class AssetReturnForm(ModelForm): attrs={"class": "oh-select oh-select-2", "required": "true"}, ), } + def clean_return_date(self): return_date = self.cleaned_data.get("return_date") @@ -292,6 +298,10 @@ class AssetBatchForm(ModelForm): A Django ModelForm for creating or updating AssetLot instances. """ + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + reload_queryset(self.fields) + class Meta: """ Specifies the model and fields to be used for the AssetBatchForm. diff --git a/asset/models.py b/asset/models.py index 3c3dcbf76..0a035df0c 100644 --- a/asset/models.py +++ b/asset/models.py @@ -8,6 +8,7 @@ from django.db import models from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ from base.models import Company +from base.horilla_company_manager import HorillaCompanyManager from employee.models import Employee @@ -19,7 +20,7 @@ class AssetCategory(models.Model): asset_category_name = models.CharField(max_length=255, unique=True) asset_category_description = models.TextField() objects = models.Manager() - company_id = models.ForeignKey(Company,null=True, editable=False, on_delete=models.PROTECT) + company_id = models.ManyToManyField(Company,blank=True, verbose_name=_("Company")) def __str__(self): return f"{self.asset_category_name}" @@ -32,8 +33,8 @@ class AssetLot(models.Model): lot_number = models.CharField(max_length=30, null=False, blank=False, unique=True) lot_description = models.TextField(null=True, blank=True) - company_id = models.ForeignKey(Company,null=True, editable=False, on_delete=models.PROTECT) - objects = models.Manager() + company_id = models.ManyToManyField(Company,blank=True, verbose_name=_("Company")) + objects = HorillaCompanyManager() def __str__(self): return f"{self.lot_number}" @@ -61,7 +62,7 @@ class Asset(models.Model): asset_lot_number_id = models.ForeignKey( AssetLot, on_delete=models.PROTECT, null=True, blank=True ) - objects = models.Manager() + objects = HorillaCompanyManager("asset_category_id__company_id") def __str__(self): return f"{self.asset_name}-{self.asset_tracking_id}" @@ -108,7 +109,7 @@ class AssetAssignment(models.Model): return_status = models.CharField( choices=STATUS, max_length=30, null=True, blank=True ) - objects = models.Manager() + objects = HorillaCompanyManager("asset_id__asset_lot_number_id__company_id") class AssetRequest(models.Model): @@ -134,4 +135,4 @@ class AssetRequest(models.Model): asset_request_status = models.CharField( max_length=30, choices=STATUS, default="Requested", null=True, blank=True ) - objects = models.Manager() + objects = HorillaCompanyManager("requested_employee_id__employee_work_info__company_id") diff --git a/asset/templates/category/asset_category_creation.html b/asset/templates/category/asset_category_creation.html index afc62fd67..e887ac027 100644 --- a/asset/templates/category/asset_category_creation.html +++ b/asset/templates/category/asset_category_creation.html @@ -26,6 +26,11 @@ {{asset_category_form.asset_category_description}} +
+ + {{asset_category_form.company_id}} + {{asset_category_form.errors}} +
+
+ {% trans "Pending Hour" %} +
{{attendance.attendance_worked_hour}}
+
{{ attendance.hours_pending }}
{{attendance.attendance_overtime}}
diff --git a/base/context_processors.py b/base/context_processors.py new file mode 100644 index 000000000..507cd2c18 --- /dev/null +++ b/base/context_processors.py @@ -0,0 +1,92 @@ +""" +context_processor.py + +This module is used to register context processor` +""" +from django.urls import path +from django.http import HttpResponse +from base.models import Company +from base.urls import urlpatterns + + +class AllCompany: + """ + Dummy class + """ + + class Urls: + url = "https://ui-avatars.com/api/?name=All+Company&background=random" + + company = "All Company" + icon = Urls() + text = "All companies" + id = None + + +def get_companies(request): + """ + This method will return the history additional field form + """ + companies = list( + [company.id, company.company, company.icon.url, False] + for company in Company.objects.all() + ) + companies = [ + [ + "all", + "All Company", + "https://ui-avatars.com/api/?name=All+Company&background=random", + False, + ], + ] + companies + selected_company = request.session.get("selected_company") + company_selected = False + if selected_company and selected_company == "all": + companies[0][3] = True + company_selected = True + else: + for company in companies: + if str(company[0]) == selected_company: + company[3] = True + company_selected = True + return {"all_companies": companies, "company_selected": company_selected} + + +def update_selected_company(request): + """ + This method is used to update the selected company on the session + """ + company_id = request.GET.get("company_id") + request.session["selected_company"] = company_id + company = ( + AllCompany() + if company_id == "all" + else ( + Company.objects.filter(id=company_id).first() + if Company.objects.filter(id=company_id).first() + else AllCompany() + ) + ) + + text = "Other Company" + if company_id == request.user.employee_get.employee_work_info.company_id: + text = "My Company" + if company_id == "all": + text = "All companies" + company = { + "company": company.company, + "icon": company.icon.url, + "text": text, + "id": company.id, + } + request.session["selected_company_instance"] = company + return HttpResponse("") + + +urlpatterns.append( + path( + "update-selected-company", + update_selected_company, + name="update-selected-company", + ) +) diff --git a/base/forms.py b/base/forms.py index d8d31d6ee..6ca206084 100644 --- a/base/forms.py +++ b/base/forms.py @@ -35,6 +35,7 @@ from base.models import ( ShiftRequest, EmployeeShiftDay, ) +from base.methods import reload_queryset # your form here @@ -146,12 +147,14 @@ class ModelForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + reload_queryset(self.fields) for field_name, field in self.fields.items(): widget = field.widget if isinstance( widget, (forms.NumberInput, forms.EmailInput, forms.TextInput, forms.FileInput), ): + label = '' if field.label is not None: label = _(field.label.title()) field.widget.attrs.update( @@ -570,6 +573,7 @@ class RotatingWorkTypeAssignUpdateForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + reload_queryset(self.fields) self.fields["rotate_every_weekend"].widget.attrs.update( { @@ -841,6 +845,7 @@ class RotatingShiftAssignForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + reload_queryset(self.fields) self.fields["rotate_every_weekend"].widget.attrs.update( { "class": "w-100 ", @@ -985,6 +990,7 @@ class RotatingShiftAssignUpdateForm(ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + reload_queryset(self.fields) self.fields["rotate_every_weekend"].widget.attrs.update( { "class": "w-100 ", @@ -1164,7 +1170,6 @@ class WorkTypeRequestForm(ModelForm): return super().save(commit) -from django.contrib.auth.models import User class ChangePasswordForm(forms.Form): diff --git a/base/horilla_company_manager.py b/base/horilla_company_manager.py new file mode 100644 index 000000000..14bc08aba --- /dev/null +++ b/base/horilla_company_manager.py @@ -0,0 +1,41 @@ +""" +horilla_company_manager.py +""" +import threading +from django.db import models +from base.thread_local_middleware import _thread_locals + + +class HorillaCompanyManager(models.Manager): + """ + HorillaCompanyManager + """ + + def __init__(self, related_company_field=None, *args, **kwargs): + super().__init__(*args, **kwargs) + self.related_company_field = related_company_field + + def get_queryset(self): + """ + get_queryset method + """ + queryset = super().get_queryset() + request = getattr(_thread_locals, "request", None) + selected_company = None + if request is not None: + selected_company = request.session.get("selected_company") + try: + queryset = ( + queryset.filter(self.model.company_filter) + if selected_company != "all" and selected_company + else queryset + ) + except Exception as e: + print(e) + try: + has_duplicates = queryset.count() != queryset.distinct().count() + if has_duplicates: + queryset = queryset.distinct() + except: + pass + return queryset diff --git a/base/methods.py b/base/methods.py index 4c2aaaf38..364934293 100644 --- a/base/methods.py +++ b/base/methods.py @@ -4,6 +4,7 @@ import random from django.apps import apps from django.core.exceptions import ObjectDoesNotExist from django.db.models import ForeignKey, ManyToManyField, OneToOneField +from django.forms.models import ModelMultipleChoiceField, ModelChoiceField from django.http import HttpResponse from django.utils.translation import gettext as _ import pandas as pd @@ -410,3 +411,13 @@ def export_data(request, model, form_class, filter_class, file_name): writer.close() return response + + +def reload_queryset(fields): + """ + This method is used to reload the querysets in the form + """ + for k, v in fields.items(): + if isinstance(v, ModelChoiceField): + v.queryset = v.queryset.model.objects.all() + return diff --git a/base/middleware.py b/base/middleware.py new file mode 100644 index 000000000..684ed636a --- /dev/null +++ b/base/middleware.py @@ -0,0 +1,97 @@ +""" +middleware.py +""" +from django.apps import apps +from django.db.models import Q +from base.horilla_company_manager import HorillaCompanyManager +from base.models import Company +from base.context_processors import AllCompany + + +class CompanyMiddleware: + """ + company middleware class + """ + + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + # Get the current user's company_id from the request + if getattr(request, "user", False) and not request.user.is_anonymous: + company_id = None + try: + + company_id = getattr( + request.user.employee_get.employee_work_info, "company_id", None + ) + except: + pass + if ( + request.session.get("selected_company") + and request.session.get("selected_company") != "all" + ): + company_id = Company.objects.filter( + id=request.session.get("selected_company") + ).first() + elif company_id and request.session.get("selected_company") != "all": + request.session["selected_company"] = company_id.id + request.session["selected_company_instance"] = { + "company": company_id.company, + "icon": company_id.icon.url, + "text": "My company", + "id": company_id.id, + } + elif not company_id: + request.session["selected_company"] = "all" + all_company = AllCompany() + request.session["selected_company_instance"] = { + "company": all_company.company, + "icon": all_company.icon.url, + "text": all_company.text, + "id": all_company.id, + } + + # for testing here only get recruitment models + app_labels = [ + "recruitment", + "employee", + "onboarding", + "attendance", + "leave", + "payroll", + "asset", + "pms", + "base", + ] + app_models = [ + model + for model in apps.get_models() + if model._meta.app_label in app_labels + ] + # Add company filter to every query + if company_id: + for ( + model + ) in app_models: # Replace YourModels with the actual models you have + if getattr(model, "company_id", None): + model.add_to_class( + "company_filter", + Q(company_id=company_id) | Q(company_id__isnull=True), + ) + elif ( + isinstance(model.objects, HorillaCompanyManager) + and model.objects.related_company_field + ): + model.add_to_class( + "company_filter", + Q(**{model.objects.related_company_field: company_id}) + | Q( + **{ + f"{model.objects.related_company_field}__isnull": True + } + ), + ) + + response = self.get_response(request) + return response diff --git a/base/models.py b/base/models.py index 8bf20ad31..d56b85b43 100644 --- a/base/models.py +++ b/base/models.py @@ -7,8 +7,7 @@ import django from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ from django.db import models -from simple_history.models import HistoricalRecords -from django.template import defaultfilters +from base.horilla_company_manager import HorillaCompanyManager # Create your models here. @@ -60,22 +59,41 @@ class Company(models.Model): def __str__(self) -> str: return str(self.company) + +from base.thread_local_middleware import _thread_locals +from django import forms class Department(models.Model): """ Department model """ - department = models.CharField(max_length=50, blank=False, unique=True) - company_id = models.ForeignKey( - Company, null=True, editable=False, on_delete=models.PROTECT - ) - objects = models.Manager() + department = models.CharField(max_length=50, blank=False) + company_id = models.ManyToManyField(Company, blank=True, verbose_name=_("Company")) + + objects = HorillaCompanyManager() class Meta: verbose_name = _("Department") verbose_name_plural = _("Departments") + + def clean(self, *args, **kwargs): + super().clean(*args, **kwargs) + request = getattr(_thread_locals, "request", None) + if request and request.POST: + company = request.POST.getlist('company_id', None) + department = request.POST.get('department', None) + if Department.objects.filter(company_id__id__in=company,department = department).exclude(id= self.id).exists(): + raise ValidationError( + "This department already exists in this company" + ) + return + + def save(self, *args, **kwargs): + super().save(*args, **kwargs) + self.clean(*args, **kwargs) + return self def __str__(self): return str(self.department) @@ -90,14 +108,12 @@ class JobPosition(models.Model): department_id = models.ForeignKey( Department, on_delete=models.PROTECT, - blank=True, related_name="job_position", verbose_name=_("Department"), ) - company_id = models.ForeignKey( - Company, null=True, editable=False, on_delete=models.PROTECT - ) - objects = models.Manager() + company_id = models.ManyToManyField(Company, blank=True, verbose_name=_("Company")) + + objects = HorillaCompanyManager("department_id__company_id") class Meta: """ @@ -118,10 +134,9 @@ class JobRole(models.Model): JobPosition, on_delete=models.PROTECT, verbose_name=_("Job Position") ) job_role = models.CharField(max_length=50, blank=False, null=True) - company_id = models.ForeignKey( - Company, null=True, editable=False, on_delete=models.PROTECT - ) - objects = models.Manager() + company_id = models.ManyToManyField(Company, blank=True, verbose_name=_("Company")) + + objects = HorillaCompanyManager("job_position_id__department_id__company_id") class Meta: """ @@ -142,10 +157,9 @@ class WorkType(models.Model): """ work_type = models.CharField(max_length=50) - company_id = models.ForeignKey( - Company, null=True, editable=False, on_delete=models.PROTECT - ) - objects = models.Manager() + company_id = models.ManyToManyField(Company, blank=True, verbose_name=_("Company")) + + objects = HorillaCompanyManager() class Meta: """ @@ -157,7 +171,24 @@ class WorkType(models.Model): def __str__(self) -> str: return str(self.work_type) - + + def clean(self, *args, **kwargs): + super().clean(*args, **kwargs) + request = getattr(_thread_locals, "request", None) + if request and request.POST: + company = request.POST.getlist('company_id', None) + work_type = request.POST.get('work_type', None) + if WorkType.objects.filter(company_id__id__in=company,work_type = work_type).exclude(id= self.id).exists(): + raise ValidationError( + "This work type already exists in this company" + ) + return + + def save(self, *args, **kwargs): + super().save(*args, **kwargs) + self.clean(*args, **kwargs) + return self + class RotatingWorkType(models.Model): """ @@ -182,7 +213,7 @@ class RotatingWorkType(models.Model): through="RotatingWorkTypeAssign", verbose_name=_("Employee"), ) - objects = models.Manager() + objects = HorillaCompanyManager("employee_id__employee_work_info__company_id") class Meta: """ @@ -276,7 +307,7 @@ class RotatingWorkTypeAssign(models.Model): ) is_active = models.BooleanField(default=True) - objects = models.Manager() + objects = HorillaCompanyManager("employee_id__employee_work_info__company_id") class Meta: """ @@ -305,10 +336,9 @@ class EmployeeType(models.Model): """ employee_type = models.CharField(max_length=50) - company_id = models.ForeignKey( - Company, null=True, editable=False, on_delete=models.PROTECT - ) - objects = models.Manager() + company_id = models.ManyToManyField(Company, blank=True, verbose_name=_("Company")) + + objects = HorillaCompanyManager("employee_id__employee_work_info__company_id") class Meta: """ @@ -328,10 +358,9 @@ class EmployeeShiftDay(models.Model): """ day = models.CharField(max_length=20, choices=DAY) - company_id = models.ForeignKey( - Company, null=True, editable=False, on_delete=models.PROTECT - ) - objects = models.Manager() + company_id = models.ManyToManyField(Company, blank=True, verbose_name=_("Company")) + + objects = HorillaCompanyManager() class Meta: """ @@ -366,10 +395,9 @@ class EmployeeShift(models.Model): full_time = models.CharField( max_length=6, default="200:00", validators=[validate_time_format] ) - company_id = models.ForeignKey( - Company, null=True, editable=False, on_delete=models.PROTECT - ) - objects = models.Manager() + company_id = models.ManyToManyField(Company, blank=True, verbose_name=_("Company")) + + objects = HorillaCompanyManager("employee_shift__company_id") class Meta: """ @@ -400,10 +428,9 @@ class EmployeeShiftSchedule(models.Model): start_time = models.TimeField(null=True) end_time = models.TimeField(null=True) is_night_shift = models.BooleanField(default=False) - company_id = models.ForeignKey( - Company, null=True, editable=False, on_delete=models.PROTECT - ) - objects = models.Manager() + company_id = models.ManyToManyField(Company, blank=True, verbose_name=_("Company")) + + objects = HorillaCompanyManager("shift_id__employee_shift__company_id") class Meta: """ @@ -444,7 +471,7 @@ class RotatingShift(models.Model): on_delete=models.PROTECT, verbose_name=_("Shift 2"), ) - objects = models.Manager() + objects = HorillaCompanyManager("employee_id__employee_work_info__company_id") class Meta: """ @@ -518,7 +545,7 @@ class RotatingShiftAssign(models.Model): verbose_name=_("Rotate Every Month"), ) is_active = models.BooleanField(default=True) - objects = models.Manager() + objects = HorillaCompanyManager("employee_id__employee_work_info__company_id") class Meta: """ @@ -581,7 +608,7 @@ class WorkTypeRequest(models.Model): canceled = models.BooleanField(default=False, verbose_name=_("Canceled")) work_type_changed = models.BooleanField(default=False) is_active = models.BooleanField(default=True) - objects = models.Manager() + objects = HorillaCompanyManager("employee_id__employee_work_info__company_id") class Meta: """ @@ -690,7 +717,7 @@ class ShiftRequest(models.Model): canceled = models.BooleanField(default=False, verbose_name=_("Canceled")) shift_changed = models.BooleanField(default=False) is_active = models.BooleanField(default=True) - objects = models.Manager() + objects = HorillaCompanyManager("employee_id__employee_work_info__company_id") class Meta: """ diff --git a/base/templates/base/company/company.html b/base/templates/base/company/company.html index 5241a3124..2a131abc5 100644 --- a/base/templates/base/company/company.html +++ b/base/templates/base/company/company.html @@ -1,70 +1,63 @@ -{% extends 'settings.html' %} -{% load i18n %} -{% block settings %} +{% extends 'settings.html' %} {% load i18n %} {% block settings %}
- {% if perms.base.add_company %} -
- {% csrf_token %} -
-

{% trans "Company" %}

-
-
-
- - {{form.company}} - {{form.company.errors}} -
-
- - {{form.hq}} -
-
- - {{form.address}} - {{form.address.errors}} -
-
- - - {{form.country.errors}} -
-
- - - {{form.state.errors}} -
-
- - {{form.city}} - {{form.city.errors}} -
-
- - {{form.zip}} - {{form.zip.errors}} -
-
- - {{form.icon}} - {{form.icon.errors}} -
-
- - -
- - {% endif %} + {% if perms.base.add_company %} +
+

{% trans "Company" %}

+ +
- {% include 'base/company/company_view.html' %} + {% include 'base/company/company_view.html' %} {% endif %} - -{% endblock settings %} \ No newline at end of file + + + + + +{% endblock settings %} diff --git a/base/templates/base/company/company_form.html b/base/templates/base/company/company_form.html new file mode 100644 index 000000000..df1f26050 --- /dev/null +++ b/base/templates/base/company/company_form.html @@ -0,0 +1,68 @@ +{% load i18n %} +
+ {% csrf_token %} +
+
+ + {{form.company}} {{form.company.errors}} +
+
+ + {{form.hq}} +
+
+ + {{form.address}} {{form.address.errors}} +
+
+ + + {{form.country.errors}} +
+
+ + + {{form.state.errors}} +
+
+ + {{form.city}} {{form.city.errors}} +
+
+ + {{form.zip}} {{form.zip.errors}} +
+
+ + {{form.icon}} {{form.icon.errors}} +
+
+ + +
+ + diff --git a/base/templates/base/company/company_view.html b/base/templates/base/company/company_view.html index b88f6c446..944ae707d 100644 --- a/base/templates/base/company/company_view.html +++ b/base/templates/base/company/company_view.html @@ -1,64 +1,70 @@ {% load i18n %}
-
-
-
-
{% trans "Company" %}
-
{% trans "Is Hq" %}
-
{% trans "Address" %}
-
{% trans "Country" %}
-
{% trans "State" %}
-
{% trans "City" %}
-
{% trans "Zip" %}
-
-
-
-
- {% for company in companies %} -
- -
-
- - Username{{company.company}} - -
-
-
- {{company.hq}} -
-
- {{company.address}} -
-
- {{company.country}} -
-
- {{company.state}} -
-
- {{company.city}} -
-
- {{company.zip}} -
+
+
+
+
{% trans "Company" %}
+
{% trans "Is Hq" %}
+
{% trans "Address" %}
+
{% trans "Country" %}
+
{% trans "State" %}
+
{% trans "City" %}
+
{% trans "Zip" %}
+
+
+
+
+ {% for company in companies %} +
+
+
+ Username{{company.company}} +
+
+
{{company.hq}}
+
{{company.address}}
+
{{company.country}}
+
{{company.state}}
+
{{company.city}}
+
{{company.zip}}
-
- -
- {% if perms.base.change_company %} - - {% endif %} - {% if perms.base.delete_company %} -
- {% csrf_token %} - -
- {% endif %} -
-
-
- {% endfor %} -
-
-
+
+
+ {% if perms.base.change_company %} + + {% endif %} {% if perms.base.delete_company %} +
+ {% csrf_token %} + +
+ {% endif %} +
+
+
+ {% endfor %} +
+
+ diff --git a/base/templates/base/department/department.html b/base/templates/base/department/department.html index 9692dbb33..fa5bed1c9 100644 --- a/base/templates/base/department/department.html +++ b/base/templates/base/department/department.html @@ -3,29 +3,64 @@ {% block settings %}
{% if perms.base.add_department %} -
- {% csrf_token %} -
+

{% trans "Department" %}

+
-
- -
- {{form.department.errors}} - {{form.department}} -
-
- - -
-
+ {% include 'base/department/department_view.html' %} + {% endif %} - {% include 'base/department/department_view.html' %}
+ + + + + + {% endblock settings %} \ No newline at end of file diff --git a/base/templates/base/department/department_form.html b/base/templates/base/department/department_form.html new file mode 100644 index 000000000..8d321750a --- /dev/null +++ b/base/templates/base/department/department_form.html @@ -0,0 +1,36 @@ +{% load i18n %} +
+ {% csrf_token %} + {{form.non_field_errors}} +
+ +
+ {{form.department.errors}} {{form.department}} +
+
+ +
+ +
+ {{form.company_id.errors}} + {{form.company_id}} +
+
+ + +
diff --git a/base/templates/base/department/department_view.html b/base/templates/base/department/department_view.html index b5020a6c6..753032888 100644 --- a/base/templates/base/department/department_view.html +++ b/base/templates/base/department/department_view.html @@ -1,35 +1,50 @@ {% load i18n %} -
-
-
-
-
{% trans "Department" %}
-
-
-
-
- {% for dep in departments %} -
- -
- {{dep}} -
-
-
- - {% if perms.base.change_department %} - - {% endif %} - {% if perms.base.delete_deaprtment %} -
- {% csrf_token %} - -
- {% endif %} -
-
-
- {% endfor %} -
-
-
\ No newline at end of file +
+
+
+
+
{% trans "Department" %}
+
+
+
+
+ {% for dep in departments %} +
+
{{dep}}
+
+
+ {% if perms.base.change_department %} + + + {% endif %} + {% if perms.base.delete_deaprtment %} +
+ {% csrf_token %} + +
+ {% endif %} +
+
+
+ {% endfor %} +
+
+
diff --git a/base/templates/base/job_position/job_position.html b/base/templates/base/job_position/job_position.html index 29044b43f..05d6c468f 100644 --- a/base/templates/base/job_position/job_position.html +++ b/base/templates/base/job_position/job_position.html @@ -1,38 +1,39 @@ -{% extends 'settings.html' %} -{% load i18n %} -{% block settings %} +{% extends 'settings.html' %} {% load i18n %} {% block settings %}
- {% if perms.base.add_jobposition %} -
- {% csrf_token %} -
-

{% trans "Job Position" %}

-
-
-
- - {{form.department_id.errors}} - {{form.department_id}} -
-
- - {{form.job_position.errors}} - {{form.job_position}} -
-
- - -
- - {% endif %} - - {% include 'base/job_position/job_position_view.html' %} + {% if perms.base.add_jobposition %} +
+

{% trans "Job Position" %}

+ +
+ {% include 'base/job_position/job_position_view.html' %} {% endif %} -{% endblock settings %} \ No newline at end of file + + +{% endblock settings %} diff --git a/base/templates/base/job_position/job_position_form.html b/base/templates/base/job_position/job_position_form.html new file mode 100644 index 000000000..c665d9e2f --- /dev/null +++ b/base/templates/base/job_position/job_position_form.html @@ -0,0 +1,34 @@ +{% load i18n %} + +
+ {% csrf_token %} +
+
+ + {{form.department_id}} {{form.department_id.errors}} +
+
+ + {{form.job_position}} {{form.job_position.errors}} +
+
+ + +
diff --git a/base/templates/base/job_position/job_position_view.html b/base/templates/base/job_position/job_position_view.html index dcb2c035c..1e54e5356 100644 --- a/base/templates/base/job_position/job_position_view.html +++ b/base/templates/base/job_position/job_position_view.html @@ -1,107 +1,121 @@ {% load i18n %}
-
-
-
-
-
-
-
-
{% trans "Department" %}
-
{% trans "Job Position" %}
-
-
+
+
+
+
+
+
+
+
{% trans "Department" %}
+
+ {% trans "Job Position" %} +
+
+
-
- {% for department in departments %} -
-
-
- - {{department}} -
-
-
- {% for job in department.job_position.all %} - -
-
- Baby C. -
- {{job|capfirst}} -
-
-
- {% if perms.base.change_jobposition %} -
- - - -
- {% endif %} {% if perms.base.delete_jobposition %} -
-
- - {% csrf_token %} -
-
- {% endif %} -
-
-
- {% endfor %} -
-
- {% endfor %} -
-
-
-
-
-
+
+ {% for department in departments %} +
+
+
+ + {{department}} +
+
+
+ + {{department.job_position.all|length}} + {% trans "Job positions" %} + + {% for job in department.job_position.all %} + +
+
+ +
+ {{job|capfirst}} +
+
+
+ {% if perms.base.change_jobposition %} +
+ + + +
+ {% endif %} + {% if perms.base.delete_jobposition %} +
+
+ + {% csrf_token %} +
+
+ {% endif %} +
+
+
+ {% endfor %} +
+
+ {% endfor %} +
+
+
+
+
+
diff --git a/base/templates/base/job_role/job_role.html b/base/templates/base/job_role/job_role.html index 8cfa1368b..105fb8cd9 100644 --- a/base/templates/base/job_role/job_role.html +++ b/base/templates/base/job_role/job_role.html @@ -1,38 +1,42 @@ -{% extends 'settings.html' %} -{% load i18n %} -{% block settings %} +{% extends 'settings.html' %} {% load i18n %} {% block settings %}
- {% if perms.base.add_jobrole %} -
- {% csrf_token %} -
-

{% trans "Job Role" %}

-
-
-
- - {{form.job_position_id.errors}} - {{form.job_position_id}} -
-
- - {{form.job_role.errors}} - {{form.job_role}} -
-
- - -
- + {% if perms.base.add_jobrole %} +
+

{% trans "Job Role" %}

+ +
+ {% include 'base/job_role/job_role_view.html' %} {% endif %} - {% include 'base/job_role/job_role_view.html' %} -{% endblock settings %} \ No newline at end of file + + + +{% endblock settings %} diff --git a/base/templates/base/job_role/job_role_form.html b/base/templates/base/job_role/job_role_form.html new file mode 100644 index 000000000..8def1e7e9 --- /dev/null +++ b/base/templates/base/job_role/job_role_form.html @@ -0,0 +1,33 @@ +{% load i18n %} + +
+ {% csrf_token %} + {{form.non_field_errors}} +
+
+ + {{form.job_position_id.errors}} {{form.job_position_id}} +
+
+ + {{form.job_role.errors}} {{form.job_role}} +
+
+ + +
diff --git a/base/templates/base/job_role/job_role_view.html b/base/templates/base/job_role/job_role_view.html index 08628ccf7..9f07fe58e 100644 --- a/base/templates/base/job_role/job_role_view.html +++ b/base/templates/base/job_role/job_role_view.html @@ -1,83 +1,115 @@ {% load i18n %} -
-
-
-
-
-
-
-
-
{% trans "Job Position" %}
-
{% trans "Job Role" %}
- -
-
- -
- {% for job in job_positions %} -
-
-
- - {{job}} -
-
-
- {% for role in job.jobrole_set.all %} - -
-
- Baby C. -
- {{role|capfirst}} -
-
-
+
+
+
+
+
+
+
+
+
+ {% trans "Job Position" %} +
+
{% trans "Job Role" %}
+
+
- {% if perms.base.view_jobrole %} -
- - - -
- {% endif %} - {% if perms.base.delete_jobrole %} -
-
- - {% csrf_token %} -
-
- {% endif %} -
-
- - {% endfor %} -
-
- {% endfor %} -
-
-
-
-
-
\ No newline at end of file +
+ {% for job in job_positions %} +
+
+
+ + {{job}} +
+
+
+ + {{job.jobrole_set.all|length}} + {% trans "Job positions" %} + + {% for role in job.jobrole_set.all %} + +
+
+ Baby C. +
+ {{role|capfirst}} +
+
+
+ {% if perms.base.view_jobrole %} +
+ + + +
+ {% endif %} {% if perms.base.delete_jobrole %} +
+
+ + {% csrf_token %} +
+
+ {% endif %} +
+
+
+ {% endfor %} +
+
+ {% endfor %} +
+
+
+
+
+
+
diff --git a/base/templates/base/shift/shift.html b/base/templates/base/shift/shift.html index 5ed9f8e27..199d0db5b 100644 --- a/base/templates/base/shift/shift.html +++ b/base/templates/base/shift/shift.html @@ -30,6 +30,13 @@ {{form.full_time.errors}} {{form.full_time}}
+
+ + {{form.company_id.errors}} + {{form.company_id}} +
-
-
- - {{form.work_type.errors}} - {{form.work_type}} -
-
- - - - + + {% endif %} {% include 'base/work_type/work_type_view.html' %} + + + + {% endblock settings %} \ No newline at end of file diff --git a/base/templates/base/work_type/work_type_form.html b/base/templates/base/work_type/work_type_form.html new file mode 100644 index 000000000..08f2b4cae --- /dev/null +++ b/base/templates/base/work_type/work_type_form.html @@ -0,0 +1,33 @@ +{% load i18n %} +
+ {% csrf_token %} + {{form.non_field_errors}} +
+
+ + {{form.work_type.errors}} {{form.work_type}} +
+
+
+
+ + {{form.company_id.errors}} {{form.company_id}} +
+
+ +
diff --git a/base/templates/base/work_type/work_type_view.html b/base/templates/base/work_type/work_type_view.html index 82376b906..987eaaff5 100644 --- a/base/templates/base/work_type/work_type_view.html +++ b/base/templates/base/work_type/work_type_view.html @@ -1,36 +1,49 @@ {% load i18n %}
-
-
-
-
{% trans "Work Type" %}
-
-
-
-
- {% for type in work_types %} -
-
{{type}}
-
- -
- {% if perms.base.change_worktype %} - - - {% endif %} - {% if perms.base.delete_worktype %} -
- {% csrf_token %} - -
- {% endif %} -
-
-
- {% endfor %} -
-
+
+
+
+
{% trans "Work Type" %}
+
+
+
+
+ {% for type in work_types %} +
+
{{type}}
+
+
+ {% if perms.base.change_worktype %} + + + {% endif %} + {% if perms.base.delete_worktype %} +
+ {% csrf_token %} + +
+ {% endif %} +
+
+
+ {% endfor %} +
+
- \ No newline at end of file diff --git a/base/thread_local_middleware.py b/base/thread_local_middleware.py new file mode 100644 index 000000000..b81e910b8 --- /dev/null +++ b/base/thread_local_middleware.py @@ -0,0 +1,17 @@ +import threading + +_thread_locals = threading.local() + + +class ThreadLocalMiddleware: + """ + ThreadLocalMiddleWare + """ + + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + _thread_locals.request = request + response = self.get_response(request) + return response diff --git a/base/urls.py b/base/urls.py index 883c028fa..dd2c78a99 100644 --- a/base/urls.py +++ b/base/urls.py @@ -68,7 +68,7 @@ urlpatterns = [ name="remove-permission", ), path("settings/company-create/", views.company_create, name="company-create"), - # path('company-view', views.company_view,name='company-view'), + path('company-view', views.company_view,name='company-view'), path( "settings/company-update//", views.company_update, @@ -79,9 +79,10 @@ urlpatterns = [ "company-delete//", views.object_delete, name="company-delete", - kwargs={"model": Company, "redirect": "/settings/company-create"}, + kwargs={"model": Company, "redirect": "/company-view"}, ), - path("settings/department-creation/", views.department, name="department-creation"), + path("settings/department-view/", views.department_view, name="department-view"), + path("settings/department-creation/", views.department_create, name="department-creation"), path( "settings/department-update//", views.department_update, @@ -92,13 +93,18 @@ urlpatterns = [ "department-delete//", views.object_delete, name="department-delete", - kwargs={"model": Department, "redirect": "/settings/department-creation"}, + kwargs={"model": Department, "redirect": "/settings/department-view"}, ), path( "settings/job-position-creation/", - views.job_position, + views.job_position_creation, name="job-position-creation", ), + path( + "settings/job-position-view/", + views.job_position, + name="job-position-view", + ), path( "settings/job-position-update//", views.job_position_update, @@ -109,9 +115,10 @@ urlpatterns = [ "job-position-delete//", views.object_delete, name="job-position-delete", - kwargs={"model": JobPosition, "redirect": "/settings/job-position-creation"}, + kwargs={"model": JobPosition, "redirect": "/settings/job-position-view"}, ), path("settings/job-role-create/", views.job_role_create, name="job-role-create"), + path("settings/job-role-view/", views.job_role_view, name="job-role-view"), path( "settings/job-role-update//", views.job_role_update, @@ -122,8 +129,9 @@ urlpatterns = [ "job-role-delete//", views.object_delete, name="job-role-delete", - kwargs={"model": JobRole, "redirect": "/settings/job-role-create"}, + kwargs={"model": JobRole, "redirect": "/settings/job-role-view"}, ), + path("settings/work-type-view/", views.work_type_view, name="work-type-view"), path("settings/work-type-create/", views.work_type_create, name="work-type-create"), path( "settings/work-type-update//", @@ -135,7 +143,7 @@ urlpatterns = [ "work-type-delete//", views.object_delete, name="work-type-delete", - kwargs={"model": WorkType, "redirect": "/settings/work-type-create"}, + kwargs={"model": WorkType, "redirect": "/settings/work-type-view"}, ), path( "settings/rotating-work-type-create/", diff --git a/base/views.py b/base/views.py index 128b174c7..b04758c44 100644 --- a/base/views.py +++ b/base/views.py @@ -516,15 +516,27 @@ def company_create(request): companies = Company.objects.all() if request.method == "POST": form = CompanyForm(request.POST, request.FILES) + if form.is_valid(): form.save() - form = CompanyForm() messages.success(request, _("Company has been created successfully!")) - return redirect(company_create) + return HttpResponse("") return render( - request, "base/company/company.html", {"form": form, "companies": companies} + request, "base/company/company_form.html", {"form": form, "companies": companies} + ) + +@login_required +@permission_required("base.add_company") +def company_view(request): + """ + This method used to view created companies + """ + + companies = Company.objects.all() + return render( + request, "base/company/company.html", {"companies": companies} ) @@ -537,7 +549,6 @@ def company_update(request, id, **kwargs): id : company instance id """ - companies = Company.objects.all() company = Company.objects.get(id=id) form = CompanyForm(instance=company) if request.method == "POST": @@ -545,34 +556,47 @@ def company_update(request, id, **kwargs): if form.is_valid(): form.save() messages.success(request, _("Company updated")) - return redirect(company_create) + return HttpResponse("") return render( - request, "base/company/company.html", {"form": form, "companies": companies} + request, "base/company/company_form.html", {"form": form, "company": company} ) @login_required @permission_required("base.add_department") -def department(request): +def department_create(request): """ - This method render renders form and template to create department + This method renders form and template to create department """ form = DepartmentForm() - departments = Department.objects.all() if request.method == "POST": form = DepartmentForm(request.POST) if form.is_valid(): form.save() form = DepartmentForm() messages.success(request, _("Department has been created successfully!")) + return HttpResponse("") + return render( + request, + "base/department/department_form.html", + {"form": form,}, + ) + + +@login_required +@permission_required("base.add_department") +def department_view(request): + """ + This method view department + """ + departments = Department.objects.all() return render( request, "base/department/department.html", - {"form": form, "departments": departments}, + {"departments": departments,}, ) - @login_required @permission_required("base.change_department") def department_update(request, id, **kwargs): @@ -582,18 +606,17 @@ def department_update(request, id, **kwargs): id : department instance id """ department = Department.objects.get(id=id) - departments = Department.objects.all() form = DepartmentForm(instance=department) if request.method == "POST": form = DepartmentForm(request.POST, instance=department) if form.is_valid(): form.save() messages.success(request, _("Department updated.")) - return redirect("/settings/department-creation") + return HttpResponse("") return render( request, - "base/department/department.html", - {"form": form, "departments": departments}, + "base/department/department_form.html", + {"form": form, "department": department}, ) @@ -601,7 +624,7 @@ def department_update(request, id, **kwargs): @permission_required("base.add_jobposition") def job_position(request): """ - This method is used to create job position + This method is used to view job position """ departments = Department.objects.all() @@ -617,6 +640,25 @@ def job_position(request): {"form": form, "departments": departments}, ) +@login_required +@permission_required("base.add_jobposition") +def job_position_creation(request): + """ + This method is used to create job position + """ + + form = JobPositionForm() + if request.method == "POST": + form = JobPositionForm(request.POST) + if form.is_valid(): + form.save() + messages.success(request, _("Job Position has been created successfully!")) + return HttpResponse("") + return render( + request, + "base/job_position/job_position_form.html", + {"form": form,}, + ) @login_required @permission_required("base.change_jobposition") @@ -628,18 +670,17 @@ def job_position_update(request, id, **kwargs): """ job_position = JobPosition.objects.get(id=id) - departments = Department.objects.all() form = JobPositionForm(instance=job_position) if request.method == "POST": form = JobPositionForm(request.POST, instance=job_position) if form.is_valid(): form.save() messages.success(request, _("Job position updated.")) - return redirect("/settings/job-position-creation") + return HttpResponse("") return render( request, - "base/job_position/job_position.html", - {"form": form, "departments": departments}, + "base/job_position/job_position_form.html", + {"form": form, "job_position": job_position}, ) @@ -651,7 +692,6 @@ def job_role_create(request): """ form = JobRoleForm() - jobs = JobPosition.objects.all() if request.method == "POST": form = JobRoleForm(request.POST) if form.is_valid(): @@ -659,8 +699,24 @@ def job_role_create(request): form = JobRoleForm() messages.success(request, _("Job role has been created successfully!")) + return HttpResponse("") + return render( - request, "base/job_role/job_role.html", {"form": form, "job_positions": jobs} + request, "base/job_role/job_role_form.html", {"form": form,} + ) + + +@login_required +@permission_required("base.add_jobrole") +def job_role_view(request): + """ + This method is used to view job role. + """ + + jobs = JobPosition.objects.all() + + return render( + request, "base/job_role/job_role.html", { "job_positions": jobs} ) @@ -675,7 +731,6 @@ def job_role_update(request, id, **kwargs): """ job_role = JobRole.objects.get(id=id) - jobs = JobPosition.objects.all() form = JobRoleForm(instance=job_role) if request.method == "POST": @@ -683,8 +738,10 @@ def job_role_update(request, id, **kwargs): if form.is_valid(): form.save() messages.success(request, _("Job role updated.")) + return HttpResponse("") + return render( - request, "base/job_role/job_role.html", {"form": form, "job_positions": jobs} + request, "base/job_role/job_role_form.html", {"form": form, "job_role": job_role,} ) @@ -704,10 +761,27 @@ def work_type_create(request): form = WorkTypeForm() messages.success(request, _("Work Type has been created successfully!")) + return HttpResponse("") + + return render( + request, + "base/work_type/work_type_form.html", + {"form": form, "work_types": work_types}, + ) + + +@login_required +@permission_required("base.add_worktype") +def work_type_view(request): + """ + This method is used to view work type + """ + + work_types = WorkType.objects.all() return render( request, "base/work_type/work_type.html", - {"form": form, "work_types": work_types}, + {"work_types": work_types}, ) @@ -722,18 +796,17 @@ def work_type_update(request, id, **kwargs): """ work_type = WorkType.objects.get(id=id) - work_types = WorkType.objects.all() form = WorkTypeForm(instance=work_type) if request.method == "POST": form = WorkTypeForm(request.POST, instance=work_type) if form.is_valid(): form.save() messages.success(request, _("Work type updated.")) - return redirect(work_type_create) + return HttpResponse("") return render( request, - "base/work_type/work_type.html", - {"form": form, "work_types": work_types}, + "base/work_type/work_type_form.html", + {"form": form, "work_type": work_type}, ) diff --git a/employee/forms.py b/employee/forms.py index 54b3107cb..2e246d3e6 100644 --- a/employee/forms.py +++ b/employee/forms.py @@ -27,6 +27,7 @@ from django.contrib.auth.models import User from django.forms import DateInput, TextInput from django.utils.translation import gettext_lazy as trans from employee.models import Employee, EmployeeWorkInformation, EmployeeBankDetails +from base.methods import reload_queryset class ModelForm(forms.ModelForm): @@ -36,6 +37,7 @@ class ModelForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + reload_queryset(self.fields) for _, field in self.fields.items(): widget = field.widget if isinstance( diff --git a/employee/models.py b/employee/models.py index fc593532f..ba163e5de 100644 --- a/employee/models.py +++ b/employee/models.py @@ -20,6 +20,7 @@ from base.models import ( Department, EmployeeShift, ) +from base.horilla_company_manager import HorillaCompanyManager from horilla_audit.models import HorillaAuditLog, HorillaAuditInfo from horilla_audit.methods import get_diff from django.core.files.storage import default_storage @@ -89,7 +90,9 @@ class Employee(models.Model): emergency_contact_relation = models.CharField(max_length=20, null=True, blank=True) is_active = models.BooleanField(default=True) additional_info = models.JSONField(null=True, blank=True) - objects = models.Manager() + objects = HorillaCompanyManager( + related_company_field="employee_work_info__company_id" + ) def get_image(self): """ @@ -119,7 +122,7 @@ class Employee(models.Model): ) if self.employee_profile: full_filename = settings.MEDIA_ROOT + self.employee_profile.name - + if default_storage.exists(full_filename): url = self.employee_profile.url return url @@ -266,7 +269,7 @@ class EmployeeWorkInformation(models.Model): HorillaAuditInfo, ], ) - objects = models.Manager() + objects = HorillaCompanyManager() def __str__(self) -> str: return f"{self.employee_id} - {self.job_position_id}" @@ -333,7 +336,9 @@ class EmployeeBankDetails(models.Model): max_length=50, null=True, blank=True, verbose_name="Bank Code #2" ) additional_info = models.JSONField(null=True, blank=True) - objects = models.Manager() + objects = HorillaCompanyManager( + related_company_field="employee_id__employee_work_info__company_id" + ) def __str__(self) -> str: return f"{self.employee_id}-{self.bank_name}" diff --git a/employee/views.py b/employee/views.py index ca2b72303..f1cf3d1eb 100755 --- a/employee/views.py +++ b/employee/views.py @@ -152,7 +152,7 @@ def employee_view_individual(request, obj_id, **kwargs): employee = Employee.objects.get(id=obj_id) employee_leaves = employee.available_leave.all() user = Employee.objects.filter(employee_user_id=request.user).first() - if user.reporting_manager.filter( + if user and user.reporting_manager.filter( employee_id=employee ).exists() or request.user.has_perm("employee.change_employee"): return render( @@ -468,7 +468,7 @@ def employee_view_update(request, obj_id, **kwargs): """ user = Employee.objects.filter(employee_user_id=request.user).first() employee = Employee.objects.filter(id=obj_id).first() - if user.reporting_manager.filter( + if user and user.reporting_manager.filter( employee_id=employee ).exists() or request.user.has_perm("employee.change_employee"): form = EmployeeForm(instance=employee) diff --git a/horilla/__init__.py b/horilla/__init__.py index 0b187b3cd..1a8421826 100755 --- a/horilla/__init__.py +++ b/horilla/__init__.py @@ -2,3 +2,5 @@ init.py """ from horilla import horilla_apps +from horilla import horilla_middlewares +from horilla import horilla_context_processors diff --git a/horilla/filters.py b/horilla/filters.py index f7a8e6e5f..840a9dded 100755 --- a/horilla/filters.py +++ b/horilla/filters.py @@ -1,7 +1,10 @@ -import django_filters -from django_filters import CharFilter -from django import forms +""" +filters.py +""" import uuid +import django_filters +from django import forms +from base.methods import reload_queryset def filter_by_name(queryset, name, value): """ @@ -25,16 +28,17 @@ def filter_by_name(queryset, name, value): class FilterSet(django_filters.FilterSet): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - for field_name, field in self.filters.items(): + reload_queryset(self.form.fields) + for field_name, field in self.form.fields.items(): filter_widget = self.filters[field_name] widget = filter_widget.field.widget if isinstance(widget, (forms.NumberInput, forms.EmailInput,forms.TextInput)): - filter_widget.field.widget.attrs.update({'class': 'oh-input w-100'}) + field.widget.attrs.update({'class': 'oh-input w-100'}) elif isinstance(widget,(forms.Select,)): - filter_widget.field.widget.attrs.update({'class': 'oh-select oh-select-2 select2-hidden-accessible','id':uuid.uuid4()}) + field.widget.attrs.update({'class': 'oh-select oh-select-2 select2-hidden-accessible','id':uuid.uuid4()}) elif isinstance(widget,(forms.Textarea)): - filter_widget.field.widget.attrs.update({'class': 'oh-input w-100'}) + field.widget.attrs.update({'class': 'oh-input w-100'}) elif isinstance(widget, (forms.CheckboxInput,forms.CheckboxSelectMultiple,)): - filter_widget.field.widget.attrs.update({'class': 'oh-switch__checkbox'}) + field.widget.attrs.update({'class': 'oh-switch__checkbox'}) elif isinstance(widget,(forms.ModelChoiceField)): - filter_widget.field.widget.attrs.update({'class': 'oh-select oh-select-2 select2-hidden-accessible',}) \ No newline at end of file + field.widget.attrs.update({'class': 'oh-select oh-select-2 select2-hidden-accessible',}) \ No newline at end of file diff --git a/horilla/horilla_context_processors.py b/horilla/horilla_context_processors.py new file mode 100644 index 000000000..2dd13736c --- /dev/null +++ b/horilla/horilla_context_processors.py @@ -0,0 +1,10 @@ +""" +horilla_context_process.py + +This module is used to register context processors without effecting the horilla/settings.py module +""" +from horilla.settings import TEMPLATES + +TEMPLATES[0]["OPTIONS"]["context_processors"].append( + "base.context_processors.get_companies", +) diff --git a/horilla/horilla_middlewares.py b/horilla/horilla_middlewares.py new file mode 100644 index 000000000..2f1d0362f --- /dev/null +++ b/horilla/horilla_middlewares.py @@ -0,0 +1,9 @@ +""" +horilla_middlewares.py + +This module is used to register horilla's middlewares without affecting the horilla/settings.py +""" +from horilla.settings import MIDDLEWARE + +MIDDLEWARE.append("base.middleware.CompanyMiddleware") +MIDDLEWARE.append("base.thread_local_middleware.ThreadLocalMiddleware") diff --git a/horilla/settings.py b/horilla/settings.py index 77ec660bc..7150f45a5 100755 --- a/horilla/settings.py +++ b/horilla/settings.py @@ -57,6 +57,7 @@ INSTALLED_APPS = [ 'asset', 'attendance', 'payroll', + 'horilla_widgets', 'widget_tweaks', "django_apscheduler", diff --git a/horilla/urls.py b/horilla/urls.py index 9bed4ea20..4934325d0 100755 --- a/horilla/urls.py +++ b/horilla/urls.py @@ -22,7 +22,7 @@ import notifications.urls urlpatterns = [ - # path('admin/',admin.site.urls), + path('admin/',admin.site.urls), path('',include('base.urls')), path('recruitment/',include('recruitment.urls')), path('employee/',include('employee.urls')), diff --git a/horilla_widgets/templates/horilla_widgets/multiselect_components/table.html b/horilla_widgets/templates/horilla_widgets/multiselect_components/table.html index adc414b60..e5189c737 100644 --- a/horilla_widgets/templates/horilla_widgets/multiselect_components/table.html +++ b/horilla_widgets/templates/horilla_widgets/multiselect_components/table.html @@ -200,4 +200,16 @@ }); }); }); + $("#choose-all-user").click(function (e) { + setTimeout(() => { + $("#choose-all-user").closest(".oh-sticky-table__th--custom")[0].click() + }, 2); + }); + $(".all-choose-user-row").click(function (e) { + e.preventDefault(); + setTimeout(() => { + $(this).closest(".oh-sticky-table__tr--custom")[0].click() + }, 1); + + }); diff --git a/leave/filters.py b/leave/filters.py index 3799f84b1..e33320612 100644 --- a/leave/filters.py +++ b/leave/filters.py @@ -22,46 +22,10 @@ from .models import ( CompanyLeave, LeaveAllocationRequest, ) +from base.filters import FilterSet -class FilterSet(FilterSet): - """ - Custom FilterSet class for styling filter widgets. - """ - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - for field_name, field in self.filters.items(): - filter_widget = self.filters[field_name] - widget = filter_widget.field.widget - if isinstance( - widget, (forms.NumberInput, forms.EmailInput, forms.TextInput) - ): - filter_widget.field.widget.attrs.update({"class": "oh-input w-100"}) - elif isinstance(widget, (forms.Select,)): - filter_widget.field.widget.attrs.update( - { - "class": "oh-select oh-select-2 select2-hidden-accessible", - } - ) - elif isinstance(widget, (forms.Textarea)): - filter_widget.field.widget.attrs.update({"class": "oh-input w-100"}) - elif isinstance( - widget, - ( - forms.CheckboxInput, - forms.CheckboxSelectMultiple, - ), - ): - filter_widget.field.widget.attrs.update( - {"class": "oh-switch__checkbox"} - ) - elif isinstance(widget, (forms.ModelChoiceField)): - filter_widget.field.widget.attrs.update( - { - "class": "oh-select oh-select-2 select2-hidden-accessible", - } - ) class LeaveTypeFilter(FilterSet): diff --git a/leave/forms.py b/leave/forms.py index 3900a3ca7..c2807f4b1 100644 --- a/leave/forms.py +++ b/leave/forms.py @@ -8,6 +8,7 @@ from django.utils.translation import gettext_lazy as _ from django.template.loader import render_to_string from employee.filters import EmployeeFilter from employee.models import Employee +from base.methods import reload_queryset from horilla_widgets.forms import HorillaForm from horilla_widgets.widgets.horilla_multi_select_field import HorillaMultiSelectField from horilla_widgets.widgets.select_widgets import HorillaMultiSelectWidget @@ -33,6 +34,7 @@ CHOICES = [("yes", _("Yes")), ("no", _("No"))] class ModelForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + reload_queryset(self.fields) for field_name, field in self.fields.items(): widget = field.widget @@ -70,6 +72,7 @@ class ModelForm(forms.ModelForm): class ConditionForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + reload_queryset(self.fields) for field_name, field in self.fields.items(): widget = field.widget if isinstance(widget, (forms.Select,)): @@ -723,16 +726,6 @@ class AssignLeaveForm(HorillaForm): ), label="Employee", ) - # leave_type_id = HorillaMultiSelectField( - # queryset=LeaveType.objects.all(), - # widget=HorillaMultiSelectWidget( - # filter_route_name="leave-type-widget-filter", - # filter_class=LeaveTypeFilter, - # filter_instance_contex_name="form", - # filter_template_path="leave/leave_type/leave_type_filter.html", - # ), - # label="Leave Type", - # ) def clean(self): cleaned_data = super().clean() @@ -755,7 +748,7 @@ class AssignLeaveForm(HorillaForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - + reload_queryset(self.fields) self.fields["employee_id"].widget.attrs.update( {"required": True, "id": uuid.uuid4()} ), diff --git a/leave/models.py b/leave/models.py index f206883a0..32cf078cf 100644 --- a/leave/models.py +++ b/leave/models.py @@ -7,6 +7,7 @@ from django.core.exceptions import ValidationError from dateutil.relativedelta import relativedelta from django.utils.translation import gettext_lazy as _ from base.models import Company +from base.horilla_company_manager import HorillaCompanyManager from employee.models import Employee from .methods import calculate_requested_days from django.core.files.storage import default_storage @@ -165,8 +166,9 @@ class LeaveType(models.Model): exclude_company_leave = models.CharField(max_length=30, choices=CHOICES) exclude_holiday = models.CharField(max_length=30, choices=CHOICES) company_id = models.ForeignKey(Company,null=True, editable=False, on_delete=models.PROTECT) - objects = models.Manager() - + objects = HorillaCompanyManager( + related_company_field="company_id" + ) class Meta: ordering = ['-id'] @@ -192,8 +194,9 @@ class Holiday(models.Model): end_date = models.DateField(null=True, blank=True, verbose_name=_("End Date")) recurring = models.BooleanField(default=False, verbose_name=_("Recurring")) company_id = models.ForeignKey(Company,null=True, editable=False, on_delete=models.PROTECT) - objects = models.Manager() - + objects = HorillaCompanyManager( + related_company_field="company_id" + ) def __str__(self): return self.name @@ -205,7 +208,9 @@ class CompanyLeave(models.Model): based_on_week_day = models.CharField(max_length=100, choices=WEEK_DAYS) company_id = models.ForeignKey(Company,null=True, editable=False, on_delete=models.PROTECT) objects = models.Manager() - + objects = HorillaCompanyManager( + related_company_field="company_id" + ) class Meta: unique_together = ("based_on_week", "based_on_week_day") @@ -242,8 +247,9 @@ class AvailableLeave(models.Model): expired_date = models.DateField( blank=True, null=True, verbose_name=_("CarryForward Expired Date") ) - objects = models.Manager() - + objects = HorillaCompanyManager( + related_company_field="employee_id__employee_work_info__company_id" + ) class Meta: unique_together = ("leave_type_id", "employee_id") @@ -410,8 +416,9 @@ class LeaveRequest(models.Model): approved_carryforward_days = models.FloatField(default=0) created_at = models.DateTimeField(auto_now="True") reject_reason = models.TextField(blank=True, verbose_name=_("Reject Reason")) - objects = models.Manager() - + objects = HorillaCompanyManager( + related_company_field="employee_id__employee_work_info__company_id" + ) def __str__(self): return f"{self.employee_id} | {self.leave_type_id} | {self.status}" @@ -578,8 +585,9 @@ class LeaveAllocationRequest(models.Model): ) created_at = models.DateTimeField(auto_now="True") reject_reason = models.TextField(blank=True) - objects = models.Manager() - + objects = HorillaCompanyManager( + related_company_field="employee_id__employee_work_info__company_id" + ) def __str__(self): return f"{self.employee_id}| {self.leave_type_id}| {self.id}" diff --git a/leave/templates/leave/leave_allocation_request/leave_allocation_request_group_by.html b/leave/templates/leave/leave_allocation_request/leave_allocation_request_group_by.html index dede9b323..eecc97427 100644 --- a/leave/templates/leave/leave_allocation_request/leave_allocation_request_group_by.html +++ b/leave/templates/leave/leave_allocation_request/leave_allocation_request_group_by.html @@ -2,6 +2,7 @@ {% load basefilters %} {% load static %} {% load i18n %} {% include 'filter_tags.html' %} +
{% if my_leave_allocation_requests %} @@ -361,6 +362,7 @@
{% endif %}
+