Skip to content

Commit

Permalink
feat: add timesheet approval and ticket resolve
Browse files Browse the repository at this point in the history
  • Loading branch information
ngurenyaga committed Aug 17, 2021
1 parent 7afb8ad commit b501923
Show file tree
Hide file tree
Showing 12 changed files with 236 additions and 11 deletions.
10 changes: 0 additions & 10 deletions fahari/common/tests/test_admin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import pytest
from django.conf import settings
from django.contrib.admin.sites import AdminSite
from model_bakery import baker

Expand All @@ -10,15 +9,6 @@
pytestmark = pytest.mark.django_db


@pytest.fixture
def request_with_user(rf, django_user_model):
url = settings.ADMIN_URL + "/common/organisation/add/"
request = rf.get(url)
user = baker.make(django_user_model)
request.user = user
return request


@pytest.fixture
def organisation_admin():
admin = OrganisationAdmin(model=Organisation, admin_site=AdminSite())
Expand Down
10 changes: 10 additions & 0 deletions fahari/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pytest
from django.conf import settings
from django.contrib.auth.models import Group, Permission
from model_bakery import baker

Expand Down Expand Up @@ -53,3 +54,12 @@ def user_with_group(user, group_with_all_permissions) -> User:
user.groups.add(group_with_all_permissions)
user.save()
return user


@pytest.fixture
def request_with_user(rf, django_user_model):
url = settings.ADMIN_URL + "/common/organisation/add/"
request = rf.get(url)
user = baker.make(django_user_model)
request.user = user
return request
4 changes: 4 additions & 0 deletions fahari/ops/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ def facility_system_name(self):
+ f"Version: {self.facility_system.version}"
)

@property
def is_resolved(self):
return self.resolved is not None

def get_absolute_url(self):
update_url = reverse_lazy("ops:ticket_update", kwargs={"pk": self.pk})
return update_url
Expand Down
21 changes: 21 additions & 0 deletions fahari/ops/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,27 @@ def test_facility_system_ticket_str():
)


def test_facility_system_ticket_is_resolved():
org = baker.make(Organisation)
facility = baker.make(Facility, organisation=org, name="Test")
system = baker.make(System, organisation=org, name="System")
vrs = "0.0.1"
facility_system = baker.make(
FacilitySystem,
facility=facility,
system=system,
version=vrs,
)
facility_system_details = baker.make(
FacilitySystemTicket,
facility_system=facility_system,
details="Details",
resolved=timezone.now(),
resolved_by="User",
)
assert facility_system_details.is_resolved is True


def test_facility_ticket_status(staff_user):
open_ticket = baker.make(FacilitySystemTicket, resolved=None, resolved_by=None)
assert open_ticket.is_open is True
Expand Down
49 changes: 48 additions & 1 deletion fahari/ops/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import uuid

import pytest
from django.urls import reverse
from model_bakery import baker
from rest_framework import status

from fahari.ops.views import FacilitySystemsView, FacilitySystemTicketsView
from fahari.ops.models import FacilitySystemTicket, TimeSheet
from fahari.ops.views import (
FacilitySystemsView,
FacilitySystemTicketResolveView,
FacilitySystemTicketsView,
TimeSheetApproveView,
)

pytestmark = pytest.mark.django_db

Expand Down Expand Up @@ -75,3 +84,41 @@ def test_tickets_context_data():
ctx = v.get_context_data()
assert ctx["active"] == "facilities-nav"
assert ctx["selected"] == "tickets"


def test_timesheet_approve_view_happy_case(request_with_user):
assert request_with_user.user is not None
timesheet = baker.make(TimeSheet, approved_by=None, approved_at=None)
view = TimeSheetApproveView()
resp = view.post(request_with_user, pk=timesheet.pk)
assert resp.status_code == 302

timesheet.refresh_from_db()
assert timesheet.approved_by is not None
assert timesheet.approved_at is not None


