import json import uuid from datetime import date, datetime from decimal import Decimal from typing import List, Optional from brewman.schemas import to_camel from brewman.schemas.master import AccountLink, CostCentreLink from brewman.schemas.voucher import ( EmployeeBenefit, Incentive, Inventory, Journal, VoucherIn, ) from fastapi import Form from pydantic import BaseModel, Field, validator from sqlalchemy.orm import Session class JournalIn(VoucherIn): journals: List[Journal] class Config: anystr_strip_whitespace = True alias_generator = to_camel json_encoders = { date: lambda v: v.strftime("%d-%b-%Y"), datetime: lambda v: v.strftime("%d-%b-%Y %H:%I"), } @validator("date_", pre=True) def parse_date(cls, value): if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @validator("journals") def validate_signed_amount(cls, value: List[Journal]): if sum(x.debit * x.amount for x in value) != 0: raise ValueError("Journal amounts do no match") return value @validator("journals") def is_distinct(cls, value: List[Journal]): journal_set = set( hash(x.account.id_) ^ hash(None if x.cost_centre is None else x.cost_centre.id_) for x in value ) if len(value) != len(journal_set): raise ValueError("Duplicate journals") return value @classmethod def load_form(cls, data: str = Form(...)): json_data = json.loads(data) return cls.parse_obj(json_data) class PurchaseIn(VoucherIn): vendor: AccountLink inventories: List[Inventory] class Config: anystr_strip_whitespace = True alias_generator = to_camel json_encoders = { date: lambda v: v.strftime("%d-%b-%Y"), datetime: lambda v: v.strftime("%d-%b-%Y %H:%I"), } @validator("date_", pre=True) def parse_date(cls, value): if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @validator("inventories") # For Purchase, Issue and Return Vouchers def validate_enough_inventories(cls, value: List[Inventory]): if len(value) < 1: raise ValueError("Not enough inventories") return value @validator("inventories") # For Purchase, Issue and Return Vouchers def validate_inventories_unique(cls, value: List[Inventory]): if len(set(x.product.id_ for x in value)) != len(value): raise ValueError("Duplicate products") return value @classmethod def load_form(cls, data: str = Form(...)): json_data = json.loads(data) return cls.parse_obj(json_data) class IssueIn(VoucherIn): source: CostCentreLink destination: CostCentreLink inventories: List[Inventory] class Config: anystr_strip_whitespace = True alias_generator = to_camel json_encoders = { date: lambda v: v.strftime("%d-%b-%Y"), datetime: lambda v: v.strftime("%d-%b-%Y %H:%I"), } @validator("date_", pre=True) def parse_date(cls, value): if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @validator("inventories") # For Purchase, Issue and Return Vouchers def validate_enough_inventories(cls, value: List[Inventory]): if len(value) < 1: raise ValueError("Not enough inventories") return value @validator("inventories") # For Purchase, Issue and Return Vouchers def validate_inventories_unique(cls, value: List[Inventory]): if len(set(x.product.id_ for x in value)) != len(value): raise ValueError("Duplicate products") return value @validator("destination") # For Purchase, Issue and Return Vouchers def source_destination_unique(cls, value: CostCentreLink, values): if value.id_ == values["source"].id_: raise ValueError("Source and destination cannot be the same") return value @classmethod def load_form(cls, data: str = Form(...)): json_data = json.loads(data) return cls.parse_obj(json_data) class EmployeeBenefitIn(VoucherIn): employee_benefits: List[EmployeeBenefit] class Config: anystr_strip_whitespace = True alias_generator = to_camel json_encoders = { date: lambda v: v.strftime("%d-%b-%Y"), datetime: lambda v: v.strftime("%d-%b-%Y %H:%I"), } @validator("date_", pre=True) def parse_date(cls, value): if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @classmethod def load_form(cls, data: str = Form(...)): json_data = json.loads(data) return cls.parse_obj(json_data) class IncentiveIn(VoucherIn): incentives: List[Incentive] class Config: anystr_strip_whitespace = True alias_generator = to_camel json_encoders = { date: lambda v: v.strftime("%d-%b-%Y"), datetime: lambda v: v.strftime("%d-%b-%Y %H:%I"), } @validator("date_", pre=True) def parse_date(cls, value): if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @classmethod def load_form(cls, data: str = Form(...)): json_data = json.loads(data) return cls.parse_obj(json_data) class IncentiveEmployee(BaseModel): id_: Optional[uuid.UUID] employee_id: uuid.UUID cost_centre_id: uuid.UUID journal: Optional[Journal] days_worked: Decimal = Field(ge=0, multiple_of=0.5) points: Decimal = Field(ge=0, multiple_of=0.01)