Complete the implementation of document repository and add invoices

This commit is contained in:
2025-02-09 18:50:39 +01:00
parent 81d251e310
commit 570c00e34f
12 changed files with 351 additions and 169 deletions

View File

@@ -60,10 +60,11 @@ class BookAdmin(SortableAdminBase, admin.ModelAdmin):
"published",
)
autocomplete_fields = ("authors", "publisher", "shop")
readonly_fields = ("creation_time", "updated_time")
readonly_fields = ("invoices", "creation_time", "updated_time")
search_fields = ("title", "publisher__name", "authors__last_name")
list_filter = ("publisher__name", "authors")
def get_fieldsets(self, request, obj=None):
fieldsets = (
(
None,
@@ -107,6 +108,9 @@ class BookAdmin(SortableAdminBase, admin.ModelAdmin):
},
),
)
if obj and obj.invoice.count() > 0:
fieldsets[1][1]["fields"] += ("invoices",)
return fieldsets
def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
@@ -115,6 +119,14 @@ class BookAdmin(SortableAdminBase, admin.ModelAdmin):
)
return form
@admin.display(description="Invoices")
def invoices(self, obj):
html = "<br>".join(
"<a href=\"{}\" target=\"_blank\">{}</a>".format(
i.file.url, i
) for i in obj.invoice.all())
return format_html(html)
@admin.display(description="Publisher")
def get_publisher(self, obj):
return obj.publisher.name
@@ -214,6 +226,7 @@ class CatalogAdmin(SortableAdminBase, admin.ModelAdmin):
search_fields = ("manufacturer__name", "years", "scales__scale")
list_filter = ("manufacturer__name", "publication_year", "scales__scale")
def get_fieldsets(self, request, obj=None):
fieldsets = (
(
None,
@@ -256,6 +269,9 @@ class CatalogAdmin(SortableAdminBase, admin.ModelAdmin):
},
),
)
if obj and obj.invoice.count() > 0:
fieldsets[1][1]["fields"] += ("invoices",)
return fieldsets
def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)

View File

@@ -7,6 +7,7 @@ class Migration(migrations.Migration):
dependencies = [
("bookshelf", "0022_basebook_shop"),
("repository", "0001_initial"),
]
operations = [

View File

@@ -7,6 +7,7 @@ class Migration(migrations.Migration):
dependencies = [
("metadata", "0023_shop"),
("repository", "0001_initial"),
]
operations = [

View File

@@ -29,10 +29,6 @@ class Document(models.Model):
upload_to="files/",
storage=DeduplicatedStorage(),
)
private = models.BooleanField(
default=False,
help_text="Document will be visible only to logged users",
)
creation_time = models.DateTimeField(auto_now_add=True)
updated_time = models.DateTimeField(auto_now=True)
@@ -61,8 +57,17 @@ class Document(models.Model):
'<a href="{0}" target="_blank">Link</a>'.format(self.file.url)
)
class PrivateDocument(Document):
private = models.BooleanField(
default=False,
help_text="Document will be visible only to logged users",
)
objects = PublicManager()
class Meta:
abstract = True
class Image(models.Model):
order = models.PositiveIntegerField(default=0, blank=False, null=False)

View File

@@ -3,7 +3,8 @@ from django.contrib import admin
from ram.admin import publish, unpublish
from repository.models import (
GenericDocument,
BaseBookDocument,
InvoiceDocument,
# BaseBookDocument,
DecoderDocument,
RollingStockDocument
)
@@ -54,6 +55,51 @@ class GenericDocumentAdmin(admin.ModelAdmin):
actions = [publish, unpublish]
@admin.register(InvoiceDocument)
class InvoiceDocumentAdmin(admin.ModelAdmin):
readonly_fields = ("size", "creation_time", "updated_time")
list_display = (
"__str__",
"description",
"size",
"download",
)
search_fields = (
"description",
"file",
)
autocomplete_fields = ("rolling_stock", "book", "catalog")
fieldsets = (
(
None,
{
"fields": (
"description",
"rolling_stock",
"book",
"catalog",
"file",
"size",
)
},
),
(
"Notes",
{"classes": ("collapse",), "fields": ("notes",)},
),
(
"Audit",
{
"classes": ("collapse",),
"fields": (
"creation_time",
"updated_time",
),
},
),
)
# @admin.register(BaseBookDocument)
# class BookDocumentAdmin(admin.ModelAdmin):
# readonly_fields = ("size",)

View File

@@ -66,8 +66,6 @@ def migrate_book(apps, schema_editor):
class Migration(migrations.Migration):
initial = True
dependencies = [
("bookshelf", "0022_basebook_shop"),
("metadata", "0023_shop"),

View File

@@ -0,0 +1,66 @@
# Generated by Django 5.1.4 on 2025-02-09 15:16
import ram.utils
import tinymce.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookshelf", "0023_delete_basebookdocument"),
("repository", "0002_alter_decoderdocument_options_and_more"),
("roster", "0036_delete_rollingstockdocument"),
]
operations = [
migrations.AlterModelOptions(
name="basebookdocument",
options={"verbose_name_plural": "Bookshelf Documents"},
),
migrations.AlterModelOptions(
name="genericdocument",
options={"verbose_name_plural": "Generic documents"},
),
migrations.CreateModel(
name="InvoiceDocument",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("description", models.CharField(blank=True, max_length=128)),
(
"file",
models.FileField(
storage=ram.utils.DeduplicatedStorage(), upload_to="files/"
),
),
("creation_time", models.DateTimeField(auto_now_add=True)),
("updated_time", models.DateTimeField(auto_now=True)),
("private", models.BooleanField(default=True, editable=False)),
("notes", tinymce.models.HTMLField(blank=True)),
(
"book",
models.ManyToManyField(
blank=True, related_name="invoice", to="bookshelf.basebook"
),
),
(
"rolling_stock",
models.ManyToManyField(
blank=True, related_name="invoice", to="roster.rollingstock"
),
),
],
options={
"verbose_name": "Invoice",
"verbose_name_plural": "Invoices",
},
),
]