def test_timesheet_approve_view_error_case(request_with_user):
fake_pk = uuid.uuid4()
view = TimeSheetApproveView()
resp = view.post(request_with_user, pk=fake_pk)
assert resp.status_code == 200 # page re-rendered with an error


def test_ticket_resolve_view_happy_case(request_with_user):
assert request_with_user.user is not None
open_ticket = baker.make(FacilitySystemTicket, resolved=None, resolved_by=None)
view = FacilitySystemTicketResolveView()
resp = view.post(request_with_user, pk=open_ticket.pk)
assert resp.status_code == 302

open_ticket.refresh_from_db()
assert open_ticket.resolved is not None
assert open_ticket.resolved_by is not None


def test_ticket_resolve_view_error_case(request_with_user):
fake_pk = uuid.uuid4()
view = FacilitySystemTicketResolveView()
resp = view.post(request_with_user, pk=fake_pk)
assert resp.status_code == 200 # page re-rendered with an error
12 changes: 12 additions & 0 deletions fahari/ops/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
FacilitySystemsView,
FacilitySystemTicketCreateView,
FacilitySystemTicketDeleteView,
FacilitySystemTicketResolveView,
FacilitySystemTicketsView,
FacilitySystemTicketUpdateView,
FacilitySystemUpdateView,
Expand All @@ -25,6 +26,7 @@
StockReceiptVerificationDeleteView,
StockReceiptVerificationUpdateView,
StockReceiptVerificationView,
TimeSheetApproveView,
TimeSheetCreateView,
TimeSheetDeleteView,
TimeSheetsView,
Expand Down Expand Up @@ -69,6 +71,11 @@
view=FacilitySystemTicketDeleteView.as_view(),
name="ticket_delete",
),
path(
"ticket_resolve/<pk>",
view=FacilitySystemTicketResolveView.as_view(),
name="ticket_resolve",
),
path("activity_logs", view=ActivityLogView.as_view(), name="activity_logs"),
path(
"activity_log_create",
Expand Down Expand Up @@ -157,6 +164,11 @@
view=TimeSheetDeleteView.as_view(),
name="timesheet_delete",
),
path(
"timesheet_approve/<pk>",
view=TimeSheetApproveView.as_view(),
name="timesheet_approve",
),
path(
"stock_receipt_verifications",
view=StockReceiptVerificationView.as_view(),
Expand Down
39 changes: 39 additions & 0 deletions fahari/ops/views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.exceptions import ValidationError
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse_lazy
from django.utils import timezone
from django.views.generic import CreateView, DeleteView, TemplateView, UpdateView

from fahari.common.views import ApprovedMixin, BaseFormMixin, BaseView
Expand Down Expand Up @@ -130,6 +134,25 @@ class FacilitySystemTicketDeleteView(FacilitySystemTicketContextMixin, DeleteVie
success_url = reverse_lazy("ops:tickets")


class FacilitySystemTicketResolveView(
FacilitySystemTicketContextMixin,
TemplateView,
):
template_name = "ops/ticket_resolve.html"
success_url = reverse_lazy("ops:tickets")

def post(self, request, *args, **kwargs):
try:
pk = kwargs["pk"]
ticket = FacilitySystemTicket.objects.get(pk=pk)
ticket.resolved_by = str(request.user)
ticket.resolved = timezone.now()
ticket.save()
return HttpResponseRedirect(self.success_url)
except (FacilitySystemTicket.DoesNotExist, ValidationError, KeyError) as e:
return render(request, self.template_name, {"errors": [e]})


class FacilitySystemTicketViewSet(BaseView):
queryset = FacilitySystemTicket.objects.filter(
active=True,
Expand Down Expand Up @@ -381,6 +404,22 @@ class TimeSheetDeleteView(TimeSheetContextMixin, DeleteView, BaseFormMixin):
success_url = reverse_lazy("ops:timesheets")


class TimeSheetApproveView(TimeSheetContextMixin, TemplateView):
template_name = "ops/timesheet_approve.html"
success_url = reverse_lazy("ops:timesheets")

def post(self, request, *args, **kwargs):
try:
pk = kwargs["pk"]
timesheet = TimeSheet.objects.get(pk=pk)
timesheet.approved_by = request.user
timesheet.approved_at = timezone.now()
timesheet.save()
return HttpResponseRedirect(self.success_url)
except (TimeSheet.DoesNotExist, ValidationError, KeyError) as e:
return render(request, self.template_name, {"errors": [e]})


class TimeSheetViewSet(BaseView):
queryset = TimeSheet.objects.filter(
active=True,
Expand Down
Binary file added fahari/static/admin/favicon.ico
Binary file not shown.
9 changes: 9 additions & 0 deletions fahari/templates/ops/facilitysystemticket_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@
<span class="text">Delete Ticket</span>
</a>
{% endif %}

{% if perms.ops.can_resolve_ticket and not object.is_resolved %}
<a href="{% url 'ops:ticket_resolve' object.pk %}" class="btn btn-secondary btn-icon-split">
<span class="icon text-white-50">
<i class="fas fa-thumbs-up"></i>
</span>
<span class="text">Resolve Ticket</span>
</a>
{% endif %}
{% endif %}
</div>
</div>
Expand Down
42 changes: 42 additions & 0 deletions fahari/templates/ops/ticket_resolve.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block title %}
Resolve Ticket
{% endblock title %}
{% block content %}
<a href="{% url 'ops:tickets' %}">&larr; Back</a>
{% if errors %}
<div class="alert alert-error">
<ul>
{% for error in errors %}
<li>{{error}}</li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="row">
<div class="col-xl-10 col-lg-12 col-md-9 col-sm-10">
<div class="card o-hidden border-0 shadow my-1">
<div class="card-body p-0">
<div class="row">
<div class="col-lg-2 d-none d-lg-block">
</div>
<div class="col-lg-6">
<div class="p-5">
<div class="text-left">
<h1 class="h4 text-gray-900 mb-4">Confirm Resolve</h1>
</div>
<form method="post">
{% csrf_token %}
<p>Are you sure you want to resolve this ticket?</p>
<input class="btn btn-outline-primary btn-lg" type="submit" value="Resolve">
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}
42 changes: 42 additions & 0 deletions fahari/templates/ops/timesheet_approve.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block title %}
Approve Time Sheet
{% endblock title %}
{% block content %}
<a href="{% url 'ops:timesheets' %}">&larr; Back</a>
{% if errors %}
<div class="alert alert-error">
<ul>
{% for error in errors %}
<li>{{error}}</li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="row">
<div class="col-xl-10 col-lg-12 col-md-9 col-sm-10">
<div class="card o-hidden border-0 shadow my-1">
<div class="card-body p-0">
<div class="row">
<div class="col-lg-2 d-none d-lg-block">
</div>
<div class="col-lg-6">
<div class="p-5">
<div class="text-left">
<h1 class="h4 text-gray-900 mb-4">Confirm Approval</h1>
</div>
<form method="post">
{% csrf_token %}
<p>Are you sure you want to approve this timesheet?</p>
<input class="btn btn-outline-primary btn-lg" type="submit" value="Approve">
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}
9 changes: 9 additions & 0 deletions fahari/templates/ops/timesheet_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@
<span class="text">Delete Time Sheet</span>
</a>
{% endif %}

{% if perms.ops.can_approve_timesheet and not object.is_approved %}
<a href="{% url 'ops:timesheet_approve' object.pk %}" class="btn btn-secondary btn-icon-split">
<span class="icon text-white-50">
<i class="fas fa-thumbs-up"></i>
</span>
<span class="text">Approve Time Sheet</span>
</a>
{% endif %}
{% endif %}
</div>
</div>
Expand Down

0 comments on commit b501923

Please sign in to comment.