diff --git a/ram/bookshelf/admin.py b/ram/bookshelf/admin.py index 9d0bcd1..0e8a88b 100644 --- a/ram/bookshelf/admin.py +++ b/ram/bookshelf/admin.py @@ -8,7 +8,7 @@ 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 BaseBookDocument +from repository.models import BookDocument, CatalogDocument from bookshelf.models import ( BaseBookProperty, BaseBookImage, @@ -28,13 +28,6 @@ class BookImageInline(SortableInlineAdminMixin, admin.TabularInline): verbose_name = "Image" -class BookDocInline(admin.TabularInline): - model = BaseBookDocument - min_num = 0 - extra = 0 - classes = ["collapse"] - - class BookPropertyInline(admin.TabularInline): model = BaseBookProperty min_num = 0 @@ -44,6 +37,17 @@ class BookPropertyInline(admin.TabularInline): verbose_name_plural = "Properties" +class BookDocInline(admin.TabularInline): + model = BookDocument + min_num = 0 + extra = 0 + classes = ["collapse"] + + +class CatalogDocInline(BookDocInline): + model = CatalogDocument + + @admin.register(Book) class BookAdmin(SortableAdminBase, admin.ModelAdmin): inlines = ( @@ -64,53 +68,50 @@ class BookAdmin(SortableAdminBase, admin.ModelAdmin): search_fields = ("title", "publisher__name", "authors__last_name") list_filter = ("publisher__name", "authors") - def get_fieldsets(self, request, obj=None): - fieldsets = ( - ( - None, - { - "fields": ( - "published", - "title", - "authors", - "publisher", - "ISBN", - "language", - "number_of_pages", - "publication_year", - "description", - "tags", - ) - }, - ), - ( - "Purchase data", - { - "fields": ( - "shop", - "purchase_date", - "price", - ) - }, - ), - ( - "Notes", - {"classes": ("collapse",), "fields": ("notes",)}, - ), - ( - "Audit", - { - "classes": ("collapse",), - "fields": ( - "creation_time", - "updated_time", - ), - }, - ), - ) - if obj and obj.invoice.count() > 0: - fieldsets[1][1]["fields"] += ("invoices",) - return fieldsets + fieldsets = ( + ( + None, + { + "fields": ( + "published", + "title", + "authors", + "publisher", + "ISBN", + "language", + "number_of_pages", + "publication_year", + "description", + "tags", + ) + }, + ), + ( + "Purchase data", + { + "fields": ( + "shop", + "purchase_date", + "price", + "invoices", + ) + }, + ), + ( + "Notes", + {"classes": ("collapse",), "fields": ("notes",)}, + ), + ( + "Audit", + { + "classes": ("collapse",), + "fields": ( + "creation_time", + "updated_time", + ), + }, + ), + ) def get_form(self, request, obj=None, **kwargs): form = super().get_form(request, obj, **kwargs) @@ -121,10 +122,13 @@ class BookAdmin(SortableAdminBase, admin.ModelAdmin): @admin.display(description="Invoices") def invoices(self, obj): - html = "
".join( - "{}".format( - i.file.url, i - ) for i in obj.invoice.all()) + if obj.invoice.exists(): + html = "
".join( + "{}".format( + i.file.url, i + ) for i in obj.invoice.all()) + else: + html = "-" return format_html(html) @admin.display(description="Publisher") @@ -212,7 +216,7 @@ class CatalogAdmin(SortableAdminBase, admin.ModelAdmin): inlines = ( BookPropertyInline, BookImageInline, - BookDocInline, + CatalogDocInline, ) list_display = ( "__str__", @@ -222,56 +226,54 @@ class CatalogAdmin(SortableAdminBase, admin.ModelAdmin): "published", ) autocomplete_fields = ("manufacturer",) - readonly_fields = ("creation_time", "updated_time") + readonly_fields = ("invoices", "creation_time", "updated_time") search_fields = ("manufacturer__name", "years", "scales__scale") list_filter = ("manufacturer__name", "publication_year", "scales__scale") - def get_fieldsets(self, request, obj=None): - fieldsets = ( - ( - None, - { - "fields": ( - "published", - "manufacturer", - "years", - "scales", - "ISBN", - "language", - "number_of_pages", - "publication_year", - "description", - "tags", - ) - }, - ), - ( - "Purchase data", - { - "fields": ( - "purchase_date", - "price", - ) - }, - ), - ( - "Notes", - {"classes": ("collapse",), "fields": ("notes",)}, - ), - ( - "Audit", - { - "classes": ("collapse",), - "fields": ( - "creation_time", - "updated_time", - ), - }, - ), - ) - if obj and obj.invoice.count() > 0: - fieldsets[1][1]["fields"] += ("invoices",) - return fieldsets + fieldsets = ( + ( + None, + { + "fields": ( + "published", + "manufacturer", + "years", + "scales", + "ISBN", + "language", + "number_of_pages", + "publication_year", + "description", + "tags", + ) + }, + ), + ( + "Purchase data", + { + "fields": ( + "shop", + "purchase_date", + "price", + "invoices", + ) + }, + ), + ( + "Notes", + {"classes": ("collapse",), "fields": ("notes",)}, + ), + ( + "Audit", + { + "classes": ("collapse",), + "fields": ( + "creation_time", + "updated_time", + ), + }, + ), + ) def get_form(self, request, obj=None, **kwargs): form = super().get_form(request, obj, **kwargs) @@ -280,6 +282,17 @@ class CatalogAdmin(SortableAdminBase, admin.ModelAdmin): ) return form + @admin.display(description="Invoices") + def invoices(self, obj): + if obj.invoice.exists(): + html = "
".join( + "{}".format( + i.file.url, i + ) for i in obj.invoice.all()) + else: + html = "-" + return format_html(html) + def download_csv(modeladmin, request, queryset): header = [ "Catalog", diff --git a/ram/ram/__init__.py b/ram/ram/__init__.py index ec8f297..909b501 100644 --- a/ram/ram/__init__.py +++ b/ram/ram/__init__.py @@ -1,4 +1,4 @@ from ram.utils import git_suffix -__version__ = "0.16.9" +__version__ = "0.17.0" __version__ += git_suffix(__file__) diff --git a/ram/ram/models.py b/ram/ram/models.py index f389de3..ed71aac 100644 --- a/ram/ram/models.py +++ b/ram/ram/models.py @@ -27,7 +27,6 @@ class Document(models.Model): description = models.CharField(max_length=128, blank=True) file = models.FileField( upload_to="files/", - storage=DeduplicatedStorage(), ) creation_time = models.DateTimeField(auto_now_add=True) updated_time = models.DateTimeField(auto_now=True) diff --git a/ram/repository/admin.py b/ram/repository/admin.py index c65bb54..546e311 100644 --- a/ram/repository/admin.py +++ b/ram/repository/admin.py @@ -4,7 +4,8 @@ from ram.admin import publish, unpublish from repository.models import ( GenericDocument, InvoiceDocument, - # BaseBookDocument, + BookDocument, + CatalogDocument, DecoderDocument, RollingStockDocument ) @@ -61,23 +62,32 @@ class InvoiceDocumentAdmin(admin.ModelAdmin): list_display = ( "__str__", "description", + "date", + "shop", "size", "download", ) search_fields = ( + "rolling_stock__manufacturer__name", + "rolling_stock__item_number", + "book__title", + "catalog__manufacturer__name", + "shop__name", "description", "file", ) - autocomplete_fields = ("rolling_stock", "book", "catalog") + autocomplete_fields = ("rolling_stock", "book", "catalog", "shop") fieldsets = ( ( None, { "fields": ( - "description", "rolling_stock", "book", "catalog", + "description", + "date", + "shop", "file", "size", ) @@ -100,39 +110,72 @@ class InvoiceDocumentAdmin(admin.ModelAdmin): ) -# @admin.register(BaseBookDocument) -# class BookDocumentAdmin(admin.ModelAdmin): -# readonly_fields = ("size",) -# list_display = ( -# "__str__", -# # FIXME -# "book__book", -# "book__catalog", -# "description", -# "private", -# "size", -# "download", -# ) -# search_fields = ( -# "book__title", -# "description", -# "file", -# ) -# fieldsets = ( -# ( -# None, -# { -# "fields": ( -# "private", -# # FIXME -# "description", -# "file", -# "size", -# ) -# }, -# ), -# ) -# actions = [publish, unpublish] +@admin.register(BookDocument) +class BookDocumentAdmin(admin.ModelAdmin): + readonly_fields = ("size",) + list_display = ( + "__str__", + "book", + "description", + "private", + "size", + "download", + ) + search_fields = ( + "book__title", + "description", + "file", + ) + autocomplete_fields = ("book",) + fieldsets = ( + ( + None, + { + "fields": ( + "private", + "book", + "description", + "file", + "size", + ) + }, + ), + ) + actions = [publish, unpublish] + + +@admin.register(CatalogDocument) +class CatalogDocumentAdmin(admin.ModelAdmin): + readonly_fields = ("size",) + list_display = ( + "__str__", + "catalog", + "description", + "private", + "size", + "download", + ) + search_fields = ( + "catalog__title", + "description", + "file", + ) + autocomplete_fields = ("catalog",) + fieldsets = ( + ( + None, + { + "fields": ( + "private", + "catalog", + "description", + "file", + "size", + ) + }, + ), + ) + actions = [publish, unpublish] @admin.register(DecoderDocument) diff --git a/ram/repository/migrations/0001_initial.py b/ram/repository/migrations/0001_initial.py index 0aff19b..06c5e9a 100644 --- a/ram/repository/migrations/0001_initial.py +++ b/ram/repository/migrations/0001_initial.py @@ -53,15 +53,26 @@ def migrate_rollingstock(apps, schema_editor): def migrate_book(apps, schema_editor): book_document = apps.get_model("bookshelf", "BaseBookDocument") book_document_new = apps.get_model("repository", "BaseBookDocument") + catalog_document_new = apps.get_model("repository", "CatalogDocument") for d in book_document.objects.all(): - book_document_new.objects.create( - book=d.book, - description=d.description, - file=d.file, - private=d.private, - creation_time=d.creation_time, - updated_time=d.updated_time, - ) + if hasattr(d.book, "book"): + book_document_new.objects.create( + book=d.book.book, + description=d.description, + file=d.file, + private=d.private, + creation_time=d.creation_time, + updated_time=d.updated_time, + ) + else: + catalog_document_new.objects.create( + catalog=d.book.catalog, + description=d.description, + file=d.file, + private=d.private, + creation_time=d.creation_time, + updated_time=d.updated_time, + ) class Migration(migrations.Migration): @@ -115,6 +126,98 @@ class Migration(migrations.Migration): "abstract": False, }, ), + migrations.CreateModel( + name="BookDocument", + 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=False, + help_text="Document will be visible only to logged users", + ), + ), + ( + "book", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="document", + to="bookshelf.book", + ), + ), + ], + options={ + "verbose_name_plural": "Book documents", + "constraints": [ + models.UniqueConstraint( + fields=("book", "file"), name="unique_book_file" + ) + ], + }, + ), + migrations.CreateModel( + name="CatalogDocument", + 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=False, + help_text="Document will be visible only to logged users", + ), + ), + ( + "catalog", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="document", + to="bookshelf.catalog", + ), + ), + ], + options={ + "verbose_name_plural": "Catalog documents", + "constraints": [ + models.UniqueConstraint( + fields=("catalog", "file"), name="unique_catalog_file" + ) + ], + }, + ), migrations.CreateModel( name="GenericDocument", fields=[ diff --git a/ram/repository/migrations/0002_alter_decoderdocument_options_and_more.py b/ram/repository/migrations/0002_alter_decoderdocument_options_and_more.py deleted file mode 100644 index 4b65285..0000000 --- a/ram/repository/migrations/0002_alter_decoderdocument_options_and_more.py +++ /dev/null @@ -1,80 +0,0 @@ -# Generated by Django 5.1.4 on 2025-02-09 13:48 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("bookshelf", "0023_delete_basebookdocument"), - ( - "metadata", - "0024_remove_genericdocument_tags_delete_decoderdocument_and_more", - ), - ("repository", "0001_initial"), - ("roster", "0036_delete_rollingstockdocument"), - ] - - operations = [ - migrations.AlterModelOptions( - name="decoderdocument", - options={}, - ), - migrations.AlterModelOptions( - name="rollingstockdocument", - options={}, - ), - migrations.AlterField( - model_name="basebookdocument", - name="book", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="document", - to="bookshelf.basebook", - ), - ), - migrations.AlterField( - model_name="decoderdocument", - name="decoder", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="document", - to="metadata.decoder", - ), - ), - migrations.AlterField( - model_name="genericdocument", - name="tags", - field=models.ManyToManyField( - blank=True, related_name="document", to="metadata.tag" - ), - ), - migrations.AlterField( - model_name="rollingstockdocument", - name="rolling_stock", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="document", - to="roster.rollingstock", - ), - ), - migrations.AddConstraint( - model_name="basebookdocument", - constraint=models.UniqueConstraint( - fields=("book", "file"), name="unique_book_file" - ), - ), - migrations.AddConstraint( - model_name="decoderdocument", - constraint=models.UniqueConstraint( - fields=("decoder", "file"), name="unique_decoder_file" - ), - ), - migrations.AddConstraint( - model_name="rollingstockdocument", - constraint=models.UniqueConstraint( - fields=("rolling_stock", "file"), name="unique_stock_file" - ), - ), - ] diff --git a/ram/repository/migrations/0002_invoicedocument_remove_basebookdocument_book_and_more.py b/ram/repository/migrations/0002_invoicedocument_remove_basebookdocument_book_and_more.py new file mode 100644 index 0000000..1716fa4 --- /dev/null +++ b/ram/repository/migrations/0002_invoicedocument_remove_basebookdocument_book_and_more.py @@ -0,0 +1,157 @@ +# Generated by Django 5.1.4 on 2025-02-09 23:10 + +import django.db.models.deletion +import tinymce.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookshelf", "0023_delete_basebookdocument"), + ( + "metadata", + "0024_remove_genericdocument_tags_delete_decoderdocument_and_more", + ), + ("repository", "0001_initial"), + ("roster", "0036_delete_rollingstockdocument"), + ] + + operations = [ + 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)), + ("creation_time", models.DateTimeField(auto_now_add=True)), + ("updated_time", models.DateTimeField(auto_now=True)), + ("private", models.BooleanField(default=True, editable=False)), + ("date", models.DateField()), + ("file", models.FileField(upload_to="files/invoices/")), + ("notes", tinymce.models.HTMLField(blank=True)), + ], + options={ + "abstract": False, + }, + ), + migrations.RemoveField( + model_name="basebookdocument", + name="book", + ), + migrations.AlterModelOptions( + name="decoderdocument", + options={}, + ), + migrations.AlterModelOptions( + name="genericdocument", + options={"verbose_name_plural": "Generic documents"}, + ), + migrations.AlterModelOptions( + name="rollingstockdocument", + options={}, + ), + migrations.AlterField( + model_name="bookdocument", + name="file", + field=models.FileField(upload_to="files/"), + ), + migrations.AlterField( + model_name="catalogdocument", + name="file", + field=models.FileField(upload_to="files/"), + ), + migrations.AlterField( + model_name="decoderdocument", + name="decoder", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="document", + to="metadata.decoder", + ), + ), + migrations.AlterField( + model_name="decoderdocument", + name="file", + field=models.FileField(upload_to="files/"), + ), + migrations.AlterField( + model_name="genericdocument", + name="file", + field=models.FileField(upload_to="files/"), + ), + migrations.AlterField( + model_name="genericdocument", + name="tags", + field=models.ManyToManyField( + blank=True, related_name="document", to="metadata.tag" + ), + ), + migrations.AlterField( + model_name="rollingstockdocument", + name="file", + field=models.FileField(upload_to="files/"), + ), + migrations.AlterField( + model_name="rollingstockdocument", + name="rolling_stock", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="document", + to="roster.rollingstock", + ), + ), + migrations.AddConstraint( + model_name="decoderdocument", + constraint=models.UniqueConstraint( + fields=("decoder", "file"), name="unique_decoder_file" + ), + ), + migrations.AddConstraint( + model_name="rollingstockdocument", + constraint=models.UniqueConstraint( + fields=("rolling_stock", "file"), name="unique_stock_file" + ), + ), + migrations.AddField( + model_name="invoicedocument", + name="book", + field=models.ManyToManyField( + blank=True, related_name="invoice", to="bookshelf.book" + ), + ), + migrations.AddField( + model_name="invoicedocument", + name="catalog", + field=models.ManyToManyField( + blank=True, related_name="invoice", to="bookshelf.catalog" + ), + ), + migrations.AddField( + model_name="invoicedocument", + name="rolling_stock", + field=models.ManyToManyField( + blank=True, related_name="invoice", to="roster.rollingstock" + ), + ), + migrations.AddField( + model_name="invoicedocument", + name="shop", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="metadata.shop", + ), + ), + migrations.DeleteModel( + name="BaseBookDocument", + ), + ] diff --git a/ram/repository/migrations/0003_alter_basebookdocument_options_and_more.py b/ram/repository/migrations/0003_alter_basebookdocument_options_and_more.py deleted file mode 100644 index bd32b29..0000000 --- a/ram/repository/migrations/0003_alter_basebookdocument_options_and_more.py +++ /dev/null @@ -1,66 +0,0 @@ -# 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", - }, - ), - ] diff --git a/ram/repository/migrations/0004_invoicedocument_catalog_alter_invoicedocument_book.py b/ram/repository/migrations/0004_invoicedocument_catalog_alter_invoicedocument_book.py deleted file mode 100644 index 74e83a1..0000000 --- a/ram/repository/migrations/0004_invoicedocument_catalog_alter_invoicedocument_book.py +++ /dev/null @@ -1,28 +0,0 @@ -# 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" - ), - ), - ] diff --git a/ram/repository/models.py b/ram/repository/models.py index b753e7d..00f8a0b 100644 --- a/ram/repository/models.py +++ b/ram/repository/models.py @@ -1,11 +1,12 @@ from django.db import models +from django.core.exceptions import ValidationError from tinymce import models as tinymce from ram.models import PrivateDocument -from metadata.models import Decoder, Tag +from metadata.models import Decoder, Shop, Tag from roster.models import RollingStock -from bookshelf.models import Book, Catalog, BaseBook +from bookshelf.models import Book, Catalog class GenericDocument(PrivateDocument): @@ -19,23 +20,21 @@ class GenericDocument(PrivateDocument): 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 + RollingStock, related_name="invoice", blank=True ) + book = models.ManyToManyField(Book, related_name="invoice", blank=True) catalog = models.ManyToManyField( - Catalog, related_name="invoice", - blank=True + Catalog, related_name="invoice", blank=True + ) + date = models.DateField() + shop = models.ForeignKey( + Shop, on_delete=models.SET_NULL, null=True, blank=True + ) + file = models.FileField( + upload_to="files/invoices/", ) notes = tinymce.HTMLField(blank=True) - class Meta: - verbose_name = "Invoice" - verbose_name_plural = "Invoices" - class DecoderDocument(PrivateDocument): decoder = models.ForeignKey( @@ -45,23 +44,35 @@ class DecoderDocument(PrivateDocument): class Meta: constraints = [ models.UniqueConstraint( - fields=["decoder", "file"], - name="unique_decoder_file" + fields=["decoder", "file"], name="unique_decoder_file" ) ] -class BaseBookDocument(PrivateDocument): +class BookDocument(PrivateDocument): book = models.ForeignKey( - BaseBook, on_delete=models.CASCADE, related_name="document" + Book, on_delete=models.CASCADE, related_name="document" ) class Meta: - verbose_name_plural = "Bookshelf Documents" + verbose_name_plural = "Book documents" constraints = [ models.UniqueConstraint( - fields=["book", "file"], - name="unique_book_file" + fields=["book", "file"], name="unique_book_file" + ) + ] + + +class CatalogDocument(PrivateDocument): + catalog = models.ForeignKey( + Catalog, on_delete=models.CASCADE, related_name="document" + ) + + class Meta: + verbose_name_plural = "Catalog documents" + constraints = [ + models.UniqueConstraint( + fields=["catalog", "file"], name="unique_catalog_file" ) ] @@ -74,7 +85,6 @@ class RollingStockDocument(PrivateDocument): class Meta: constraints = [ models.UniqueConstraint( - fields=["rolling_stock", "file"], - name="unique_stock_file" + fields=["rolling_stock", "file"], name="unique_stock_file" ) ] diff --git a/ram/roster/admin.py b/ram/roster/admin.py index 8f94842..f1962aa 100644 --- a/ram/roster/admin.py +++ b/ram/roster/admin.py @@ -152,65 +152,62 @@ class RollingStockAdmin(SortableAdminBase, admin.ModelAdmin): ' {}'.format(obj.country.flag, obj.country) ) - def get_fieldsets(self, request, obj=None): - fieldsets = ( - ( - None, - { - "fields": ( - "preview", - "published", - "rolling_class", - "road_number", - "scale", - "manufacturer", - "item_number", - "set", - "era", - "description", - "production_year", - "tags", - ) - }, - ), - ( - "DCC", - { - "fields": ( - "decoder_interface", - "decoder", - "address", - ) - }, - ), - ( - "Purchase data", - { - "fields": ( - "shop", - "purchase_date", - "price", - ) - }, - ), - ( - "Notes", - {"classes": ("collapse",), "fields": ("notes",)}, - ), - ( - "Audit", - { - "classes": ("collapse",), - "fields": ( - "creation_time", - "updated_time", - ), - }, - ), - ) - if obj and obj.invoice.count() > 0: - fieldsets[2][1]["fields"] += ("invoices",) - return fieldsets + fieldsets = ( + ( + None, + { + "fields": ( + "preview", + "published", + "rolling_class", + "road_number", + "scale", + "manufacturer", + "item_number", + "set", + "era", + "description", + "production_year", + "tags", + ) + }, + ), + ( + "DCC", + { + "fields": ( + "decoder_interface", + "decoder", + "address", + ) + }, + ), + ( + "Purchase data", + { + "fields": ( + "shop", + "purchase_date", + "price", + "invoices", + ) + }, + ), + ( + "Notes", + {"classes": ("collapse",), "fields": ("notes",)}, + ), + ( + "Audit", + { + "classes": ("collapse",), + "fields": ( + "creation_time", + "updated_time", + ), + }, + ), + ) def get_form(self, request, obj=None, **kwargs): form = super().get_form(request, obj, **kwargs) @@ -221,10 +218,13 @@ class RollingStockAdmin(SortableAdminBase, admin.ModelAdmin): @admin.display(description="Invoices") def invoices(self, obj): - html = "
".join( - "{}".format( - i.file.url, i - ) for i in obj.invoice.all()) + if obj.invoice.exists(): + html = "
".join( + "{}".format( + i.file.url, i + ) for i in obj.invoice.all()) + else: + html = "-" return format_html(html) def download_csv(modeladmin, request, queryset):