View File

@@ -0,0 +1,28 @@
# Generated by Django 5.1.4 on 2025-02-09 17:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookshelf", "0023_delete_basebookdocument"),
("repository", "0003_alter_basebookdocument_options_and_more"),
]
operations = [
migrations.AddField(
model_name="invoicedocument",
name="catalog",
field=models.ManyToManyField(
blank=True, related_name="invoice", to="bookshelf.catalog"
),
),
migrations.AlterField(
model_name="invoicedocument",
name="book",
field=models.ManyToManyField(
blank=True, related_name="invoice", to="bookshelf.book"
),
),
]

View File

@@ -1,30 +1,43 @@
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import fields
from tinymce import models as tinymce
from ram.models import Document
from ram.models import PrivateDocument
from metadata.models import Decoder, Tag
from roster.models import RollingStock
from bookshelf.models import BaseBook
from bookshelf.models import Book, Catalog, BaseBook
class GenericDocument(Document):
class GenericDocument(PrivateDocument):
notes = tinymce.HTMLField(blank=True)
tags = models.ManyToManyField(Tag, blank=True, related_name="document")
class Meta:
verbose_name_plural = "Generic Documents"
verbose_name_plural = "Generic documents"
class InvoiceDocument(Document):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = fields.GenericForeignKey("content_type", "object_id")
class InvoiceDocument(PrivateDocument):
private = models.BooleanField(default=True, editable=False)
rolling_stock = models.ManyToManyField(
RollingStock, related_name="invoice",
blank=True
)
book = models.ManyToManyField(
Book, related_name="invoice",
blank=True
)
catalog = models.ManyToManyField(
Catalog, related_name="invoice",
blank=True
)
notes = tinymce.HTMLField(blank=True)
class Meta:
verbose_name = "Invoice"
verbose_name_plural = "Invoices"
class DecoderDocument(Document):
class DecoderDocument(PrivateDocument):
decoder = models.ForeignKey(
Decoder, on_delete=models.CASCADE, related_name="document"
)
@@ -38,13 +51,13 @@ class DecoderDocument(Document):
]
class BaseBookDocument(Document):
class BaseBookDocument(PrivateDocument):
book = models.ForeignKey(
BaseBook, on_delete=models.CASCADE, related_name="document"
)
class Meta:
verbose_name_plural = "Documents"
verbose_name_plural = "Bookshelf Documents"
constraints = [
models.UniqueConstraint(
fields=["book", "file"],
@@ -53,7 +66,7 @@ class BaseBookDocument(Document):
]
class RollingStockDocument(Document):
class RollingStockDocument(PrivateDocument):
rolling_stock = models.ForeignKey(
RollingStock, on_delete=models.CASCADE, related_name="document"
)

View File

@@ -8,8 +8,8 @@ from adminsortable2.admin import SortableAdminBase, SortableInlineAdminMixin
from ram.admin import publish, unpublish
from ram.utils import generate_csv
from portal.utils import get_site_conf
from repository.models import RollingStockDocument
from portal.utils import get_site_conf
from roster.models import (
RollingClass,
RollingClassProperty,
@@ -118,7 +118,7 @@ class RollingStockAdmin(SortableAdminBase, admin.ModelAdmin):
RollingStockJournalInline,
)
autocomplete_fields = ("rolling_class", "shop")
readonly_fields = ("preview", "creation_time", "updated_time")
readonly_fields = ("preview", "invoices", "creation_time", "updated_time")
list_display = (
"__str__",
"address",
@@ -152,6 +152,7 @@ class RollingStockAdmin(SortableAdminBase, admin.ModelAdmin):
'<img src="{}" /> {}'.format(obj.country.flag, obj.country)
)
def get_fieldsets(self, request, obj=None):
fieldsets = (
(
None,
@@ -207,6 +208,9 @@ class RollingStockAdmin(SortableAdminBase, admin.ModelAdmin):
},
),
)
if obj and obj.invoice.count() > 0:
fieldsets[2][1]["fields"] += ("invoices",)
return fieldsets
def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
@@ -215,6 +219,14 @@ class RollingStockAdmin(SortableAdminBase, admin.ModelAdmin):
)
return form
@admin.display(description="Invoices")
def invoices(self, obj):
html = "<br>".join(
"<a href=\"{}\" target=\"_blank\">{}</a>".format(
i.file.url, i
) for i in obj.invoice.all())
return format_html(html)
def download_csv(modeladmin, request, queryset):
header = [
"Name",

View File

@@ -7,6 +7,7 @@ class Migration(migrations.Migration):
dependencies = [
("roster", "0035_alter_rollingstock_shop"),
("repository", "0001_initial"),
]
operations = [

View File

@@ -5,14 +5,12 @@ from django.db import models
from django.urls import reverse
from django.conf import settings
from django.dispatch import receiver
from django.contrib.contenttypes import fields
from tinymce import models as tinymce
from ram.models import BaseModel, Image, PropertyInstance
from ram.utils import DeduplicatedStorage, slugify
from ram.managers import PublicManager
from repository.models import InvoiceDocument
from metadata.models import (
Scale,
Manufacturer,
@@ -115,9 +113,6 @@ class RollingStock(BaseModel):
null=True,
blank=True,
)
invoice = fields.GenericRelation(
app.get_model("repository", "InvoiceDocument"),
InvoiceDocument)
tags = models.ManyToManyField(
Tag, related_name="rolling_stock", blank=True
)