Files
ihrm/asset/models.py

668 lines
19 KiB
Python
Raw Normal View History

2023-08-21 17:25:49 +05:30
"""
Models for Asset Management System
This module defines Django models to manage assets, their categories, assigning, and requests
within an Asset Management System.
"""
2023-08-21 17:25:49 +05:30
from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse, reverse_lazy
from django.utils.html import format_html
2023-05-10 15:06:57 +05:30
from django.utils.translation import gettext_lazy as _
2023-12-01 15:36:51 +05:30
from base.horilla_company_manager import HorillaCompanyManager
from base.models import Company
2023-08-21 17:25:49 +05:30
from employee.models import Employee
from horilla.models import HorillaModel, upload_path
from horilla_views.cbv_methods import render_template
2023-05-10 15:06:57 +05:30
class AssetCategory(HorillaModel):
2023-08-21 17:25:49 +05:30
"""
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")
)
2023-08-21 17:25:49 +05:30
objects = models.Manager()
company_id = models.ManyToManyField(Company, blank=True, verbose_name=_("Company"))
objects = HorillaCompanyManager("company_id")
2023-08-08 10:13:50 +05:30
class Meta:
"""
Meta class to add additional options
"""
verbose_name = _("Asset Category")
verbose_name_plural = _("Asset Categories")
2023-05-10 15:06:57 +05:30
def __str__(self):
2023-08-08 10:13:50 +05:30
return f"{self.asset_category_name}"
2023-05-10 15:06:57 +05:30
class AssetLot(HorillaModel):
2023-08-21 17:25:49 +05:30
"""
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, verbose_name=_("Description")
)
company_id = models.ManyToManyField(Company, blank=True, verbose_name=_("Company"))
2023-12-01 15:36:51 +05:30
objects = HorillaCompanyManager()
2023-08-08 10:13:50 +05:30
class Meta:
"""
Meta class to add additional options
"""
ordering = ["-created_at"]
verbose_name = _("Asset Batch")
verbose_name_plural = _("Asset Batches")
2023-05-10 15:06:57 +05:30
def __str__(self):
2023-08-08 10:13:50 +05:30
return f"{self.lot_number}"
def actions(self):
"""
This method for get custom column for action.
"""
return render_template(
path="cbv/asset_batch_no/actions.html",
context={"instance": self},
)
def asset_batch_detail(self):
"""
detail view
"""
url = reverse("asset-batch-detail-view", kwargs={"pk": self.pk})
return url
def assets_column(self):
"""
This method for get custom column for action.
"""
return render_template(
path="cbv/asset_batch_no/assets_col.html",
context={"instance": self},
)
def detail_actions(self):
return render_template(
path="cbv/asset_batch_no/detail_actions.html",
context={"instance": self},
)
def get_update_url(self):
"""
This method to get update url
"""
url = reverse_lazy("asset-batch-update", kwargs={"pk": self.pk})
return url
def get_delete_url(self):
"""
This method to get delete url
"""
url = reverse_lazy("asset-batch-number-delete", kwargs={"batch_id": self.pk})
return url
2023-05-10 15:06:57 +05:30
class Asset(HorillaModel):
2023-08-21 17:25:49 +05:30
"""
Represents a asset with various attributes.
"""
2023-05-10 15:06:57 +05:30
ASSET_STATUS = [
("In use", _("In Use")),
2023-08-08 10:13:50 +05:30
("Available", _("Available")),
("Not-Available", _("Not-Available")),
2023-05-10 15:06:57 +05:30
]
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")
)
2023-08-08 10:13:50 +05:30
asset_status = models.CharField(
choices=ASSET_STATUS,
default="Available",
max_length=40,
verbose_name=_("Status"),
2023-08-08 10:13:50 +05:30
)
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)")
2023-08-08 10:13:50 +05:30
)
2023-12-01 15:36:51 +05:30
objects = HorillaCompanyManager("asset_category_id__company_id")
2023-08-08 10:13:50 +05:30
class Meta:
ordering = ["-created_at"]
verbose_name = _("Asset")
verbose_name_plural = _("Assets")
2023-05-10 15:06:57 +05:30
def __str__(self):
2023-08-08 10:13:50 +05:30
return f"{self.asset_name}-{self.asset_tracking_id}"
def action_column(self):
"""
Action column for asset
"""
return render_template(
path="asset/action_column.html", context={"instance": self}
)
def detail_view_action(self):
"""
This method for get custome coloumn .
"""
return render_template(
path="cbv/asset/detail_action.html",
context={"instance": self},
)
def asset_detail(self):
"""
detail view url
"""
url = reverse_lazy("asset-information", kwargs={"pk": self.pk})
return url
def get_update_url(self):
"""
This method to get update url
"""
url = reverse_lazy("asset-update", kwargs={"pk": self.pk})
return url
def get_delete_url(self):
"""
This method to get delete url
"""
url = reverse_lazy("asset-delete", kwargs={"asset_id": self.pk})
return url
2023-08-21 17:25:49 +05:30
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()
2023-05-10 15:06:57 +05:30
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)
2024-02-07 13:00:09 +05:30
class AssetAssignment(HorillaModel):
2023-08-21 17:25:49 +05:30
"""
Represents the allocation and return of assets to and from employees.
"""
2023-05-10 15:06:57 +05:30
STATUS = [
2023-08-08 10:13:50 +05:30
("Minor damage", _("Minor damage")),
("Major damage", _("Major damage")),
("Healthy", _("Healthy")),
2023-05-10 15:06:57 +05:30
]
2023-08-08 10:13:50 +05:30
asset_id = models.ForeignKey(
Asset, on_delete=models.PROTECT, verbose_name=_("Asset")
2023-08-08 10:13:50 +05:30
)
assigned_to_employee_id = models.ForeignKey(
Employee,
on_delete=models.PROTECT,
related_name="allocated_employee",
verbose_name=_("Assigned To"),
2023-08-08 10:13:50 +05:30
)
2023-05-10 15:06:57 +05:30
assigned_date = models.DateField(auto_now_add=True)
2023-08-08 10:13:50 +05:30
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, verbose_name=_("Return Condition")
2023-08-08 10:13:50 +05:30
)
return_status = models.CharField(
choices=STATUS,
max_length=30,
null=True,
blank=True,
verbose_name=_("Return Status"),
2023-08-08 10:13:50 +05:30
)
return_request = models.BooleanField(default=False)
2023-12-01 15:36:51 +05:30
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"
)
2024-02-07 13:00:09 +05:30
class Meta:
"""Meta class for AssetAssignment model"""
ordering = ["-id"]
verbose_name = _("Asset Allocation")
verbose_name_plural = _("Asset Allocations")
2024-02-07 13:00:09 +05:30
def __str__(self):
return f"{self.assigned_to_employee_id} --- {self.asset_id} --- {self.return_status}"
2023-08-08 10:13:50 +05:30
def get_avatar(self):
"""
Method will retun the api to the avatar or path to the profile image
"""
url = f"https://ui-avatars.com/api/?name={self.asset_id}&background=random"
return url
def asset_detail_view(self):
"""
for detail view of page
"""
url = reverse("asset-history-detail-view", kwargs={"pk": self.pk})
return url
def assign_condition_img(self):
"""
This method for get custome coloumn .
"""
return render_template(
path="cbv/asset_history/assign_condition.html",
context={"instance": self},
)
def return_condition_img(self):
"""
This method for get custome coloumn .
"""
return render_template(
path="cbv/asset_history/return_condition.html",
context={"instance": self},
)
def asset_action(self):
"""
This method for get custom column for asset tab action.
"""
return render_template(
path="cbv/request_and_allocation/asset_actions.html",
context={"instance": self},
)
def return_status_col(self):
"""
This method for get custom column for return date.
"""
return render_template(
path="cbv/request_and_allocation/return_status.html",
context={"instance": self},
)
def allocation_action(self):
"""
This method for get custom column for asset allocation tab actions.
"""
return render_template(
path="cbv/request_and_allocation/asset_allocation_action.html",
context={"instance": self},
)
def allocation_option(self):
"""
This method for get custom column for asset tab action.
"""
return render_template(
path="cbv/request_and_allocation/allocation_option.html",
context={"instance": self},
)
def asset_detail_action(self):
"""
This method for get custom column for asset detail actions.
"""
return render_template(
path="cbv/request_and_allocation/asset_detail_action.html",
context={"instance": self},
)
def asset_allocation_detail_action(self):
"""
This method for get custom column for asset detail actions.
"""
return render_template(
path="cbv/request_and_allocation/detail_action_asset_allocation.html",
context={"instance": self},
)
def get_avatar(self):
"""
Method will retun the api to the avatar or path to the question template
"""
url = f"https://ui-avatars.com/api/?name={self.asset_id.asset_name}&background=random"
return url
def detail_view_asset(self):
"""
detail view
"""
url = reverse("asset-detail-view", kwargs={"pk": self.pk})
return url
def detail_view_asset_allocation(self):
"""
detail view
"""
url = reverse("asset-allocation-detail-view", kwargs={"pk": self.pk})
return url
def asset_detail_status(self):
"""
Asset tab detail status
"""
return (
'<span class="link-primary">Requested to return</span>'
if self.return_request
else '<span style = "color : yellowgreen;">In use</span>'
)
def detail_status(self):
"""
Asset allocation tab detail status
"""
if self.return_date:
status = '<span style = "color : red;" >Returned</span>'
elif self.return_request:
status = '<span class="link-primary">Requested to return</span>'
else:
status = '<span style = "color : yellowgreen;" >Allocated</span>'
return status
def asset_allocation_detail_subtitle(self):
"""
Return subtitle containing both department and job position information.
"""
return f"{self.assigned_to_employee_id.get_department()} / {self.assigned_to_employee_id.get_job_position()}"
def status_display(self):
status = self.asset_id.asset_status
color_class = "oh-dot--warning" # Adjust based on your status
return format_html(
'<span class="oh-dot oh-dot--small me-1 oh-dot--color {color_class}"></span>'
'<span class="link-warning">{status}</span>',
color_class=color_class,
status=status,
)
def assigned_date_display(self):
date_col = self.assigned_date
color_class = "oh-dot--success" # Adjust based on your status
return format_html(
'<span class="oh-dot oh-dot--small me-1 oh-dot--color {color_class}"></span>'
'<span class="link-success dateformat_changer">{date_col}</span>',
color_class=color_class,
date_col=date_col,
)
def get_asset_of_offboarding_employee(self):
url = f"{reverse('asset-request-allocation-view')}?assigned_to_employee_id={self.assigned_to_employee_id.id}"
return url
def get_send_mail_employee_link(self):
if not self.assigned_to_employee_id:
return ""
url = reverse(
"send-mail-employee", kwargs={"emp_id": self.assigned_to_employee_id.id}
)
title = _("Send Mail")
html = f"""
<a
onclick="event.stopPropagation()"
hx-get="{url}"
data-toggle="oh-modal-toggle"
data-target="#sendMailModal"
title="{title}"
hx-target="#mail-content"
>
<ion-icon name="mail-outline"></ion-icon>
</a>
"""
return format_html(html)
2023-05-10 15:06:57 +05:30
class AssetRequest(HorillaModel):
2023-08-21 17:25:49 +05:30
"""
Represents a request for assets made by employees.
"""
2023-08-08 10:13:50 +05:30
STATUS = [
("Requested", _("Requested")),
("Approved", _("Approved")),
("Rejected", _("Rejected")),
2023-05-10 15:06:57 +05:30
]
2023-08-08 10:13:50 +05:30
requested_employee_id = models.ForeignKey(
Employee,
2023-09-19 15:56:53 +05:30
on_delete=models.PROTECT,
2023-08-08 10:13:50 +05:30
related_name="requested_employee",
null=False,
blank=False,
verbose_name=_("Requesting User"),
2023-08-08 10:13:50 +05:30
)
[IMP] Remove inter module dependency (#274) This commit introduces significant changes to the architecture of the Horilla HRMS system by decoupling interdependent modules. The following modifications were made: 1. **Module Independence**: Each module has been refactored to eliminate reliance on other modules, promoting a more modular and maintainable codebase. 2. **Refactored Imports and Dependencies**: Adjusted import statements and dependency injections to support independent module operation. 3. **Compatibility and Functionality**: Ensured that all modules are compatible with existing systems and maintain their intended functionality both independently and when integrated with other modules. These changes enhance the modularity, maintainability, and scalability of the Horilla HRMS, allowing developers to work on individual modules without affecting the entire system. Future development and deployment will be more efficient and less prone to issues arising from tightly coupled code. **NOTE** For existing Horilla users, if you face any issues during the migrations, please run the following command and try again the migrations. - `python3 manage.py makemigrations` - `python3 manage.py migrate base` - `python3 manage.py migrate` * [IMP] ASSET: Asset module dependency removal from other Horilla apps * [IMP] ATTENDANCE: Attendance module dependency removal from other Horilla apps * [IMP] BASE: Base module dependency removal from other Horilla apps * [IMP] EMPLOYEE: Employee module dependency removal from other Horilla apps * [IMP] HELPDESK: Helpdesk module dependency removal from other Horilla apps * [IMP] HORILLA AUDIT: Horilla Audit module dependency removal from other Horilla apps * [IMP] HORILLA CRUMBS: Horilla Crumbs module dependency removal from other Horilla apps * [IMP] HORILLA AUTOMATIONS: Horilla Automations module dependency removal from other Horilla apps * [IMP] HORILLA VIEWS: Horilla Views module dependency removal from other Horilla apps * [IMP] LEAVE: Leave module dependency removal from other Horilla apps * [IMP] OFFBOARDING: Offboarding module dependency removal from other Horilla apps * [IMP] ONBOARDING: Onboarding module dependency removal from other Horilla apps * [IMP] PMS: PMS module dependency removal from other Horilla apps * [IMP] PAYROLL: Payroll module dependency removal from other Horilla apps * [IMP] RECRUITMENT: Recruitment module dependency removal from other Horilla apps * [IMP] HORILLA: Dependency removal updates * [IMP] TEMPLATES: Dependency removal updates * [IMP] STATIC: Dependency removal updates * [IMP] HORILLA DOCUMENTS: Horilla Documents module dependency removal from other Horilla apps * [ADD] HORILLA: methods.py * [UPDT] HORILLA: Settings.py * [FIX] EMPLOYEE: About tab issue * Update horilla_settings.py * Remove dummy db init password
2024-08-05 14:22:44 +05:30
asset_category_id = models.ForeignKey(
AssetCategory, on_delete=models.PROTECT, verbose_name=_("Asset Category")
)
2023-05-10 15:06:57 +05:30
asset_request_date = models.DateField(auto_now_add=True)
description = models.TextField(
null=True, blank=True, max_length=255, verbose_name=_("Description")
)
2023-08-08 10:13:50 +05:30
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_col(self):
"""
This method for get custom coloumn for status.
"""
return render_template(
path="cbv/request_and_allocation/status.html",
context={"instance": self},
)
def action_col(self):
"""
This method for get custom coloumn for action.
"""
return render_template(
path="cbv/request_and_allocation/asset_request_action.html",
context={"instance": self},
)
def detail_action_col(self):
"""
This method for get custom coloumn for detail action.
"""
return render_template(
path="cbv/request_and_allocation/asset_request_detail_action.html",
context={"instance": self},
)
def option_col(self):
"""
This method for get custom coloumn for action.
"""
return render_template(
path="cbv/request_and_allocation/asset_request_option.html",
context={"instance": self},
)
def asset_request_detail_subtitle(self):
"""
Return subtitle containing both department and job position information.
"""
return f"{self.requested_employee_id.get_department()} / {self.requested_employee_id.get_job_position()}"
def detail_view_asset_request(self):
"""
detail view
"""
url = reverse("asset-request-detail-view", kwargs={"pk": self.pk})
return url
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),
}