Remove product image and add initials

This commit is contained in:
Gabriel Augendre 2023-04-02 15:51:54 +02:00
parent 90787707d1
commit 93829df543
6 changed files with 206 additions and 66 deletions

View file

@ -9,8 +9,15 @@ from purchase.templatetags.purchase import currency
@register(Product) @register(Product)
class ProductAdmin(admin.ModelAdmin): class ProductAdmin(admin.ModelAdmin):
list_display = ["name", "display_order", "unit_price", "sold", "turnover"] list_display = [
list_editable = ["display_order"] "name",
"display_order",
"initials",
"unit_price",
"sold",
"turnover",
]
list_editable = ["display_order", "initials"]
search_fields = ["name"] search_fields = ["name"]
def get_queryset(self, request): def get_queryset(self, request):

View file

@ -3,10 +3,10 @@
"model": "purchase.product", "model": "purchase.product",
"fields": { "fields": {
"created_at": "2022-04-26T20:30:22.959Z", "created_at": "2022-04-26T20:30:22.959Z",
"updated_at": "2022-04-26T20:30:22.959Z", "updated_at": "2023-04-02T13:47:16.252Z",
"name": "Clou", "name": "Clou",
"image": "", "unit_price_cents": 140,
"unit_price_cents": 134, "initials": "C",
"display_order": 1 "display_order": 1
} }
}, },
@ -14,10 +14,10 @@
"model": "purchase.product", "model": "purchase.product",
"fields": { "fields": {
"created_at": "2022-04-26T20:30:22.959Z", "created_at": "2022-04-26T20:30:22.959Z",
"updated_at": "2022-04-27T21:04:04.197Z", "updated_at": "2023-04-02T13:47:52.602Z",
"name": "Villard'Ain", "name": "Villard'Ain",
"image": "", "unit_price_cents": 310,
"unit_price_cents": 290, "initials": "V",
"display_order": 2 "display_order": 2
} }
}, },
@ -25,10 +25,10 @@
"model": "purchase.product", "model": "purchase.product",
"fields": { "fields": {
"created_at": "2022-04-26T20:30:22.959Z", "created_at": "2022-04-26T20:30:22.959Z",
"updated_at": "2022-04-28T16:09:00.142Z", "updated_at": "2023-04-02T13:47:52.608Z",
"name": "Herbier", "name": "Herbier",
"image": "", "unit_price_cents": 360,
"unit_price_cents": 330, "initials": "Hb",
"display_order": 4 "display_order": 4
} }
}, },
@ -36,21 +36,21 @@
"model": "purchase.product", "model": "purchase.product",
"fields": { "fields": {
"created_at": "2022-04-26T20:30:22.959Z", "created_at": "2022-04-26T20:30:22.959Z",
"updated_at": "2022-04-27T21:04:04.192Z", "updated_at": "2023-04-02T13:47:52.622Z",
"name": "Blanc vache", "name": "Blanc vache",
"image": "", "unit_price_cents": 360,
"unit_price_cents": 650, "initials": "BV",
"display_order": 3 "display_order": 10
} }
}, },
{ {
"model": "purchase.product", "model": "purchase.product",
"fields": { "fields": {
"created_at": "2022-04-28T16:08:08.298Z", "created_at": "2022-04-28T16:08:08.298Z",
"updated_at": "2022-04-28T16:09:13.196Z", "updated_at": "2023-04-02T13:47:52.610Z",
"name": "Ap\u00e9rich\u00e8vres", "name": "Ap\u00e9rich\u00e8vres",
"image": "", "unit_price_cents": 360,
"unit_price_cents": 330, "initials": "Ac",
"display_order": 5 "display_order": 5
} }
}, },
@ -58,10 +58,10 @@
"model": "purchase.product", "model": "purchase.product",
"fields": { "fields": {
"created_at": "2022-04-28T16:08:16.542Z", "created_at": "2022-04-28T16:08:16.542Z",
"updated_at": "2022-04-28T16:09:25.690Z", "updated_at": "2023-04-02T13:47:52.614Z",
"name": "Clougert", "name": "Clougert",
"image": "", "unit_price_cents": 470,
"unit_price_cents": 450, "initials": "CM",
"display_order": 6 "display_order": 6
} }
}, },
@ -69,10 +69,10 @@
"model": "purchase.product", "model": "purchase.product",
"fields": { "fields": {
"created_at": "2022-04-28T16:08:20.471Z", "created_at": "2022-04-28T16:08:20.471Z",
"updated_at": "2022-04-28T16:09:34.003Z", "updated_at": "2023-04-02T13:47:52.617Z",
"name": "Brique", "name": "Brique",
"image": "", "unit_price_cents": 310,
"unit_price_cents": 290, "initials": "Bq",
"display_order": 7 "display_order": 7
} }
}, },
@ -80,11 +80,154 @@
"model": "purchase.product", "model": "purchase.product",
"fields": { "fields": {
"created_at": "2022-04-28T16:08:20.471Z", "created_at": "2022-04-28T16:08:20.471Z",
"updated_at": "2022-04-28T16:09:34.003Z", "updated_at": "2023-04-02T13:47:52.619Z",
"name": "Tomme de ch\u00e8vre", "name": "Tomme de ch\u00e8vre",
"image": "",
"unit_price_cents": 0, "unit_price_cents": 0,
"initials": "T",
"display_order": 8 "display_order": 8
} }
},
{
"model": "purchase.product",
"fields": {
"created_at": "2023-04-02T12:47:15.522Z",
"updated_at": "2023-04-02T13:47:52.640Z",
"name": "Cr\u00eapes chocolat",
"unit_price_cents": 200,
"initials": "CrC",
"display_order": 20
}
},
{
"model": "purchase.product",
"fields": {
"created_at": "2023-04-02T13:02:38.149Z",
"updated_at": "2023-04-02T13:47:52.605Z",
"name": "Caprinoux",
"unit_price_cents": 100,
"initials": "Cap",
"display_order": 3
}
},
{
"model": "purchase.product",
"fields": {
"created_at": "2023-04-02T13:31:13.712Z",
"updated_at": "2023-04-02T13:47:52.624Z",
"name": "Clou affin\u00e9",
"unit_price_cents": 160,
"initials": "CA",
"display_order": 11
}
},
{
"model": "purchase.product",
"fields": {
"created_at": "2023-04-02T13:32:12.289Z",
"updated_at": "2023-04-02T13:47:52.626Z",
"name": "Ap\u00e9rich\u00e8vres frais",
"unit_price_cents": 250,
"initials": "AcF",
"display_order": 12
}
},
{
"model": "purchase.product",
"fields": {
"created_at": "2023-04-02T13:32:29.365Z",
"updated_at": "2023-04-02T13:47:52.628Z",
"name": "Blanc ch\u00e8vre",
"unit_price_cents": 480,
"initials": "BC",
"display_order": 13
}
},
{
"model": "purchase.product",
"fields": {
"created_at": "2023-04-02T13:32:41.308Z",
"updated_at": "2023-04-02T13:47:52.629Z",
"name": "Gros de vache",
"unit_price_cents": 200,
"initials": "GV",
"display_order": 14
}
},
{
"model": "purchase.product",
"fields": {
"created_at": "2023-04-02T13:32:48.404Z",
"updated_at": "2023-04-02T13:47:52.631Z",
"name": "P'tit clou",
"unit_price_cents": 60,
"initials": "PC",
"display_order": 15
}
},
{
"model": "purchase.product",
"fields": {
"created_at": "2023-04-02T13:33:12.184Z",
"updated_at": "2023-04-02T13:47:52.633Z",
"name": "Ap\u00e9rivache",
"unit_price_cents": 300,
"initials": "AV",
"display_order": 16
}
},
{
"model": "purchase.product",
"fields": {
"created_at": "2023-04-02T13:33:24.357Z",
"updated_at": "2023-04-02T13:47:52.635Z",
"name": "Fromage fort",
"unit_price_cents": 360,
"initials": "FF",
"display_order": 17
}
},
{
"model": "purchase.product",
"fields": {
"created_at": "2023-04-02T13:33:49.793Z",
"updated_at": "2023-04-02T13:47:52.636Z",
"name": "Lait de ch\u00e8vre",
"unit_price_cents": 200,
"initials": "L",
"display_order": 18
}
},
{
"model": "purchase.product",
"fields": {
"created_at": "2023-04-02T13:33:54.405Z",
"updated_at": "2023-04-02T13:47:52.638Z",
"name": "Verre",
"unit_price_cents": 100,
"initials": "V",
"display_order": 19
}
},
{
"model": "purchase.product",
"fields": {
"created_at": "2023-04-02T13:34:34.802Z",
"updated_at": "2023-04-02T13:47:52.641Z",
"name": "Cr\u00eape sucre",
"unit_price_cents": 150,
"initials": "CrS",
"display_order": 21
}
},
{
"model": "purchase.product",
"fields": {
"created_at": "2023-04-02T13:34:44.535Z",
"updated_at": "2023-04-02T13:47:52.643Z",
"name": "Cr\u00eape nature",
"unit_price_cents": 140,
"initials": "CrN",
"display_order": 22
}
} }
] ]

