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
```
This commit is contained in:
sankalp committed 2025-12-18 21:00:23 +05:45
1 parent 2baa947c21
commit 6a9996f2ed
6 files changed
+184 -92

No files matched your search

+18
View File
@@ -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/<int:pk>/edit/", views.invoice_edit, name="invoice_edit"),
path("invoices/<int:pk>/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/<int:pk>/edit/", views.expense_edit, name="expense_edit"),
path("expenses/<int:pk>/delete/", views.expense_delete, name="expense_delete"),
]
+29 -15
View File
@@ -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"])
+53 -10
View File
@@ -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"}
),
}
+15 -10
View File
@@ -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('<int:pk>/', views.project_detail, name='project_detail'),
path('<int:pk>/edit/', views.project_edit, name='project_edit'),
path('<int:pk>/delete/', views.project_delete, name='project_delete'),
path('<int:project_id>/tasks/create/', views.task_create, name='task_create'),
path('tasks/<int:task_id>/update/', views.task_update_status, name='task_update_status'),
path('tasks/<int:task_id>/edit/', views.task_edit, name='task_edit'),
path('tasks/<int:task_id>/detail/', views.task_detail, name='task_detail'),
path('tasks/<int:task_id>/delete/', views.task_delete, name='task_delete'),
path("", views.project_list, name="project_list"),
path("create/", views.project_create, name="project_create"),
path("<int:pk>/", views.project_detail, name="project_detail"),
path("<int:pk>/edit/", views.project_edit, name="project_edit"),
path("<int:pk>/delete/", views.project_delete, name="project_delete"),
path("<int:project_id>/tasks/create/", views.task_create, name="task_create"),
path(
"tasks/<int:task_id>/update/",
views.task_update_status,
name="task_update_status",
),
path("tasks/<int:task_id>/edit/", views.task_edit, name="task_edit"),
path("tasks/<int:task_id>/detail/", views.task_detail, name="task_detail"),
path("tasks/<int:task_id>/delete/", views.task_delete, name="task_delete"),
]
+69 -56
View File
@@ -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"""
<div class="text-xs text-base-content/60 mt-1 flex items-center gap-1">
<span class="icon-[tabler--calendar] size-3"></span>
{task.due_date.strftime("%b %d")}
</div>'''
</div>"""
html = f'''<div class="task-item card bg-base-100 shadow-sm hover:shadow-md p-3 cursor-move"
draggable="true"
data-task-id="{task.id}"
@@ -104,43 +108,46 @@ def task_create(request, project_id):
</div>
</div>
</div>'''
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"""
<div class="text-xs text-base-content/60 mt-1 flex items-center gap-1">
<span class="icon-[tabler--calendar] size-3"></span>
{task.due_date.strftime("%b %d")}
</div>'''
</div>"""
html = f'''<div class="task-item card bg-base-100 shadow-sm hover:shadow-md p-3 cursor-move"
draggable="true"
data-task-id="{task.id}"
@@ -174,18 +181,20 @@ def task_edit(request, task_id):
</svg>
</div>
<ul tabindex="0" class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-32">
<li><a class="edit-task-btn" data-task-id="{task.id}" data-task-title="{task.title}" data-task-due-date="{task.due_date.strftime('%Y-%m-%d') if task.due_date else ''}">Edit</a></li>
<li><a class="edit-task-btn" data-task-id="{task.id}" data-task-title="{task.title}" data-task-due-date="{task.due_date.strftime("%Y-%m-%d") if task.due_date else ""}">Edit</a></li>
<li><a class="delete-task-btn" data-task-id="{task.id}">Delete</a></li>
</ul>
</div>
</div>
</div>'''
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")
@@ -5,7 +5,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("src", "0004_alter_invoice_due_date"),
]