import json import uuid from datetime import date, datetime from decimal import Decimal from typing import Any, List, Optional from brewman.schemas import to_camel from brewman.schemas.master import ( AccountLink, CostCentreLink, EmployeeLink, ProductLink, ) from fastapi import Form from pydantic import BaseModel, Field, validator class ImageUpload(BaseModel): id_: Optional[uuid.UUID] resized: Optional[str] thumbnail: Optional[str] class Config: alias_generator = to_camel class UserLink(BaseModel): id_: uuid.UUID name: str class Config: alias_generator = to_camel class Journal(BaseModel): id_: Optional[uuid.UUID] debit: int = Field(ge=-1, le=1, multiple_of=1) amount: Decimal = Field(ge=0, multiple_of=0.01) account: AccountLink cost_centre: Optional[CostCentreLink] class Config: anystr_strip_whitespace = True alias_generator = to_camel class Batch(BaseModel): id_: uuid.UUID name: str product: ProductLink quantity_remaining: Decimal rate: Decimal tax: Decimal discount: Decimal class Config: alias_generator = to_camel class Inventory(BaseModel): id_: Optional[uuid.UUID] product: ProductLink batch: Optional[Batch] quantity: Decimal = Field(ge=0, multiple_of=0.01) rate: Decimal = Field(ge=0, multiple_of=0.01) tax: Decimal = Field(ge=0, multiple_of=0.00001, le=5) discount: Decimal = Field(ge=0, multiple_of=0.00001, le=1) amount: Optional[Decimal] class Config: alias_generator = to_camel class EmployeeBenefit(BaseModel): id_: Optional[uuid.UUID] employee: Optional[EmployeeLink] gross_salary: int = Field(ge=0) days_worked: int = Field(ge=0) esi_employee: Optional[int] = Field(ge=0) pf_employee: Optional[int] = Field(ge=0) esi_employer: Optional[int] = Field(ge=0) pf_employer: Optional[int] = Field(ge=0) class Config: alias_generator = to_camel class Incentive(BaseModel): employee_id: uuid.UUID name: str designation: str department: str days_worked: Decimal = Field(ge=0, multiple_of=0.5) points: Decimal = Field(ge=0, multiple_of=0.01) class Config: alias_generator = to_camel class VoucherIn(BaseModel): date_: date narration: str is_starred: bool type_: str files: List[ImageUpload] @classmethod def load_form(cls, data: str = Form(...)): json_data = json.loads(data) return cls.parse_obj(json_data) class Voucher(VoucherIn): id_: Optional[uuid.UUID] is_reconciled: Optional[bool] reconcile_date: Optional[date] creation_date: Optional[datetime] last_edit_date: Optional[datetime] user: Optional[UserLink] posted: Optional[bool] poster_id: Optional[uuid.UUID] journals: List[Journal] inventories: List[Inventory] vendor: Optional[AccountLink] source: Optional[CostCentreLink] destination: Optional[CostCentreLink] incentives: Optional[List[Incentive]] incentive: Optional[Decimal] employee_benefits: List[EmployeeBenefit] files: List[ImageUpload] 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("creation_date", pre=True) def parse_creation_date(cls, value): if isinstance(value, datetime): return value return datetime.strptime(value, "%d-%b-%Y %H:%M") @validator("last_edit_date", pre=True) def parse_last_edit_date(cls, value): if isinstance(value, datetime): return value return datetime.strptime(value, "%d-%b-%Y %H:%M") @validator("reconcile_date", pre=True) def parse_reconcile_date(cls, value): if value is None or value == "": return None elif isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() # @validator("journals") # def validate_enough_journals(cls, value: List[Journal]): # if 0 < len(value) < 2: # raise ValueError("Not enough journals") # return value # @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 class AttendanceType(BaseModel): id_: int name: Optional[str] value: Optional[Decimal] class Config: alias_generator = to_camel class AttendanceItem(BaseModel): id_: uuid.UUID code: int name: str designation: str department: str attendance_type: AttendanceType prints: str hours_worked: str full_day: Optional[bool] class Config: alias_generator = to_camel class Attendance(BaseModel): date_: Optional[date] body: List[AttendanceItem] @validator("date_", pre=True) def parse_date(cls, value): if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() class Config: anystr_strip_whitespace = True alias_generator = to_camel json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} class EmployeeAttendanceItem(BaseModel): date_: date attendance_type: AttendanceType prints: str hours_worked: str full_day: Optional[bool] @validator("date_", pre=True) def parse_date(cls, value): if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() class Config: anystr_strip_whitespace = True alias_generator = to_camel json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} class EmployeeAttendance(BaseModel): start_date: Optional[date] finish_date: Optional[date] employee: Optional[AccountLink] body: List[EmployeeAttendanceItem] @validator("start_date", pre=True) def parse_start_date(cls, value): if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @validator("finish_date", pre=True) def parse_finish_date(cls, value): if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() class Config: anystr_strip_whitespace = True alias_generator = to_camel json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} class Fingerprint(BaseModel): id: uuid.UUID employee_id: uuid.UUID date: date class DbImage(BaseModel): id: uuid.UUID resource_id: uuid.UUID resource_type: str image: bytes thumbnail: bytes creation_date: datetime