From 6a9996f2eddf297db6e5d4b8824437736a53ac82 Mon Sep 17 00:00:00 2001 From: S4NKALP Date: Thu, 18 Dec 2025 21:00:23 +0545 Subject: [PATCH] feat(migrations): update client phone field migration This commit modifies the migration file for altering the client phone field. - **Enhancements & Refinements**: - Removed an unnecessary blank line in the migration class definition - Cleaned up formatting in the `0005_alter_client_phone.py` migration file ``` --- src/api/finance/urls.py | 18 ++++ src/api/finance/views.py | 44 +++++--- src/api/projects/forms.py | 63 +++++++++-- src/api/projects/urls.py | 25 +++-- src/api/projects/views.py | 125 ++++++++++++---------- src/migrations/0005_alter_client_phone.py | 1 - 6 files changed, 184 insertions(+), 92 deletions(-) diff --git a/src/api/finance/urls.py b/src/api/finance/urls.py index e69de29..fc2e4f0 100644 --- a/src/api/finance/urls.py +++ b/src/api/finance/urls.py @@ -0,0 +1,18 @@ +from django.urls import path +from . import views + +urlpatterns = [ + path("", views.finance_dashboard, name="finance_dashboard"), + + # Invoices + path("invoices/", views.invoice_list, name="invoice_list"), + path("invoices/create/", views.invoice_create, name="invoice_create"), + path("invoices//edit/", views.invoice_edit, name="invoice_edit"), + path("invoices//delete/", views.invoice_delete, name="invoice_delete"), + + # Expenses + path("expenses/", views.expense_list, name="expense_list"), + path("expenses/create/", views.expense_create, name="expense_create"), + path("expenses//edit/", views.expense_edit, name="expense_edit"), + path("expenses//delete/", views.expense_delete, name="expense_delete"), +] diff --git a/src/api/finance/views.py b/src/api/finance/views.py index f56b9b1..9a23900 100644 --- a/src/api/finance/views.py +++ b/src/api/finance/views.py @@ -5,6 +5,7 @@ from django.views.decorators.http import require_http_methods from src.models.clients import Client from src.models.finance import Expense, Invoice from src.models.projects import Project +from src.models.services import Service def finance_dashboard(request): @@ -45,22 +46,18 @@ def invoice_create(request): if request.method == "POST": client_id = request.POST.get("client") project_id = request.POST.get("project") - invoice_number = request.POST.get("invoice_number") amount = request.POST.get("amount") - date_issued = request.POST.get("date_issued") due_date = request.POST.get("due_date") status = request.POST.get("status") - if client_id and invoice_number and amount: + if client_id and project_id and amount and due_date: client = get_object_or_404(Client, id=client_id) - project = get_object_or_404(Project, id=project_id) if project_id else None + project = get_object_or_404(Project, id=project_id) Invoice.objects.create( client=client, project=project, - invoice_number=invoice_number, amount=amount, - date_issued=date_issued, due_date=due_date, status=status, ) @@ -78,21 +75,17 @@ def invoice_edit(request, pk): if request.method == "POST": client_id = request.POST.get("client") project_id = request.POST.get("project") - invoice_number = request.POST.get("invoice_number") amount = request.POST.get("amount") - date_issued = request.POST.get("date_issued") due_date = request.POST.get("due_date") status = request.POST.get("status") - if client_id and invoice_number and amount: + if client_id and project_id and amount and due_date: client = get_object_or_404(Client, id=client_id) - project = get_object_or_404(Project, id=project_id) if project_id else None + project = get_object_or_404(Project, id=project_id) invoice.client = client invoice.project = project - invoice.invoice_number = invoice_number invoice.amount = amount - invoice.date_issued = date_issued invoice.due_date = due_date invoice.status = status invoice.save() @@ -127,14 +120,24 @@ def expense_create(request): amount = request.POST.get("amount") date = request.POST.get("date") category = request.POST.get("category") + service_id = request.POST.get("service") if description and amount and date: + service = None + if service_id: + service = get_object_or_404(Service, id=service_id) + Expense.objects.create( - description=description, amount=amount, date=date, category=category + description=description, + amount=amount, + date=date, + category=category, + service=service, ) return redirect("finance_dashboard") - return render(request, "finance/expense_form.html") + services = Service.objects.all() + return render(request, "finance/expense_form.html", {"services": services}) def expense_edit(request, pk): @@ -144,16 +147,27 @@ def expense_edit(request, pk): amount = request.POST.get("amount") date = request.POST.get("date") category = request.POST.get("category") + service_id = request.POST.get("service") if description and amount and date: + service = None + if service_id: + service = get_object_or_404(Service, id=service_id) + expense.description = description expense.amount = amount expense.date = date expense.category = category + expense.service = service expense.save() return redirect("finance_dashboard") - return render(request, "finance/expense_form.html", {"expense": expense}) + services = Service.objects.all() + return render( + request, + "finance/expense_form.html", + {"expense": expense, "services": services}, + ) @require_http_methods(["DELETE", "POST"]) diff --git a/src/api/projects/forms.py b/src/api/projects/forms.py index 6b7f7cb..1509267 100644 --- a/src/api/projects/forms.py +++ b/src/api/projects/forms.py @@ -1,27 +1,70 @@ from django import forms + from src.models.projects import Project, Task + class ProjectForm(forms.ModelForm): class Meta: model = Project - fields = ["name", "client", "description", "start_date", "deadline", "budget", "status"] + fields = [ + "name", + "client", + "description", + "start_date", + "deadline", + "budget", + "status", + ] widgets = { - "name": forms.TextInput(attrs={"class": "input input-bordered w-full", "placeholder": "Project Name"}), + "name": forms.TextInput( + attrs={ + "class": "input input-bordered w-full", + "placeholder": "Project Name", + } + ), "client": forms.Select(attrs={"class": "select select-bordered w-full"}), - "description": forms.Textarea(attrs={"class": "textarea textarea-bordered w-full", "rows": 4, "placeholder": "Project Description"}), - "start_date": forms.DateInput(attrs={"class": "input input-bordered w-full", "type": "date"}), - "deadline": forms.DateInput(attrs={"class": "input input-bordered w-full", "type": "date"}), - "budget": forms.NumberInput(attrs={"class": "input input-bordered w-full", "placeholder": "0.00"}), + "description": forms.Textarea( + attrs={ + "class": "textarea textarea-bordered w-full", + "rows": 4, + "placeholder": "Project Description", + } + ), + "start_date": forms.DateInput( + attrs={"class": "input input-bordered w-full", "type": "date"} + ), + "deadline": forms.DateInput( + attrs={"class": "input input-bordered w-full", "type": "date"} + ), + "budget": forms.NumberInput( + attrs={"class": "input input-bordered w-full", "placeholder": "0.00"} + ), "status": forms.Select(attrs={"class": "select select-bordered w-full"}), } + class TaskForm(forms.ModelForm): class Meta: model = Task fields = ["title", "description", "status", "due_date"] widgets = { - "title": forms.TextInput(attrs={"class": "input input-bordered w-full input-sm", "placeholder": "Task Title"}), - "description": forms.Textarea(attrs={"class": "textarea textarea-bordered w-full textarea-sm", "rows": 2, "placeholder": "Task Description"}), - "status": forms.Select(attrs={"class": "select select-bordered w-full select-sm"}), - "due_date": forms.DateInput(attrs={"class": "input input-bordered w-full input-sm", "type": "date"}), + "title": forms.TextInput( + attrs={ + "class": "input input-bordered w-full input-sm", + "placeholder": "Task Title", + } + ), + "description": forms.Textarea( + attrs={ + "class": "textarea textarea-bordered w-full textarea-sm", + "rows": 2, + "placeholder": "Task Description", + } + ), + "status": forms.Select( + attrs={"class": "select select-bordered w-full select-sm"} + ), + "due_date": forms.DateInput( + attrs={"class": "input input-bordered w-full input-sm", "type": "date"} + ), } diff --git a/src/api/projects/urls.py b/src/api/projects/urls.py index 39287e9..61a7680 100644 --- a/src/api/projects/urls.py +++ b/src/api/projects/urls.py @@ -1,15 +1,20 @@ from django.urls import path + from src.api.projects import views urlpatterns = [ - path('', views.project_list, name='project_list'), - path('create/', views.project_create, name='project_create'), - path('/', views.project_detail, name='project_detail'), - path('/edit/', views.project_edit, name='project_edit'), - path('/delete/', views.project_delete, name='project_delete'), - path('/tasks/create/', views.task_create, name='task_create'), - path('tasks//update/', views.task_update_status, name='task_update_status'), - path('tasks//edit/', views.task_edit, name='task_edit'), - path('tasks//detail/', views.task_detail, name='task_detail'), - path('tasks//delete/', views.task_delete, name='task_delete'), + path("", views.project_list, name="project_list"), + path("create/", views.project_create, name="project_create"), + path("/", views.project_detail, name="project_detail"), + path("/edit/", views.project_edit, name="project_edit"), + path("/delete/", views.project_delete, name="project_delete"), + path("/tasks/create/", views.task_create, name="task_create"), + path( + "tasks//update/", + views.task_update_status, + name="task_update_status", + ), + path("tasks//edit/", views.task_edit, name="task_edit"), + path("tasks//detail/", views.task_detail, name="task_detail"), + path("tasks//delete/", views.task_delete, name="task_delete"), ] diff --git a/src/api/projects/views.py b/src/api/projects/views.py index 6e417ab..020d3fd 100644 --- a/src/api/projects/views.py +++ b/src/api/projects/views.py @@ -1,88 +1,92 @@ -from django.shortcuts import render, get_object_or_404, redirect, HttpResponse +from django.shortcuts import HttpResponse, get_object_or_404, redirect, render from django.views.decorators.http import require_http_methods -from src.models.projects import Project, Task + from src.api.projects.forms import ProjectForm, TaskForm +from src.models.projects import Project, Task + def project_list(request): - projects = Project.objects.all().select_related('client') - return render(request, 'projects/project_list.html', {'projects': projects}) + projects = Project.objects.all().select_related("client") + return render(request, "projects/project_list.html", {"projects": projects}) + def project_create(request): - if request.method == 'POST': + if request.method == "POST": form = ProjectForm(request.POST) if form.is_valid(): form.save() - return redirect('project_list') + return redirect("project_list") else: form = ProjectForm() - return render(request, 'projects/project_form.html', {'form': form}) + return render(request, "projects/project_form.html", {"form": form}) + def get_kanban_context(project): tasks = project.tasks.all() return { - 'project': project, - 'tasks_todo': tasks.filter(status='TODO'), - 'tasks_in_progress': tasks.filter(status='IN_PROGRESS'), - 'tasks_review': tasks.filter(status='REVIEW'), - 'tasks_done': tasks.filter(status='DONE'), + "project": project, + "tasks_todo": tasks.filter(status="TODO"), + "tasks_in_progress": tasks.filter(status="IN_PROGRESS"), + "tasks_review": tasks.filter(status="REVIEW"), + "tasks_done": tasks.filter(status="DONE"), } + def project_detail(request, pk): project = get_object_or_404(Project, pk=pk) context = get_kanban_context(project) # Add a blank task form for the "Add Task" modal/section - context['task_form'] = TaskForm() - return render(request, 'projects/project_detail.html', context) + context["task_form"] = TaskForm() + return render(request, "projects/project_detail.html", context) + @require_http_methods(["POST"]) def task_create(request, project_id): import logging from datetime import datetime + logger = logging.getLogger(__name__) - + logger.info(f"Task create called for project {project_id}") logger.info(f"POST data: {request.POST}") - + project = get_object_or_404(Project, pk=project_id) - title = request.POST.get('title', '').strip() - status = request.POST.get('status', 'TODO') - due_date_str = request.POST.get('due_date', '').strip() - + title = request.POST.get("title", "").strip() + status = request.POST.get("status", "TODO") + due_date_str = request.POST.get("due_date", "").strip() + # Parse due_date string to date object due_date = None if due_date_str: try: - due_date = datetime.strptime(due_date_str, '%Y-%m-%d').date() + due_date = datetime.strptime(due_date_str, "%Y-%m-%d").date() except ValueError: logger.warning(f"Invalid due_date format: {due_date_str}") - + logger.info(f"Title: '{title}', Status: '{status}', Due Date: '{due_date}'") - + if not title: logger.error("Task title is empty") return HttpResponse("Task title is required", status=400) - + try: task = Task.objects.create( - project=project, - title=title, - status=status, - due_date=due_date + project=project, title=title, status=status, due_date=due_date ) logger.info(f"Task created successfully: {task.id}") except Exception as e: logger.error(f"Error creating task: {e}") return HttpResponse(f"Error creating task: {str(e)}", status=500) - + # Return the task HTML directly - due_date_html = '' + due_date_html = "" if task.due_date: - due_date_html = f''' + due_date_html = f"""
{task.due_date.strftime("%b %d")} -
''' - + """ + html = f'''
''' - + logger.info(f"Returning HTML response (length: {len(html)})") return HttpResponse(html) + @require_http_methods(["POST"]) def task_update_status(request, task_id): task = get_object_or_404(Task, pk=task_id) - status = request.POST.get('status') + status = request.POST.get("status") if status in dict(Task.STATUS_CHOICES): task.status = status task.save() - + project = task.project context = get_kanban_context(project) - return render(request, 'projects/partials/kanban_board.html', context) + return render(request, "projects/partials/kanban_board.html", context) + @require_http_methods(["POST"]) def task_edit(request, task_id): import logging from datetime import datetime + logger = logging.getLogger(__name__) - + task = get_object_or_404(Task, pk=task_id) - title = request.POST.get('title', '').strip() - due_date_str = request.POST.get('due_date', '').strip() - + title = request.POST.get("title", "").strip() + due_date_str = request.POST.get("due_date", "").strip() + # Parse due_date string to date object due_date = None if due_date_str: try: - due_date = datetime.strptime(due_date_str, '%Y-%m-%d').date() + due_date = datetime.strptime(due_date_str, "%Y-%m-%d").date() except ValueError: logger.warning(f"Invalid due_date format: {due_date_str}") - + if not title: return HttpResponse("Task title is required", status=400) - + try: task.title = title task.due_date = due_date @@ -149,16 +156,16 @@ def task_edit(request, task_id): except Exception as e: logger.error(f"Error updating task: {e}") return HttpResponse(f"Error updating task: {str(e)}", status=500) - + # Return the updated task HTML - due_date_html = '' + due_date_html = "" if task.due_date: - due_date_html = f''' + due_date_html = f"""
{task.due_date.strftime("%b %d")} -
''' - + """ + html = f'''
''' - + return HttpResponse(html) + def task_detail(request, task_id): task = get_object_or_404(Task, pk=task_id) - return render(request, 'projects/partials/task_item.html', {'task': task}) + return render(request, "projects/partials/task_item.html", {"task": task}) + @require_http_methods(["DELETE", "POST"]) def task_delete(request, task_id): @@ -193,16 +202,20 @@ def task_delete(request, task_id): task.delete() return HttpResponse("") + def project_edit(request, pk): project = get_object_or_404(Project, pk=pk) - if request.method == 'POST': + if request.method == "POST": form = ProjectForm(request.POST, instance=project) if form.is_valid(): form.save() - return redirect('project_detail', pk=project.pk) + return redirect("project_detail", pk=project.pk) else: form = ProjectForm(instance=project) - return render(request, 'projects/project_form.html', {'form': form, 'project': project}) + return render( + request, "projects/project_form.html", {"form": form, "project": project} + ) + @require_http_methods(["DELETE", "POST"]) def project_delete(request, pk): @@ -210,4 +223,4 @@ def project_delete(request, pk): project.delete() if request.htmx: return HttpResponse("") - return redirect('project_list') + return redirect("project_list") diff --git a/src/migrations/0005_alter_client_phone.py b/src/migrations/0005_alter_client_phone.py index 7393344..c35e942 100644 --- a/src/migrations/0005_alter_client_phone.py +++ b/src/migrations/0005_alter_client_phone.py @@ -5,7 +5,6 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ ("src", "0004_alter_invoice_due_date"), ]