diff --git a/brewman/alembic/versions/226cadcec9ac_timezone.py b/brewman/alembic/versions/226cadcec9ac_timezone.py index dd5d264b..401c1b95 100644 --- a/brewman/alembic/versions/226cadcec9ac_timezone.py +++ b/brewman/alembic/versions/226cadcec9ac_timezone.py @@ -5,70 +5,108 @@ Revises: 002f51d87565 Create Date: 2021-04-19 09:58:54.556818 """ -from alembic import op import sqlalchemy as sa + +from alembic import op from sqlalchemy.dialects import postgresql + # revision identifiers, used by Alembic. -revision = '226cadcec9ac' -down_revision = '002f51d87565' +revision = "226cadcec9ac" +down_revision = "002f51d87565" branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.alter_column('attendances', 'creation_date', - existing_type=postgresql.TIMESTAMP(timezone=True), - type_=sa.DateTime(), - existing_nullable=True) - op.alter_column('auth_clients', 'creation_date', - existing_type=postgresql.TIMESTAMP(timezone=True), - type_=sa.DateTime(), - existing_nullable=False) - op.alter_column('auth_login_history', 'date', - existing_type=postgresql.TIMESTAMP(timezone=True), - type_=sa.DateTime(), - existing_nullable=False) - op.alter_column('images', 'creation_date', - existing_type=postgresql.TIMESTAMP(timezone=True), - type_=sa.DateTime(), - existing_nullable=False) - op.alter_column('vouchers', 'creation_date', - existing_type=postgresql.TIMESTAMP(timezone=True), - type_=sa.DateTime(), - existing_nullable=False) - op.alter_column('vouchers', 'last_edit_date', - existing_type=postgresql.TIMESTAMP(timezone=True), - type_=sa.DateTime(), - existing_nullable=False) + op.alter_column( + "attendances", + "creation_date", + existing_type=postgresql.TIMESTAMP(timezone=True), + type_=sa.DateTime(), + existing_nullable=True, + ) + op.alter_column( + "auth_clients", + "creation_date", + existing_type=postgresql.TIMESTAMP(timezone=True), + type_=sa.DateTime(), + existing_nullable=False, + ) + op.alter_column( + "auth_login_history", + "date", + existing_type=postgresql.TIMESTAMP(timezone=True), + type_=sa.DateTime(), + existing_nullable=False, + ) + op.alter_column( + "images", + "creation_date", + existing_type=postgresql.TIMESTAMP(timezone=True), + type_=sa.DateTime(), + existing_nullable=False, + ) + op.alter_column( + "vouchers", + "creation_date", + existing_type=postgresql.TIMESTAMP(timezone=True), + type_=sa.DateTime(), + existing_nullable=False, + ) + op.alter_column( + "vouchers", + "last_edit_date", + existing_type=postgresql.TIMESTAMP(timezone=True), + type_=sa.DateTime(), + existing_nullable=False, + ) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.alter_column('vouchers', 'last_edit_date', - existing_type=sa.DateTime(), - type_=postgresql.TIMESTAMP(timezone=True), - existing_nullable=False) - op.alter_column('vouchers', 'creation_date', - existing_type=sa.DateTime(), - type_=postgresql.TIMESTAMP(timezone=True), - existing_nullable=False) - op.alter_column('images', 'creation_date', - existing_type=sa.DateTime(), - type_=postgresql.TIMESTAMP(timezone=True), - existing_nullable=False) - op.alter_column('auth_login_history', 'date', - existing_type=sa.DateTime(), - type_=postgresql.TIMESTAMP(timezone=True), - existing_nullable=False) - op.alter_column('auth_clients', 'creation_date', - existing_type=sa.DateTime(), - type_=postgresql.TIMESTAMP(timezone=True), - existing_nullable=False) - op.alter_column('attendances', 'creation_date', - existing_type=sa.DateTime(), - type_=postgresql.TIMESTAMP(timezone=True), - existing_nullable=True) + op.alter_column( + "vouchers", + "last_edit_date", + existing_type=sa.DateTime(), + type_=postgresql.TIMESTAMP(timezone=True), + existing_nullable=False, + ) + op.alter_column( + "vouchers", + "creation_date", + existing_type=sa.DateTime(), + type_=postgresql.TIMESTAMP(timezone=True), + existing_nullable=False, + ) + op.alter_column( + "images", + "creation_date", + existing_type=sa.DateTime(), + type_=postgresql.TIMESTAMP(timezone=True), + existing_nullable=False, + ) + op.alter_column( + "auth_login_history", + "date", + existing_type=sa.DateTime(), + type_=postgresql.TIMESTAMP(timezone=True), + existing_nullable=False, + ) + op.alter_column( + "auth_clients", + "creation_date", + existing_type=sa.DateTime(), + type_=postgresql.TIMESTAMP(timezone=True), + existing_nullable=False, + ) + op.alter_column( + "attendances", + "creation_date", + existing_type=sa.DateTime(), + type_=postgresql.TIMESTAMP(timezone=True), + existing_nullable=True, + ) # ### end Alembic commands ### diff --git a/brewman/alembic/versions/ad8b2d208492_settings_validity.py b/brewman/alembic/versions/ad8b2d208492_settings_validity.py new file mode 100644 index 00000000..b7950b24 --- /dev/null +++ b/brewman/alembic/versions/ad8b2d208492_settings_validity.py @@ -0,0 +1,42 @@ +"""settings validity + +Revision ID: ad8b2d208492 +Revises: 226cadcec9ac +Create Date: 2021-09-08 11:53:14.164305 + +""" +import sqlalchemy as sa + +from alembic import op + +# revision identifiers, used by Alembic. +from sqlalchemy import table + + +revision = "ad8b2d208492" +down_revision = "226cadcec9ac" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + set = table("settings") + op.execute(set.delete()) + + setting_type = sa.Enum("VOUCHER_LOCK", name="setting_type") + setting_type.create(op.get_bind()) + op.add_column("settings", sa.Column("setting_type", setting_type, nullable=False)) + op.add_column("settings", sa.Column("valid_from", sa.Date(), nullable=True)) + op.add_column("settings", sa.Column("valid_till", sa.Date(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("settings", "valid_till") + op.drop_column("settings", "valid_from") + op.drop_column("settings", "setting_type") + setting_type = sa.Enum("VOUCHER_LOCK", name="setting_type") + setting_type.drop(op.get_bind()) + # ### end Alembic commands ### diff --git a/brewman/brewman/db/base.py b/brewman/brewman/db/base.py index ee658bdf..7671ef98 100644 --- a/brewman/brewman/db/base.py +++ b/brewman/brewman/db/base.py @@ -1,28 +1,28 @@ # Import all the models, so that Base has them before being # imported by Alembic -from .db.base_class import Base # noqa: F401 -from .models.account import Account # noqa: F401 -from .models.account_base import AccountBase # noqa: F401 -from .models.attendance import Attendance # noqa: F401 -from .models.batch import Batch # noqa: F401 -from .models.client import Client # noqa: F401 -from .models.cost_centre import CostCentre # noqa: F401 -from .models.db_image import DbImage # noqa: F401 -from .models.db_setting import DbSetting # noqa: F401 -from .models.employee import Employee # noqa: F401 -from .models.emplyee_benefit import EmployeeBenefit # noqa: F401 -from .models.fingerprint import Fingerprint # noqa: F401 -from .models.incentive import Incentive # noqa: F401 -from .models.inventory import Inventory # noqa: F401 -from .models.journal import Journal # noqa: F401 -from .models.login_history import LoginHistory # noqa: F401 -from .models.permission import Permission # noqa: F401 -from .models.product import Product # noqa: F401 -from .models.product_group import ProductGroup # noqa: F401 -from .models.recipe import Recipe # noqa: F401 -from .models.recipe_item import RecipeItem # noqa: F401 -from .models.role import Role # noqa: F401 -from .models.role_permission import role_permission # noqa: F401 -from .models.user import User # noqa: F401 -from .models.user_role import user_role # noqa: F401 -from .models.voucher import Voucher # noqa: F401 +from ..models.account import Account # noqa: F401 +from ..models.account_base import AccountBase # noqa: F401 +from ..models.attendance import Attendance # noqa: F401 +from ..models.batch import Batch # noqa: F401 +from ..models.client import Client # noqa: F401 +from ..models.cost_centre import CostCentre # noqa: F401 +from ..models.db_image import DbImage # noqa: F401 +from ..models.db_setting import DbSetting # noqa: F401 +from ..models.employee import Employee # noqa: F401 +from ..models.emplyee_benefit import EmployeeBenefit # noqa: F401 +from ..models.fingerprint import Fingerprint # noqa: F401 +from ..models.incentive import Incentive # noqa: F401 +from ..models.inventory import Inventory # noqa: F401 +from ..models.journal import Journal # noqa: F401 +from ..models.login_history import LoginHistory # noqa: F401 +from ..models.permission import Permission # noqa: F401 +from ..models.product import Product # noqa: F401 +from ..models.product_group import ProductGroup # noqa: F401 +from ..models.recipe import Recipe # noqa: F401 +from ..models.recipe_item import RecipeItem # noqa: F401 +from ..models.role import Role # noqa: F401 +from ..models.role_permission import role_permission # noqa: F401 +from ..models.user import User # noqa: F401 +from ..models.user_role import user_role # noqa: F401 +from ..models.voucher import Voucher # noqa: F401 +from .base_class import Base # noqa: F401 diff --git a/brewman/brewman/main.py b/brewman/brewman/main.py index b77a4ead..649a9228 100644 --- a/brewman/brewman/main.py +++ b/brewman/brewman/main.py @@ -39,6 +39,7 @@ from .routers import ( role, user, voucher, + voucher_types, ) from .routers.reports import ( balance_sheet, @@ -69,6 +70,7 @@ app.include_router(db_image.router, prefix="/db-image", tags=["db-image"]) app.include_router(login.router, tags=["login"]) app.include_router(account.router, prefix="/api/accounts", tags=["accounts"]) app.include_router(account_types.router, prefix="/api/account-types", tags=["accounts"]) +app.include_router(voucher_types.router, prefix="/api/voucher-types", tags=["accounts"]) app.include_router(attendance.router, prefix="/api/attendance", tags=["attendance"]) app.include_router(attendance_types.router, prefix="/api/attendance-types", tags=["attendance"]) app.include_router(employee_attendance.router, prefix="/api/employee-attendance", tags=["attendance"]) diff --git a/brewman/brewman/models/account_type.py b/brewman/brewman/models/account_type.py index 5ebd5dd2..d0690432 100644 --- a/brewman/brewman/models/account_type.py +++ b/brewman/brewman/models/account_type.py @@ -38,9 +38,6 @@ class AccountType: AccountType(12, "Revenue", False, False, "Operating", 30000, True), AccountType(13, "Tax", True, False, "Operating", 80000, True), ] - # list.append(AccountType(8, 'Discount', False, False, True, 30, True)) - # list.append(AccountType(14, 'Total', False, False, False, 900, False)) - # list.append(AccountType(15, 'Net', False, False, False, 1000, False)) @classmethod def by_name(cls, name): diff --git a/brewman/brewman/models/db_setting.py b/brewman/brewman/models/db_setting.py index 80aa7f3e..f70abdb2 100644 --- a/brewman/brewman/models/db_setting.py +++ b/brewman/brewman/models/db_setting.py @@ -1,9 +1,10 @@ import uuid -from sqlalchemy import Column, PickleType, Unicode +from sqlalchemy import Column, Date, Enum, PickleType, Unicode from sqlalchemy.dialects.postgresql import UUID from .meta import Base +from .setting_type import SettingType class DbSetting(Base): @@ -12,8 +13,15 @@ class DbSetting(Base): id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) name = Column("name", Unicode(255), unique=True, nullable=False) data = Column("data", PickleType) + setting_type = Column("setting_type", Enum(SettingType), nullable=False) - def __init__(self, id_=None, name=None, data=None): + valid_from = Column("valid_from", Date(), nullable=True) + valid_till = Column("valid_till", Date(), nullable=True) + + def __init__(self, id_=None, setting_type=None, name=None, data=None, valid_from=None, valid_till=None): self.id = id_ + self.setting_type = setting_type self.name = name self.data = data + self.valid_from = valid_from + self.valid_till = valid_till diff --git a/brewman/brewman/models/setting_type.py b/brewman/brewman/models/setting_type.py new file mode 100644 index 00000000..0ca14741 --- /dev/null +++ b/brewman/brewman/models/setting_type.py @@ -0,0 +1,5 @@ +import enum + + +class SettingType(enum.IntEnum): + VOUCHER_LOCK = 0 diff --git a/brewman/brewman/routers/__init__.py b/brewman/brewman/routers/__init__.py index fe6b0433..63d397bc 100644 --- a/brewman/brewman/routers/__init__.py +++ b/brewman/brewman/routers/__init__.py @@ -3,36 +3,84 @@ import uuid from datetime import date, timedelta from decimal import Decimal -from typing import Optional +from typing import Dict, List, Optional -from sqlalchemy import select +from sqlalchemy import or_, select from sqlalchemy.orm import Session +from ..models.account_type import AccountType from ..models.db_setting import DbSetting +from ..models.setting_type import SettingType +from ..models.voucher_type import VoucherType +from ..schemas.settings import ( + AccountTypesSelected, + LockDate, + LockInformation, + VoucherTypesSelected, +) -def get_lock_info(db: Session) -> (Optional[date], Optional[date]): +def get_lock_info( + voucher_dates: List[date], voucher_type: VoucherType, account_types: List[AccountType], db: Session +) -> (bool, str): + date_ = date.today() start: Optional[date] finish: Optional[date] - data = db.execute(select(DbSetting).where(DbSetting.name == "Lock Info")).scalars().one_or_none() - if data is None: - return None, None + data: List = ( + db.execute( + select(DbSetting.data).where( + DbSetting.setting_type == SettingType.VOUCHER_LOCK, + or_( + DbSetting.valid_from == None, # noqa: E711 + DbSetting.valid_from <= date_, + ), + or_( + DbSetting.valid_till == None, # noqa: E711 + DbSetting.valid_till >= date_, + ), + ) + ) + .scalars() + .all() + ) + data = sorted(data, key=lambda d: d["index"], reverse=True) + for it in data: + li: LockInformation = LockInformation( + voucherTypes=[ + VoucherTypesSelected(id=vt["id_"], name=VoucherType.by_id(vt["id_"]).name) for vt in it["voucher_types"] + ], + accountTypes=[ + AccountTypesSelected(id=at["id_"], name=AccountType.by_id(at["id_"]).name) for at in it["account_types"] + ], + start=LockDate(days=it["start"]["days"], date=it["start"]["date_"]), + finish=LockDate(days=it["finish"]["days"], date=it["finish"]["date_"]), + index=it["index"], + ) + # Data format + if len(li.voucher_types) != 0 and voucher_type not in li.voucher_types: + continue + if len(li.account_types) != 0 and len(set(account_types) & set(li.account_types)) == 0: + continue - data = data.data - if not data["Start"]["Locked"]: - start = None - elif data["Start"]["Rolling"]: - start: date = date.today() - timedelta(days=data["Start"]["Days"]) - else: - start = data["Start"]["Date"].date() + if li.start.days is not None: + start = date.today() - timedelta(days=li.start.days) + if sum(1 for v in voucher_dates if v < start): + return False, f"Vouchers before {start.strftime('%d-%b-%Y')} have been locked." + if li.start.date_ is not None: + start = li.start.date_ + if sum(1 for v in voucher_dates if v < start): + return False, f"Vouchers before {start.strftime('%d-%b-%Y')} have been locked." - if not data["Finish"]["Locked"]: - finish = None - elif data["Finish"]["Rolling"]: - finish = date.today() + timedelta(days=data["Finish"]["Days"]) - else: - finish = data["Finish"]["Date"].date() - return start, finish + if li.finish.days is not None: + finish = date.today() + timedelta(days=li.finish.days) + if sum(1 for v in voucher_dates if v > finish): + return False, f"Vouchers after {finish.strftime('%d-%b-%Y')} have been locked." + if li.finish.date_ is not None: + finish = li.start.date_ + if sum(1 for v in voucher_dates if v > finish): + return False, f"Vouchers after {finish.strftime('%d-%b-%Y')} have been locked." + + return True, "Voucher allowed" def to_uuid(value): diff --git a/brewman/brewman/routers/attendance.py b/brewman/brewman/routers/attendance.py index 71e1bb78..d878a5db 100644 --- a/brewman/brewman/routers/attendance.py +++ b/brewman/brewman/routers/attendance.py @@ -1,6 +1,6 @@ from datetime import date, datetime, timedelta -import brewman.schemas.voucher as schemas +import brewman.schemas.attendance as schemas from fastapi import APIRouter, HTTPException, Request, Security, status from sqlalchemy import or_, select diff --git a/brewman/brewman/routers/db_image.py b/brewman/brewman/routers/db_image.py index eb16b21c..c210cba8 100644 --- a/brewman/brewman/routers/db_image.py +++ b/brewman/brewman/routers/db_image.py @@ -3,7 +3,7 @@ import uuid from io import BytesIO from typing import List -import brewman.schemas.voucher as output +import brewman.schemas.image_upload as output from fastapi import APIRouter from fastapi.responses import StreamingResponse diff --git a/brewman/brewman/routers/employee_attendance.py b/brewman/brewman/routers/employee_attendance.py index fb68060d..ecceaf2f 100644 --- a/brewman/brewman/routers/employee_attendance.py +++ b/brewman/brewman/routers/employee_attendance.py @@ -2,7 +2,7 @@ import uuid from datetime import date, datetime -import brewman.schemas.voucher as schemas +import brewman.schemas.employee_attendance as schemas from fastapi import APIRouter, Request, Security from sqlalchemy import select diff --git a/brewman/brewman/routers/employee_benefit.py b/brewman/brewman/routers/employee_benefit.py index 7dcd6dc1..58f227e0 100644 --- a/brewman/brewman/routers/employee_benefit.py +++ b/brewman/brewman/routers/employee_benefit.py @@ -8,7 +8,7 @@ import brewman.schemas.input as schema_in import brewman.schemas.voucher as output from fastapi import APIRouter, Depends, File, HTTPException, Request, Security, status -from sqlalchemy import select +from sqlalchemy import distinct, select from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session @@ -22,14 +22,11 @@ from ..models.journal import Journal from ..models.validations import check_journals_are_valid from ..models.voucher import Voucher from ..models.voucher_type import VoucherType +from ..schemas.employee_benefit import EmployeeBenefit as EmployeeBenefitSchema from ..schemas.user import UserToken +from . import get_lock_info from .db_image import save_files, update_files -from .voucher import ( - blank_voucher, - check_voucher_edit_allowed, - check_voucher_lock_info, - voucher_info, -) +from .voucher import blank_voucher, check_voucher_edit_allowed, voucher_info router = APIRouter() @@ -42,7 +39,7 @@ def save_route( i: List[bytes] = File(None), t: List[bytes] = File(None), user: UserToken = Security(get_user, scopes=["employee-benefit"]), -): +) -> output.Voucher: try: dt = get_last_day(data.date_) days_in_month = dt.day @@ -64,7 +61,15 @@ def save_route( def save(data: schema_in.EmployeeBenefitIn, date_: date, user: UserToken, db: Session) -> Voucher: - check_voucher_lock_info(None, date_, db) + account_types = db.execute( + select(distinct(AccountBase.type)).where(AccountBase.id.in_([dj.employee.id_ for dj in data.employee_benefits])) + ) + allowed, message = get_lock_info([data.date_], VoucherType.by_name(data.type_), account_types, db) + if not allowed: + raise HTTPException( + status_code=status.HTTP_423_LOCKED, + detail=message, + ) voucher = Voucher( date=date_, narration=data.narration, @@ -78,7 +83,7 @@ def save(data: schema_in.EmployeeBenefitIn, date_: date, user: UserToken, db: Se def save_employee_benefits( voucher: Voucher, - employee_benefits: List[schema_in.EmployeeBenefit], + employee_benefits: List[EmployeeBenefitSchema], days_in_month: int, db: Session, ): @@ -169,7 +174,19 @@ def update_route( def update_voucher(id_: uuid.UUID, data: schema_in.EmployeeBenefitIn, user: UserToken, db: Session) -> Voucher: voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one() - check_voucher_lock_info(voucher.date, data.date_, db) + account_types = db.execute( + select(distinct(AccountBase.type)).where( + AccountBase.id.in_( + [dj.employee.id_ for dj in data.employee_benefits] + [vj.account_id for vj in voucher.journals] + ) + ) + ) + allowed, message = get_lock_info([voucher.date, data.date_], voucher.type, account_types, db) + if not allowed: + raise HTTPException( + status_code=status.HTTP_423_LOCKED, + detail=message, + ) check_voucher_edit_allowed(voucher, user) voucher.is_starred = data.is_starred voucher.narration = data.narration @@ -181,7 +198,7 @@ def update_voucher(id_: uuid.UUID, data: schema_in.EmployeeBenefitIn, user: User def update_employee_benefits( voucher: Voucher, - employee_benefits: List[schema_in.EmployeeBenefit], + employee_benefits: List[EmployeeBenefitSchema], days_in_month: int, db: Session, ): diff --git a/brewman/brewman/routers/incentive.py b/brewman/brewman/routers/incentive.py index 7c22d53f..c9d57e80 100644 --- a/brewman/brewman/routers/incentive.py +++ b/brewman/brewman/routers/incentive.py @@ -8,7 +8,7 @@ import brewman.schemas.input as schema_in import brewman.schemas.voucher as output from fastapi import APIRouter, Depends, HTTPException, Request, Security, status -from sqlalchemy import and_, func, or_, select +from sqlalchemy import and_, distinct, func, or_, select from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session @@ -16,6 +16,7 @@ from ..core.security import get_current_active_user as get_user from ..core.session import get_date, get_first_day, set_date from ..db.session import SessionFuture from ..models.account import Account +from ..models.account_base import AccountBase from ..models.attendance import Attendance from ..models.attendance_type import AttendanceType from ..models.employee import Employee @@ -24,13 +25,10 @@ from ..models.journal import Journal from ..models.validations import check_journals_are_valid from ..models.voucher import Voucher from ..models.voucher_type import VoucherType +from ..schemas.incentive import Incentive as IncentiveSchema from ..schemas.user import UserToken -from .voucher import ( - blank_voucher, - check_voucher_edit_allowed, - check_voucher_lock_info, - voucher_info, -) +from . import get_lock_info +from .voucher import blank_voucher, check_voucher_edit_allowed, voucher_info router = APIRouter() @@ -63,7 +61,15 @@ def save_route( def save(data: schema_in.IncentiveIn, user: UserToken, db: Session) -> Voucher: - check_voucher_lock_info(None, data.date_, db) + account_types = db.execute( + select(distinct(AccountBase.type)).where(AccountBase.id.in_([dj.employee_id for dj in data.incentives])) + ) + allowed, message = get_lock_info([data.date_], VoucherType.by_name(data.type_), account_types, db) + if not allowed: + raise HTTPException( + status_code=status.HTTP_423_LOCKED, + detail=message, + ) voucher = Voucher( date=data.date_, narration=data.narration, @@ -131,7 +137,17 @@ def update_route( def update_voucher(id_: uuid.UUID, data: schema_in.IncentiveIn, user: UserToken, db: Session) -> Voucher: voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one() - check_voucher_lock_info(voucher.date, data.date_, db) + account_types = db.execute( + select(distinct(AccountBase.type)).where( + AccountBase.id.in_([dj.employee_id for dj in data.incentives] + [vj.account_id for vj in voucher.journals]) + ) + ) + allowed, message = get_lock_info([voucher.date, data.date_], voucher.type, account_types, db) + if not allowed: + raise HTTPException( + status_code=status.HTTP_423_LOCKED, + detail=message, + ) check_voucher_edit_allowed(voucher, user) voucher.is_starred = data.is_starred voucher.narration = data.narration @@ -190,7 +206,7 @@ def show_blank( def get_employees( start_date: date, finish_date: date, - incentives: List[schema_in.Incentive], + incentives: List[IncentiveSchema], journals: Optional[List[Journal]], db: Session, ) -> List[schema_in.IncentiveEmployee]: @@ -253,7 +269,7 @@ def balance(date_: date, voucher_id: Optional[uuid.UUID], db: Session): def check_if_employees_changed( - json: List[schema_in.Incentive], + json: List[IncentiveSchema], db: List[Employee], voucher: Optional[List[Journal]], ): diff --git a/brewman/brewman/routers/issue.py b/brewman/brewman/routers/issue.py index 8cd7eda7..cd6b20d7 100644 --- a/brewman/brewman/routers/issue.py +++ b/brewman/brewman/routers/issue.py @@ -8,7 +8,7 @@ import brewman.schemas.input as schema_in import brewman.schemas.voucher as output from fastapi import APIRouter, Depends, File, HTTPException, Request, Security, status -from sqlalchemy import select +from sqlalchemy import distinct, or_, select from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session @@ -20,17 +20,15 @@ from ..models.batch import Batch from ..models.cost_centre import CostCentre from ..models.inventory import Inventory from ..models.journal import Journal +from ..models.product import Product from ..models.validations import check_inventories_are_valid, check_journals_are_valid from ..models.voucher import Voucher from ..models.voucher_type import VoucherType +from ..schemas.inventory import Inventory as InventorySchema from ..schemas.user import UserToken +from . import get_lock_info from .db_image import save_files, update_files -from .voucher import ( - blank_voucher, - check_voucher_edit_allowed, - check_voucher_lock_info, - voucher_info, -) +from .voucher import blank_voucher, check_voucher_edit_allowed, voucher_info router = APIRouter() @@ -64,7 +62,14 @@ def save_route( def save(data: schema_in.IssueIn, user: UserToken, db: Session) -> (Voucher, Optional[bool]): - check_voucher_lock_info(None, data.date_, db) + product_accounts = select(Product.account_id).where(Product.id.in_([i.product.id_ for i in data.inventories])) + account_types = db.execute(select(distinct(AccountBase.type)).where(AccountBase.id.in_(product_accounts))) + allowed, message = get_lock_info([data.date_], VoucherType.by_name(data.type_), account_types, db) + if not allowed: + raise HTTPException( + status_code=status.HTTP_423_LOCKED, + detail=message, + ) voucher = Voucher( date=data.date_, narration=data.narration, @@ -89,7 +94,7 @@ def save(data: schema_in.IssueIn, user: UserToken, db: Session) -> (Voucher, Opt def save_inventories( voucher: Voucher, - inventories: List[schema_in.Inventory], + inventories: List[InventorySchema], batch_consumed: Optional[bool], db: Session, ) -> Decimal: @@ -183,7 +188,21 @@ def update_route( def update_voucher(id_: uuid.UUID, data: schema_in.IssueIn, user: UserToken, db: Session) -> (Voucher, Optional[bool]): voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one() - check_voucher_lock_info(voucher.date, data.date_, db) + product_accounts = select(Product.account_id).where(Product.id.in_([i.product.id_ for i in data.inventories])) + account_types = db.execute( + select(distinct(AccountBase.type)).where( + or_( + AccountBase.id.in_([vj.account_id for vj in voucher.journals]), + AccountBase.id.in_(product_accounts), + ) + ) + ) + allowed, message = get_lock_info([voucher.date, data.date_], voucher.type, account_types, db) + if not allowed: + raise HTTPException( + status_code=status.HTTP_423_LOCKED, + detail=message, + ) check_voucher_edit_allowed(voucher, user) voucher.date = data.date_ voucher.is_starred = data.is_starred @@ -222,7 +241,7 @@ def update_voucher(id_: uuid.UUID, data: schema_in.IssueIn, user: UserToken, db: def update_inventories( voucher: Voucher, - inventories: List[schema_in.Inventory], + inventories: List[InventorySchema], batch_consumed: Optional[bool], db: Session, ): diff --git a/brewman/brewman/routers/journal.py b/brewman/brewman/routers/journal.py index 5760cdf0..d3cfe70a 100644 --- a/brewman/brewman/routers/journal.py +++ b/brewman/brewman/routers/journal.py @@ -7,7 +7,7 @@ import brewman.schemas.input as schema_in import brewman.schemas.voucher as output from fastapi import APIRouter, Depends, File, HTTPException, Request, Security, status -from sqlalchemy import select +from sqlalchemy import distinct, select from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session @@ -20,13 +20,9 @@ from ..models.validations import check_journals_are_valid from ..models.voucher import Voucher from ..models.voucher_type import VoucherType from ..schemas.user import UserToken +from . import get_lock_info from .db_image import save_files, update_files -from .voucher import ( - blank_voucher, - check_voucher_edit_allowed, - check_voucher_lock_info, - voucher_info, -) +from .voucher import blank_voucher, check_voucher_edit_allowed, voucher_info router = APIRouter() @@ -58,7 +54,15 @@ def save_route( def save(data: schema_in.JournalIn, user: UserToken, db: Session) -> Voucher: - check_voucher_lock_info(None, data.date_, db) + account_types = db.execute( + select(distinct(AccountBase.type)).where(AccountBase.id.in_([dj.account.id_ for dj in data.journals])) + ) + allowed, message = get_lock_info([data.date_], VoucherType.by_name(data.type_), account_types, db) + if not allowed: + raise HTTPException( + status_code=status.HTTP_423_LOCKED, + detail=message, + ) voucher = Voucher( date=data.date_, narration=data.narration, @@ -110,7 +114,18 @@ def update_route( def update_voucher(id_: uuid.UUID, data: schema_in.JournalIn, user: UserToken, db: Session) -> Voucher: voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one() - check_voucher_lock_info(voucher.date, data.date_, db) + account_types = db.execute( + select(distinct(AccountBase.type)).where( + AccountBase.id.in_([dj.account.id_ for dj in data.journals] + [vj.account_id for vj in voucher.journals]) + ) + ) + allowed, message = get_lock_info([voucher.date, data.date_], voucher.type, account_types, db) + if not allowed: + raise HTTPException( + status_code=status.HTTP_423_LOCKED, + detail=message, + ) + check_voucher_edit_allowed(voucher, user) voucher.date = data.date_ voucher.is_starred = data.is_starred diff --git a/brewman/brewman/routers/lock_information.py b/brewman/brewman/routers/lock_information.py index 620794e4..9232351e 100644 --- a/brewman/brewman/routers/lock_information.py +++ b/brewman/brewman/routers/lock_information.py @@ -1,94 +1,97 @@ +import uuid + +from typing import List + from fastapi import APIRouter, Security -from sqlalchemy import select +from sqlalchemy import delete, select +from sqlalchemy.orm import Session from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture +from ..models.account_type import AccountType from ..models.db_setting import DbSetting -from ..schemas.settings import LockInformation +from ..models.setting_type import SettingType +from ..models.voucher_type import VoucherType +from ..schemas.settings import ( + AccountTypesSelected, + LockDate, + LockInformation, + VoucherTypesSelected, +) from ..schemas.user import UserToken router = APIRouter() -@router.post("", response_model=LockInformation) +@router.post("", response_model=List[LockInformation]) def post( data_in: LockInformation, user: UserToken = Security(get_user, scopes=["lock-date"]), -): - data = { - "Start": {"Locked": data_in.lock_older}, - "Finish": {"Locked": data_in.lock_newer}, - } - - if data_in.lock_older: - data["Start"]["Rolling"] = data_in.older_rolling - if data_in.older_rolling: - data["Start"]["Days"] = data_in.older_days - else: - data["Start"]["Date"] = data_in.older_date - if data_in.lock_newer: - data["Finish"]["Rolling"] = data_in.newer_rolling - if data_in.newer_rolling: - data["Finish"]["Days"] = data_in.newer_days - else: - data["Finish"]["Date"] = data_in.newer_date +) -> List[LockInformation]: + id_ = uuid.uuid4() + data_in.id_ = id_ + data_dict = data_in.dict() + data_dict["voucher_types"] = [item["id_"] for item in data_dict["voucher_types"]] + data_dict["account_types"] = [item["id_"] for item in data_dict["account_types"]] with SessionFuture() as db: - lock_date = db.execute(select(DbSetting).where(DbSetting.name == "Lock Info")).scalar_one() - if lock_date is not None: - lock_date.data = data - else: - lock_date = DbSetting(name="Lock Info", data=data) - db.add(lock_date) + lock_date = DbSetting( + id_=id_, + name=f"Lock Info {str(id_)}", + setting_type=SettingType.VOUCHER_LOCK, + data=data_in.dict(), + valid_from=data_in.valid_from, + valid_till=data_in.valid_till, + ) + db.add(lock_date) db.commit() - return get_info(data) + return get_info(db) -@router.delete("") +@router.delete("/{id_}", response_model=List[LockInformation]) def delete_route( + id_: uuid.UUID, user: UserToken = Security(get_user, scopes=["lock-date"]), ): with SessionFuture() as db: - lock_date = db.execute(select(DbSetting).where(DbSetting.name == "Lock Info")).scalar_one() - if lock_date is not None and lock_date.data is not None: - lock_date.data = None + db.execute(delete(DbSetting).where(DbSetting.id == id_)) db.commit() - return get_info({}) + return get_info(db) -@router.get("") +@router.get("", response_model=List[LockInformation]) def get( user: UserToken = Security(get_user), -): +) -> List[LockInformation]: with SessionFuture() as db: - data = db.execute(select(DbSetting).where(DbSetting.name == "Lock Info")).scalars().one_or_none() - if data is None: - return get_info({}) - else: - return get_info(data.data) + return get_info(db) -def get_info(data): - info = {} - if "Start" not in data: - info["lockOlder"] = False - else: - info["lockOlder"] = data["Start"]["Locked"] - if data["Start"]["Locked"]: - info["olderRolling"] = data["Start"]["Rolling"] - if data["Start"]["Rolling"]: - info["olderDays"] = data["Start"]["Days"] - else: - info["olderDate"] = data["Start"]["Date"].strftime("%d-%b-%Y") - if "Finish" not in data: - info["lockNewer"] = False - else: - info["lockNewer"] = data["Finish"]["Locked"] - if data["Finish"]["Locked"]: - info["newerRolling"] = data["Finish"]["Rolling"] - if data["Finish"]["Rolling"]: - info["newerDays"] = data["Finish"]["Days"] - else: - info["newerDate"] = data["Finish"]["Date"].strftime("%d-%b-%Y") - return info +def get_info(db: Session) -> List[LockInformation]: + data = db.execute(select(DbSetting.data).where(DbSetting.setting_type == SettingType.VOUCHER_LOCK)).scalars().all() + return [ + LockInformation( + id=item["id_"], + validFrom=item["valid_from"], + validTill=item["valid_till"], + voucherTypes=[ + VoucherTypesSelected( + id=vt["id_"], + name=VoucherType.by_id(vt["id_"]).name, + ) + for vt in item["voucher_types"] + ], + accountTypes=[ + AccountTypesSelected( + id=at["id_"], + name=AccountType.by_id(at["id_"]).name, + ) + for at in item["account_types"] + ], + start=LockDate(days=item["start"]["days"], date=item["start"]["date_"]), + finish=LockDate(days=item["finish"]["days"], date=item["finish"]["date_"]), + index=item["index"], + ) + for item in sorted(data, key=lambda d: d["index"], reverse=True) + ] diff --git a/brewman/brewman/routers/purchase.py b/brewman/brewman/routers/purchase.py index 29a87a6b..a8d9a432 100644 --- a/brewman/brewman/routers/purchase.py +++ b/brewman/brewman/routers/purchase.py @@ -8,7 +8,7 @@ import brewman.schemas.input as schema_in import brewman.schemas.voucher as output from fastapi import APIRouter, Depends, File, HTTPException, Request, Security, status -from sqlalchemy import func, select +from sqlalchemy import distinct, func, or_, select from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session @@ -23,14 +23,11 @@ from ..models.product import Product from ..models.validations import check_inventories_are_valid, check_journals_are_valid from ..models.voucher import Voucher from ..models.voucher_type import VoucherType +from ..schemas.inventory import Inventory as InventorySchema from ..schemas.user import UserToken +from . import get_lock_info from .db_image import save_files, update_files -from .voucher import ( - blank_voucher, - check_voucher_edit_allowed, - check_voucher_lock_info, - voucher_info, -) +from .voucher import blank_voucher, check_voucher_edit_allowed, voucher_info router = APIRouter() @@ -64,7 +61,21 @@ def save_route( def save(data: schema_in.PurchaseIn, user: UserToken, db: Session) -> Voucher: - check_voucher_lock_info(None, data.date_, db) + product_accounts = select(Product.account_id).where(Product.id.in_([i.product.id_ for i in data.inventories])) + account_types = db.execute( + select(distinct(AccountBase.type)).where( + or_( + AccountBase.id == data.vendor.id_, + AccountBase.id.in_(product_accounts), + ) + ) + ) + allowed, message = get_lock_info([data.date_], VoucherType.by_name(data.type_), account_types, db) + if not allowed: + raise HTTPException( + status_code=status.HTTP_423_LOCKED, + detail=message, + ) voucher = Voucher( date=data.date_, narration=data.narration, @@ -76,7 +87,7 @@ def save(data: schema_in.PurchaseIn, user: UserToken, db: Session) -> Voucher: return voucher -def save_inventories(voucher: Voucher, inventories: List[schema_in.Inventory], db: Session): +def save_inventories(voucher: Voucher, inventories: List[InventorySchema], db: Session): for item in inventories: product: Product = db.execute(select(Product).where(Product.id == item.product.id_)).scalar_one() batch = Batch( @@ -158,7 +169,22 @@ def update_route( def update_voucher(id_: uuid.UUID, data: schema_in.PurchaseIn, user: UserToken, db: Session) -> Voucher: voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one() - check_voucher_lock_info(voucher.date, data.date_, db) + product_accounts = select(Product.account_id).where(Product.id.in_([i.product.id_ for i in data.inventories])) + account_types = db.execute( + select(distinct(AccountBase.type)).where( + or_( + AccountBase.id.in_([data.vendor.id_] + [vj.account_id for vj in voucher.journals]), + AccountBase.id.in_(product_accounts), + ) + ) + ) + allowed, message = get_lock_info([voucher.date, data.date_], voucher.type, account_types, db) + if not allowed: + raise HTTPException( + status_code=status.HTTP_423_LOCKED, + detail=message, + ) + check_voucher_edit_allowed(voucher, user) voucher.date = data.date_ voucher.is_starred = data.is_starred @@ -169,7 +195,7 @@ def update_voucher(id_: uuid.UUID, data: schema_in.PurchaseIn, user: UserToken, return voucher -def update_inventory(voucher: Voucher, new_inventories: List[schema_in.Inventory], db: Session): +def update_inventory(voucher: Voucher, new_inventories: List[InventorySchema], db: Session): for it in range(len(voucher.inventories), 0, -1): item = voucher.inventories[it - 1] found = False diff --git a/brewman/brewman/routers/purchase_return.py b/brewman/brewman/routers/purchase_return.py index b796e5d8..6f488e02 100644 --- a/brewman/brewman/routers/purchase_return.py +++ b/brewman/brewman/routers/purchase_return.py @@ -8,7 +8,7 @@ import brewman.schemas.input as schema_in import brewman.schemas.voucher as output from fastapi import APIRouter, Depends, File, HTTPException, Request, Security, status -from sqlalchemy import select +from sqlalchemy import distinct, or_, select from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session @@ -19,17 +19,15 @@ from ..models.account_base import AccountBase from ..models.batch import Batch from ..models.inventory import Inventory from ..models.journal import Journal +from ..models.product import Product from ..models.validations import check_inventories_are_valid, check_journals_are_valid from ..models.voucher import Voucher from ..models.voucher_type import VoucherType +from ..schemas.inventory import Inventory as InventorySchema from ..schemas.user import UserToken +from . import get_lock_info from .db_image import save_files, update_files -from .voucher import ( - blank_voucher, - check_voucher_edit_allowed, - check_voucher_lock_info, - voucher_info, -) +from .voucher import blank_voucher, check_voucher_edit_allowed, voucher_info router = APIRouter() @@ -63,7 +61,21 @@ def save_route( def save(data: schema_in.PurchaseIn, user: UserToken, db: Session) -> Voucher: - check_voucher_lock_info(None, data.date_, db) + product_accounts = select(Product.account_id).where(Product.id.in_([i.product.id_ for i in data.inventories])) + account_types = db.execute( + select(distinct(AccountBase.type)).where( + or_( + AccountBase.id == data.vendor.id_, + AccountBase.id.in_(product_accounts), + ) + ) + ) + allowed, message = get_lock_info([data.date_], VoucherType.by_name(data.type_), account_types, db) + if not allowed: + raise HTTPException( + status_code=status.HTTP_423_LOCKED, + detail=message, + ) voucher = Voucher( date=data.date_, narration=data.narration, @@ -75,7 +87,7 @@ def save(data: schema_in.PurchaseIn, user: UserToken, db: Session) -> Voucher: return voucher -def save_inventories(voucher: Voucher, inventories: List[schema_in.Inventory], db: Session): +def save_inventories(voucher: Voucher, inventories: List[InventorySchema], db: Session): for item in inventories: batch = db.execute(select(Batch).where(Batch.id == item.batch.id_)).scalar_one() @@ -164,7 +176,21 @@ def update_route( def update_voucher(id_: uuid.UUID, data: schema_in.PurchaseIn, user: UserToken, db: Session) -> Voucher: voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one() - check_voucher_lock_info(voucher.date, data.date_, db) + product_accounts = select(Product.account_id).where(Product.id.in_([i.product.id_ for i in data.inventories])) + account_types = db.execute( + select(distinct(AccountBase.type)).where( + or_( + AccountBase.id.in_([data.vendor.id_] + [vj.account_id for vj in voucher.journals]), + AccountBase.id.in_(product_accounts), + ) + ) + ) + allowed, message = get_lock_info([voucher.date, data.date_], voucher.type, account_types, db) + if not allowed: + raise HTTPException( + status_code=status.HTTP_423_LOCKED, + detail=message, + ) check_voucher_edit_allowed(voucher, user) voucher.date = data.date_ voucher.is_starred = data.is_starred @@ -175,7 +201,7 @@ def update_voucher(id_: uuid.UUID, data: schema_in.PurchaseIn, user: UserToken, return voucher -def update_inventory(voucher: Voucher, new_inventories: List[schema_in.Inventory], db: Session): +def update_inventory(voucher: Voucher, new_inventories: List[InventorySchema], db: Session): for it in range(len(voucher.inventories), 0, -1): item = voucher.inventories[it - 1] found = False diff --git a/brewman/brewman/routers/voucher.py b/brewman/brewman/routers/voucher.py index cdc270c6..00ef1914 100644 --- a/brewman/brewman/routers/voucher.py +++ b/brewman/brewman/routers/voucher.py @@ -2,12 +2,12 @@ import uuid from datetime import date, datetime from decimal import Decimal -from typing import Optional +from typing import List, Optional import brewman.schemas.voucher as output from fastapi import APIRouter, HTTPException, Security, status -from sqlalchemy import and_, func, or_, select +from sqlalchemy import and_, distinct, func, or_, select from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session @@ -16,6 +16,7 @@ from ..core.session import get_first_day from ..db.session import SessionFuture from ..models.account import Account from ..models.account_base import AccountBase +from ..models.account_type import AccountType from ..models.attendance import Attendance from ..models.attendance_type import AttendanceType from ..models.cost_centre import CostCentre @@ -40,7 +41,15 @@ def post_voucher( try: with SessionFuture() as db: voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one() - check_voucher_lock_info(voucher.date, voucher.date, db) + account_types = db.execute( + select(distinct(AccountBase.type)).where(AccountBase.id.in_([vj.account_id for vj in voucher.journals])) + ) + allowed, message = get_lock_info([voucher.date], voucher.type, account_types, db) + if not allowed: + raise HTTPException( + status_code=status.HTTP_423_LOCKED, + detail=message, + ) voucher.posted = True voucher.poster_id = user.id_ db.commit() @@ -82,7 +91,15 @@ def delete_voucher( voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one() images = db.execute(select(DbImage).where(DbImage.resource_id == voucher.id)).scalars().all() check_delete_permissions(voucher, user) - check_voucher_lock_info(None, voucher.date, db) + account_types = db.execute( + select(distinct(AccountBase.type)).where(AccountBase.id.in_([vj.account_id for vj in voucher.journals])) + ) + allowed, message = get_lock_info([voucher.date], voucher.type, account_types, db) + if not allowed: + raise HTTPException( + status_code=status.HTTP_423_LOCKED, + detail=message, + ) json_voucher = voucher_info(voucher, db) batches_to_delete = [] if voucher.type == VoucherType.by_name("Issue").id: @@ -392,30 +409,6 @@ def incentive_employees(date_, db: Session): return details, amount -def check_voucher_lock_info(voucher_date: Optional[date], data_date: date, db: Session): - start, finish = get_lock_info(db) - if start is not None and start > data_date or voucher_date is not None and start > voucher_date: - raise HTTPException( - status_code=status.HTTP_423_LOCKED, - detail=f"Vouchers before {start.strftime('%d-%b-%Y')} have been locked.", - ) - elif finish is not None and finish < data_date or voucher_date is not None and finish < voucher_date: - raise HTTPException( - status_code=status.HTTP_423_LOCKED, - detail=f"Vouchers after {finish.strftime('%d-%b-%Y')} have been locked.", - ) - # elif start is not None and (start > data.date_ or start > item.date): - # raise HTTPException( - # status_code=status.HTTP_403_FORBIDDEN, - # detail=f"Vouchers before {start.strftime('%d-%b-%Y')} have been locked.", - # ) - # elif finish is not None and (finish < data.date_ or finish < item.date): - # raise HTTPException( - # status_code=status.HTTP_403_FORBIDDEN, - # detail=f"Vouchers after {finish.strftime('%d-%b-%Y')} have been locked.", - # ) - - def check_voucher_edit_allowed(voucher: Voucher, user: UserToken): if voucher.posted and "edit-posted-vouchers" not in user.permissions: raise HTTPException( diff --git a/brewman/brewman/routers/voucher_types.py b/brewman/brewman/routers/voucher_types.py new file mode 100644 index 00000000..efafee93 --- /dev/null +++ b/brewman/brewman/routers/voucher_types.py @@ -0,0 +1,17 @@ +from typing import List + +import brewman.schemas.account_type as schemas + +from fastapi import APIRouter, Depends + +from ..core.security import get_current_active_user as get_user +from ..models.voucher_type import VoucherType +from ..schemas.user import UserToken + + +router = APIRouter() + + +@router.get("", response_model=List[schemas.AccountType]) +def account_type_list(user: UserToken = Depends(get_user)) -> List[schemas.AccountType]: + return [schemas.AccountType(id=item.id, name=item.name) for item in VoucherType.list()] diff --git a/brewman/brewman/schemas/attendance.py b/brewman/brewman/schemas/attendance.py new file mode 100644 index 00000000..f28faee1 --- /dev/null +++ b/brewman/brewman/schemas/attendance.py @@ -0,0 +1,39 @@ +import uuid + +from datetime import date, datetime +from typing import List, Optional + +from brewman.schemas import to_camel +from brewman.schemas.attendance_type import AttendanceType +from pydantic import BaseModel, validator + + +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")} diff --git a/brewman/brewman/schemas/attendance_type.py b/brewman/brewman/schemas/attendance_type.py new file mode 100644 index 00000000..2ca091bc --- /dev/null +++ b/brewman/brewman/schemas/attendance_type.py @@ -0,0 +1,14 @@ +from decimal import Decimal +from typing import Optional + +from brewman.schemas import to_camel +from pydantic import BaseModel + + +class AttendanceType(BaseModel): + id_: int + name: Optional[str] + value: Optional[Decimal] + + class Config: + alias_generator = to_camel diff --git a/brewman/brewman/schemas/batch.py b/brewman/brewman/schemas/batch.py new file mode 100644 index 00000000..27849271 --- /dev/null +++ b/brewman/brewman/schemas/batch.py @@ -0,0 +1,20 @@ +import uuid + +from decimal import Decimal + +from brewman.schemas import to_camel +from brewman.schemas.product import ProductLink +from pydantic import BaseModel + + +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 diff --git a/brewman/brewman/schemas/db_image.py b/brewman/brewman/schemas/db_image.py new file mode 100644 index 00000000..ad5d6328 --- /dev/null +++ b/brewman/brewman/schemas/db_image.py @@ -0,0 +1,14 @@ +import uuid + +from datetime import datetime + +from pydantic import BaseModel + + +class DbImage(BaseModel): + id: uuid.UUID + resource_id: uuid.UUID + resource_type: str + image: bytes + thumbnail: bytes + creation_date: datetime diff --git a/brewman/brewman/schemas/employee_attendance.py b/brewman/brewman/schemas/employee_attendance.py new file mode 100644 index 00000000..0e8cad08 --- /dev/null +++ b/brewman/brewman/schemas/employee_attendance.py @@ -0,0 +1,50 @@ +from datetime import date, datetime +from typing import List, Optional + +from brewman.schemas import to_camel +from brewman.schemas.account import AccountLink +from brewman.schemas.attendance_type import AttendanceType +from pydantic import BaseModel, validator + + +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")} diff --git a/brewman/brewman/schemas/employee_benefit.py b/brewman/brewman/schemas/employee_benefit.py new file mode 100644 index 00000000..b62b7933 --- /dev/null +++ b/brewman/brewman/schemas/employee_benefit.py @@ -0,0 +1,21 @@ +import uuid + +from typing import Optional + +from brewman.schemas import to_camel +from brewman.schemas.employee import EmployeeLink +from pydantic import BaseModel, Field + + +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 diff --git a/brewman/brewman/schemas/fingerprint.py b/brewman/brewman/schemas/fingerprint.py new file mode 100644 index 00000000..a070df56 --- /dev/null +++ b/brewman/brewman/schemas/fingerprint.py @@ -0,0 +1,11 @@ +import uuid + +from datetime import date + +from pydantic import BaseModel + + +class Fingerprint(BaseModel): + id: uuid.UUID + employee_id: uuid.UUID + date: date diff --git a/brewman/brewman/schemas/image_upload.py b/brewman/brewman/schemas/image_upload.py new file mode 100644 index 00000000..f7363f32 --- /dev/null +++ b/brewman/brewman/schemas/image_upload.py @@ -0,0 +1,15 @@ +import uuid + +from typing import Optional + +from brewman.schemas import to_camel +from pydantic import BaseModel + + +class ImageUpload(BaseModel): + id_: Optional[uuid.UUID] + resized: Optional[str] + thumbnail: Optional[str] + + class Config: + alias_generator = to_camel diff --git a/brewman/brewman/schemas/incentive.py b/brewman/brewman/schemas/incentive.py new file mode 100644 index 00000000..b3f93722 --- /dev/null +++ b/brewman/brewman/schemas/incentive.py @@ -0,0 +1,18 @@ +import uuid + +from decimal import Decimal + +from brewman.schemas import to_camel +from pydantic import BaseModel, Field + + +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 diff --git a/brewman/brewman/schemas/input.py b/brewman/brewman/schemas/input.py index c46fbff8..fd535f8e 100644 --- a/brewman/brewman/schemas/input.py +++ b/brewman/brewman/schemas/input.py @@ -11,7 +11,11 @@ from pydantic import BaseModel, Field, validator from . import to_camel from .account import AccountLink from .cost_centre import CostCentreLink -from .voucher import EmployeeBenefit, Incentive, Inventory, Journal, VoucherIn +from .employee_benefit import EmployeeBenefit +from .incentive import Incentive +from .inventory import Inventory +from .journal import Journal +from .voucher import VoucherIn class JournalIn(VoucherIn): diff --git a/brewman/brewman/schemas/inventory.py b/brewman/brewman/schemas/inventory.py new file mode 100644 index 00000000..47e24cab --- /dev/null +++ b/brewman/brewman/schemas/inventory.py @@ -0,0 +1,23 @@ +import uuid + +from decimal import Decimal +from typing import Optional + +from brewman.schemas import to_camel +from brewman.schemas.batch import Batch +from brewman.schemas.product import ProductLink +from pydantic import BaseModel, Field + + +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 diff --git a/brewman/brewman/schemas/journal.py b/brewman/brewman/schemas/journal.py new file mode 100644 index 00000000..82e414a9 --- /dev/null +++ b/brewman/brewman/schemas/journal.py @@ -0,0 +1,21 @@ +import uuid + +from decimal import Decimal +from typing import Optional + +from brewman.schemas import to_camel +from brewman.schemas.account import AccountLink +from brewman.schemas.cost_centre import CostCentreLink +from pydantic import BaseModel, Field + + +class Journal(BaseModel): + id_: Optional[uuid.UUID] + debit: int = Field(ge=-1, le=1, multiple_of=1) + amount: Decimal = Field(ge=0) + account: AccountLink + cost_centre: Optional[CostCentreLink] + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel diff --git a/brewman/brewman/schemas/settings.py b/brewman/brewman/schemas/settings.py index 6df8857a..f1ae8cbb 100644 --- a/brewman/brewman/schemas/settings.py +++ b/brewman/brewman/schemas/settings.py @@ -1,21 +1,62 @@ +import uuid + from datetime import date, datetime from decimal import Decimal -from typing import Optional +from typing import List, Optional from pydantic import BaseModel, validator from . import to_camel +class LockDate(BaseModel): + days: Optional[int] + date_: Optional[date] + + class Config: + fields = {"date_": "date"} + json_encoders = { + date: lambda v: v.strftime("%d-%b-%Y"), + } + + @validator("date_", pre=True) + def parse_date(cls, value): + if value is None: + return None + if isinstance(value, date): + return value + return datetime.strptime(value, "%d-%b-%Y").date() + + +class VoucherTypesSelected(BaseModel): + id_: int + name: str + + class Config: + fields = {"id_": "id"} + + +class AccountTypesSelected(BaseModel): + id_: int + name: str + + class Config: + alias_generator = to_camel + + class LockInformation(BaseModel): - lock_older: bool - lock_newer: bool - older_rolling: Optional[bool] - older_days: Optional[int] - older_date: Optional[date] - newer_rolling: Optional[bool] - newer_days: Optional[int] - newer_date: Optional[date] + id_: Optional[uuid.UUID] + + valid_from: Optional[date] + valid_till: Optional[date] + + voucher_types: List[VoucherTypesSelected] + account_types: List[AccountTypesSelected] + + start: LockDate + finish: LockDate + + index: int class Config: alias_generator = to_camel @@ -23,14 +64,18 @@ class LockInformation(BaseModel): date: lambda v: v.strftime("%d-%b-%Y"), } - @validator("older_date", pre=True) - def parse_older_date(cls, value): + @validator("valid_from", pre=True) + def parse_valid_from(cls, value): + if value is None: + return None if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() - @validator("newer_date", pre=True) - def parse_newer_date(cls, value): + @validator("valid_till", pre=True) + def parse_valid_till(cls, value): + if value is None: + return None if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/user_link.py b/brewman/brewman/schemas/user_link.py new file mode 100644 index 00000000..86ab3dd1 --- /dev/null +++ b/brewman/brewman/schemas/user_link.py @@ -0,0 +1,12 @@ +import uuid + +from brewman.schemas import to_camel +from pydantic import BaseModel + + +class UserLink(BaseModel): + id_: uuid.UUID + name: str + + class Config: + alias_generator = to_camel diff --git a/brewman/brewman/schemas/voucher.py b/brewman/brewman/schemas/voucher.py index 891d97ec..c92d35cb 100644 --- a/brewman/brewman/schemas/voucher.py +++ b/brewman/brewman/schemas/voucher.py @@ -6,95 +6,17 @@ from decimal import Decimal from typing import List, Optional from fastapi import Form -from pydantic import BaseModel, Field, validator +from pydantic import BaseModel, validator from . import to_camel from .account import AccountLink from .cost_centre import CostCentreLink -from .employee import EmployeeLink -from .product import ProductLink - - -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) - 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 +from .employee_benefit import EmployeeBenefit +from .image_upload import ImageUpload +from .incentive import Incentive +from .inventory import Inventory +from .journal import Journal +from .user_link import UserLink class VoucherIn(BaseModel): @@ -162,101 +84,3 @@ class Voucher(VoucherIn): elif isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() - - -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 diff --git a/overlord/src/app/settings/lock-datasource.ts b/overlord/src/app/settings/lock-datasource.ts new file mode 100644 index 00000000..ce69922e --- /dev/null +++ b/overlord/src/app/settings/lock-datasource.ts @@ -0,0 +1,16 @@ +import { DataSource } from '@angular/cdk/collections'; +import { Observable } from 'rxjs'; + +import { LockInfo } from './lock-info'; + +export class LockDataSource extends DataSource { + constructor(private data: Observable) { + super(); + } + + connect(): Observable { + return this.data; + } + + disconnect() {} +} diff --git a/overlord/src/app/settings/lock-info.ts b/overlord/src/app/settings/lock-info.ts index 03a5e984..e3d1185b 100644 --- a/overlord/src/app/settings/lock-info.ts +++ b/overlord/src/app/settings/lock-info.ts @@ -1,17 +1,23 @@ +export class LockDate { + days?: number; + date?: string; +} export class LockInfo { - lockOlder: boolean; - olderRolling?: boolean; - olderDays?: number; - olderDate?: string; - - lockNewer: boolean; - newerRolling?: boolean; - newerDays?: number; - newerDate?: string; + id?: string; + validFrom?: string; + validTill?: string; + voucherTypes: { id: number; name: string }[]; + accountTypes: { id: number; name: string }[]; + start: LockDate; + finish: LockDate; + index: number; public constructor(init?: Partial) { - this.lockOlder = false; - this.lockNewer = false; + this.voucherTypes = []; + this.accountTypes = []; + this.index = 0; + this.start = new LockDate(); + this.finish = new LockDate(); Object.assign(this, init); } } diff --git a/overlord/src/app/settings/lock-information-resolver.service.ts b/overlord/src/app/settings/lock-information-resolver.service.ts index 9781898a..d4943359 100644 --- a/overlord/src/app/settings/lock-information-resolver.service.ts +++ b/overlord/src/app/settings/lock-information-resolver.service.ts @@ -8,10 +8,10 @@ import { SettingsService } from './settings.service'; @Injectable({ providedIn: 'root', }) -export class LockInformationResolver implements Resolve { +export class LockInformationResolver implements Resolve<[LockInfo]> { constructor(private ser: SettingsService) {} - resolve(): Observable { + resolve(): Observable<[LockInfo]> { return this.ser.getLockInformation(); } } diff --git a/overlord/src/app/settings/settings-routing.module.ts b/overlord/src/app/settings/settings-routing.module.ts index d967410c..92bb3436 100644 --- a/overlord/src/app/settings/settings-routing.module.ts +++ b/overlord/src/app/settings/settings-routing.module.ts @@ -2,11 +2,13 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { AccountTypeResolver } from '../account/account-type-resolver.service'; import { AuthGuard } from '../auth/auth-guard.service'; import { LockInformationResolver } from './lock-information-resolver.service'; import { MaintenanceResolver } from './maintenance-resolver.service'; import { SettingsComponent } from './settings.component'; +import { VoucherTypeResolver } from './voucher-type-resolver.service'; const settingsRoutes: Routes = [ { @@ -19,6 +21,8 @@ const settingsRoutes: Routes = [ resolve: { maintenance: MaintenanceResolver, lockInformation: LockInformationResolver, + accountTypes: AccountTypeResolver, + voucherTypes: VoucherTypeResolver, }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/settings/settings.component.html b/overlord/src/app/settings/settings.component.html index bc5177ff..c02720f3 100644 --- a/overlord/src/app/settings/settings.component.html +++ b/overlord/src/app/settings/settings.component.html @@ -2,7 +2,7 @@ -
+
Lock Older - + Is Rolling - + - +
@@ -48,21 +37,13 @@ fxLayoutGap.lt-md="0px" > Lock Newer - + Is Rolling - + - + +
+
+

Account Types

+
+ {{ at.name }} +
+
+
+

Voucher Types

+
+ {{ vt.name }} +
+
+
+
+ + + + + + + + + + + + Index + + + +
+ + + + Index + {{ row.index }} + + + + + Validity + {{ !!row.validFrom ? row.validFrom : '\u221E' }} - + {{ !!row.validTill ? row.validTill : '\u221E' }} + + + + + Voucher Types + +
    +
  • {{ vt.name }}
  • +
+
+
+ + + + Account Types + +
    +
  • {{ at.name }}
  • +
+
+
+ + + + Lock Dates + {{ !!row.start.days ? row.start.days : '' + }}{{ !!row.start.date ? row.start.date : '' + }}{{ !row.start.days && !row.start.date ? '\u221E' : '' }} - + {{ !!row.finish.days ? row.finish.days : '' + }}{{ !!row.finish.date ? row.finish.date : '' + }}{{ !row.finish.days && !row.finish.date ? '\u221E' : '' }} + + + + + Action + + + + + + + +
- - - -
diff --git a/overlord/src/app/settings/settings.component.ts b/overlord/src/app/settings/settings.component.ts index ec9a4c41..82af0169 100644 --- a/overlord/src/app/settings/settings.component.ts +++ b/overlord/src/app/settings/settings.component.ts @@ -1,19 +1,21 @@ import { Component, OnInit } from '@angular/core'; -import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; +import { FormArray, FormBuilder, FormControl, FormGroup } from '@angular/forms'; import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute, Router } from '@angular/router'; import * as moment from 'moment'; -import { Observable, of as observableOf } from 'rxjs'; +import { BehaviorSubject, Observable, of as observableOf } from 'rxjs'; import { debounceTime, distinctUntilChanged, map, startWith, switchMap } from 'rxjs/operators'; import { environment } from '../../environments/environment'; import { AuthService } from '../auth/auth.service'; +import { AccountType } from '../core/account-type'; import { Product } from '../core/product'; import { ToasterService } from '../core/toaster.service'; import { ProductService } from '../product/product.service'; import { ConfirmDialogComponent } from '../shared/confirm-dialog/confirm-dialog.component'; +import { LockDataSource } from './lock-datasource'; import { LockInfo } from './lock-info'; import { SettingsService } from './settings.service'; @@ -23,8 +25,18 @@ import { SettingsService } from './settings.service'; styleUrls: ['./settings.component.css'], }) export class SettingsComponent implements OnInit { - lockDatesForm: FormGroup; - lockInformation: LockInfo; + public lockObservable = new BehaviorSubject([]); + dataSource: LockDataSource = new LockDataSource(this.lockObservable); + + accountTypes: AccountType[] = []; + voucherTypes: AccountType[] = []; + + lockInfoForm: FormGroup; + lockInformation: LockInfo[]; + lockOlder: boolean = false; + olderRolling: boolean = false; + lockNewer: boolean = false; + newerRolling: boolean = false; rebaseDataForm: FormGroup; @@ -34,6 +46,8 @@ export class SettingsComponent implements OnInit { maintenance: { enabled: boolean; user: string }; + displayedColumns = ['index', 'validity', 'voucherTypes', 'accountTypes', 'lock', 'action']; + version: string; constructor( @@ -48,17 +62,22 @@ export class SettingsComponent implements OnInit { ) { const startDate = moment().date(1); const finishDate = moment().date(startDate.daysInMonth()); - this.lockDatesForm = this.fb.group({ - lockOlder: null, - olderRolling: null, - olderDate: startDate, + this.lockInfoForm = this.fb.group({ + validFrom: '', + validTill: '', + index: 0, + lockOlder: '', + olderRolling: '', + olderDate: finishDate, olderDays: 0, - lockNewer: null, - newerRolling: null, + lockNewer: '', + newerRolling: '', newerDate: finishDate, newerDays: 0, + accountTypes: this.fb.array([]), + voucherTypes: this.fb.array([]), }); - + this.lockInformation = []; this.rebaseDataForm = this.fb.group({ date: moment(), }); @@ -70,12 +89,6 @@ export class SettingsComponent implements OnInit { quantity: 0, }); this.listenToLockForm(); - this.lockInformation = { - lockOlder: false, - olderRolling: false, - lockNewer: false, - newerRolling: false, - }; // Listen to Product Changes this.products = (this.resetStockForm.get('product') as FormControl).valueChanges.pipe( startWith(''), @@ -92,65 +105,103 @@ export class SettingsComponent implements OnInit { this.route.data.subscribe((value) => { const data = value as { maintenance: { enabled: boolean; user: string }; - lockInformation: LockInfo; + lockInformation: [LockInfo]; + accountTypes: AccountType[]; + voucherTypes: AccountType[]; }; - + this.accountTypes = data.accountTypes; + this.voucherTypes = data.voucherTypes; + this.showLockInformation(data.lockInformation); this.maintenance = data.maintenance; - this.lockInformation = data.lockInformation; - this.lockDatesForm.patchValue({ - lockOlder: this.lockInformation.lockOlder, - olderRolling: this.lockInformation.olderRolling, - olderDate: this.lockInformation.olderDate, - olderDays: this.lockInformation.olderDays, - lockNewer: this.lockInformation.lockNewer, - newerRolling: this.lockInformation.newerRolling, - newerDate: this.lockInformation.newerDate, - newerDays: this.lockInformation.newerDays, - }); }); } + showLockInformation(info: LockInfo[]) { + this.lockInformation = info; + this.lockInfoForm.setControl( + 'accountTypes', + this.fb.array( + this.accountTypes.map((x) => + this.fb.group({ + accountType: false, + }), + ), + ), + ); + this.lockInfoForm.setControl( + 'voucherTypes', + this.fb.array( + this.voucherTypes.map((x) => + this.fb.group({ + voucherType: false, + }), + ), + ), + ); + this.dataSource = new LockDataSource(this.lockObservable); + this.lockObservable.next(this.lockInformation); + } + listenToLockForm() { - (this.lockDatesForm.get('lockOlder') as FormControl).valueChanges.subscribe((x) => { - this.lockInformation.lockOlder = x; + (this.lockInfoForm.get('lockOlder') as FormControl).valueChanges.subscribe((x) => { + this.lockOlder = x; }); - (this.lockDatesForm.get('lockNewer') as FormControl).valueChanges.subscribe((x) => { - this.lockInformation.lockNewer = x; + (this.lockInfoForm.get('lockNewer') as FormControl).valueChanges.subscribe((x) => { + this.lockNewer = x; }); - (this.lockDatesForm.get('olderRolling') as FormControl).valueChanges.subscribe((x) => { - this.lockInformation.olderRolling = x; + (this.lockInfoForm.get('olderRolling') as FormControl).valueChanges.subscribe((x) => { + this.olderRolling = x; }); - (this.lockDatesForm.get('newerRolling') as FormControl).valueChanges.subscribe((x) => { - this.lockInformation.newerRolling = x; + (this.lockInfoForm.get('newerRolling') as FormControl).valueChanges.subscribe((x) => { + this.newerRolling = x; }); } - clearLockDates() { - this.ser.deleteLockInformation().subscribe((x) => { - this.lockInformation = x; + saveLock() { + const item = new LockInfo(); + if (this.lockOlder && this.olderRolling) { + item.start.days = +(this.lockInfoForm.get('olderDays') as FormControl).value; + } else if (this.lockOlder && !this.olderRolling) { + item.start.date = moment((this.lockInfoForm.get('olderDate') as FormControl).value).format( + 'DD-MMM-YYYY', + ); + } + if (this.lockNewer && this.newerRolling) { + item.finish.days = +(this.lockInfoForm.get('newerDays') as FormControl).value; + } + if (this.lockNewer && !this.newerRolling) { + item.finish.date = moment((this.lockInfoForm.get('newerDate') as FormControl).value).format( + 'DD-MMM-YYYY', + ); + } + const atArray = this.lockInfoForm.get('accountTypes') as FormArray; + this.accountTypes.forEach((at, index) => { + if (atArray.controls[index].value.accountType) { + item.accountTypes.push({ id: at.id, name: at.name }); + } }); - } - setLockDates() { - if (this.lockInformation.lockOlder && this.lockInformation.olderRolling) { - this.lockInformation.olderDays = +(this.lockDatesForm.get('olderDays') as FormControl).value; + const vtArray = this.lockInfoForm.get('voucherTypes') as FormArray; + this.voucherTypes.forEach((vt, index) => { + if (vtArray.controls[index].value.voucherType) { + item.voucherTypes.push({ id: vt.id, name: vt.name }); + } + }); + + const validFrom = (this.lockInfoForm.get('validFrom') as FormControl).value; + if (validFrom) { + item.validFrom = moment(validFrom).format('DD-MMM-YYYY'); } - if (this.lockInformation.lockOlder && !this.lockInformation.olderRolling) { - this.lockInformation.olderDate = moment( - (this.lockDatesForm.get('olderDate') as FormControl).value, - ).format('DD-MMM-YYYY'); + + const validTill = (this.lockInfoForm.get('validTill') as FormControl).value; + if (validTill) { + item.validTill = moment(validTill).format('DD-MMM-YYYY'); } - if (this.lockInformation.lockNewer && this.lockInformation.newerRolling) { - this.lockInformation.newerDays = +(this.lockDatesForm.get('newerDays') as FormControl).value; - } - if (this.lockInformation.lockOlder && !this.lockInformation.newerRolling) { - this.lockInformation.newerDate = moment( - (this.lockDatesForm.get('newerDate') as FormControl).value, - ).format('DD-MMM-YYYY'); - } - this.ser.setLockInformation(this.lockInformation).subscribe( + item.index = +(this.lockInfoForm.get('index') as FormControl).value; + + this.ser.setLockInformation(item).subscribe( (result) => { - this.lockInformation = result; + this.showLockInformation(result); this.toaster.show('Success', 'Lock information Updated'); }, (error) => { @@ -159,6 +210,12 @@ export class SettingsComponent implements OnInit { ); } + deleteLock(row: LockInfo) { + this.ser.deleteLockInformation(row.id as string).subscribe((x) => { + this.showLockInformation(x); + }); + } + confirmRebase(): void { const rebaseDate = moment((this.rebaseDataForm.get('date') as FormControl).value).format( 'DD-MMM-YYYY', diff --git a/overlord/src/app/settings/settings.service.ts b/overlord/src/app/settings/settings.service.ts index 23bf0d45..b90c5ca8 100644 --- a/overlord/src/app/settings/settings.service.ts +++ b/overlord/src/app/settings/settings.service.ts @@ -14,31 +14,31 @@ const serviceName = 'SettingsService'; export class SettingsService { constructor(private http: HttpClient, private log: ErrorLoggerService) {} - getLockInformation(): Observable { + getLockInformation(): Observable<[LockInfo]> { const url = '/api/lock-information'; return this.http - .get(url) - .pipe( - catchError(this.log.handleError(serviceName, 'getLockInformation')), - ) as Observable; + .get<[LockInfo]>(url) + .pipe(catchError(this.log.handleError(serviceName, 'getLockInformation'))) as Observable< + [LockInfo] + >; } - setLockInformation(lockInformation: LockInfo): Observable { + setLockInformation(lockInformation: LockInfo): Observable { const url = '/api/lock-information'; return this.http - .post(url, lockInformation) - .pipe( - catchError(this.log.handleError(serviceName, 'setLockInformation')), - ) as Observable; + .post(url, lockInformation) + .pipe(catchError(this.log.handleError(serviceName, 'setLockInformation'))) as Observable< + LockInfo[] + >; } - deleteLockInformation(): Observable { + deleteLockInformation(id: string): Observable { const url = '/api/lock-information'; return this.http - .delete(url) - .pipe( - catchError(this.log.handleError(serviceName, 'deleteLockInformation')), - ) as Observable; + .delete(`${url}/${id}`) + .pipe(catchError(this.log.handleError(serviceName, 'deleteLockInformation'))) as Observable< + LockInfo[] + >; } resetStock( diff --git a/overlord/src/app/settings/voucher-type-resolver.service.ts b/overlord/src/app/settings/voucher-type-resolver.service.ts new file mode 100644 index 00000000..a3769f1d --- /dev/null +++ b/overlord/src/app/settings/voucher-type-resolver.service.ts @@ -0,0 +1,18 @@ +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; +import { Observable } from 'rxjs/internal/Observable'; + +import { AccountType } from '../core/account-type'; + +import { VoucherTypeService } from './voucher-type.service'; + +@Injectable({ + providedIn: 'root', +}) +export class VoucherTypeResolver implements Resolve { + constructor(private ser: VoucherTypeService) {} + + resolve(): Observable { + return this.ser.list(); + } +} diff --git a/overlord/src/app/settings/voucher-type.service.ts b/overlord/src/app/settings/voucher-type.service.ts new file mode 100644 index 00000000..1b8004e3 --- /dev/null +++ b/overlord/src/app/settings/voucher-type.service.ts @@ -0,0 +1,23 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs/internal/Observable'; +import { catchError } from 'rxjs/operators'; + +import { AccountType } from '../core/account-type'; +import { ErrorLoggerService } from '../core/error-logger.service'; + +const url = '/api/voucher-types'; +const serviceName = 'VoucherTypeService'; + +@Injectable({ + providedIn: 'root', +}) +export class VoucherTypeService { + constructor(private http: HttpClient, private log: ErrorLoggerService) {} + + list(): Observable { + return this.http + .get(url) + .pipe(catchError(this.log.handleError(serviceName, 'list'))) as Observable; + } +}