feat: ruff'ed
This commit is contained in:
+205
-466
@@ -19,12 +19,8 @@ User = get_user_model()
|
||||
|
||||
|
||||
class TimestampedModel(models.Model):
|
||||
creation_date = models.DateTimeField(
|
||||
auto_now_add=True, verbose_name=_("fecha de creación")
|
||||
)
|
||||
last_modification_date = models.DateTimeField(
|
||||
auto_now=True, verbose_name=_("fecha de última modificación")
|
||||
)
|
||||
creation_date = models.DateTimeField(auto_now_add=True, verbose_name=_('fecha de creación'))
|
||||
last_modification_date = models.DateTimeField(auto_now=True, verbose_name=_('fecha de última modificación'))
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
@@ -37,33 +33,20 @@ class Tag(models.Model):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("etiqueta")
|
||||
verbose_name_plural = _("etiquetas")
|
||||
ordering = ("name",)
|
||||
verbose_name = _('etiqueta')
|
||||
verbose_name_plural = _('etiquetas')
|
||||
ordering = ('name',)
|
||||
|
||||
|
||||
class ProductCategory(models.Model):
|
||||
name = models.CharField(max_length=32)
|
||||
parent = models.ForeignKey(
|
||||
"shop.ProductCategory",
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name=_("categoría padre"),
|
||||
)
|
||||
promoted = models.BooleanField(default=False, verbose_name=_("promocionada"))
|
||||
hidden = models.BooleanField(default=False, verbose_name=_("oculta"))
|
||||
show_in_navbar = models.BooleanField(
|
||||
default=False, verbose_name=_("mostrar en cabecera")
|
||||
)
|
||||
slug = models.SlugField(
|
||||
max_length=48,
|
||||
blank=True,
|
||||
default="",
|
||||
verbose_name=_("slug"),
|
||||
unique=True,
|
||||
db_index=True,
|
||||
'shop.ProductCategory', on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_('categoría padre')
|
||||
)
|
||||
promoted = models.BooleanField(default=False, verbose_name=_('promocionada'))
|
||||
hidden = models.BooleanField(default=False, verbose_name=_('oculta'))
|
||||
show_in_navbar = models.BooleanField(default=False, verbose_name=_('mostrar en cabecera'))
|
||||
slug = models.SlugField(max_length=48, blank=True, default='', verbose_name=_('slug'), unique=True, db_index=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@@ -73,58 +56,24 @@ class ProductCategory(models.Model):
|
||||
super().save()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("categoría")
|
||||
verbose_name_plural = _("categorías")
|
||||
ordering = ("name",)
|
||||
verbose_name = _('categoría')
|
||||
verbose_name_plural = _('categorías')
|
||||
ordering = ('name',)
|
||||
|
||||
|
||||
class Product(TimestampedModel):
|
||||
sku = models.CharField(
|
||||
max_length=64,
|
||||
blank=False,
|
||||
null=False,
|
||||
unique=True,
|
||||
verbose_name=_("código de referencia"),
|
||||
)
|
||||
name = models.CharField(
|
||||
max_length=96,
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_("nombre"),
|
||||
)
|
||||
description = models.TextField(
|
||||
blank=True,
|
||||
default="",
|
||||
verbose_name=_("descripción"),
|
||||
)
|
||||
stock = models.DecimalField(
|
||||
max_digits=13,
|
||||
decimal_places=4,
|
||||
default=Decimal("0"),
|
||||
verbose_name=_("stock"),
|
||||
)
|
||||
is_digital_asset = models.BooleanField(
|
||||
default=False, verbose_name=_("es un activo digital")
|
||||
)
|
||||
url = models.URLField(blank=True, verbose_name=_("URL de descarga"))
|
||||
tags = models.ManyToManyField("shop.Tag", blank=True, verbose_name=_("etiquetas"))
|
||||
categories = models.ManyToManyField(
|
||||
"shop.ProductCategory", blank=True, verbose_name=_("categorías")
|
||||
)
|
||||
brand = models.ForeignKey(
|
||||
"shop.Brand",
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name=_("marca"),
|
||||
)
|
||||
slug = models.SlugField(
|
||||
default="", blank=True, null=True, max_length=128, verbose_name=_("Slug")
|
||||
)
|
||||
hidden = models.BooleanField(default=False, db_index=True, verbose_name=_("oculto"))
|
||||
is_shipping_method = models.BooleanField(
|
||||
default=False, verbose_name=_("es forma de envío")
|
||||
)
|
||||
sku = models.CharField(max_length=64, blank=False, null=False, unique=True, verbose_name=_('código de referencia'))
|
||||
name = models.CharField(max_length=96, blank=False, null=False, verbose_name=_('nombre'))
|
||||
description = models.TextField(blank=True, default='', verbose_name=_('descripción'))
|
||||
stock = models.DecimalField(max_digits=13, decimal_places=4, default=Decimal('0'), verbose_name=_('stock'))
|
||||
is_digital_asset = models.BooleanField(default=False, verbose_name=_('es un activo digital'))
|
||||
url = models.URLField(blank=True, verbose_name=_('URL de descarga'))
|
||||
tags = models.ManyToManyField('shop.Tag', blank=True, verbose_name=_('etiquetas'))
|
||||
categories = models.ManyToManyField('shop.ProductCategory', blank=True, verbose_name=_('categorías'))
|
||||
brand = models.ForeignKey('shop.Brand', blank=True, null=True, on_delete=models.SET_NULL, verbose_name=_('marca'))
|
||||
slug = models.SlugField(default='', blank=True, null=True, max_length=128, verbose_name=_('Slug'))
|
||||
hidden = models.BooleanField(default=False, db_index=True, verbose_name=_('oculto'))
|
||||
is_shipping_method = models.BooleanField(default=False, verbose_name=_('es forma de envío'))
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@@ -139,38 +88,25 @@ class Product(TimestampedModel):
|
||||
return self.prices.filter(current=True).first()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("producto")
|
||||
verbose_name_plural = _("productos")
|
||||
ordering = ("id",)
|
||||
verbose_name = _('producto')
|
||||
verbose_name_plural = _('productos')
|
||||
ordering = ('id',)
|
||||
|
||||
|
||||
class ProductPrice(TimestampedModel):
|
||||
price = models.DecimalField(
|
||||
max_digits=11, decimal_places=2, verbose_name=_("precio")
|
||||
)
|
||||
date = models.DateTimeField(default=timezone.now, verbose_name=_("fecha"))
|
||||
price = models.DecimalField(max_digits=11, decimal_places=2, verbose_name=_('precio'))
|
||||
date = models.DateTimeField(default=timezone.now, verbose_name=_('fecha'))
|
||||
product = models.ForeignKey(
|
||||
"shop.Product",
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_("producto"),
|
||||
related_name="prices",
|
||||
)
|
||||
tax = models.ForeignKey(
|
||||
"shop.Tax",
|
||||
on_delete=models.PROTECT,
|
||||
verbose_name=_("impuesto aplicable"),
|
||||
'shop.Product', on_delete=models.CASCADE, verbose_name=_('producto'), related_name='prices'
|
||||
)
|
||||
tax = models.ForeignKey('shop.Tax', on_delete=models.PROTECT, verbose_name=_('impuesto aplicable'))
|
||||
price_with_tax = models.DecimalField(
|
||||
max_digits=11,
|
||||
decimal_places=2,
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name=_("precio con impuestos"),
|
||||
max_digits=11, decimal_places=2, blank=True, null=True, verbose_name=_('precio con impuestos')
|
||||
)
|
||||
current = models.BooleanField(default=False, verbose_name=_("es el precio actual"))
|
||||
current = models.BooleanField(default=False, verbose_name=_('es el precio actual'))
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.price} - {self.tax.code}"
|
||||
return f'{self.price} - {self.tax.code}'
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
tax_value = self.price * Decimal(self.tax.value / 100)
|
||||
@@ -178,63 +114,41 @@ class ProductPrice(TimestampedModel):
|
||||
super().save()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("precio de producto")
|
||||
verbose_name_plural = _("precio de producto")
|
||||
ordering = ("-date",)
|
||||
verbose_name = _('precio de producto')
|
||||
verbose_name_plural = _('precio de producto')
|
||||
ordering = ('-date',)
|
||||
|
||||
|
||||
class ProductImage(models.Model):
|
||||
product = models.ForeignKey(
|
||||
"shop.Product",
|
||||
'shop.Product',
|
||||
on_delete=models.CASCADE,
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_("Producto"),
|
||||
related_name="images",
|
||||
)
|
||||
original = models.ImageField(
|
||||
upload_to="uploads", blank=False, null=False, verbose_name=_("Imagen original")
|
||||
verbose_name=_('Producto'),
|
||||
related_name='images',
|
||||
)
|
||||
original = models.ImageField(upload_to='uploads', blank=False, null=False, verbose_name=_('Imagen original'))
|
||||
|
||||
xl = models.ImageField(
|
||||
upload_to="uploads", blank=True, null=True, verbose_name=_("XL")
|
||||
)
|
||||
xl_dark = models.ImageField(
|
||||
upload_to="uploads", blank=True, null=True, verbose_name=_("XL (fondo oscuro)")
|
||||
)
|
||||
xl = models.ImageField(upload_to='uploads', blank=True, null=True, verbose_name=_('XL'))
|
||||
xl_dark = models.ImageField(upload_to='uploads', blank=True, null=True, verbose_name=_('XL (fondo oscuro)'))
|
||||
|
||||
l = models.ImageField(
|
||||
upload_to="uploads", blank=True, null=True, verbose_name=_("L")
|
||||
)
|
||||
l_dark = models.ImageField(
|
||||
upload_to="uploads", blank=True, null=True, verbose_name=_("L (fondo oscuro)")
|
||||
)
|
||||
l = models.ImageField(upload_to='uploads', blank=True, null=True, verbose_name=_('L'))
|
||||
l_dark = models.ImageField(upload_to='uploads', blank=True, null=True, verbose_name=_('L (fondo oscuro)'))
|
||||
|
||||
m = models.ImageField(
|
||||
upload_to="uploads", blank=True, null=True, verbose_name=_("M")
|
||||
)
|
||||
m_dark = models.ImageField(
|
||||
upload_to="uploads", blank=True, null=True, verbose_name=_("M (fondo oscuro)")
|
||||
)
|
||||
m = models.ImageField(upload_to='uploads', blank=True, null=True, verbose_name=_('M'))
|
||||
m_dark = models.ImageField(upload_to='uploads', blank=True, null=True, verbose_name=_('M (fondo oscuro)'))
|
||||
|
||||
s = models.ImageField(
|
||||
upload_to="uploads", blank=True, null=True, verbose_name=_("S")
|
||||
)
|
||||
s_dark = models.ImageField(
|
||||
upload_to="uploads", blank=True, null=True, verbose_name=_("S (fondo oscuro)")
|
||||
)
|
||||
s = models.ImageField(upload_to='uploads', blank=True, null=True, verbose_name=_('S'))
|
||||
s_dark = models.ImageField(upload_to='uploads', blank=True, null=True, verbose_name=_('S (fondo oscuro)'))
|
||||
|
||||
xs = models.ImageField(
|
||||
upload_to="uploads", blank=True, null=True, verbose_name=_("XS")
|
||||
)
|
||||
xs_dark = models.ImageField(
|
||||
upload_to="uploads", blank=True, null=True, verbose_name=_("XS (fondo oscuro)")
|
||||
)
|
||||
xs = models.ImageField(upload_to='uploads', blank=True, null=True, verbose_name=_('XS'))
|
||||
xs_dark = models.ImageField(upload_to='uploads', blank=True, null=True, verbose_name=_('XS (fondo oscuro)'))
|
||||
|
||||
def __str__(self):
|
||||
return self.original.url
|
||||
|
||||
def make_square(self, image, max_size=512, fill_color="#000000"):
|
||||
def make_square(self, image, max_size=512, fill_color='#000000'):
|
||||
width, height = image.size
|
||||
|
||||
if width > height:
|
||||
@@ -247,7 +161,7 @@ class ProductImage(models.Model):
|
||||
|
||||
resized_image = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
|
||||
|
||||
new_image = Image.new("RGB", (max_size, max_size), fill_color)
|
||||
new_image = Image.new('RGB', (max_size, max_size), fill_color)
|
||||
|
||||
position_x = int((max_size - new_width) / 2)
|
||||
position_y = int((max_size - new_height) / 2)
|
||||
@@ -256,503 +170,328 @@ class ProductImage(models.Model):
|
||||
|
||||
return new_image
|
||||
|
||||
def get_resized_image(self, image, size, fill_color="#000000"):
|
||||
def get_resized_image(self, image, size, fill_color='#000000'):
|
||||
image = Image.open(image)
|
||||
image = self.make_square(image, size, fill_color)
|
||||
buffer = BytesIO()
|
||||
image.save(fp=buffer, format="WEBP")
|
||||
image.save(fp=buffer, format='WEBP')
|
||||
|
||||
return ContentFile(buffer.getvalue(), name=f"{self.product.slug}_{size}.webp")
|
||||
return ContentFile(buffer.getvalue(), name=f'{self.product.slug}_{size}.webp')
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.xl = self.get_resized_image(self.original, 1024, fill_color="#FFFFFF")
|
||||
self.xl = self.get_resized_image(self.original, 1024, fill_color='#FFFFFF')
|
||||
self.xl_dark = self.get_resized_image(self.original, 1024)
|
||||
|
||||
self.l = self.get_resized_image(self.original, 512, fill_color="#FFFFFF")
|
||||
self.l = self.get_resized_image(self.original, 512, fill_color='#FFFFFF')
|
||||
self.l_dark = self.get_resized_image(self.original, 512)
|
||||
|
||||
self.m = self.get_resized_image(self.original, 256, fill_color="#FFFFFF")
|
||||
self.m = self.get_resized_image(self.original, 256, fill_color='#FFFFFF')
|
||||
self.m_dark = self.get_resized_image(self.original, 256)
|
||||
|
||||
self.s = self.get_resized_image(self.original, 128, fill_color="#FFFFFF")
|
||||
self.s = self.get_resized_image(self.original, 128, fill_color='#FFFFFF')
|
||||
self.s_dark = self.get_resized_image(self.original, 128)
|
||||
|
||||
self.xs = self.get_resized_image(self.original, 96, fill_color="#FFFFFF")
|
||||
self.xs = self.get_resized_image(self.original, 96, fill_color='#FFFFFF')
|
||||
self.xs_dark = self.get_resized_image(self.original, 96)
|
||||
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('imágenes de producto')
|
||||
verbose_name_plural = _('imágenes de producto')
|
||||
|
||||
|
||||
class ProductBatch(TimestampedModel):
|
||||
code = models.CharField(
|
||||
max_length=32, unique=True, db_index=True, verbose_name=_("código")
|
||||
)
|
||||
product = models.ForeignKey(
|
||||
"shop.Product", on_delete=models.PROTECT, verbose_name=_("producto")
|
||||
)
|
||||
quantity = models.DecimalField(
|
||||
max_digits=13,
|
||||
decimal_places=4,
|
||||
default=Decimal("1"),
|
||||
verbose_name=_("cantidad"),
|
||||
)
|
||||
code = models.CharField(max_length=32, unique=True, db_index=True, verbose_name=_('código'))
|
||||
product = models.ForeignKey('shop.Product', on_delete=models.PROTECT, verbose_name=_('producto'))
|
||||
quantity = models.DecimalField(max_digits=13, decimal_places=4, default=Decimal('1'), verbose_name=_('cantidad'))
|
||||
expiration_date = models.DateField(
|
||||
blank=True, null=True, verbose_name=_("fecha de caducidad / consumo preferente")
|
||||
blank=True, null=True, verbose_name=_('fecha de caducidad / consumo preferente')
|
||||
)
|
||||
provider = models.ForeignKey(
|
||||
"shop.Provider",
|
||||
on_delete=models.SET_NULL,
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name=_("proveedor"),
|
||||
'shop.Provider', on_delete=models.SET_NULL, blank=True, null=True, verbose_name=_('proveedor')
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.code} - {self.product.name} - {self.quantity}"
|
||||
return f'{self.code} - {self.product.name} - {self.quantity}'
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("remesa de producto")
|
||||
verbose_name_plural = _("remesas de productos")
|
||||
verbose_name = _('remesa de producto')
|
||||
verbose_name_plural = _('remesas de productos')
|
||||
|
||||
|
||||
class Tax(TimestampedModel):
|
||||
code = models.CharField(
|
||||
max_length=8, blank=False, unique=True, verbose_name=_("código de impuesto")
|
||||
)
|
||||
value = models.PositiveIntegerField(
|
||||
null=False, blank=False, verbose_name=_("valor entero (porcentaje)")
|
||||
)
|
||||
code = models.CharField(max_length=8, blank=False, unique=True, verbose_name=_('código de impuesto'))
|
||||
value = models.PositiveIntegerField(null=False, blank=False, verbose_name=_('valor entero (porcentaje)'))
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.code} - {self.value}%"
|
||||
return f'{self.code} - {self.value}%'
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("impuesto")
|
||||
verbose_name_plural = _("impuestos")
|
||||
ordering = ("id",)
|
||||
verbose_name = _('impuesto')
|
||||
verbose_name_plural = _('impuestos')
|
||||
ordering = ('id',)
|
||||
|
||||
|
||||
class Provider(TimestampedModel):
|
||||
vat_id = models.CharField(
|
||||
max_length=32,
|
||||
blank=False,
|
||||
unique=True,
|
||||
verbose_name=_("documento de identidad"),
|
||||
)
|
||||
name = models.CharField(max_length=64, blank=False, verbose_name=_("nombre"))
|
||||
vat_id = models.CharField(max_length=32, blank=False, unique=True, verbose_name=_('documento de identidad'))
|
||||
name = models.CharField(max_length=64, blank=False, verbose_name=_('nombre'))
|
||||
|
||||
email = models.EmailField(blank=False, verbose_name=_("e-mail de contacto"))
|
||||
phone = models.CharField(
|
||||
max_length=16,
|
||||
blank=True,
|
||||
null=True,
|
||||
default="",
|
||||
verbose_name=_("Teléfono de contacto"),
|
||||
)
|
||||
email = models.EmailField(blank=False, verbose_name=_('e-mail de contacto'))
|
||||
phone = models.CharField(max_length=16, blank=True, null=True, default='', verbose_name=_('Teléfono de contacto'))
|
||||
|
||||
address = models.CharField(max_length=255, blank=False, verbose_name=_("dirección"))
|
||||
city = models.CharField(max_length=64, blank=False, verbose_name=_("ciudad"))
|
||||
state = models.CharField(max_length=64, blank=False, verbose_name=_("región"))
|
||||
country = models.CharField(max_length=64, blank=False, verbose_name=_("país"))
|
||||
zip = models.CharField(max_length=32, blank=False, verbose_name=_("código postal"))
|
||||
address = models.CharField(max_length=255, blank=False, verbose_name=_('dirección'))
|
||||
city = models.CharField(max_length=64, blank=False, verbose_name=_('ciudad'))
|
||||
state = models.CharField(max_length=64, blank=False, verbose_name=_('región'))
|
||||
country = models.CharField(max_length=64, blank=False, verbose_name=_('país'))
|
||||
zip = models.CharField(max_length=32, blank=False, verbose_name=_('código postal'))
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.vat_id} - {self.name}"
|
||||
return f'{self.vat_id} - {self.name}'
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("proveedor")
|
||||
verbose_name_plural = _("proveedores")
|
||||
verbose_name = _('proveedor')
|
||||
verbose_name_plural = _('proveedores')
|
||||
|
||||
|
||||
class Brand(TimestampedModel):
|
||||
name = models.CharField(
|
||||
max_length=100, unique=True, blank=False, null=False, verbose_name=_("nombre")
|
||||
)
|
||||
name = models.CharField(max_length=100, unique=True, blank=False, null=False, verbose_name=_('nombre'))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("marca")
|
||||
verbose_name_plural = _("marcas")
|
||||
verbose_name = _('marca')
|
||||
verbose_name_plural = _('marcas')
|
||||
|
||||
|
||||
class OrderLine(TimestampedModel):
|
||||
order = models.ForeignKey(
|
||||
"shop.Order",
|
||||
on_delete=models.CASCADE,
|
||||
related_name="lines",
|
||||
verbose_name=_("pedido"),
|
||||
)
|
||||
order = models.ForeignKey('shop.Order', on_delete=models.CASCADE, related_name='lines', verbose_name=_('pedido'))
|
||||
product = models.ForeignKey(
|
||||
"shop.Product",
|
||||
on_delete=models.CASCADE,
|
||||
null=False,
|
||||
blank=False,
|
||||
verbose_name=_("producto"),
|
||||
'shop.Product', on_delete=models.CASCADE, null=False, blank=False, verbose_name=_('producto')
|
||||
)
|
||||
quantity = models.DecimalField(
|
||||
max_digits=13,
|
||||
decimal_places=4,
|
||||
default=Decimal("1"),
|
||||
null=False,
|
||||
blank=False,
|
||||
verbose_name=_("cantidad"),
|
||||
max_digits=13, decimal_places=4, default=Decimal('1'), null=False, blank=False, verbose_name=_('cantidad')
|
||||
)
|
||||
|
||||
# Precio del producto en el momento en el que se crea el pedido
|
||||
price = models.DecimalField(
|
||||
max_digits=13, decimal_places=4, verbose_name=_("precio")
|
||||
)
|
||||
price = models.DecimalField(max_digits=13, decimal_places=4, verbose_name=_('precio'))
|
||||
|
||||
# base_total = quantity * price
|
||||
base_total = models.DecimalField(
|
||||
max_digits=13,
|
||||
decimal_places=4,
|
||||
default=Decimal("0"),
|
||||
default=Decimal('0'),
|
||||
null=False,
|
||||
blank=False,
|
||||
verbose_name=_("total sin impuestos"),
|
||||
verbose_name=_('total sin impuestos'),
|
||||
)
|
||||
|
||||
# Valor entero del impuesto en el momento en el que se crea el pedido
|
||||
tax_value = models.PositiveIntegerField(
|
||||
null=False, blank=False, verbose_name=_("valor de impuestos")
|
||||
)
|
||||
tax_value = models.PositiveIntegerField(null=False, blank=False, verbose_name=_('valor de impuestos'))
|
||||
|
||||
# taxes = price * quantity * (tax_value / 100)
|
||||
taxes = models.DecimalField(
|
||||
max_digits=13, decimal_places=4, verbose_name=_("impuestos")
|
||||
)
|
||||
taxes = models.DecimalField(max_digits=13, decimal_places=4, verbose_name=_('impuestos'))
|
||||
|
||||
# total = base_total + taxes
|
||||
total = models.DecimalField(
|
||||
max_digits=13, decimal_places=4, verbose_name=_("total")
|
||||
)
|
||||
total = models.DecimalField(max_digits=13, decimal_places=4, verbose_name=_('total'))
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.product.name} - {self.quantity} - {self.price}"
|
||||
return f'{self.product.name} - {self.quantity} - {self.price}'
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("línea de pedido")
|
||||
verbose_name_plural = _("líneas de pedido")
|
||||
verbose_name = _('línea de pedido')
|
||||
verbose_name_plural = _('líneas de pedido')
|
||||
|
||||
|
||||
def create_order_code(*args, **kwargs):
|
||||
return (
|
||||
timezone.now()
|
||||
.isoformat()
|
||||
.replace("-", "")
|
||||
.replace("T", "")
|
||||
.replace(":", "")
|
||||
.replace(".", "")[:-5]
|
||||
)
|
||||
return timezone.now().isoformat().replace('-', '').replace('T', '').replace(':', '').replace('.', '')[:-5]
|
||||
|
||||
|
||||
class Order(TimestampedModel):
|
||||
class Statuses(models.TextChoices):
|
||||
STATUS_PENDING = "PEN", _("pendiente de pago")
|
||||
STATUS_PAID = "PAI", _("pagado")
|
||||
STATUS_RETURNED = "RTN", _("devuelto")
|
||||
STATUS_ERROR = "ERR", _("Error")
|
||||
STATUS_PENDING = 'PEN', _('pendiente de pago')
|
||||
STATUS_PAID = 'PAI', _('pagado')
|
||||
STATUS_RETURNED = 'RTN', _('devuelto')
|
||||
STATUS_ERROR = 'ERR', _('Error')
|
||||
|
||||
class ShippingStatuses(models.TextChoices):
|
||||
STATUS_NOT_READY = "NOT", _("no preparado")
|
||||
STATUS_READY = "RDY", _("preparado")
|
||||
STATUS_TO_BE_SENT = "TBS", _("listo para ser enviado")
|
||||
STATUS_SENT = "SNT", _("enviado")
|
||||
STATUS_DELIVERED = "DLV", _("entregado")
|
||||
STATUS_FINISHED = "FIN", _("finalizado")
|
||||
STATUS_CANCELED = "CAN", _("cancelado")
|
||||
STATUS_REQUESTED_RETURN = "RQT", _("devolución solicitada")
|
||||
STATUS_RETURNED = "RTN", _("devuelto")
|
||||
STATUS_NOT_READY = 'NOT', _('no preparado')
|
||||
STATUS_READY = 'RDY', _('preparado')
|
||||
STATUS_TO_BE_SENT = 'TBS', _('listo para ser enviado')
|
||||
STATUS_SENT = 'SNT', _('enviado')
|
||||
STATUS_DELIVERED = 'DLV', _('entregado')
|
||||
STATUS_FINISHED = 'FIN', _('finalizado')
|
||||
STATUS_CANCELED = 'CAN', _('cancelado')
|
||||
STATUS_REQUESTED_RETURN = 'RQT', _('devolución solicitada')
|
||||
STATUS_RETURNED = 'RTN', _('devuelto')
|
||||
|
||||
uuid = models.UUIDField(default=uuid4, verbose_name=_("UUID"), db_index=True)
|
||||
uuid = models.UUIDField(default=uuid4, verbose_name=_('UUID'), db_index=True)
|
||||
code = models.CharField(default=create_order_code, max_length=20, unique=True)
|
||||
|
||||
status = models.CharField(
|
||||
max_length=3, default=Statuses.STATUS_PENDING, verbose_name=_("estado")
|
||||
)
|
||||
status = models.CharField(max_length=3, default=Statuses.STATUS_PENDING, verbose_name=_('estado'))
|
||||
|
||||
shipping_status = models.CharField(
|
||||
max_length=3,
|
||||
default=ShippingStatuses.STATUS_NOT_READY,
|
||||
verbose_name=_("estado de envío"),
|
||||
max_length=3, default=ShippingStatuses.STATUS_NOT_READY, verbose_name=_('estado de envío')
|
||||
)
|
||||
|
||||
base_total = models.DecimalField(
|
||||
default=Decimal("0"),
|
||||
max_digits=13,
|
||||
decimal_places=2,
|
||||
verbose_name=_("base imponible"),
|
||||
default=Decimal('0'), max_digits=13, decimal_places=2, verbose_name=_('base imponible')
|
||||
)
|
||||
|
||||
total = models.DecimalField(
|
||||
default=Decimal("0"), max_digits=13, decimal_places=2, verbose_name=_("total")
|
||||
)
|
||||
total = models.DecimalField(default=Decimal('0'), max_digits=13, decimal_places=2, verbose_name=_('total'))
|
||||
|
||||
user = models.ForeignKey(
|
||||
User, blank=True, null=True, on_delete=models.PROTECT, verbose_name=_("cliente")
|
||||
)
|
||||
email = models.EmailField(blank=True, verbose_name=_("e-mail"))
|
||||
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.PROTECT, verbose_name=_('cliente'))
|
||||
email = models.EmailField(blank=True, verbose_name=_('e-mail'))
|
||||
|
||||
# Datos de facturación
|
||||
billing_address = models.CharField(
|
||||
max_length=255, blank=False, verbose_name=_("dirección de facturación")
|
||||
)
|
||||
billing_city = models.CharField(
|
||||
max_length=64, blank=False, verbose_name=_("ciudad de facturación")
|
||||
)
|
||||
billing_state = models.CharField(
|
||||
max_length=64, blank=False, verbose_name=_("región de facturación")
|
||||
)
|
||||
billing_country = models.CharField(
|
||||
max_length=64, blank=False, verbose_name=_("país de facturación")
|
||||
)
|
||||
billing_zip = models.CharField(
|
||||
max_length=32, blank=False, verbose_name=_("código postal de facturación")
|
||||
)
|
||||
billing_address = models.CharField(max_length=255, blank=False, verbose_name=_('dirección de facturación'))
|
||||
billing_city = models.CharField(max_length=64, blank=False, verbose_name=_('ciudad de facturación'))
|
||||
billing_state = models.CharField(max_length=64, blank=False, verbose_name=_('región de facturación'))
|
||||
billing_country = models.CharField(max_length=64, blank=False, verbose_name=_('país de facturación'))
|
||||
billing_zip = models.CharField(max_length=32, blank=False, verbose_name=_('código postal de facturación'))
|
||||
|
||||
# Datos de envío
|
||||
shipping_address = models.CharField(
|
||||
max_length=255, blank=False, verbose_name=_("dirección")
|
||||
)
|
||||
shipping_city = models.CharField(
|
||||
max_length=64, blank=False, verbose_name=_("ciudad")
|
||||
)
|
||||
shipping_state = models.CharField(
|
||||
max_length=64, blank=False, verbose_name=_("región")
|
||||
)
|
||||
shipping_country = models.CharField(
|
||||
max_length=64, blank=False, verbose_name=_("país")
|
||||
)
|
||||
shipping_zip = models.CharField(
|
||||
max_length=32, blank=False, verbose_name=_("código postal")
|
||||
)
|
||||
contact_phone = models.CharField(
|
||||
max_length=32, blank=True, verbose_name=_("teléfono de contacto")
|
||||
)
|
||||
shipping_address = models.CharField(max_length=255, blank=False, verbose_name=_('dirección'))
|
||||
shipping_city = models.CharField(max_length=64, blank=False, verbose_name=_('ciudad'))
|
||||
shipping_state = models.CharField(max_length=64, blank=False, verbose_name=_('región'))
|
||||
shipping_country = models.CharField(max_length=64, blank=False, verbose_name=_('país'))
|
||||
shipping_zip = models.CharField(max_length=32, blank=False, verbose_name=_('código postal'))
|
||||
contact_phone = models.CharField(max_length=32, blank=True, verbose_name=_('teléfono de contacto'))
|
||||
|
||||
amount_paid = models.DecimalField(
|
||||
default=Decimal("0.00"),
|
||||
max_digits=11,
|
||||
decimal_places=2,
|
||||
verbose_name=_("cantidad pagada"),
|
||||
default=Decimal('0.00'), max_digits=11, decimal_places=2, verbose_name=_('cantidad pagada')
|
||||
)
|
||||
|
||||
shipping_method = models.ForeignKey(
|
||||
"shop.ShippingMethod",
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name=_("método de envío"),
|
||||
'shop.ShippingMethod', blank=True, null=True, on_delete=models.SET_NULL, verbose_name=_('método de envío')
|
||||
)
|
||||
|
||||
from_cart = models.ForeignKey(
|
||||
"shop.Cart",
|
||||
on_delete=models.SET_NULL,
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name=_("carrito de origen de pedido"),
|
||||
'shop.Cart', on_delete=models.SET_NULL, blank=True, null=True, verbose_name=_('carrito de origen de pedido')
|
||||
)
|
||||
|
||||
def calculate_total_from_lines(self):
|
||||
self.base_total = self.lines.aggregate(base_total=models.Sum("base_total")).get(
|
||||
"base_total"
|
||||
)
|
||||
self.total = self.lines.aggregate(base_total=models.Sum("total")).get(
|
||||
"base_total"
|
||||
)
|
||||
self.base_total = self.lines.aggregate(base_total=models.Sum('base_total')).get('base_total')
|
||||
self.total = self.lines.aggregate(base_total=models.Sum('total')).get('base_total')
|
||||
|
||||
self.save()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("pedido")
|
||||
verbose_name_plural = _("pedidos")
|
||||
ordering = ("creation_date",)
|
||||
verbose_name = _('pedido')
|
||||
verbose_name_plural = _('pedidos')
|
||||
ordering = ('creation_date',)
|
||||
|
||||
|
||||
class Cart(models.Model):
|
||||
uuid = models.UUIDField(default=uuid4, primary_key=True, verbose_name=_("uuid"))
|
||||
user = models.ForeignKey(
|
||||
User,
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_("usuario"),
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
creation_date = models.DateTimeField(
|
||||
auto_now_add=True, verbose_name=_("fecha de creación")
|
||||
)
|
||||
uuid = models.UUIDField(default=uuid4, primary_key=True, verbose_name=_('uuid'))
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_('usuario'), blank=True, null=True)
|
||||
creation_date = models.DateTimeField(auto_now_add=True, verbose_name=_('fecha de creación'))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("carrito")
|
||||
verbose_name_plural = _("carritos")
|
||||
verbose_name = _('carrito')
|
||||
verbose_name_plural = _('carritos')
|
||||
|
||||
|
||||
class CartItem(models.Model):
|
||||
cart = models.ForeignKey(
|
||||
"shop.Cart",
|
||||
on_delete=models.CASCADE,
|
||||
related_name="items",
|
||||
verbose_name=_("carrito"),
|
||||
)
|
||||
product = models.ForeignKey(
|
||||
"shop.Product", on_delete=models.CASCADE, verbose_name=_("producto")
|
||||
)
|
||||
quantity = models.PositiveIntegerField(default=1, verbose_name=_("cantidad"))
|
||||
cart = models.ForeignKey('shop.Cart', on_delete=models.CASCADE, related_name='items', verbose_name=_('carrito'))
|
||||
product = models.ForeignKey('shop.Product', on_delete=models.CASCADE, verbose_name=_('producto'))
|
||||
quantity = models.PositiveIntegerField(default=1, verbose_name=_('cantidad'))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("línea de carrito")
|
||||
verbose_name_plural = _("líneas de carrito")
|
||||
verbose_name = _('línea de carrito')
|
||||
verbose_name_plural = _('líneas de carrito')
|
||||
|
||||
|
||||
class CustomerAddress(models.Model):
|
||||
class Types(models.TextChoices):
|
||||
BILLING = "BILL", _("facturación")
|
||||
SHIPPING = "SHIP", _("envío")
|
||||
BILLING = 'BILL', _('facturación')
|
||||
SHIPPING = 'SHIP', _('envío')
|
||||
|
||||
user = models.ForeignKey(
|
||||
User, on_delete=models.CASCADE, null=True, verbose_name=_("usuario")
|
||||
)
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, verbose_name=_('usuario'))
|
||||
vat_id = models.CharField(
|
||||
max_length=16,
|
||||
blank=True,
|
||||
null=True,
|
||||
default="",
|
||||
verbose_name=_("número de identificación fiscal"),
|
||||
)
|
||||
full_name = models.CharField(
|
||||
max_length=128, blank=False, null=False, verbose_name=_("dirección")
|
||||
)
|
||||
address = models.CharField(
|
||||
max_length=128, blank=False, null=False, verbose_name=_("dirección")
|
||||
)
|
||||
address_town = models.CharField(
|
||||
max_length=64, blank=False, null=False, verbose_name=_("localidad")
|
||||
)
|
||||
address_zip = models.CharField(
|
||||
max_length=16, blank=False, null=False, verbose_name=_("código postal")
|
||||
)
|
||||
address_state = models.CharField(
|
||||
max_length=64, blank=False, null=False, verbose_name=_("provincia")
|
||||
max_length=16, blank=True, null=True, default='', verbose_name=_('número de identificación fiscal')
|
||||
)
|
||||
full_name = models.CharField(max_length=128, blank=False, null=False, verbose_name=_('dirección'))
|
||||
address = models.CharField(max_length=128, blank=False, null=False, verbose_name=_('dirección'))
|
||||
address_town = models.CharField(max_length=64, blank=False, null=False, verbose_name=_('localidad'))
|
||||
address_zip = models.CharField(max_length=16, blank=False, null=False, verbose_name=_('código postal'))
|
||||
address_state = models.CharField(max_length=64, blank=False, null=False, verbose_name=_('provincia'))
|
||||
address_country = models.CharField(
|
||||
max_length=64,
|
||||
default=settings.SHOP_COUNTRY,
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_("país"),
|
||||
)
|
||||
address_phone = models.CharField(
|
||||
max_length=16, blank=True, null=True, default="", verbose_name=_("teléfono")
|
||||
max_length=64, default=settings.SHOP_COUNTRY, blank=False, null=False, verbose_name=_('país')
|
||||
)
|
||||
address_phone = models.CharField(max_length=16, blank=True, null=True, default='', verbose_name=_('teléfono'))
|
||||
|
||||
address_type = models.CharField(
|
||||
max_length=4,
|
||||
choices=Types.choices,
|
||||
default=Types.SHIPPING,
|
||||
verbose_name=_("tipo de dirección"),
|
||||
max_length=4, choices=Types.choices, default=Types.SHIPPING, verbose_name=_('tipo de dirección')
|
||||
)
|
||||
email = models.EmailField(blank=False, null=False, verbose_name=_("e-mail"))
|
||||
default = models.BooleanField(default=True, verbose_name=_("por defecto"))
|
||||
hidden = models.BooleanField(default=False, db_index=True, verbose_name=_("oculto"))
|
||||
email = models.EmailField(blank=False, null=False, verbose_name=_('e-mail'))
|
||||
default = models.BooleanField(default=True, verbose_name=_('por defecto'))
|
||||
hidden = models.BooleanField(default=False, db_index=True, verbose_name=_('oculto'))
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.full_name} - {self.address}, {self.address_zip}, {self.address_state} - {self.address_country}"
|
||||
return f'{self.full_name} - {self.address}, {self.address_zip}, {self.address_state} - {self.address_country}'
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("dirección de cliente")
|
||||
verbose_name_plural = _("direcciones de cliente")
|
||||
verbose_name = _('dirección de cliente')
|
||||
verbose_name_plural = _('direcciones de cliente')
|
||||
|
||||
|
||||
class ShippingMethod(models.Model):
|
||||
name = models.CharField(max_length=64, verbose_name=_("nombre"))
|
||||
description = models.CharField(
|
||||
max_length=256, default="", blank=True, verbose_name=_("description")
|
||||
)
|
||||
url = models.URLField(default="", blank=True, verbose_name=_("url"))
|
||||
shipping_product = models.ForeignKey(
|
||||
"shop.Product", on_delete=models.CASCADE, verbose_name=_("producto de envío")
|
||||
)
|
||||
enabled = models.BooleanField(default=True, verbose_name=_("habilitado"))
|
||||
name = models.CharField(max_length=64, verbose_name=_('nombre'))
|
||||
description = models.CharField(max_length=256, default='', blank=True, verbose_name=_('description'))
|
||||
url = models.URLField(default='', blank=True, verbose_name=_('url'))
|
||||
shipping_product = models.ForeignKey('shop.Product', on_delete=models.CASCADE, verbose_name=_('producto de envío'))
|
||||
enabled = models.BooleanField(default=True, verbose_name=_('habilitado'))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("método de envío")
|
||||
verbose_name_plural = _("métodos de envío")
|
||||
verbose_name = _('método de envío')
|
||||
verbose_name_plural = _('métodos de envío')
|
||||
|
||||
|
||||
class WishlistedProduct(TimestampedModel):
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_("usuario"))
|
||||
product = models.ForeignKey(
|
||||
"shop.Product", on_delete=models.CASCADE, verbose_name=_("producto")
|
||||
)
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_('usuario'))
|
||||
product = models.ForeignKey('shop.Product', on_delete=models.CASCADE, verbose_name=_('producto'))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("productos deseados")
|
||||
verbose_name_plural = _("productos deseados")
|
||||
unique_together = (
|
||||
"user",
|
||||
"product",
|
||||
)
|
||||
verbose_name = _('productos deseados')
|
||||
verbose_name_plural = _('productos deseados')
|
||||
unique_together = ('user', 'product')
|
||||
|
||||
|
||||
class Payment(models.Model):
|
||||
class MethodChoices(models.TextChoices):
|
||||
REDSYS = "REDSYS"
|
||||
REDSYS = 'REDSYS'
|
||||
|
||||
hash = models.UUIDField(verbose_name=_("Hash"), default=uuid4, primary_key=True)
|
||||
amount = models.DecimalField(
|
||||
max_digits=8,
|
||||
decimal_places=2,
|
||||
default=Decimal("0"),
|
||||
verbose_name=_("Cantidad"),
|
||||
)
|
||||
hash = models.UUIDField(verbose_name=_('Hash'), default=uuid4, primary_key=True)
|
||||
amount = models.DecimalField(max_digits=8, decimal_places=2, default=Decimal('0'), verbose_name=_('Cantidad'))
|
||||
order = models.ForeignKey(
|
||||
"shop.Order",
|
||||
on_delete=models.PROTECT,
|
||||
verbose_name=_("pedido"),
|
||||
related_name="payments",
|
||||
)
|
||||
user = models.ForeignKey(
|
||||
User, blank=True, null=True, on_delete=models.PROTECT, verbose_name=_("cliente")
|
||||
'shop.Order', on_delete=models.PROTECT, verbose_name=_('pedido'), related_name='payments'
|
||||
)
|
||||
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.PROTECT, verbose_name=_('cliente'))
|
||||
|
||||
creation_date = models.DateTimeField(
|
||||
auto_now_add=True, verbose_name=_("Fecha de creación")
|
||||
)
|
||||
creation_date = models.DateTimeField(auto_now_add=True, verbose_name=_('Fecha de creación'))
|
||||
|
||||
metadata = models.JSONField(default=dict, verbose_name=_("Metadata"))
|
||||
metadata = models.JSONField(default=dict, verbose_name=_('Metadata'))
|
||||
|
||||
method = models.CharField(
|
||||
max_length=6,
|
||||
choices=MethodChoices.choices,
|
||||
default=MethodChoices.REDSYS,
|
||||
verbose_name=_("método de pago"),
|
||||
max_length=6, choices=MethodChoices.choices, default=MethodChoices.REDSYS, verbose_name=_('método de pago')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Pago")
|
||||
verbose_name_plural = _("Pagos")
|
||||
verbose_name = _('Pago')
|
||||
verbose_name_plural = _('Pagos')
|
||||
|
||||
|
||||
class ShopSettings(SingletonModel):
|
||||
debug = models.BooleanField(default=True, verbose_name=_("Modo depuración"))
|
||||
debug = models.BooleanField(default=True, verbose_name=_('Modo depuración'))
|
||||
merchant_code = models.CharField(
|
||||
max_length=9,
|
||||
blank=False,
|
||||
null=False,
|
||||
verbose_name=_("Identificación de comercio"),
|
||||
max_length=9, blank=False, null=False, verbose_name=_('Identificación de comercio')
|
||||
)
|
||||
currency_code = models.CharField(
|
||||
max_length=4,
|
||||
default="978",
|
||||
choices=CURRENCY_CODES,
|
||||
verbose_name=_("Código de moneda"),
|
||||
max_length=4, default='978', choices=CURRENCY_CODES, verbose_name=_('Código de moneda')
|
||||
) # 978 == EURO
|
||||
terminal = models.CharField(max_length=8, verbose_name=_("Terminal"))
|
||||
shared_secret = models.CharField(max_length=100, verbose_name=_("Clave de Redsys"))
|
||||
tpv_domain = models.URLField(
|
||||
max_length=128, blank=True, null=True, verbose_name=_("dominio del tpv")
|
||||
)
|
||||
terminal = models.CharField(max_length=8, verbose_name=_('Terminal'))
|
||||
shared_secret = models.CharField(max_length=100, verbose_name=_('Clave de Redsys'))
|
||||
tpv_domain = models.URLField(max_length=128, blank=True, null=True, verbose_name=_('dominio del tpv'))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("ajustes de la tienda")
|
||||
verbose_name_plural = _("ajustes de la tienda")
|
||||
verbose_name = _('ajustes de la tienda')
|
||||
verbose_name_plural = _('ajustes de la tienda')
|
||||
|
||||
Reference in New Issue
Block a user