diff --git a/barker/barker/models/inventory.py b/barker/barker/models/inventory.py index ae88faa..a3697b2 100644 --- a/barker/barker/models/inventory.py +++ b/barker/barker/models/inventory.py @@ -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") diff --git a/barker/barker/models/user.py b/barker/barker/models/user.py index c2de9b6..80f1da1 100644 --- a/barker/barker/models/user.py +++ b/barker/barker/models/user.py @@ -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 diff --git a/barker/barker/schemas/master.py b/barker/barker/schemas/master.py index 8944920..766d9b7 100644 --- a/barker/barker/schemas/master.py +++ b/barker/barker/schemas/master.py @@ -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 diff --git a/barker/barker/schemas/voucher.py b/barker/barker/schemas/voucher.py index 9012bdb..07b9dff 100644 --- a/barker/barker/schemas/voucher.py +++ b/barker/barker/schemas/voucher.py @@ -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( diff --git a/barker/pyproject.toml b/barker/pyproject.toml index 60af26a..df52c30 100644 --- a/barker/pyproject.toml +++ b/barker/pyproject.toml @@ -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