Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions entities/migrations/0002_entity_price_cents.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 6.0.1 on 2026-01-24 03:43

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('entities', '0001_initial'),
]

operations = [
migrations.AddField(
model_name='entity',
name='price_cents',
field=models.IntegerField(default=0),
),
]
41 changes: 41 additions & 0 deletions entities/migrations/0003_entity_unit_price.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from decimal import Decimal
from django.db import migrations, models


def forwards(apps, schema_editor):
Entity = apps.get_model('entities', 'Entity')
for obj in Entity.objects.all():
# convert existing price_cents (int) to unit_price Decimal
cents = getattr(obj, 'price_cents', None)
if cents is not None:
obj.unit_price = Decimal(cents) / Decimal('100')
obj.save(update_fields=['unit_price'])


def backwards(apps, schema_editor):
Entity = apps.get_model('entities', 'Entity')
for obj in Entity.objects.all():
val = getattr(obj, 'unit_price', None)
if val is not None:
obj.price_cents = int((Decimal(val) * Decimal('100')).to_integral_value())
obj.save(update_fields=['price_cents'])


class Migration(migrations.Migration):

dependencies = [
('entities', '0002_entity_price_cents'),
]

operations = [
migrations.AddField(
model_name='entity',
name='unit_price',
field=models.DecimalField(decimal_places=4, default=Decimal('0.0000'), max_digits=12),
),
migrations.RunPython(forwards, backwards),
migrations.RemoveField(
model_name='entity',
name='price_cents',
),
]
15 changes: 14 additions & 1 deletion entities/models.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
from django.db import models
from decimal import Decimal, ROUND_DOWN


class Entity(models.Model):
type = models.CharField(max_length=100)
name = models.CharField(max_length=255, blank=True, null=True)
description = models.TextField(blank=True, null=True)
# store unit price as Decimal with 4 decimal places to preserve precision from measurements
unit_price = models.DecimalField(max_digits=12, decimal_places=4, default=Decimal('0.0000'))
created_at = models.DateTimeField(auto_now_add=True)

def __str__(self):
return f"{self.type}: {self.name or self.pk}"
return f"{self.type}: {self.name or self.pk}"

def unit_price_float(self) -> float:
"""Return unit price as float for convenience (not used for storage)."""
return float(self.unit_price or Decimal('0'))

def price_cents_truncated(self) -> int:
"""Return price in cents after truncating to 2 decimal places (no rounding)."""
val = (Decimal(self.unit_price or Decimal('0'))).quantize(Decimal('0.01'), rounding=ROUND_DOWN)
return int((val * 100).to_integral_value())
8 changes: 7 additions & 1 deletion entities/schemas.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
from pydantic import BaseModel, constr
from typing import Optional
from datetime import datetime
from decimal import Decimal

class EntityBase(BaseModel):
type: constr(max_length=100)
name: Optional[constr(max_length=255)] = None
description: Optional[str] = None
# store and accept unit_price with Decimal precision; API accepts float/str and will be parsed
unit_price: Optional[Decimal] = None

model_config = {"from_attributes": True}

Expand All @@ -16,9 +19,12 @@ class EntityUpdate(BaseModel):
type: Optional[constr(max_length=100)] = None
name: Optional[constr(max_length=255)] = None
description: Optional[str] = None
unit_price: Optional[Decimal] = None

model_config = {"from_attributes": True}

class EntityOut(EntityBase):
id: int
created_at: datetime
created_at: datetime
# unit_price returned as Decimal (serialized by Pydantic)
unit_price: Decimal
22 changes: 22 additions & 0 deletions entities/services.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Dict, Any
from django.db import transaction
from .models import Entity
from decimal import Decimal, InvalidOperation, ROUND_DOWN

def list_entities(
name: str = None,
Expand Down Expand Up @@ -35,11 +36,32 @@ def get_entity(entity_id: int) -> Entity:
return Entity.objects.get(pk=entity_id)

def create_entity(data: Dict[str, Any]) -> Entity:
# accept `unit_price` (e.g. 50.99) and store as Decimal with 4 decimal places
unit = data.pop('unit_price', None)
if unit is not None:
# accept both 50.99 and '50,99' by normalizing comma to dot
unit_str = str(unit).replace(',', '.')
try:
d = Decimal(unit_str)
except (InvalidOperation, ValueError):
d = Decimal(float(unit_str))
# store with 4 decimal places to preserve measurement precision, truncate (ROUND_DOWN)
data['unit_price'] = d.quantize(Decimal('0.0001'), rounding=ROUND_DOWN)
with transaction.atomic():
return Entity.objects.create(**data)

def update_entity(entity_id: int, data: Dict[str, Any]) -> Entity:
with transaction.atomic():
# support updating via `unit_price` as well
unit = data.pop('unit_price', None)
if unit is not None:
unit_str = str(unit).replace(',', '.')
try:
d = Decimal(unit_str)
except (InvalidOperation, ValueError):
d = Decimal(float(unit_str))
data['unit_price'] = d.quantize(Decimal('0.0001'), rounding=ROUND_DOWN)

obj = Entity.objects.get(pk=entity_id)
for k, v in data.items():
setattr(obj, k, v)
Expand Down
Loading