Add invoice model with metadata and tags; update search and templates for invoice handling
Progress #44
This commit is contained in:
parent
608da4be55
commit
c153000d3d
8 changed files with 87 additions and 9 deletions
|
@ -7,3 +7,12 @@ def get_icons():
|
|||
data = json.loads(request.urlopen(url).read())
|
||||
|
||||
return [i.removesuffix("-line") for i in data.keys() if i.endswith("-line")]
|
||||
|
||||
|
||||
def pdf_outline_to_str(outline):
|
||||
return " ".join(
|
||||
(
|
||||
dest.title if not isinstance(dest, list) else pdf_outline_to_str(dest)
|
||||
for dest in outline
|
||||
)
|
||||
)
|
||||
|
|
|
@ -41,7 +41,13 @@
|
|||
{% transaction_table transactions n_max=8 transactions_url=t_url %}
|
||||
</section>
|
||||
{% endif %}
|
||||
{% if not accounts and not categories and not transactions %}
|
||||
{% if invoices %}
|
||||
<section>
|
||||
<h3>{% translate "Invoices" %}</h3>
|
||||
{% invoice_table invoices=invoices %}
|
||||
</section>
|
||||
{% endif %}
|
||||
{% if not accounts and not categories and not transactions and not invoices %}
|
||||
<p>{% translate "No results found." %}</p>
|
||||
{% endif %}
|
||||
{% endblock body %}
|
||||
|
|
|
@ -26,5 +26,6 @@ class SearchView(LoginRequiredMixin, TemplateView):
|
|||
context["transactions"] = _user.transaction_set.search(self.kwargs["search"])
|
||||
context["accounts"] = _user.account_set.search(self.kwargs["search"])
|
||||
context["categories"] = _user.category_set.search(self.kwargs["search"])
|
||||
context["invoices"] = _user.invoice_set.search(self.kwargs["search"])[:10]
|
||||
|
||||
return context
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
# Generated by Django 4.2.7 on 2025-01-05 14:51
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("transaction", "0004_remove_transaction_account"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="invoice",
|
||||
name="metadata",
|
||||
field=models.TextField(blank=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="invoice",
|
||||
name="tags",
|
||||
field=models.TextField(blank=True),
|
||||
),
|
||||
]
|
|
@ -8,7 +8,9 @@ from django.db import models
|
|||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from main.models import NummiModel, NummiQuerySet
|
||||
from main.utils import pdf_outline_to_str
|
||||
from media.utils import get_path
|
||||
from pypdf import PdfReader
|
||||
from statement.models import Statement
|
||||
|
||||
|
||||
|
@ -74,6 +76,13 @@ class Transaction(NummiModel):
|
|||
verbose_name_plural = _("Transactions")
|
||||
|
||||
|
||||
class InvoiceQuerySet(NummiQuerySet):
|
||||
fields = {
|
||||
"metadata": "B",
|
||||
"tags": "C",
|
||||
}
|
||||
|
||||
|
||||
class Invoice(NummiModel):
|
||||
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
|
||||
name = models.CharField(
|
||||
|
@ -88,12 +97,35 @@ class Invoice(NummiModel):
|
|||
transaction = models.ForeignKey(
|
||||
Transaction, on_delete=models.CASCADE, editable=False
|
||||
)
|
||||
metadata = models.TextField(blank=True)
|
||||
tags = models.TextField(blank=True)
|
||||
|
||||
objects = InvoiceQuerySet.as_manager()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if Invoice.objects.filter(id=self.id).exists():
|
||||
_prever = Invoice.objects.get(id=self.id)
|
||||
if _prever.file and _prever.file != self.file:
|
||||
Path(_prever.file.path).unlink(missing_ok=True)
|
||||
|
||||
reader = PdfReader(self.file)
|
||||
|
||||
self.metadata = " ".join(
|
||||
(
|
||||
m
|
||||
for m in (
|
||||
reader.metadata.title,
|
||||
reader.metadata.author,
|
||||
reader.metadata.subject,
|
||||
)
|
||||
if m
|
||||
)
|
||||
)
|
||||
|
||||
_tags = pdf_outline_to_str(reader.outline)
|
||||
_tags += " ".join((page.extract_text() for page in reader.pages))
|
||||
self.tags = " ".join((tag for tag in _tags.split() if len(tag) >= 3))
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
|
|
|
@ -3,14 +3,19 @@
|
|||
<ul class="invoices">
|
||||
{% for invoice in invoices %}
|
||||
<li>
|
||||
{% if not transaction %}<span>{{ invoice.transaction.name }}</span>{% endif %}
|
||||
<a class="title" href="{{ invoice.file.url }}">{{ "file"|remix }}{{ invoice.name }} [{{ invoice.file|extension }}]</a>
|
||||
<a href="{{ invoice.get_absolute_url }}">{{ "file-edit"|remix }}{% translate "Edit" %}</a>
|
||||
{% if transaction %}
|
||||
<a href="{{ invoice.get_absolute_url }}">{{ "file-edit"|remix }}{% translate "Edit" %}</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="new">
|
||||
<span>
|
||||
<a href="{% url "new_invoice" transaction.pk %}">{{ "file-add"|remix }}{% translate "New invoice" %}</a>
|
||||
</span>
|
||||
</li>
|
||||
{% if transaction %}
|
||||
<li class="new">
|
||||
<span>
|
||||
<a href="{% url "new_invoice" transaction.pk %}">{{ "file-add"|remix }}{% translate "New invoice" %}</a>
|
||||
</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -36,10 +36,12 @@ def transaction_filters(context, **kwargs):
|
|||
|
||||
|
||||
@register.inclusion_tag("transaction/invoice_table.html")
|
||||
def invoice_table(transaction, **kwargs):
|
||||
def invoice_table(transaction=None, **kwargs):
|
||||
if transaction:
|
||||
kwargs.setdefault("invoices", transaction.invoice_set.all())
|
||||
|
||||
return kwargs | {
|
||||
"transaction": transaction,
|
||||
"invoices": transaction.invoice_set.all(),
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ depends=(
|
|||
"python-toml"
|
||||
"python-psycopg"
|
||||
"python-dateutil"
|
||||
"python-pypdf"
|
||||
)
|
||||
makedepends=("git")
|
||||
optdepends=("postgresql: database")
|
||||
|
|
Loading…
Add table
Reference in a new issue