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:
parent
26310dfc42
commit
96e54e2a0a
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user