From 96e54e2a0a76ec164b67081712d6172cd3a443c1 Mon Sep 17 00:00:00 2001 From: Amritanshu Date: Wed, 26 Jul 2023 13:00:01 +0530 Subject: [PATCH] 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 --- barker/barker/models/inventory.py | 33 +++++++++++++++--------- barker/barker/models/user.py | 4 +-- barker/barker/schemas/master.py | 42 +------------------------------ barker/barker/schemas/voucher.py | 30 +++++++++++++++++++++- barker/pyproject.toml | 5 ++++ 5 files changed, 58 insertions(+), 56 deletions(-) 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