Fix: Repint was creating new bills. Rounding caused it to think that the amounts had changed.

Made the inventory amount expression round to even and the inventory schema to validate to round the inputs
This commit is contained in:
Amritanshu Agrawal 2023-07-26 13:00:01 +05:30
parent 26310dfc42
commit 96e54e2a0a
5 changed files with 58 additions and 56 deletions

View File

@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, List
from barker.models.product import Product
from sqlalchemy import (
Boolean,
ColumnElement,
ForeignKey,
Integer,
Numeric,
@ -13,6 +14,7 @@ from sqlalchemy import (
case,
func,
text,
type_coerce,
)
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.ext.hybrid import hybrid_property
@ -86,37 +88,44 @@ class Inventory:
self.tax = tax
@hybrid_property
def effective_price(self):
def effective_price(self) -> Decimal:
return 0 if self.is_happy_hour else self.price
@effective_price.inplace.expression
@classmethod
def _effective_price_expression(cls):
return case((cls.is_happy_hour == True, 0), else_=cls.price) # noqa: E712
def _effective_price_expression(cls) -> ColumnElement[Decimal]:
return type_coerce(
case((cls.is_happy_hour == True, 0), else_=cls.price), Numeric(precision=15, scale=2) # noqa: E712
).label("effective_price")
@hybrid_property
def net(self):
def net(self) -> Decimal:
return self.effective_price * self.quantity * (1 - self.discount)
@net.inplace.expression
@classmethod
def _net_expression(cls):
return cls.effective_price * cls.quantity * (1 - cls.discount)
def _net_expression(cls) -> ColumnElement[Decimal]:
return type_coerce(
cls.effective_price * cls.quantity * (1 - cls.discount), Numeric(precision=15, scale=2)
).label("net")
@hybrid_property
def tax_amount(self):
def tax_amount(self) -> Decimal:
return self.net * self.tax_rate
@tax_amount.inplace.expression
@classmethod
def _tax_amount_expression(cls):
return cls.net * cls.tax_rate
def _tax_amount_expression(cls) -> ColumnElement[Decimal]:
return type_coerce(cls.net * cls.tax_rate, Numeric(precision=15, scale=2)).label("tax_amount")
@hybrid_property
def amount(self):
def amount(self) -> Decimal:
return round(Decimal(self.net * (1 + self.tax_rate)), 2)
@amount.inplace.expression
@classmethod
def _amount_expression(cls):
return func.round(cls.net * (1 + cls.tax_rate), 2)
def _amount_expression(cls) -> ColumnElement[Decimal]:
# func.round(cls.net * (1 + cls.tax_rate), 2)
return type_coerce(
func.round(cls.net * (1 + cls.tax_rate) / 0.02, 0) * 0.02, Numeric(precision=15, scale=2)
).label("amount")

View File

@ -39,8 +39,8 @@ class User:
return self._password
@password.inplace.setter
def _password_setter(self, password):
self._password = encrypt(password)
def _password_setter(self, value: str) -> None:
self._password = encrypt(value)
def __init__(self, name=None, password=None, locked_out=None, id_=None):
self.name = name

View File

@ -1,9 +1,6 @@
import uuid
from datetime import date, datetime
from decimal import Decimal
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
from pydantic import BaseModel, ConfigDict, Field
from . import to_camel
@ -26,43 +23,6 @@ class Account(AccountIn):
is_fixture: bool
class EmployeeIn(AccountBase):
designation: str
salary: int = Field(ge=0)
points: Decimal = Field(ge=0, lt=1000)
joining_date: date
leaving_date: date | None = None
@field_validator("joining_date", mode="before")
@classmethod
def parse_joining_date(cls, value):
return datetime.strptime(value, "%d-%b-%Y").date()
@field_validator("leaving_date", mode="before")
@classmethod
def parse_leaving_date(cls, value):
if value is None or value == "":
return None
else:
return datetime.strptime(value, "%d-%b-%Y").date()
@model_validator(mode="after")
def leaving_date_more_than_joining_date(self) -> "EmployeeIn":
if self.is_active:
self.leaving_date = None
if (not self.is_active) and (self.leaving_date is None):
raise ValueError("Need leaving date for employee")
if self.leaving_date < self.joining_date:
raise ValueError("Leaving Date cannot be less than Joining Date")
return self
class Employee(EmployeeIn):
id_: uuid.UUID
code: int
is_fixture: bool
class DbSetting(BaseModel):
id_: uuid.UUID
name: str

View File

@ -2,7 +2,7 @@ import uuid
from decimal import Decimal
from pydantic import BaseModel, ConfigDict, Field, model_validator
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
from . import to_camel
from .customer import CustomerLink
@ -25,6 +25,34 @@ class Inventory(BaseModel):
amount: Decimal | None = None
model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
@field_validator("quantity")
@classmethod
def _quantity(cls, value: Decimal | None) -> Decimal:
if value is None:
return Decimal(0)
return round(value, 2)
@field_validator("price")
@classmethod
def _price(cls, value: Decimal | None) -> Decimal:
if value is None:
return Decimal(0)
return round(value, 2)
@field_validator("tax_rate")
@classmethod
def _tax_rate(cls, value: Decimal | None) -> Decimal:
if value is None:
return Decimal(0)
return round(value, 5)
@field_validator("discount")
@classmethod
def _discount(cls, value: Decimal | None) -> Decimal:
if value is None:
return Decimal(0)
return round(value, 5)
@model_validator(mode="after")
def calculate_amount(self) -> "Inventory":
self.amount = round(

View File

@ -39,6 +39,11 @@ tomli = "^2.0.1"
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.ruff]
line-length = 120
# Assume Python 3.11.
target-version = "py311"
[tool.isort]
profile = "black"
atomic = true