Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Generated by Django 5.2.2 on 2025-06-19 12:00

import django.db.models.deletion
import django_countries.fields
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("sample_plans", "0002_billinginfo_created_billinginfo_updated_at_and_more"),
]

operations = [
migrations.AddField(
model_name="invoice",
name="cancellation_reason",
field=models.TextField(blank=True, verbose_name="Cancellation reason"),
),
migrations.AddField(
model_name="invoice",
name="credit_note_for",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="issued_credit_notes",
to="sample_plans.invoice",
verbose_name="Credit note for",
),
),
migrations.AlterField(
model_name="invoice",
name="type",
field=models.IntegerField(
choices=[
(1, "Invoice"),
(2, "Invoice Duplicate"),
(3, "Order confirmation"),
(4, "Credit note"),
],
db_index=True,
default=1,
),
),
]
175 changes: 173 additions & 2 deletions plans/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@

from django.contrib import admin
from django.contrib.auth import get_user_model
from django.http import HttpResponseRedirect
from django.template.response import TemplateResponse
from django.urls import reverse
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _
from ordered_model.admin import OrderedModelAdmin

from .forms import PartialCreditNoteForm

from plans.base.models import (
AbstractBillingInfo,
AbstractInvoice,
Expand Down Expand Up @@ -211,6 +215,80 @@ def queryset(self, request):
)


def cancel_selected_invoices(modeladmin, request, queryset):
for invoice in queryset:
try:
invoice.cancel_invoice()
modeladmin.message_user(
request, f"Invoice {invoice.full_number} cancelled successfully."
)
except Exception as e:
modeladmin.message_user(
request, f"Could not cancel {invoice.full_number}: {e}", level="ERROR"
)


cancel_selected_invoices.short_description = _("Cancel and issue credit note")


def create_partial_credit_note(modeladmin, request, queryset):
"""Create partial credit notes with custom amounts"""
if queryset.count() != 1:
modeladmin.message_user(
request,
"Please select exactly one invoice to create a partial credit note.",
level="ERROR",
)
return

invoice = queryset.first()
if invoice.type != invoice.INVOICE_TYPES.INVOICE:
modeladmin.message_user(
request,
"Only regular invoices can have partial credit notes.",
level="ERROR",
)
return

if request.method == "POST" and "apply" in request.POST:
form = PartialCreditNoteForm(request.POST)
if form.is_valid():
credit_note = invoice.create_partial_credit_note(
form.cleaned_data["net_amount"],
form.cleaned_data["tax_amount"],
form.cleaned_data["reason"],
)
modeladmin.message_user(
request,
"Partial credit note %s created successfully for invoice %s."
% (credit_note.full_number, invoice.full_number),
)
return HttpResponseRedirect(reverse("admin:plans_invoice_changelist"))
else:
# Initialize form with default values from invoice
initial_data = {
"net_amount": invoice.total_net,
"tax_amount": invoice.tax_total,
"reason": "",
}
form = PartialCreditNoteForm(initial=initial_data)

context = {
"title": "Create Partial Credit Note for Invoice %s" % invoice.full_number,
"invoice": invoice,
"opts": modeladmin.model._meta,
"has_view_permission": modeladmin.has_view_permission(request),
"form": form,
}

return TemplateResponse(
request, "admin/plans/invoice/partial_credit_note_form.html", context
)


create_partial_credit_note.short_description = _("Create partial credit note")


class InvoiceAdmin(admin.ModelAdmin):
search_fields = ("full_number", "buyer_tax_number", "user__username", "user__email")
list_filter = (
Expand All @@ -231,10 +309,103 @@ class InvoiceAdmin(admin.ModelAdmin):
"buyer_city",
"buyer_tax_number",
)
readonly_fields = ("created", "updated_at")
readonly_fields = (
"created",
"updated_at",
"full_number",
"number",
"total_net",
"tax_total",
"total",
)
list_display_links = list_display
list_select_related = True
raw_id_fields = ("user", "order")
raw_id_fields = ("user", "order", "credit_note_for")
actions = (cancel_selected_invoices, create_partial_credit_note)
fieldsets = (
(
_("Invoice Details"),
{
"fields": (
"type",
"full_number",
"number",
"user",
"order",
"credit_note_for",
"cancellation_reason",
)
},
),
(
_("Dates"),
{
"fields": (
"issued",
"selling_date",
"payment_date",
"issued_duplicate",
"created",
"updated_at",
)
},
),
(
_("Billing Details"),
{
"fields": (
"item_description",
"quantity",
"unit_price_net",
"tax",
"rebate",
"currency",
"total_net",
"tax_total",
"total",
)
},
),
(
_("Buyer Details"),
{
"fields": (
"buyer_name",
"buyer_street",
"buyer_zipcode",
"buyer_city",
"buyer_country",
"buyer_tax_number",
)
},
),
(
_("Shipping Details"),
{
"fields": (
"require_shipment",
"shipping_name",
"shipping_street",
"shipping_zipcode",
"shipping_city",
"shipping_country",
)
},
),
(
_("Issuer Details"),
{
"fields": (
"issuer_name",
"issuer_street",
"issuer_zipcode",
"issuer_city",
"issuer_country",
"issuer_tax_number",
)
},
),
)


class RecurringPlanInline(admin.StackedInline):
Expand Down
Loading
Loading