Files
timerr/apps/tasks/models.py
T
2026-07-15 00:12:31 +05:00

56 lines
2.9 KiB
Python

from decimal import Decimal, ROUND_HALF_UP
from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator
from django.db import models
from apps.projects.models import Project, ProjectMember
class Task(models.Model):
class Status(models.TextChoices):
COMPLETED = "completed", "Выполнена"
CANCELLED = "cancelled", "Отменена"
project = models.ForeignKey(Project, on_delete=models.PROTECT, related_name="tasks")
assignee = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name="tasks")
title = models.CharField("Задача", max_length=200)
description = models.TextField("Описание выполненной работы")
work_date = models.DateField("Дата выполнения")
duration_minutes = models.PositiveIntegerField("Продолжительность, минут", validators=[MinValueValidator(1)])
hourly_rate = models.DecimalField("Ставка", max_digits=12, decimal_places=2, validators=[MinValueValidator(Decimal("0"))])
amount = models.DecimalField("Сумма", max_digits=14, decimal_places=2, validators=[MinValueValidator(Decimal("0"))])
status = models.CharField("Статус", max_length=16, choices=Status.choices, default=Status.COMPLETED)
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name="created_tasks")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ("-work_date", "-id")
indexes = [
models.Index(fields=("assignee", "work_date")),
models.Index(fields=("project", "work_date")),
models.Index(fields=("status", "work_date")),
]
@staticmethod
def calculate_amount(duration_minutes, hourly_rate):
return (Decimal(duration_minutes) * Decimal(hourly_rate) / Decimal(60)).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
def clean(self):
super().clean()
if not self.project_id or not self.assignee_id:
return
if not ProjectMember.objects.filter(project_id=self.project_id, user_id=self.assignee_id, is_active=True).exists():
raise ValidationError({"assignee": "Пользователь не является активным участником проекта."})
if not self.project.is_active or not self.project.company.is_active:
raise ValidationError({"project": "Компания и проект должны быть активны."})
def apply_current_rate(self):
membership = ProjectMember.objects.get(project=self.project, user=self.assignee, is_active=True)
self.hourly_rate = membership.hourly_rate
self.amount = self.calculate_amount(self.duration_minutes, self.hourly_rate)
def __str__(self):
return f"{self.work_date}: {self.title}"