1
asset/__init__.py
Normal file
1
asset/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from asset import scheduler
|
||||
34
asset/admin.py
Normal file
34
asset/admin.py
Normal file
@@ -0,0 +1,34 @@
|
||||
"""
|
||||
Module: admin.py
|
||||
Description: This module is responsible for registering models
|
||||
to be managed through the Django admin interface.
|
||||
Models Registered:
|
||||
- Asset: Represents a physical asset with relevant details.
|
||||
- AssetCategory: Categorizes assets for better organization.
|
||||
- AssetRequest: Manages requests for acquiring assets.
|
||||
- AssetAssignment: Tracks the assets assigned to employees.
|
||||
- AssetLot: Represents a collection of assets under a lot number.
|
||||
"""
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import (
|
||||
Asset,
|
||||
AssetAssignment,
|
||||
AssetCategory,
|
||||
AssetDocuments,
|
||||
AssetLot,
|
||||
AssetReport,
|
||||
AssetRequest,
|
||||
)
|
||||
|
||||
# Register your models here.
|
||||
|
||||
|
||||
admin.site.register(Asset)
|
||||
admin.site.register(AssetRequest)
|
||||
admin.site.register(AssetCategory)
|
||||
admin.site.register(AssetAssignment)
|
||||
admin.site.register(AssetLot)
|
||||
admin.site.register(AssetReport)
|
||||
admin.site.register(AssetDocuments)
|
||||
33
asset/apps.py
Normal file
33
asset/apps.py
Normal file
@@ -0,0 +1,33 @@
|
||||
"""
|
||||
Module: apps.py
|
||||
Description: Configuration for the 'asset' app.
|
||||
"""
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AssetConfig(AppConfig):
|
||||
"""
|
||||
Class: AssetConfig
|
||||
Description: Configuration class for the 'asset' app.
|
||||
|
||||
Attributes:
|
||||
default_auto_field (str): Default auto-generated field type for primary keys.
|
||||
name (str): Name of the app.
|
||||
"""
|
||||
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "asset"
|
||||
|
||||
def ready(self):
|
||||
from django.urls import include, path
|
||||
|
||||
from horilla.horilla_settings import APP_URLS, APPS
|
||||
from horilla.urls import urlpatterns
|
||||
|
||||
APPS.append("asset")
|
||||
urlpatterns.append(
|
||||
path("asset/", include("asset.urls")),
|
||||
)
|
||||
APP_URLS.append("asset.urls")
|
||||
super().ready()
|
||||
375
asset/filters.py
Normal file
375
asset/filters.py
Normal file
@@ -0,0 +1,375 @@
|
||||
"""
|
||||
Module containing custom filter classes for various models.
|
||||
"""
|
||||
|
||||
import uuid
|
||||
|
||||
import django_filters
|
||||
from django import forms
|
||||
from django.db.models import Q
|
||||
from django_filters import FilterSet
|
||||
|
||||
from base.methods import reload_queryset
|
||||
|
||||
from .models import Asset, AssetAssignment, AssetCategory, AssetRequest
|
||||
|
||||
|
||||
class CustomFilterSet(FilterSet):
|
||||
"""
|
||||
Custom FilterSet class that applies specific CSS classes to filter
|
||||
widgets.
|
||||
|
||||
The class applies CSS classes to different types of filter widgets,
|
||||
such as NumberInput, EmailInput, TextInput, Select, Textarea,
|
||||
CheckboxInput, CheckboxSelectMultiple, and ModelChoiceField. The
|
||||
CSS classes are applied to enhance the styling and behavior of the
|
||||
filter widgets.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
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)
|
||||
):
|
||||
field.widget.attrs.update({"class": "oh-input w-100"})
|
||||
elif isinstance(widget, (forms.Select,)):
|
||||
field.widget.attrs.update(
|
||||
{
|
||||
"class": "oh-select oh-select-2",
|
||||
}
|
||||
)
|
||||
elif isinstance(widget, (forms.Textarea)):
|
||||
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)):
|
||||
field.widget.attrs.update(
|
||||
{
|
||||
"class": "oh-select oh-select-2 ",
|
||||
}
|
||||
)
|
||||
elif isinstance(widget, (forms.DateField)):
|
||||
field.widget.attrs.update({"type": "date", "class": "oh-input w-100"})
|
||||
if isinstance(field, django_filters.CharFilter):
|
||||
field.lookup_expr = "icontains"
|
||||
|
||||
|
||||
class AssetExportFilter(CustomFilterSet):
|
||||
"""
|
||||
Custom filter class for exporting filtered Asset data.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
A nested class that specifies the configuration for the filter.
|
||||
model(class): The Asset model is used to filter.
|
||||
fields (str): A special value "__all__" to include all fields
|
||||
of the model in the filter.
|
||||
"""
|
||||
|
||||
model = Asset
|
||||
fields = "__all__"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.form.fields["asset_purchase_date"].widget.attrs.update({"type": "date"})
|
||||
|
||||
|
||||
class AssetFilter(CustomFilterSet):
|
||||
"""
|
||||
Custom filter set for Asset instances.
|
||||
"""
|
||||
|
||||
search = django_filters.CharFilter(method="search_method")
|
||||
category = django_filters.CharFilter(field_name="asset_category_id")
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
A nested class that specifies the configuration for the filter.
|
||||
model(class): The Asset model is used to filter.
|
||||
fields (str): A special value "__all__" to include all fields
|
||||
of the model in the filter.
|
||||
"""
|
||||
|
||||
model = Asset
|
||||
fields = "__all__"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
for visible in self.form.visible_fields():
|
||||
visible.field.widget.attrs["id"] = str(uuid.uuid4())
|
||||
|
||||
def search_method(self, queryset, _, value):
|
||||
"""
|
||||
Search method
|
||||
"""
|
||||
return (
|
||||
queryset.filter(asset_name__icontains=value)
|
||||
| queryset.filter(asset_category_id__asset_category_name__icontains=value)
|
||||
).distinct()
|
||||
|
||||
|
||||
class CustomAssetFilter(CustomFilterSet):
|
||||
"""
|
||||
Custom filter set for asset assigned to employees instances.
|
||||
"""
|
||||
|
||||
asset_id__asset_name = django_filters.CharFilter(lookup_expr="icontains")
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
Specifies the model and fields to be used for filtering AssetAssignment instances.
|
||||
|
||||
Attributes:
|
||||
model (class): The model class AssetAssignment to be filtered.
|
||||
fields (list): The fields to include in the filter, referring to
|
||||
related AssetAssignment fields.
|
||||
"""
|
||||
|
||||
model = AssetAssignment
|
||||
fields = [
|
||||
"asset_id__asset_name",
|
||||
"asset_id__asset_status",
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
for visible in self.form.visible_fields():
|
||||
visible.field.widget.attrs["id"] = str(uuid.uuid4())
|
||||
|
||||
|
||||
class AssetRequestFilter(CustomFilterSet):
|
||||
"""
|
||||
Custom filter set for AssetRequest instances.
|
||||
"""
|
||||
|
||||
search = django_filters.CharFilter(method="search_method")
|
||||
|
||||
def search_method(self, queryset, _, value: str):
|
||||
"""
|
||||
This method is used to search employees
|
||||
"""
|
||||
values = value.split(" ")
|
||||
empty = queryset.model.objects.none()
|
||||
for split in values:
|
||||
empty = empty | (
|
||||
queryset.filter(
|
||||
requested_employee_id__employee_first_name__icontains=split
|
||||
)
|
||||
| queryset.filter(
|
||||
requested_employee_id__employee_last_name__icontains=split
|
||||
)
|
||||
)
|
||||
return empty.distinct()
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
Specifies the model and fields to be used for filtering AssetRequest instances.
|
||||
|
||||
Attributes:
|
||||
model (class): The model class AssetRequest to be filtered.
|
||||
fields (str): A special value "__all__" to include all fields of the model in the filter.
|
||||
"""
|
||||
|
||||
model = AssetRequest
|
||||
fields = "__all__"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
for visible in self.form.visible_fields():
|
||||
visible.field.widget.attrs["id"] = str(uuid.uuid4())
|
||||
|
||||
|
||||
class AssetAllocationFilter(CustomFilterSet):
|
||||
"""
|
||||
Custom filter set for AssetAllocation instances.
|
||||
"""
|
||||
|
||||
search = django_filters.CharFilter(method="search_method")
|
||||
|
||||
def search_method(self, queryset, _, value: str):
|
||||
"""
|
||||
This method is used to search employees
|
||||
"""
|
||||
values = value.split(" ")
|
||||
empty = queryset.model.objects.none()
|
||||
for split in values:
|
||||
empty = empty | (
|
||||
queryset.filter(
|
||||
assigned_to_employee_id__employee_first_name__icontains=split
|
||||
)
|
||||
| queryset.filter(
|
||||
assigned_to_employee_id__employee_last_name__icontains=split
|
||||
)
|
||||
)
|
||||
return empty.distinct()
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
Specifies the model and fields to be used for filtering AssetAllocation instances.
|
||||
|
||||
Attributes:
|
||||
model (class): The model class AssetAssignment to be filtered.
|
||||
fields (str): A special value "__all__" to include all fields
|
||||
of the model in the filter.
|
||||
"""
|
||||
|
||||
model = AssetAssignment
|
||||
fields = "__all__"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
for visible in self.form.visible_fields():
|
||||
visible.field.widget.attrs["id"] = str(uuid.uuid4())
|
||||
|
||||
|
||||
class AssetCategoryFilter(CustomFilterSet):
|
||||
"""
|
||||
Custom filter set for AssetCategory instances.
|
||||
"""
|
||||
|
||||
search = django_filters.CharFilter(method="search_method")
|
||||
|
||||
class Meta:
|
||||
model = AssetCategory
|
||||
fields = "__all__"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
for visible in self.form.visible_fields():
|
||||
visible.field.widget.attrs["id"] = str(uuid.uuid4())
|
||||
|
||||
def search_method(self, queryset, name, value):
|
||||
"""
|
||||
Search method to filter by asset category name or related asset name.
|
||||
"""
|
||||
if not value:
|
||||
return queryset # Return unfiltered queryset if no search term is provided
|
||||
|
||||
return queryset.filter(
|
||||
Q(asset_category_name__icontains=value)
|
||||
| Q(asset__asset_name__icontains=value)
|
||||
).distinct()
|
||||
|
||||
def filter_queryset(self, queryset):
|
||||
"""
|
||||
Filters queryset and applies AssetFilter if necessary.
|
||||
"""
|
||||
# Get the base filtered queryset
|
||||
queryset = super().filter_queryset(queryset)
|
||||
|
||||
# Filter by assets if asset data is present in the GET request
|
||||
if self.data and "asset__pk" in self.data:
|
||||
assets = AssetFilter(data=self.data).qs
|
||||
queryset = queryset.filter(
|
||||
asset__pk__in=assets.values_list("pk", flat=True)
|
||||
)
|
||||
|
||||
return queryset.distinct()
|
||||
|
||||
|
||||
class AssetRequestReGroup:
|
||||
"""
|
||||
Class to keep the field name for group by option
|
||||
"""
|
||||
|
||||
fields = [
|
||||
("", "Select"),
|
||||
("requested_employee_id", "Employee"),
|
||||
("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
|
||||
"""
|
||||
|
||||
fields = [
|
||||
("", "Select"),
|
||||
("assigned_to_employee_id", "Employee"),
|
||||
("assigned_date", "Assigned Date"),
|
||||
("return_date", "Return Date"),
|
||||
]
|
||||
|
||||
|
||||
class AssetHistoryFilter(CustomFilterSet):
|
||||
"""
|
||||
Custom filter set for AssetAssignment instances for filtering in asset history view.
|
||||
"""
|
||||
|
||||
search = django_filters.CharFilter(
|
||||
field_name="asset_id__asset_name", lookup_expr="icontains"
|
||||
)
|
||||
returned_assets = django_filters.CharFilter(
|
||||
field_name="return_status", method="exclude_none"
|
||||
)
|
||||
return_date_gte = django_filters.DateFilter(
|
||||
field_name="return_date",
|
||||
lookup_expr="gte",
|
||||
widget=forms.DateInput(attrs={"type": "date"}),
|
||||
)
|
||||
return_date_lte = django_filters.DateFilter(
|
||||
field_name="return_date",
|
||||
lookup_expr="lte",
|
||||
widget=forms.DateInput(attrs={"type": "date"}),
|
||||
)
|
||||
assigned_date_gte = django_filters.DateFilter(
|
||||
field_name="assigned_date",
|
||||
lookup_expr="gte",
|
||||
widget=forms.DateInput(attrs={"type": "date"}),
|
||||
)
|
||||
assigned_date_lte = django_filters.DateFilter(
|
||||
field_name="assigned_date",
|
||||
lookup_expr="lte",
|
||||
widget=forms.DateInput(attrs={"type": "date"}),
|
||||
)
|
||||
|
||||
def exclude_none(self, queryset, name, value):
|
||||
"""
|
||||
Exclude objects with a null return_status from the queryset if value is "True"
|
||||
"""
|
||||
if value == "True":
|
||||
queryset = queryset.filter(return_status__isnull=False)
|
||||
return queryset
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
Specifies the model and fields to be used for filtering AssetAllocation instances.
|
||||
|
||||
Attributes:
|
||||
model (class): The model class AssetAssignment to be filtered.
|
||||
fields (str): A special value "__all__" to include all fields
|
||||
of the model in the filter.
|
||||
"""
|
||||
|
||||
model = AssetAssignment
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class AssetHistoryReGroup:
|
||||
"""
|
||||
Class to keep the field name for group by option
|
||||
"""
|
||||
|
||||
fields = [
|
||||
("", "Select"),
|
||||
("asset_id", "Asset"),
|
||||
("assigned_to_employee_id", "Employee"),
|
||||
("assigned_date", "Assigned Date"),
|
||||
("return_date", "Return Date"),
|
||||
]
|
||||
384
asset/forms.py
Normal file
384
asset/forms.py
Normal file
@@ -0,0 +1,384 @@
|
||||
"""
|
||||
forms.py
|
||||
Asset Management Forms
|
||||
|
||||
This module contains Django ModelForms for handling various aspects of asset management,
|
||||
including asset creation, allocation, return, category assignment, and batch handling.
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from datetime import date
|
||||
|
||||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from asset.models import (
|
||||
Asset,
|
||||
AssetAssignment,
|
||||
AssetCategory,
|
||||
AssetDocuments,
|
||||
AssetLot,
|
||||
AssetReport,
|
||||
AssetRequest,
|
||||
)
|
||||
from base.forms import ModelForm
|
||||
from base.methods import reload_queryset
|
||||
from employee.forms import MultipleFileField
|
||||
from employee.models import Employee
|
||||
from horilla.horilla_middlewares import _thread_locals
|
||||
|
||||
|
||||
def set_date_field_initial(instance):
|
||||
"""this is used to update change the date value format"""
|
||||
initial = {}
|
||||
if instance.asset_purchase_date is not None:
|
||||
initial["asset_purchase_date"] = instance.asset_purchase_date.strftime(
|
||||
"%Y-%m-%d"
|
||||
)
|
||||
|
||||
return initial
|
||||
|
||||
|
||||
class AssetForm(ModelForm):
|
||||
"""
|
||||
A ModelForm for creating and updating asset information.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = Asset
|
||||
fields = "__all__"
|
||||
exclude = ["is_active"]
|
||||
widgets = {
|
||||
"asset_lot_number_id": forms.Select(
|
||||
attrs={"onchange": "batchNoChange($(this))"}
|
||||
),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
request = getattr(_thread_locals, "request", None)
|
||||
instance = kwargs.get("instance")
|
||||
|
||||
if instance:
|
||||
kwargs.setdefault("initial", set_date_field_initial(instance))
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
uuid_map = {
|
||||
field: str(uuid.uuid4())
|
||||
for field in ["asset_category_id", "asset_lot_number_id", "asset_status"]
|
||||
}
|
||||
for field, uuid_value in uuid_map.items():
|
||||
self.fields[field].widget.attrs["id"] = uuid_value
|
||||
|
||||
if request and request.user.has_perm("asset.add_assetlot"):
|
||||
batch_no_choices = list(
|
||||
self.fields["asset_lot_number_id"].queryset.values_list(
|
||||
"id", "lot_number"
|
||||
)
|
||||
)
|
||||
batch_no_choices.insert(0, ("", _("---Choose Batch No.---")))
|
||||
|
||||
if not self.instance.pk:
|
||||
batch_no_choices.append(("create", _("Create new batch number")))
|
||||
|
||||
self.fields["asset_lot_number_id"].choices = batch_no_choices
|
||||
|
||||
def clean(self):
|
||||
instance = self.instance
|
||||
prev_instance = Asset.objects.filter(id=instance.pk).first()
|
||||
if instance.pk:
|
||||
if (
|
||||
self.cleaned_data.get("asset_status", None)
|
||||
and self.cleaned_data.get("asset_status", None)
|
||||
!= prev_instance.asset_status
|
||||
):
|
||||
if instance.assetassignment_set.filter(
|
||||
return_status__isnull=True
|
||||
).exists():
|
||||
raise ValidationError(
|
||||
{"asset_status": '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()
|
||||
):
|
||||
raise ValidationError(
|
||||
{"asset_tracking_id": "Already asset with this tracking id exists."}
|
||||
)
|
||||
|
||||
|
||||
class DocumentForm(forms.ModelForm):
|
||||
"""
|
||||
Form for uploading documents related to an asset.
|
||||
|
||||
Attributes:
|
||||
- file: A FileField with a TextInput widget for file upload, allowing multiple files.
|
||||
"""
|
||||
|
||||
file = forms.FileField(
|
||||
widget=forms.TextInput(
|
||||
attrs={
|
||||
"name": "file",
|
||||
"type": "File",
|
||||
"class": "form-control",
|
||||
"multiple": "True",
|
||||
"accept": ".jpeg, .jpg, .png, .pdf",
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
Metadata options for the DocumentForm.
|
||||
|
||||
Attributes:
|
||||
- model: The model associated with this form (AssetDocuments).
|
||||
- fields: Fields to include in the form ('file').
|
||||
- exclude: Fields to exclude from the form ('is_active').
|
||||
"""
|
||||
|
||||
model = AssetDocuments
|
||||
fields = [
|
||||
"file",
|
||||
]
|
||||
exclude = ["is_active"]
|
||||
|
||||
|
||||
class AssetReportForm(ModelForm):
|
||||
"""
|
||||
Form for creating and updating asset reports.
|
||||
|
||||
Metadata:
|
||||
- model: The model associated with this form (AssetReport).
|
||||
- fields: Fields to include in the form ('title', 'asset_id').
|
||||
- exclude: Fields to exclude from the form ('is_active').
|
||||
|
||||
Methods:
|
||||
- __init__: Initializes the form, disabling the 'asset_id' field.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
Metadata options for the AssetReportForm.
|
||||
|
||||
Attributes:
|
||||
- model: The model associated with this form (AssetReport).
|
||||
- fields: Fields to include in the form ('title', 'asset_id').
|
||||
- exclude: Fields to exclude from the form ('is_active').
|
||||
"""
|
||||
|
||||
model = AssetReport
|
||||
fields = [
|
||||
"title",
|
||||
"asset_id",
|
||||
]
|
||||
exclude = ["is_active"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""
|
||||
Initialize the AssetReportForm, disabling the 'asset_id' field.
|
||||
|
||||
Args:
|
||||
- *args: Variable length argument list.
|
||||
- **kwargs: Arbitrary keyword arguments.
|
||||
"""
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["asset_id"].widget.attrs["disabled"] = "disabled"
|
||||
|
||||
|
||||
class AssetCategoryForm(ModelForm):
|
||||
"""
|
||||
A form for creating and updating AssetCategory instances.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
Specifies the model and fields to be used for the AssetForm.
|
||||
Attributes:
|
||||
model (class): The model class AssetCategory to be used for the form.
|
||||
fields (str): A special value "__all__" to include all fields
|
||||
of the model in the form.
|
||||
"""
|
||||
|
||||
model = AssetCategory
|
||||
fields = "__all__"
|
||||
exclude = ["is_active"]
|
||||
|
||||
|
||||
class AssetRequestForm(ModelForm):
|
||||
"""
|
||||
A Django ModelForm for creating and updating AssetRequest instances.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
Specifies the model and fields to be used for the AssetRequestForm.
|
||||
Attributes:
|
||||
model (class): The model class AssetRequest to be used for the form.
|
||||
fields (str): A special value "__all__" to include all fields
|
||||
of the model in the form.
|
||||
widgets (dict): A dictionary containing widget configurations for
|
||||
specific form fields.
|
||||
"""
|
||||
|
||||
model = AssetRequest
|
||||
fields = "__all__"
|
||||
exclude = ["is_active"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
user = kwargs.pop("user", None)
|
||||
super().__init__(*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(
|
||||
id=user.employee_get.id
|
||||
).first()
|
||||
else:
|
||||
self.fields["requested_employee_id"].queryset = Employee.objects.filter(
|
||||
employee_user_id=user
|
||||
)
|
||||
self.fields["requested_employee_id"].initial = user.employee_get
|
||||
|
||||
self.fields["asset_category_id"].widget.attrs.update({"id": str(uuid.uuid4())})
|
||||
|
||||
|
||||
class AssetAllocationForm(ModelForm):
|
||||
"""
|
||||
A Django ModelForm for creating and updating AssetAssignment instances.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
reload_queryset(self.fields)
|
||||
self.fields["asset_id"].queryset = Asset.objects.filter(
|
||||
asset_status="Available"
|
||||
)
|
||||
|
||||
self.fields["assign_images"] = MultipleFileField(
|
||||
label=_("Assign Condition Images")
|
||||
)
|
||||
self.fields["assign_images"].required = True
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
Specifies the model and fields to be used for the AssetAllocationForm.
|
||||
Attributes:
|
||||
model (class): The model class AssetAssignment to be used for the form.
|
||||
fields (str): A special value "__all__" to include all fields
|
||||
of the model in the form.
|
||||
widgets (dict): A dictionary containing widget configurations for
|
||||
specific form fields.
|
||||
"""
|
||||
|
||||
model = AssetAssignment
|
||||
fields = "__all__"
|
||||
exclude = [
|
||||
"return_date",
|
||||
"return_condition",
|
||||
"assigned_date",
|
||||
"return_images",
|
||||
"is_active",
|
||||
]
|
||||
widgets = {
|
||||
"asset_id": forms.Select(attrs={"class": "oh-select oh-select-2 "}),
|
||||
"assigned_to_employee_id": forms.Select(
|
||||
attrs={"class": "oh-select oh-select-2 "}
|
||||
),
|
||||
"assigned_by_employee_id": forms.Select(
|
||||
attrs={
|
||||
"class": "oh-select oh-select-2 ",
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
# def clean(self):
|
||||
# cleaned_data = super.clean()
|
||||
|
||||
|
||||
class AssetReturnForm(ModelForm):
|
||||
"""
|
||||
A Django ModelForm for updating AssetAssignment instances during asset return.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
Specifies the model and fields to be used for the AssetReturnForm.
|
||||
Attributes:
|
||||
model (class): The model class AssetAssignment to be used for the form.
|
||||
fields (list): The fields to include in the form, referring to
|
||||
related AssetAssignment fields.
|
||||
widgets (dict): A dictionary containing widget configurations for
|
||||
specific form fields.
|
||||
"""
|
||||
|
||||
model = AssetAssignment
|
||||
fields = ["return_date", "return_condition", "return_status", "return_images"]
|
||||
widgets = {
|
||||
"return_condition": forms.Textarea(
|
||||
attrs={
|
||||
"class": "oh-input oh-input--textarea oh-input--block",
|
||||
"rows": 3,
|
||||
"cols": 40,
|
||||
"placeholder": _(
|
||||
"on returns the laptop. However, it has suffered minor damage."
|
||||
),
|
||||
}
|
||||
),
|
||||
"return_status": forms.Select(
|
||||
attrs={"class": "oh-select oh-select-2", "required": "true"},
|
||||
),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""
|
||||
Initializes the AssetReturnForm with initial values and custom field settings.
|
||||
"""
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["return_date"].widget.attrs.update({"required": "true"})
|
||||
self.fields["return_images"] = MultipleFileField(
|
||||
label=_("Return Condition Images")
|
||||
)
|
||||
self.fields["return_images"].required = True
|
||||
|
||||
def clean_return_date(self):
|
||||
"""
|
||||
Validates the 'return_date' field.
|
||||
|
||||
Ensures that the return date is not in the future. If the return date is in the future,
|
||||
a ValidationError is raised.
|
||||
|
||||
Returns:
|
||||
- The cleaned return date.
|
||||
|
||||
Raises:
|
||||
- forms.ValidationError: If the return date is in the future.
|
||||
"""
|
||||
return_date = self.cleaned_data.get("return_date")
|
||||
|
||||
if return_date and return_date > date.today():
|
||||
raise forms.ValidationError(_("Return date cannot be in the future."))
|
||||
|
||||
return return_date
|
||||
|
||||
|
||||
class AssetBatchForm(ModelForm):
|
||||
"""
|
||||
A Django ModelForm for creating or updating AssetLot instances.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
Specifies the model and fields to be used for the AssetBatchForm.
|
||||
Attributes:
|
||||
model (class): The model class AssetLot to be used for the form.
|
||||
fields (str): A special value "__all__" to include all fields
|
||||
of the model in the form.
|
||||
widgets (dict): A dictionary containing widget configurations for
|
||||
specific form fields.
|
||||
"""
|
||||
|
||||
model = AssetLot
|
||||
fields = "__all__"
|
||||
331
asset/models.py
Normal file
331
asset/models.py
Normal file
@@ -0,0 +1,331 @@
|
||||
"""
|
||||
Models for Asset Management System
|
||||
|
||||
This module defines Django models to manage assets, their categories, assigning, and requests
|
||||
within an Asset Management System.
|
||||
"""
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from base.horilla_company_manager import HorillaCompanyManager
|
||||
from base.models import Company
|
||||
from employee.models import Employee
|
||||
from horilla.models import HorillaModel, upload_path
|
||||
|
||||
|
||||
class AssetCategory(HorillaModel):
|
||||
"""
|
||||
Represents a category for different types of assets.
|
||||
"""
|
||||
|
||||
asset_category_name = models.CharField(
|
||||
max_length=255, unique=True, verbose_name=_("Name")
|
||||
)
|
||||
asset_category_description = models.TextField(
|
||||
max_length=255, verbose_name=_("Description")
|
||||
)
|
||||
objects = models.Manager()
|
||||
company_id = models.ManyToManyField(Company, blank=True, verbose_name=_("Company"))
|
||||
objects = HorillaCompanyManager("company_id")
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
Meta class to add additional options
|
||||
"""
|
||||
|
||||
verbose_name = _("Asset Category")
|
||||
verbose_name_plural = _("Asset Categories")
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.asset_category_name}"
|
||||
|
||||
|
||||
class AssetLot(HorillaModel):
|
||||
"""
|
||||
Represents a lot associated with a collection of assets.
|
||||
"""
|
||||
|
||||
lot_number = models.CharField(
|
||||
max_length=30,
|
||||
null=False,
|
||||
blank=False,
|
||||
unique=True,
|
||||
verbose_name=_("Batch Number"),
|
||||
)
|
||||
lot_description = models.TextField(
|
||||
null=True, blank=True, max_length=255, verbose_name=_("Description")
|
||||
)
|
||||
company_id = models.ManyToManyField(Company, blank=True, verbose_name=_("Company"))
|
||||
objects = HorillaCompanyManager()
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
Meta class to add additional options
|
||||
"""
|
||||
|
||||
ordering = ["-created_at"]
|
||||
verbose_name = _("Asset Batch")
|
||||
verbose_name_plural = _("Asset Batches")
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.lot_number}"
|
||||
|
||||
|
||||
class Asset(HorillaModel):
|
||||
"""
|
||||
Represents a asset with various attributes.
|
||||
"""
|
||||
|
||||
ASSET_STATUS = [
|
||||
("In use", _("In Use")),
|
||||
("Available", _("Available")),
|
||||
("Not-Available", _("Not-Available")),
|
||||
]
|
||||
asset_name = models.CharField(max_length=255, verbose_name=_("Asset Name"))
|
||||
owner = models.ForeignKey(
|
||||
Employee,
|
||||
on_delete=models.PROTECT,
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name=_("Current User"),
|
||||
)
|
||||
asset_description = models.TextField(
|
||||
null=True, blank=True, max_length=255, verbose_name=_("Description")
|
||||
)
|
||||
asset_tracking_id = models.CharField(
|
||||
max_length=30, null=False, unique=True, verbose_name=_("Tracking Id")
|
||||
)
|
||||
asset_purchase_date = models.DateField(verbose_name=_("Purchase Date"))
|
||||
asset_purchase_cost = models.DecimalField(
|
||||
max_digits=10, decimal_places=2, verbose_name=_("Cost")
|
||||
)
|
||||
asset_category_id = models.ForeignKey(
|
||||
AssetCategory, on_delete=models.PROTECT, verbose_name=_("Category")
|
||||
)
|
||||
asset_status = models.CharField(
|
||||
choices=ASSET_STATUS,
|
||||
default="Available",
|
||||
max_length=40,
|
||||
verbose_name=_("Status"),
|
||||
)
|
||||
asset_lot_number_id = models.ForeignKey(
|
||||
AssetLot,
|
||||
on_delete=models.PROTECT,
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name=_("Batch No"),
|
||||
)
|
||||
expiry_date = models.DateField(null=True, blank=True, verbose_name=_("Expiry Date"))
|
||||
notify_before = models.IntegerField(
|
||||
default=1, null=True, verbose_name=_("Notify Before (days)")
|
||||
)
|
||||
objects = HorillaCompanyManager("asset_category_id__company_id")
|
||||
|
||||
class Meta:
|
||||
ordering = ["-created_at"]
|
||||
verbose_name = _("Asset")
|
||||
verbose_name_plural = _("Assets")
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.asset_name}-{self.asset_tracking_id}"
|
||||
|
||||
def clean(self):
|
||||
existing_asset = Asset.objects.filter(
|
||||
asset_tracking_id=self.asset_tracking_id
|
||||
).exclude(
|
||||
id=self.pk
|
||||
) # Exclude the current instance if updating
|
||||
if existing_asset.exists():
|
||||
raise ValidationError(
|
||||
{
|
||||
"asset_description": _(
|
||||
"An asset with this tracking ID already exists."
|
||||
)
|
||||
}
|
||||
)
|
||||
return super().clean()
|
||||
|
||||
|
||||
class AssetReport(HorillaModel):
|
||||
"""
|
||||
Model representing a report for an asset.
|
||||
|
||||
Attributes:
|
||||
- title: A CharField for the title of the report (optional).
|
||||
- asset_id: A ForeignKey to the Asset model, linking the report to a specific asset.
|
||||
"""
|
||||
|
||||
title = models.CharField(max_length=255, blank=True, null=True)
|
||||
asset_id = models.ForeignKey(
|
||||
Asset, related_name="asset_report", on_delete=models.CASCADE
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
Returns a string representation of the AssetReport instance.
|
||||
If a title is present, it returns "asset_id - title".
|
||||
Otherwise, it returns "report for asset_id".
|
||||
"""
|
||||
return (
|
||||
f"{self.asset_id} - {self.title}"
|
||||
if self.title
|
||||
else f"report for {self.asset_id}"
|
||||
)
|
||||
|
||||
|
||||
class AssetDocuments(HorillaModel):
|
||||
"""
|
||||
Model representing documents associated with an asset report.
|
||||
|
||||
Attributes:
|
||||
- asset_report: A ForeignKey to the AssetReport model, linking the document to
|
||||
a specific asset report.
|
||||
- file: A FileField for uploading the document file (optional).
|
||||
"""
|
||||
|
||||
asset_report = models.ForeignKey(
|
||||
"AssetReport", related_name="documents", on_delete=models.CASCADE
|
||||
)
|
||||
file = models.FileField(upload_to=upload_path, blank=True, null=True)
|
||||
objects = models.Manager()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Asset Document")
|
||||
verbose_name_plural = _("Asset Documents")
|
||||
|
||||
def __str__(self):
|
||||
return f"document for {self.asset_report}"
|
||||
|
||||
|
||||
class ReturnImages(HorillaModel):
|
||||
"""
|
||||
Model representing images associated with a returned asset.
|
||||
|
||||
Attributes:
|
||||
- image: A FileField for uploading the image file (optional).
|
||||
"""
|
||||
|
||||
image = models.FileField(upload_to=upload_path, blank=True, null=True)
|
||||
|
||||
|
||||
class AssetAssignment(HorillaModel):
|
||||
"""
|
||||
Represents the allocation and return of assets to and from employees.
|
||||
"""
|
||||
|
||||
STATUS = [
|
||||
("Minor damage", _("Minor damage")),
|
||||
("Major damage", _("Major damage")),
|
||||
("Healthy", _("Healthy")),
|
||||
]
|
||||
asset_id = models.ForeignKey(
|
||||
Asset, on_delete=models.PROTECT, verbose_name=_("Asset")
|
||||
)
|
||||
assigned_to_employee_id = models.ForeignKey(
|
||||
Employee,
|
||||
on_delete=models.PROTECT,
|
||||
related_name="allocated_employee",
|
||||
verbose_name=_("Assigned To"),
|
||||
)
|
||||
assigned_date = models.DateField(auto_now_add=True)
|
||||
assigned_by_employee_id = models.ForeignKey(
|
||||
Employee,
|
||||
on_delete=models.PROTECT,
|
||||
related_name="assigned_by",
|
||||
verbose_name=_("Assigned By"),
|
||||
)
|
||||
return_date = models.DateField(null=True, blank=True, verbose_name=_("Return Date"))
|
||||
return_condition = models.TextField(
|
||||
null=True, blank=True, max_length=255, verbose_name=_("Return Condition")
|
||||
)
|
||||
return_status = models.CharField(
|
||||
choices=STATUS,
|
||||
max_length=30,
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name=_("Return Status"),
|
||||
)
|
||||
return_request = models.BooleanField(default=False)
|
||||
objects = HorillaCompanyManager("asset_id__asset_lot_number_id__company_id")
|
||||
return_images = models.ManyToManyField(
|
||||
ReturnImages, blank=True, related_name="return_images"
|
||||
)
|
||||
assign_images = models.ManyToManyField(
|
||||
ReturnImages,
|
||||
blank=True,
|
||||
related_name="assign_images",
|
||||
verbose_name=_("Assign Condition Images"),
|
||||
)
|
||||
objects = HorillaCompanyManager(
|
||||
"assigned_to_employee_id__employee_work_info__company_id"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
"""Meta class for AssetAssignment model"""
|
||||
|
||||
ordering = ["-id"]
|
||||
verbose_name = _("Asset Allocation")
|
||||
verbose_name_plural = _("Asset Allocations")
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.assigned_to_employee_id} --- {self.asset_id} --- {self.return_status}"
|
||||
|
||||
|
||||
class AssetRequest(HorillaModel):
|
||||
"""
|
||||
Represents a request for assets made by employees.
|
||||
"""
|
||||
|
||||
STATUS = [
|
||||
("Requested", _("Requested")),
|
||||
("Approved", _("Approved")),
|
||||
("Rejected", _("Rejected")),
|
||||
]
|
||||
requested_employee_id = models.ForeignKey(
|
||||
Employee,
|
||||
on_delete=models.PROTECT,
|
||||
related_name="requested_employee",
|
||||
null=False,
|
||||
blank=False,
|
||||
verbose_name=_("Requesting User"),
|
||||
)
|
||||
asset_category_id = models.ForeignKey(
|
||||
AssetCategory, on_delete=models.PROTECT, verbose_name=_("Asset Category")
|
||||
)
|
||||
asset_request_date = models.DateField(auto_now_add=True)
|
||||
description = models.TextField(
|
||||
null=True, blank=True, max_length=255, verbose_name=_("Description")
|
||||
)
|
||||
asset_request_status = models.CharField(
|
||||
max_length=30, choices=STATUS, default="Requested", null=True, blank=True
|
||||
)
|
||||
objects = HorillaCompanyManager(
|
||||
"requested_employee_id__employee_work_info__company_id"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
"""Meta class for AssetRequest model"""
|
||||
|
||||
ordering = ["-id"]
|
||||
verbose_name = _("Asset Request")
|
||||
verbose_name_plural = _("Asset Requests")
|
||||
|
||||
def status_html_class(self):
|
||||
COLOR_CLASS = {
|
||||
"Approved": "oh-dot--success",
|
||||
"Requested": "oh-dot--info",
|
||||
"Rejected": "oh-dot--danger",
|
||||
}
|
||||
|
||||
LINK_CLASS = {
|
||||
"Approved": "link-success",
|
||||
"Requested": "link-info",
|
||||
"Rejected": "link-danger",
|
||||
}
|
||||
status = self.asset_request_status
|
||||
return {
|
||||
"color": COLOR_CLASS.get(status),
|
||||
"link": LINK_CLASS.get(status),
|
||||
}
|
||||
21
asset/resources.py
Normal file
21
asset/resources.py
Normal file
@@ -0,0 +1,21 @@
|
||||
"""
|
||||
Module: resources.py
|
||||
This module defines classes for handling resources related to assets.
|
||||
"""
|
||||
|
||||
from import_export import resources
|
||||
|
||||
from .models import Asset
|
||||
|
||||
|
||||
class AssetResource(resources.ModelResource):
|
||||
"""
|
||||
This class is used to import and export Asset data using the import_export library.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
Specifies the model to be used for import and export.
|
||||
"""
|
||||
|
||||
model = Asset
|
||||
102
asset/scheduler.py
Normal file
102
asset/scheduler.py
Normal file
@@ -0,0 +1,102 @@
|
||||
"""
|
||||
scheduler.py
|
||||
|
||||
This module is used to register scheduled tasks
|
||||
"""
|
||||
|
||||
import sys
|
||||
from datetime import date, timedelta
|
||||
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from django.urls import reverse
|
||||
|
||||
from notifications.signals import notify
|
||||
|
||||
|
||||
def notify_expiring_assets():
|
||||
"""
|
||||
Finds all Expiring Assets and send a notification on the notify_before date.
|
||||
"""
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from asset.models import Asset
|
||||
|
||||
today = date.today()
|
||||
assets = Asset.objects.all()
|
||||
|
||||
# Cache bot & superuser once
|
||||
bot = User.objects.filter(username="Horilla Bot").only("id").first()
|
||||
superuser = User.objects.filter(is_superuser=True).only("id").first()
|
||||
|
||||
# Query only assets that are expiring today
|
||||
assets = Asset.objects.filter(
|
||||
expiry_date__isnull=False,
|
||||
expiry_date__gte=today,
|
||||
)
|
||||
|
||||
for asset in assets:
|
||||
if asset.expiry_date:
|
||||
expiry_date = asset.expiry_date
|
||||
notify_date = expiry_date - timedelta(days=asset.notify_before)
|
||||
recipient = getattr(asset.owner, "employee_user_id", None) or superuser
|
||||
if notify_date == today and recipient:
|
||||
notify.send(
|
||||
bot,
|
||||
recipient=recipient,
|
||||
verb=f"The Asset '{asset.asset_name}' expires in {asset.notify_before} days",
|
||||
verb_ar=f"تنتهي صلاحية الأصل '{asset.asset_name}' خلال {asset.notify_before} من الأيام",
|
||||
verb_de=f"Das Asset {asset.asset_name} läuft in {asset.notify_before} Tagen ab.",
|
||||
verb_es=f"El activo {asset.asset_name} caduca en {asset.notify_before} días.",
|
||||
verb_fr=f"L'actif {asset.asset_name} expire dans {asset.notify_before} jours.",
|
||||
redirect=reverse("asset-category-view"),
|
||||
label="System",
|
||||
icon="information",
|
||||
)
|
||||
|
||||
|
||||
def notify_expiring_documents():
|
||||
"""
|
||||
Finds all Expiring Documents and send a notification on the notify_before date.
|
||||
"""
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from horilla_documents.models import Document
|
||||
|
||||
today = date.today()
|
||||
documents = Document.objects.all()
|
||||
bot = User.objects.filter(username="Horilla Bot").first()
|
||||
for document in documents:
|
||||
if document.expiry_date:
|
||||
expiry_date = document.expiry_date
|
||||
notify_date = expiry_date - timedelta(days=document.notify_before)
|
||||
|
||||
if notify_date == today:
|
||||
notify.send(
|
||||
bot,
|
||||
recipient=document.employee_id.employee_user_id,
|
||||
verb=f"The document ' {document.title} ' expires in {document.notify_before}\
|
||||
days",
|
||||
verb_ar=f"تنتهي صلاحية المستند '{document.title}' خلال {document.notify_before}\
|
||||
يوم",
|
||||
verb_de=f"Das Dokument '{document.title}' läuft in {document.notify_before}\
|
||||
Tagen ab.",
|
||||
verb_es=f"El documento '{document.title}' caduca en {document.notify_before}\
|
||||
días",
|
||||
verb_fr=f"Le document '{document.title}' expire dans {document.notify_before}\
|
||||
jours",
|
||||
redirect=reverse("asset-category-view"),
|
||||
label="System",
|
||||
icon="information",
|
||||
)
|
||||
if today >= expiry_date:
|
||||
document.is_active = False
|
||||
|
||||
|
||||
if not any(
|
||||
cmd in sys.argv
|
||||
for cmd in ["makemigrations", "migrate", "compilemessages", "flush", "shell"]
|
||||
):
|
||||
scheduler = BackgroundScheduler()
|
||||
scheduler.add_job(notify_expiring_assets, "interval", days=1)
|
||||
scheduler.add_job(notify_expiring_documents, "interval", hours=4)
|
||||
scheduler.start()
|
||||
59
asset/sidebar.py
Normal file
59
asset/sidebar.py
Normal file
@@ -0,0 +1,59 @@
|
||||
"""
|
||||
assets/sidebar.py
|
||||
"""
|
||||
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
MENU = _("Assets")
|
||||
IMG_SRC = "images/ui/assets.svg"
|
||||
|
||||
SUBMENUS = [
|
||||
{
|
||||
"menu": _("Dashboard"),
|
||||
"redirect": reverse("asset-dashboard"),
|
||||
"accessibility": "asset.sidebar.dashboard_accessibility",
|
||||
},
|
||||
{
|
||||
"menu": _("Asset View"),
|
||||
"redirect": reverse("asset-category-view"),
|
||||
"accessibility": "asset.sidebar.dashboard_accessibility",
|
||||
},
|
||||
{
|
||||
"menu": _("Asset Batches"),
|
||||
"redirect": reverse("asset-batch-view"),
|
||||
"accessibility": "asset.sidebar.lot_accessibility",
|
||||
},
|
||||
{
|
||||
"menu": _("Request and Allocation"),
|
||||
"redirect": reverse("asset-request-allocation-view"),
|
||||
},
|
||||
{
|
||||
"menu": _("Asset History"),
|
||||
"redirect": reverse("asset-history"),
|
||||
"accessibility": "asset.sidebar.history_accessibility",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def dashboard_accessibility(request, submenu, user_perms, *args, **kwargs):
|
||||
"""
|
||||
Determine if the user has the necessary permissions to access the
|
||||
dashboard and asset category view.
|
||||
"""
|
||||
return request.user.has_perm("asset.view_assetcategory")
|
||||
|
||||
|
||||
def history_accessibility(request, submenu, user_perms, *args, **kwargs):
|
||||
"""
|
||||
Determine if the user has the necessary permissions to access the
|
||||
dashboard and asset category view.
|
||||
"""
|
||||
return request.user.has_perm("asset.view_assetassignment")
|
||||
|
||||
|
||||
def lot_accessibility(request, subment, user_perms, *args, **kwargs):
|
||||
"""
|
||||
Asset batch sidebar accessibility method
|
||||
"""
|
||||
return request.user.has_perm("asset.view_assetlot")
|
||||
5
asset/tests.py
Normal file
5
asset/tests.py
Normal file
@@ -0,0 +1,5 @@
|
||||
"""
|
||||
This module contains test cases for the assets application.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
207
asset/urls.py
Normal file
207
asset/urls.py
Normal file
@@ -0,0 +1,207 @@
|
||||
"""
|
||||
URL configuration for asset-related views.
|
||||
"""
|
||||
|
||||
from django import views
|
||||
from django.urls import path
|
||||
|
||||
from asset.forms import AssetCategoryForm, AssetForm
|
||||
from asset.models import Asset, AssetCategory
|
||||
from base.views import object_duplicate
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path(
|
||||
"asset-creation/<int:asset_category_id>/",
|
||||
views.asset_creation,
|
||||
name="asset-creation",
|
||||
),
|
||||
path("asset-list/<int:cat_id>", views.asset_list, name="asset-list"),
|
||||
path("asset-update/<int:asset_id>/", views.asset_update, name="asset-update"),
|
||||
path(
|
||||
"duplicate-asset/<int:obj_id>/",
|
||||
object_duplicate,
|
||||
name="duplicate-asset",
|
||||
kwargs={
|
||||
"model": Asset,
|
||||
"form": AssetForm,
|
||||
"form_name": "asset_creation_form",
|
||||
"template": "asset/asset_creation.html",
|
||||
},
|
||||
),
|
||||
path("asset-delete/<int:asset_id>/", views.asset_delete, name="asset-delete"),
|
||||
path(
|
||||
"asset-information/<int:asset_id>/",
|
||||
views.asset_information,
|
||||
name="asset-information",
|
||||
),
|
||||
path("asset-category-view/", views.asset_category_view, name="asset-category-view"),
|
||||
path(
|
||||
"asset-category-view-search-filter",
|
||||
views.asset_category_view_search_filter,
|
||||
name="asset-category-view-search-filter",
|
||||
),
|
||||
path(
|
||||
"asset-category-duplicate/<int:obj_id>/",
|
||||
object_duplicate,
|
||||
name="asset-category-duplicate",
|
||||
kwargs={
|
||||
"model": AssetCategory,
|
||||
"form": AssetCategoryForm,
|
||||
"form_name": "form",
|
||||
"template": "category/asset_category_form.html",
|
||||
},
|
||||
),
|
||||
path(
|
||||
"asset-category-creation",
|
||||
views.asset_category_creation,
|
||||
name="asset-category-creation",
|
||||
),
|
||||
path(
|
||||
"asset-category-update/<int:cat_id>",
|
||||
views.asset_category_update,
|
||||
name="asset-category-update",
|
||||
),
|
||||
path(
|
||||
"asset-category-delete/<int:cat_id>",
|
||||
views.delete_asset_category,
|
||||
name="asset-category-delete",
|
||||
),
|
||||
path(
|
||||
"asset-request-creation",
|
||||
views.asset_request_creation,
|
||||
name="asset-request-creation",
|
||||
),
|
||||
path(
|
||||
"asset-request-allocation-view/",
|
||||
views.asset_request_allocation_view,
|
||||
name="asset-request-allocation-view",
|
||||
),
|
||||
path(
|
||||
"asset-request-individual-view/<int:asset_request_id>",
|
||||
views.asset_request_individual_view,
|
||||
name="asset-request-individual-view",
|
||||
),
|
||||
path(
|
||||
"own-asset-individual-view/<int:asset_id>",
|
||||
views.own_asset_individual_view,
|
||||
name="own-asset-individual-view",
|
||||
),
|
||||
path(
|
||||
"asset-allocation-individual-view/<int:asset_allocation_id>",
|
||||
views.asset_allocation_individual_view,
|
||||
name="asset-allocation-individual-view",
|
||||
),
|
||||
path(
|
||||
"asset-request-allocation-view-search-filter",
|
||||
views.asset_request_alloaction_view_search_filter,
|
||||
name="asset-request-allocation-view-search-filter",
|
||||
),
|
||||
path(
|
||||
"asset-request-approve/<int:req_id>/",
|
||||
views.asset_request_approve,
|
||||
name="asset-request-approve",
|
||||
),
|
||||
path(
|
||||
"asset-request-reject/<int:req_id>/",
|
||||
views.asset_request_reject,
|
||||
name="asset-request-reject",
|
||||
),
|
||||
path(
|
||||
"asset-allocate-creation",
|
||||
views.asset_allocate_creation,
|
||||
name="asset-allocate-creation",
|
||||
),
|
||||
path(
|
||||
"asset-allocate-return/<int:asset_id>/",
|
||||
views.asset_allocate_return,
|
||||
name="asset-allocate-return",
|
||||
),
|
||||
path(
|
||||
"asset-allocate-return-request/<int:asset_id>/",
|
||||
views.asset_allocate_return_request,
|
||||
name="asset-allocate-return-request",
|
||||
),
|
||||
path("asset-excel", views.asset_excel, name="asset-excel"),
|
||||
path("asset-import", views.asset_import, name="asset-import"),
|
||||
path("asset-export-excel", views.asset_export_excel, name="asset-export-excel"),
|
||||
path(
|
||||
"asset-batch-number-creation",
|
||||
views.asset_batch_number_creation,
|
||||
name="asset-batch-number-creation",
|
||||
),
|
||||
path("asset-batch-view", views.asset_batch_view, name="asset-batch-view"),
|
||||
path(
|
||||
"asset-batch-number-search",
|
||||
views.asset_batch_number_search,
|
||||
name="asset-batch-number-search",
|
||||
),
|
||||
path(
|
||||
"asset-batch-update/<int:batch_id>",
|
||||
views.asset_batch_update,
|
||||
name="asset-batch-update",
|
||||
),
|
||||
path(
|
||||
"asset-batch-number-delete/<int:batch_id>",
|
||||
views.asset_batch_number_delete,
|
||||
name="asset-batch-number-delete",
|
||||
),
|
||||
path("asset-count-update", views.asset_count_update, name="asset-count-update"),
|
||||
path("add-asset-report/", views.add_asset_report, name="add-asset-report"),
|
||||
path(
|
||||
"add-asset-report/<int:asset_id>",
|
||||
views.add_asset_report,
|
||||
name="add-asset-report",
|
||||
),
|
||||
path("dashboard/", views.asset_dashboard, name="asset-dashboard"),
|
||||
path(
|
||||
"asset-dashboard-requests/",
|
||||
views.asset_dashboard_requests,
|
||||
name="asset-dashboard-requests",
|
||||
),
|
||||
path(
|
||||
"asset-dashboard-allocates/",
|
||||
views.asset_dashboard_allocates,
|
||||
name="asset-dashboard-allocates",
|
||||
),
|
||||
path(
|
||||
"asset-available-chart/",
|
||||
views.asset_available_chart,
|
||||
name="asset-available-chart",
|
||||
),
|
||||
path(
|
||||
"asset-category-chart/", views.asset_category_chart, name="asset-category-chart"
|
||||
),
|
||||
path(
|
||||
"asset-history",
|
||||
views.asset_history,
|
||||
name="asset-history",
|
||||
),
|
||||
path(
|
||||
"asset-history-single-view/<int:asset_id>",
|
||||
views.asset_history_single_view,
|
||||
name="asset-history-single-view",
|
||||
),
|
||||
path(
|
||||
"asset-history-search",
|
||||
views.asset_history_search,
|
||||
name="asset-history-search",
|
||||
),
|
||||
path("asset-tab/<int:emp_id>", views.asset_tab, name="asset-tab"),
|
||||
path(
|
||||
"profile-asset-tab/<int:emp_id>",
|
||||
views.profile_asset_tab,
|
||||
name="profile-asset-tab",
|
||||
),
|
||||
path(
|
||||
"asset-request-tab/<int:emp_id>",
|
||||
views.asset_request_tab,
|
||||
name="asset-request-tab",
|
||||
),
|
||||
path(
|
||||
"main-dashboard-asset-requests",
|
||||
views.asset_dashboard_requests,
|
||||
name="main-dashboard-asset-requests",
|
||||
),
|
||||
]
|
||||
1878
asset/views.py
Normal file
1878
asset/views.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user