- update pyproject.toml and models/clients.py

This commit is contained in:
sankalp committed 2025-12-09 14:28:35 +05:45
1 parent 8b50ff5412
commit 4afe6eaa3a
8 files changed
+590 -11

No files matched your search

+34 -10
View File
@@ -1,19 +1,32 @@
from django.contrib import admin
from django.forms import ModelForm
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _
from phonenumber_field.formfields import SplitPhoneNumberField
# Assuming these imports are correct based on your setup
from src.admin.base import admin_site
from src.models.clients import Client
class ClientAdminForm(ModelForm):
phone = SplitPhoneNumberField(required=True)
class Meta:
model = Client
fields = "__all__"
@admin.register(Client, site=admin_site)
class ClientAdmin(admin.ModelAdmin):
form = ClientAdminForm
list_display = (
"name_display",
"short_code",
"company_name",
"email",
"phone",
"formatted_phone",
"created_at",
"updated_at",
)
@@ -42,7 +55,7 @@ class ClientAdmin(admin.ModelAdmin):
_("Timestamps"),
{
"fields": ("created_at", "updated_at"),
"classes": ("collapse",), # collapsible section for cleaner UI
"classes": ("collapse",),
},
),
)
@@ -59,15 +72,26 @@ class ClientAdmin(admin.ModelAdmin):
name_display.short_description = "Client"
def formatted_phone(self, obj):
"""Displays the phone number in International format on the list view."""
if obj.phone:
# Uses the .as_international property from the PhoneNumber wrapper
return obj.phone.as_international
return "—"
formatted_phone.short_description = "Phone"
def save_model(self, request, obj, form, change):
"""
Ensure short_code generation happens gracefully and show a message.
"""
if not obj.short_code:
generated = obj.generate_short_code()
obj.short_code = generated
# Check if the short_code was empty before this save operation
short_code_was_empty = not obj.short_code
# Call the parent save method, which calls obj.save() and triggers
# the short_code generation logic you put in the model.
super().save_model(request, obj, form, change)
# Check if it was generated and display a message
if short_code_was_empty and obj.short_code:
self.message_user(
request,
f"Short code '{generated}' was automatically generated for client '{obj.name}'.",
f"Short code '{obj.short_code}' was automatically generated for client '{obj.name}'.",
)
super().save_model(request, obj, form, change)
+521
View File
@@ -0,0 +1,521 @@
# Generated by Django 6.0 on 2025-12-09 07:58
import django.db.models.deletion
import phonenumber_field.modelfields
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="Note",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("title", models.CharField(db_index=True, max_length=255)),
("content", models.TextField()),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
],
options={
"ordering": ["-created_at"],
},
),
migrations.CreateModel(
name="Client",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("name", models.CharField(db_index=True, max_length=255)),
("short_code", models.CharField(blank=True, max_length=6, unique=True)),
("email", models.EmailField(max_length=254)),
(
"phone",
phonenumber_field.modelfields.PhoneNumberField(
max_length=128, region=None
),
),
("address", models.TextField()),
("company_name", models.CharField(blank=True, max_length=255)),
],
options={
"ordering": ["name"],
"indexes": [
models.Index(fields=["name"], name="src_client_name_ffc910_idx"),
models.Index(
fields=["short_code"], name="src_client_short_c_058134_idx"
),
],
},
),
migrations.CreateModel(
name="Invoice",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"invoice_number",
models.CharField(editable=False, max_length=20, unique=True),
),
("amount", models.DecimalField(decimal_places=2, max_digits=10)),
("date_issued", models.DateField(auto_now_add=True)),
("due_date", models.DateField(auto_now=True)),
(
"status",
models.CharField(
choices=[
("DRAFT", "Draft"),
("SENT", "Sent"),
("PAID", "Paid"),
("OVERDUE", "Overdue"),
("CANCELLED", "Cancelled"),
],
default="DRAFT",
max_length=10,
),
),
(
"client",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="invoices",
to="src.client",
),
),
],
options={
"abstract": False,
},
),
migrations.CreateModel(
name="Payment",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("amount", models.DecimalField(decimal_places=2, max_digits=10)),
("date", models.DateField()),
("method", models.CharField(blank=True, max_length=50)),
("transaction_id", models.CharField(blank=True, max_length=100)),
(
"invoice",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="payments",
to="src.invoice",
),
),
],
),
migrations.CreateModel(
name="Project",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("name", models.CharField(db_index=True, max_length=255)),
("description", models.TextField()),
("start_date", models.DateField(blank=True, null=True)),
("deadline", models.DateField(blank=True, null=True)),
(
"status",
models.CharField(
choices=[
("PLANNING", "Planning"),
("IN_PROGRESS", "In Progress"),
("ON_HOLD", "On Hold"),
("COMPLETED", "Completed"),
("CANCELLED", "Cancelled"),
],
default="PLANNING",
max_length=20,
),
),
(
"budget",
models.DecimalField(
blank=True, decimal_places=2, max_digits=10, null=True
),
),
(
"client",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="projects",
to="src.client",
),
),
],
options={
"ordering": ["-created_at"],
},
),
migrations.CreateModel(
name="Milestone",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("title", models.CharField(max_length=255)),
("due_date", models.DateField(blank=True, null=True)),
("is_completed", models.BooleanField(default=False)),
(
"project",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="milestones",
to="src.project",
),
),
],
options={
"ordering": ["due_date"],
},
),
migrations.AddField(
model_name="invoice",
name="project",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="invoices",
to="src.project",
),
),
migrations.CreateModel(
name="Service",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"service_type",
models.CharField(
choices=[
("DOMAIN", "Domain"),
("HOSTING", "Hosting"),
("VPS", "VPS"),
("SSL", "SSL"),
("OTHER", "Other"),
],
max_length=20,
),
),
(
"name",
models.CharField(
db_index=True,
help_text="e.g. example.com or VPS - 4GB",
max_length=255,
),
),
("provider", models.CharField(blank=True, max_length=255)),
(
"cost",
models.DecimalField(decimal_places=2, default=0.0, max_digits=10),
),
(
"renewal_price",
models.DecimalField(decimal_places=2, default=0.0, max_digits=10),
),
("start_date", models.DateField(blank=True, null=True)),
("expiry_date", models.DateField(blank=True, null=True)),
("auto_renew", models.BooleanField(default=False)),
(
"client",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="services",
to="src.client",
),
),
],
options={
"ordering": ["-created_at"],
},
),
migrations.CreateModel(
name="Expense",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("description", models.CharField(max_length=255)),
("amount", models.DecimalField(decimal_places=2, max_digits=10)),
("date", models.DateField()),
("category", models.CharField(blank=True, max_length=100)),
("created_at", models.DateTimeField(auto_now_add=True)),
(
"service",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="expenses",
to="src.service",
),
),
],
),
migrations.CreateModel(
name="Credential",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("url", models.URLField(blank=True)),
("username", models.CharField(blank=True, max_length=255)),
("password", models.CharField(blank=True, max_length=255)),
("notes", models.TextField(blank=True)),
(
"service",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="credentials",
to="src.service",
),
),
],
options={
"ordering": ["service"],
},
),
migrations.CreateModel(
name="Task",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("title", models.CharField(db_index=True, max_length=255)),
("description", models.TextField(blank=True)),
(
"status",
models.CharField(
choices=[
("TODO", "To Do"),
("IN_PROGRESS", "In Progress"),
("REVIEW", "Review"),
("DONE", "Done"),
],
default="TODO",
max_length=20,
),
),
("due_date", models.DateField(blank=True, null=True)),
(
"project",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="tasks",
to="src.project",
),
),
],
options={
"ordering": ["status", "due_date"],
},
),
migrations.CreateModel(
name="TimeEntry",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("description", models.CharField(max_length=255)),
("start_time", models.DateTimeField()),
("end_time", models.DateTimeField(blank=True, null=True)),
(
"duration",
models.DurationField(blank=True, editable=False, null=True),
),
(
"project",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="time_entries",
to="src.project",
),
),
(
"task",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="time_entries",
to="src.task",
),
),
],
options={
"ordering": ["-start_time"],
},
),
migrations.AddIndex(
model_name="project",
index=models.Index(fields=["status"], name="src_project_status_58968d_idx"),
),
migrations.AddIndex(
model_name="project",
index=models.Index(
fields=["client"], name="src_project_client__f7a74d_idx"
),
),
migrations.AddIndex(
model_name="milestone",
index=models.Index(
fields=["project"], name="src_milesto_project_a28344_idx"
),
),
migrations.AddIndex(
model_name="milestone",
index=models.Index(
fields=["due_date"], name="src_milesto_due_dat_c4307d_idx"
),
),
migrations.AddIndex(
model_name="service",
index=models.Index(
fields=["service_type"], name="src_service_service_a91261_idx"
),
),
migrations.AddIndex(
model_name="service",
index=models.Index(
fields=["client"], name="src_service_client__283c36_idx"
),
),
migrations.AddIndex(
model_name="service",
index=models.Index(
fields=["expiry_date"], name="src_service_expiry__bec77a_idx"
),
),
migrations.AddIndex(
model_name="credential",
index=models.Index(
fields=["service"], name="src_credent_service_0fdfd4_idx"
),
),
migrations.AddIndex(
model_name="task",
index=models.Index(fields=["project"], name="src_task_project_1c66bb_idx"),
),
migrations.AddIndex(
model_name="task",
index=models.Index(fields=["status"], name="src_task_status_4e81ec_idx"),
),
migrations.AddIndex(
model_name="task",
index=models.Index(fields=["due_date"], name="src_task_due_dat_68af9f_idx"),
),
migrations.AddIndex(
model_name="timeentry",
index=models.Index(
fields=["start_time"], name="src_timeent_start_t_0e6952_idx"
),
),
migrations.AddIndex(
model_name="timeentry",
index=models.Index(
fields=["end_time"], name="src_timeent_end_tim_faabc6_idx"
),
),
migrations.AddIndex(
model_name="timeentry",
index=models.Index(
fields=["project"], name="src_timeent_project_142fd7_idx"
),
),
migrations.AddIndex(
model_name="timeentry",
index=models.Index(fields=["task"], name="src_timeent_task_id_c75729_idx"),
),
]
+21
View File
@@ -0,0 +1,21 @@
# Generated by Django 6.0 on 2025-12-09 08:21
import phonenumber_field.modelfields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("src", "0001_initial"),
]
operations = [
migrations.AlterField(
model_name="client",
name="phone",
field=phonenumber_field.modelfields.PhoneNumberField(
max_length=128, region="NP"
),
),
]
View File
Whitespace-only changes.
+1 -1
View File
@@ -9,7 +9,7 @@ class Client(TimeStampedModel):
name = models.CharField(max_length=255, db_index=True)
short_code = models.CharField(max_length=6, unique=True, blank=True)
email = models.EmailField()
phone = PhoneNumberField()
phone = PhoneNumberField(region="NP")
address = models.TextField()
company_name = models.CharField(max_length=255, blank=True)
+1
View File
@@ -23,6 +23,7 @@ THIRD_PARTY_APPS = [
"django_htmx",
"nepali_datetime_field",
"phonenumber_field",
"babel",
]
USER_DEFINED_APPS = [