diff --git a/counter/admin.py b/counter/admin.py index ed24dd23..425134ef 100644 --- a/counter/admin.py +++ b/counter/admin.py @@ -39,8 +39,9 @@ class ProductAdmin(SearchModelAdmin): "code", "product_type", "selling_price", - "profit", "archived", + "created_at", + "updated_at", ) list_select_related = ("product_type",) search_fields = ("name", "code") diff --git a/counter/migrations/0036_product_created_at_product_updated_at.py b/counter/migrations/0036_product_created_at_product_updated_at.py new file mode 100644 index 00000000..5fe622f2 --- /dev/null +++ b/counter/migrations/0036_product_created_at_product_updated_at.py @@ -0,0 +1,67 @@ +# Generated by Django 5.2.8 on 2026-02-10 15:40 +from operator import attrgetter + +import django.utils.timezone +from django.db import migrations, models +from django.db.migrations.state import StateApps +from django.db.models import OuterRef, Subquery + +from counter.models import Selling + + +def apply_product_history_dates(apps: StateApps, schema_editor): + """Approximate a posteriori the value of created_at and updated_at.""" + Product = apps.get_model("counter", "Product") + sales_subquery = Selling.objects.filter(product=OuterRef("pk")).values("date") + + # for products that have an associated sale, we set the creation date + # to the one of the first sale, and the update date to the one of the last sale + products = list( + Product.objects.exclude(sellings=None) + .annotate( + new_created_at=Subquery(sales_subquery.order_by("date")[:1]), + new_updated_at=Subquery(sales_subquery.order_by("-date")[:1]), + ) + .only("id") + ) + for product in products: + product.created_at = product.new_created_at + product.updated_at = product.new_updated_at + + # For the remaining products (those without sale), + # they are given the creation and update date of the previous product having sales. + products_without_sale = list(Product.objects.filter(sellings=None).only("id")) + for product in products_without_sale: + previous_product = max( + (p for p in products if p.id < product.id), key=attrgetter("id") + ) + product.created_at = previous_product.created_at + product.updated_at = previous_product.updated_at + products.extend(products_without_sale) + + Product.objects.bulk_update(products, fields=["created_at", "updated_at"]) + + +class Migration(migrations.Migration): + dependencies = [("counter", "0035_remove_selling_is_validated_and_more")] + + operations = [ + migrations.AddField( + model_name="product", + name="created_at", + field=models.DateTimeField( + auto_now_add=True, + default=django.utils.timezone.now, + verbose_name="created at", + ), + preserve_default=False, + ), + migrations.AddField( + model_name="product", + name="updated_at", + field=models.DateTimeField(auto_now=True, verbose_name="updated at"), + ), + migrations.RunPython( + apply_product_history_dates, reverse_code=migrations.RunPython.noop + ), + ] diff --git a/counter/models.py b/counter/models.py index 6ceb9ea8..44101d42 100644 --- a/counter/models.py +++ b/counter/models.py @@ -399,6 +399,8 @@ class Product(models.Model): Group, related_name="products", verbose_name=_("buying groups"), blank=True ) archived = models.BooleanField(_("archived"), default=False) + created_at = models.DateTimeField(_("created at"), auto_now_add=True) + updated_at = models.DateTimeField(_("updated at"), auto_now=True) class Meta: verbose_name = _("product")