update
This commit is contained in:
107
inventory/models.py
Normal file
107
inventory/models.py
Normal file
@@ -0,0 +1,107 @@
|
||||
from django.conf import settings
|
||||
from django.core.validators import MinValueValidator
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
class ItemTemplate(models.Model):
|
||||
name = models.CharField(max_length=120, unique=True)
|
||||
description = models.TextField(blank=True)
|
||||
category = models.CharField(max_length=80)
|
||||
# field_definitions is a list of objects describing extra fields for items of this template.
|
||||
# Example: [{"name": "rewind", "label": "Rewind?", "type": "boolean"}, {"name":"output_count","label":"Outputs","type":"number"}]
|
||||
field_definitions = models.JSONField(default=list, blank=True)
|
||||
default_purchase_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
|
||||
default_estimated_resale_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
|
||||
default_notes = models.TextField(blank=True)
|
||||
is_active = models.BooleanField(default=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
|
||||
class Item(models.Model):
|
||||
class Status(models.TextChoices):
|
||||
IN_STOCK = "in_stock", "In stock"
|
||||
LISTED = "listed", "Listed"
|
||||
SOLD = "sold", "Sold"
|
||||
DONATED = "donated", "Donated"
|
||||
|
||||
template = models.ForeignKey(ItemTemplate, null=True, blank=True, on_delete=models.SET_NULL, related_name="items")
|
||||
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name="items_created")
|
||||
title = models.CharField(max_length=200)
|
||||
brand = models.CharField(max_length=120, blank=True)
|
||||
category = models.CharField(max_length=80)
|
||||
condition = models.CharField(max_length=80, blank=True)
|
||||
size = models.CharField(max_length=50, blank=True)
|
||||
color = models.CharField(max_length=80, blank=True)
|
||||
purchase_price = models.DecimalField(max_digits=10, decimal_places=2, validators=[MinValueValidator(0)])
|
||||
estimated_resale_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
|
||||
sold_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
|
||||
ebay_fee = models.DecimalField(max_digits=10, decimal_places=2, default=0)
|
||||
shipping_cost = models.DecimalField(max_digits=10, decimal_places=2, default=0)
|
||||
status = models.CharField(max_length=20, choices=Status.choices, default=Status.IN_STOCK)
|
||||
notes = models.TextField(blank=True)
|
||||
# properties stores template-specific values keyed by field name
|
||||
properties = models.JSONField(default=dict, blank=True)
|
||||
acquisition_date = models.DateField(auto_now_add=True)
|
||||
sold_at = models.DateTimeField(null=True, blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
@property
|
||||
def total_cost(self):
|
||||
return self.purchase_price + self.ebay_fee + self.shipping_cost
|
||||
|
||||
@property
|
||||
def profit(self):
|
||||
if self.sold_price is None:
|
||||
return None
|
||||
return self.sold_price - self.total_cost
|
||||
|
||||
@property
|
||||
def margin(self):
|
||||
if self.profit is None or self.sold_price in (None, 0):
|
||||
return None
|
||||
return self.profit / self.sold_price
|
||||
|
||||
def mark_sold(self, sold_price, sold_at=None):
|
||||
self.sold_price = sold_price
|
||||
self.status = self.Status.SOLD
|
||||
self.sold_at = sold_at or timezone.now()
|
||||
self.save(update_fields=["sold_price", "status", "sold_at", "updated_at"])
|
||||
|
||||
def get_absolute_url(self):
|
||||
from django.urls import reverse
|
||||
|
||||
return reverse("item-detail", kwargs={"pk": self.pk})
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.title
|
||||
|
||||
|
||||
class PriceEstimate(models.Model):
|
||||
item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name="price_estimates")
|
||||
source = models.CharField(max_length=80)
|
||||
source_url = models.URLField(blank=True)
|
||||
estimated_price = models.DecimalField(max_digits=10, decimal_places=2)
|
||||
notes = models.TextField(blank=True)
|
||||
retrieved_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ["-retrieved_at"]
|
||||
|
||||
|
||||
class ItemNote(models.Model):
|
||||
item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name="notes_history")
|
||||
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
|
||||
body = models.TextField()
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
|
||||
class ItemPhoto(models.Model):
|
||||
item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name="photos")
|
||||
file = models.FileField(upload_to="item-photos/")
|
||||
uploaded_at = models.DateTimeField(auto_now_add=True)
|
||||
Reference in New Issue
Block a user