View file

@ -0,0 +1,22 @@
# Generated by Django 4.1.7 on 2023-04-02 13:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("purchase", "0014_alter_basket_payment_method"),
]
operations = [
migrations.RemoveField(
model_name="product",
name="image",
),
migrations.AddField(
model_name="product",
name="initials",
field=models.CharField(default="", max_length=10, verbose_name="initials"),
preserve_default=False,
),
]

View file

@ -9,7 +9,6 @@ from django.db.models.functions import Coalesce
from django.urls import reverse from django.urls import reverse
from django.utils.translation import gettext from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from PIL import Image, ImageOps
from solo.models import SingletonModel from solo.models import SingletonModel
@ -91,13 +90,18 @@ class ProductManager(models.Manager):
class Product(Model): class Product(Model):
name = models.CharField(max_length=250, unique=True, verbose_name=_("name")) name = models.CharField(max_length=250, unique=True, verbose_name=_("name"))
image = models.ImageField(null=True, blank=True, verbose_name=_("image"))
unit_price_cents = models.PositiveIntegerField( unit_price_cents = models.PositiveIntegerField(
verbose_name=_("unit price (cents)"), verbose_name=_("unit price (cents)"),
help_text=_( help_text=_(
"Unit price in cents. Use zero to denote that the product has no fixed price.", "Unit price in cents. Use zero to denote that the product has no fixed price.",
), ),
) )
initials = models.CharField(
max_length=10,
verbose_name=_("initials"),
blank=False,
null=False,
)
display_order = models.PositiveIntegerField( display_order = models.PositiveIntegerField(
default=default_product_display_order, default=default_product_display_order,
verbose_name=_("display order"), verbose_name=_("display order"),
@ -127,42 +131,6 @@ class Product(Model):
def has_fixed_price(self) -> bool: def has_fixed_price(self) -> bool:
return self.unit_price_cents > 0 return self.unit_price_cents > 0
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if not self.image:
return
with Image.open(self.image.path) as img_file:
img = ImageOps.exif_transpose(img_file)
width, height = img.size # Get dimensions
image_max_size = 300
if width > image_max_size and height > image_max_size:
# keep ratio but shrink down
img.thumbnail((width, height))
# check which one is smaller
if height < width:
# make square by cutting off equal amounts left and right
left = (width - height) / 2
right = (width + height) / 2
top = 0
bottom = height
img = img.crop((left, top, right, bottom))
elif width < height:
# make square by cutting off bottom
left = 0
right = width
top = 0
bottom = width
img = img.crop((left, top, right, bottom))
if width > image_max_size and height > image_max_size:
img.thumbnail((image_max_size, image_max_size))
img.save(self.image.path)
class BasketQuerySet(models.QuerySet): class BasketQuerySet(models.QuerySet):
def priced(self) -> BasketQuerySet: def priced(self) -> BasketQuerySet:

View file

@ -7,7 +7,7 @@
<div class="card-img product-img-placeholder" <div class="card-img product-img-placeholder"
style="background-color: hsl({{ product.color_hue }}, 60%, 80%)"> style="background-color: hsl({{ product.color_hue }}, 60%, 80%)">
<span> <span>
{{ product.name|slice:"1" }} {{ product.initials }}
</span> </span>
</div> </div>
{% endif %} {% endif %}

View file

@ -7,7 +7,7 @@
<div class="card-img product-img-placeholder" <div class="card-img product-img-placeholder"
style="background-color: hsl({{ product.color_hue }}, 60%, 80%)"> style="background-color: hsl({{ product.color_hue }}, 60%, 80%)">
<span> <span>
{{ product.name|slice:"1" }} {{ product.initials }}
</span> </span>
</div> </div>
{% endif %} {% endif %}