diff --git a/brewman/alembic.ini b/brewman/alembic.ini index defbffbb..e552f1a2 100644 --- a/brewman/alembic.ini +++ b/brewman/alembic.ini @@ -1,3 +1,4 @@ [alembic] # path to migration scripts script_location = alembic +prepend_sys_path = . \ No newline at end of file diff --git a/brewman/alembic/env.py b/brewman/alembic/env.py index fbe6df32..97cc6672 100644 --- a/brewman/alembic/env.py +++ b/brewman/alembic/env.py @@ -2,6 +2,7 @@ import logging from alembic import context from brewman.core.config import settings +from brewman.db.base import reg from sqlalchemy import create_engine, pool @@ -12,10 +13,8 @@ logging.getLogger("alembic").setLevel(settings.ALEMBIC_LOG_LEVEL) # Interpret the config file for Python logging. # This line sets up loggers basically. -from brewman.db.base import User # noqa - -target_metadata = User.metadata +target_metadata = reg.metadata # other values from the config, defined by the needs of env.py, # can be acquired: diff --git a/brewman/alembic/versions/0bf3d70ee7de_initial_commit.py b/brewman/alembic/versions/0bf3d70ee7de_initial_commit.py index 43922c9e..297c6fc0 100644 --- a/brewman/alembic/versions/0bf3d70ee7de_initial_commit.py +++ b/brewman/alembic/versions/0bf3d70ee7de_initial_commit.py @@ -295,8 +295,8 @@ def upgrade(): ) op.create_table( "stock_keeping_units", - sa.Column("id", postgresql.UUID(as_uuid=True), nullable=False), - sa.Column("product_id", postgresql.UUID(as_uuid=True), nullable=False), + sa.Column("id", postgresql.Uuid, nullable=False), + sa.Column("product_id", postgresql.Uuid, nullable=False), sa.Column("units", sa.Unicode(length=255), nullable=False), sa.Column("fraction", sa.Numeric(precision=15, scale=5), nullable=False), sa.Column("product_yield", sa.Numeric(precision=15, scale=5), nullable=False), @@ -368,7 +368,7 @@ def upgrade(): ) op.create_table( "periods", - sa.Column("id", postgresql.UUID(as_uuid=True), nullable=False), + sa.Column("id", postgresql.Uuid, nullable=False), sa.Column("valid_from", sa.Date(), nullable=False), sa.Column("valid_till", sa.Date(), nullable=False), sa.PrimaryKeyConstraint("id", name=op.f("pk_periods")), @@ -392,7 +392,7 @@ def upgrade(): sa.Column("cost_price", sa.Numeric(precision=15, scale=2), nullable=False), sa.Column("sale_price", sa.Numeric(precision=15, scale=2), nullable=False), sa.Column("notes", sa.Unicode(length=255), nullable=True), - sa.Column("period_id", postgresql.UUID(as_uuid=True), nullable=False), + sa.Column("period_id", postgresql.Uuid, nullable=False), sa.ForeignKeyConstraint( ["sku_id"], ["stock_keeping_units.id"], @@ -493,13 +493,13 @@ def upgrade(): ) op.create_table( "rate_contracts", - sa.Column("id", postgresql.UUID(as_uuid=True), nullable=False), + sa.Column("id", postgresql.Uuid, nullable=False), sa.Column("date", sa.Date(), nullable=False), - sa.Column("vendor_id", postgresql.UUID(as_uuid=True), nullable=False), + sa.Column("vendor_id", postgresql.Uuid, nullable=False), sa.Column("valid_from", sa.Date(), nullable=True), sa.Column("valid_till", sa.Date(), nullable=True), sa.Column("narration", sa.Unicode(length=1000), nullable=False), - sa.Column("user_id", postgresql.UUID(as_uuid=True), nullable=False), + sa.Column("user_id", postgresql.Uuid, nullable=False), sa.Column("creation_date", sa.DateTime(), nullable=False), sa.Column("last_edit_date", sa.DateTime(), nullable=False), sa.ForeignKeyConstraint(["user_id"], ["users.id"], name=op.f("fk_rate_contracts_user_id_users")), @@ -509,9 +509,9 @@ def upgrade(): op.create_index(op.f("ix_rate_contracts_date"), "rate_contracts", ["date"], unique=False) op.create_table( "rate_contract_items", - sa.Column("id", postgresql.UUID(as_uuid=True), nullable=False), - sa.Column("rate_contract_id", postgresql.UUID(as_uuid=True), nullable=False), - sa.Column("sku_id", postgresql.UUID(as_uuid=True), nullable=False), + sa.Column("id", postgresql.Uuid, nullable=False), + sa.Column("rate_contract_id", postgresql.Uuid, nullable=False), + sa.Column("sku_id", postgresql.Uuid, nullable=False), sa.Column("cost_price", sa.Numeric(precision=15, scale=2), nullable=False), sa.ForeignKeyConstraint( ["sku_id"], ["stock_keeping_units.id"], name=op.f("fk_rate_contract_items_sku_id_stock_keeping_units") @@ -529,10 +529,10 @@ def upgrade(): ) op.create_table( "closing_stocks", - sa.Column("id", postgresql.UUID(as_uuid=True), nullable=False), + sa.Column("id", postgresql.Uuid, nullable=False), sa.Column("date", sa.Date(), nullable=False), - sa.Column("cost_centre_id", postgresql.UUID(as_uuid=True), nullable=False), - sa.Column("sku_id", postgresql.UUID(as_uuid=True), nullable=False), + sa.Column("cost_centre_id", postgresql.Uuid, nullable=False), + sa.Column("sku_id", postgresql.Uuid, nullable=False), sa.Column("quantity", sa.Numeric(precision=15, scale=2), nullable=False), sa.ForeignKeyConstraint( ["cost_centre_id"], ["cost_centres.id"], name=op.f("fk_closing_stocks_cost_centre_id_cost_centres") diff --git a/brewman/alembic/versions/12262aadbc08_recipe_templates.py b/brewman/alembic/versions/12262aadbc08_recipe_templates.py new file mode 100644 index 00000000..1ead19e5 --- /dev/null +++ b/brewman/alembic/versions/12262aadbc08_recipe_templates.py @@ -0,0 +1,46 @@ +"""recipe templates + +Revision ID: 12262aadbc08 +Revises: a1372ed99c45 +Create Date: 2023-04-14 07:50:22.110724 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '12262aadbc08' +down_revision = 'a1372ed99c45' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('recipe_templates', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('name', sa.Unicode(), nullable=False), + sa.Column('date', sa.Date(), nullable=False), + sa.Column('text', sa.Unicode(), nullable=False), + sa.Column('selected', sa.Boolean(), nullable=False), + sa.PrimaryKeyConstraint('id', name=op.f('pk_recipe_templates')), + sa.UniqueConstraint('name', name=op.f('uq_recipe_templates_name')) + ) + op.create_index('only_one_selected_template', 'recipe_templates', ['selected'], unique=True, postgresql_where=sa.text('selected = true')) + op.alter_column('recipes', 'notes', + existing_type=sa.VARCHAR(length=255), + type_=sa.Text(), + existing_nullable=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('recipes', 'notes', + existing_type=sa.Text(), + type_=sa.VARCHAR(length=255), + existing_nullable=False) + op.drop_index('only_one_selected_template', table_name='recipe_templates', postgresql_where=sa.text('selected = true')) + op.drop_table('recipe_templates') + # ### end Alembic commands ### diff --git a/brewman/alembic/versions/185c674cc392_load_data.py b/brewman/alembic/versions/185c674cc392_load_data.py index 01ca8eac..1ed930c8 100644 --- a/brewman/alembic/versions/185c674cc392_load_data.py +++ b/brewman/alembic/versions/185c674cc392_load_data.py @@ -329,14 +329,14 @@ def add_all_permissions_to_owner(): r = table("roles", column("id", postgresql.UUID()), column("name", sa.String)) p = table( "permissions", - column("id", postgresql.UUID(as_uuid=True)), + column("id", postgresql.Uuid), column("name", sa.Unicode(length=255)), ) rp = table( "role_permissions", - column("id", postgresql.UUID(as_uuid=True)), - column("permission_id", postgresql.UUID(as_uuid=True)), - column("role_id", postgresql.UUID(as_uuid=True)), + column("id", postgresql.Uuid), + column("permission_id", postgresql.Uuid), + column("role_id", postgresql.Uuid), ) op.execute( insert(rp).from_select( @@ -357,9 +357,9 @@ def add_all_roles_to_admin(): r = table("roles", column("id", postgresql.UUID()), column("name", sa.String)) ur = table( "user_roles", - column("id", postgresql.UUID(as_uuid=True)), - column("user_id", postgresql.UUID(as_uuid=True)), - column("role_id", postgresql.UUID(as_uuid=True)), + column("id", postgresql.Uuid), + column("user_id", postgresql.Uuid), + column("role_id", postgresql.Uuid), ) op.execute( insert(ur).from_select( diff --git a/brewman/alembic/versions/a1372ed99c45_recipe_upgrade.py b/brewman/alembic/versions/a1372ed99c45_recipe_upgrade.py new file mode 100644 index 00000000..a82fad68 --- /dev/null +++ b/brewman/alembic/versions/a1372ed99c45_recipe_upgrade.py @@ -0,0 +1,174 @@ +"""Recipe upgrade + +Revision ID: a1372ed99c45 +Revises: 185c674cc392 +Create Date: 2023-03-31 05:03:40.408240 + +""" +import sqlalchemy as sa + +from alembic import op +from sqlalchemy.dialects import postgresql +from sqlalchemy import func + +# revision identifiers, used by Alembic. +revision = "a1372ed99c45" +down_revision = "185c674cc392" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "tags", + sa.Column("id", sa.Uuid(), nullable=False), + sa.Column("name", sa.Unicode(), nullable=False), + sa.PrimaryKeyConstraint("id", name=op.f("pk_tags")), + sa.UniqueConstraint("name", name=op.f("uq_tags_name")), + ) + op.create_table( + "recipe_tags", + sa.Column("id", sa.Uuid(), nullable=False), + sa.Column("recipe_id", sa.Uuid(), nullable=False), + sa.Column("tag_id", sa.Uuid(), nullable=False), + sa.ForeignKeyConstraint(["recipe_id"], ["recipes.id"], name=op.f("fk_recipe_tags_recipe_id_recipes")), + sa.ForeignKeyConstraint(["tag_id"], ["tags.id"], name=op.f("fk_recipe_tags_tag_id_tags")), + sa.PrimaryKeyConstraint("id", name=op.f("pk_recipe_tags")), + sa.UniqueConstraint("recipe_id", "tag_id", name=op.f("uq_recipe_tags_recipe_id")), + ) + op.alter_column("attendances", "employee_id", existing_type=sa.UUID(), nullable=False) + op.alter_column("attendances", "attendance_type", existing_type=sa.INTEGER(), nullable=False) + op.alter_column("attendances", "amount", existing_type=sa.NUMERIC(precision=5, scale=2), nullable=False) + op.alter_column("attendances", "creation_date", existing_type=postgresql.TIMESTAMP(), nullable=False) + op.alter_column("attendances", "user_id", existing_type=sa.UUID(), nullable=False) + op.alter_column("attendances", "is_valid", existing_type=sa.BOOLEAN(), nullable=False) + op.alter_column("cost_centres", "name", existing_type=sa.VARCHAR(length=255), nullable=False) + op.alter_column("employee_benefit", "gross_salary", existing_type=sa.INTEGER(), nullable=False) + op.alter_column("employee_benefit", "days_worked", existing_type=sa.INTEGER(), nullable=False) + op.alter_column("employee_benefit", "esi_employee", existing_type=sa.INTEGER(), nullable=False) + op.alter_column("employee_benefit", "pf_employee", existing_type=sa.INTEGER(), nullable=False) + op.alter_column("employee_benefit", "esi_employer", existing_type=sa.INTEGER(), nullable=False) + op.alter_column("employee_benefit", "pf_employer", existing_type=sa.INTEGER(), nullable=False) + op.alter_column("fingerprints", "employee_id", existing_type=sa.UUID(), nullable=False) + op.alter_column("fingerprints", "date", existing_type=postgresql.TIMESTAMP(), nullable=False) + op.alter_column("permissions", "name", existing_type=sa.VARCHAR(length=255), nullable=False) + op.alter_column("product_groups", "name", existing_type=sa.VARCHAR(length=255), nullable=False) + op.add_column("products", sa.Column("description", sa.Text(), nullable=True)) + op.alter_column("products", "code", existing_type=sa.INTEGER(), nullable=False) + op.add_column("recipes", sa.Column("date", sa.Date(), nullable=False, server_default=func.now())) + op.add_column("recipes", sa.Column("source", sa.Text(), nullable=False, server_default="")) + op.add_column("recipes", sa.Column("instructions", sa.Text(), nullable=False, server_default="")) + op.add_column("recipes", sa.Column("garnishing", sa.Text(), nullable=False, server_default="")) + op.add_column("recipes", sa.Column("plating", sa.Text(), nullable=False, server_default="")) + op.alter_column("recipes", "notes", existing_type=sa.VARCHAR(length=255),type=sa.Text(), nullable=False) + op.create_unique_constraint(op.f('uq_recipes_sku_id'), 'recipes', ['sku_id', 'date']) + op.create_index(op.f("ix_recipes_date"), "recipes", ["date"], unique=False) + op.drop_constraint(op.f("uq_recipes_sku_id"), "recipes", type_="unique") + op.drop_constraint("fk_recipes_period_id_periods", "recipes", type_="foreignkey") + op.drop_column("recipes", "period_id") + op.drop_column('recipes', 'sale_price') + op.drop_column('recipes', 'cost_price') + op.add_column('recipe_items', sa.Column('description', sa.Text(), nullable=False, server_default="")) + op.drop_column('recipe_items', 'price') + op.alter_column("role_permissions", "permission_id", existing_type=sa.UUID(), nullable=False) + op.alter_column("role_permissions", "role_id", existing_type=sa.UUID(), nullable=False) + op.create_unique_constraint( + op.f("uq_role_permissions_permission_id"), "role_permissions", ["permission_id", "role_id"] + ) + op.alter_column("roles", "name", existing_type=sa.VARCHAR(length=255), nullable=False) + op.alter_column("settings", "data", existing_type=postgresql.BYTEA(), nullable=False) + op.alter_column("user_roles", "user_id", existing_type=sa.UUID(), nullable=False) + op.alter_column("user_roles", "role_id", existing_type=sa.UUID(), nullable=False) + op.create_unique_constraint(op.f("uq_user_roles_user_id"), "user_roles", ["user_id", "role_id"]) + op.alter_column("users", "username", existing_type=sa.VARCHAR(length=255), nullable=False) + op.alter_column("users", "password", existing_type=sa.VARCHAR(length=60), nullable=False) + op.alter_column("users", "disabled", existing_type=sa.BOOLEAN(), nullable=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column( + "vouchers", + "voucher_type", + existing_type=sa.Enum( + "JOURNAL", + "PURCHASE", + "ISSUE", + "PAYMENT", + "RECEIPT", + "PURCHASE_RETURN", + "OPENING_ACCOUNTS", + "OPENING_BATCHES", + "CLOSING_STOCK", + "OPENING_BALANCE", + "CLOSING_BALANCE", + "EMPLOYEE_BENEFIT", + "INCENTIVE", + name="vouchertype", + ), + type_=postgresql.ENUM( + "JOURNAL", + "PURCHASE", + "ISSUE", + "PAYMENT", + "RECEIPT", + "PURCHASE_RETURN", + "OPENING_ACCOUNTS", + "OPENING_BATCHES", + "CLOSING_STOCK", + "OPENING_BALANCE", + "CLOSING_BALANCE", + "EMPLOYEE_BENEFIT", + "INCENTIVE", + name="voucher_type", + ), + existing_nullable=False, + ) + op.alter_column("users", "disabled", existing_type=sa.BOOLEAN(), nullable=True) + op.alter_column("users", "username", existing_type=sa.VARCHAR(length=255), nullable=True) + op.drop_constraint(op.f("uq_user_roles_user_id"), "user_roles", type_="unique") + op.alter_column("user_roles", "role_id", existing_type=sa.UUID(), nullable=True) + op.alter_column("user_roles", "user_id", existing_type=sa.UUID(), nullable=True) + op.alter_column( + "settings", + "setting_type", + existing_type=sa.Enum("VOUCHER_LOCK", "MAINTENANCE_MODE", name="settingtype"), + type_=postgresql.ENUM("VOUCHER_LOCK", name="setting_type"), + existing_nullable=False, + ) + op.alter_column("settings", "data", existing_type=postgresql.BYTEA(), nullable=True) + op.alter_column("roles", "name", existing_type=sa.VARCHAR(length=255), nullable=True) + op.drop_constraint(op.f("uq_role_permissions_permission_id"), "role_permissions", type_="unique") + op.alter_column("role_permissions", "role_id", existing_type=sa.UUID(), nullable=True) + op.alter_column("role_permissions", "permission_id", existing_type=sa.UUID(), nullable=True) + op.drop_index(op.f("ix_recipes_date"), table_name="recipes") + op.alter_column("recipes", "notes", existing_type=sa.VARCHAR(length=255), nullable=True) + op.drop_column("recipes", "plating") + op.drop_column("recipes", "garnishing") + op.drop_column("recipes", "instructions") + op.drop_column("recipes", "source") + op.drop_column("recipes", "date") + op.alter_column("products", "code", existing_type=sa.INTEGER(), nullable=True) + op.drop_column("products", "description") + op.alter_column("product_groups", "name", existing_type=sa.VARCHAR(length=255), nullable=True) + op.alter_column("permissions", "name", existing_type=sa.VARCHAR(length=255), nullable=True) + op.alter_column("fingerprints", "date", existing_type=postgresql.TIMESTAMP(), nullable=True) + op.alter_column("fingerprints", "employee_id", existing_type=sa.UUID(), nullable=True) + op.alter_column("employee_benefit", "pf_employer", existing_type=sa.INTEGER(), nullable=True) + op.alter_column("employee_benefit", "esi_employer", existing_type=sa.INTEGER(), nullable=True) + op.alter_column("employee_benefit", "pf_employee", existing_type=sa.INTEGER(), nullable=True) + op.alter_column("employee_benefit", "esi_employee", existing_type=sa.INTEGER(), nullable=True) + op.alter_column("employee_benefit", "days_worked", existing_type=sa.INTEGER(), nullable=True) + op.alter_column("employee_benefit", "gross_salary", existing_type=sa.INTEGER(), nullable=True) + op.alter_column("cost_centres", "name", existing_type=sa.VARCHAR(length=255), nullable=True) + op.alter_column("attendances", "is_valid", existing_type=sa.BOOLEAN(), nullable=True) + op.alter_column("attendances", "user_id", existing_type=sa.UUID(), nullable=True) + op.alter_column("attendances", "creation_date", existing_type=postgresql.TIMESTAMP(), nullable=True) + op.alter_column("attendances", "amount", existing_type=sa.NUMERIC(precision=5, scale=2), nullable=True) + op.alter_column("attendances", "attendance_type", existing_type=sa.INTEGER(), nullable=True) + op.alter_column("attendances", "employee_id", existing_type=sa.UUID(), nullable=True) + op.drop_table("recipe_tags") + op.drop_table("tags") + # ### end Alembic commands ### diff --git a/brewman/brewman/core/config.py b/brewman/brewman/core/config.py index cd07d1a8..ead7858a 100644 --- a/brewman/brewman/core/config.py +++ b/brewman/brewman/core/config.py @@ -1,6 +1,6 @@ import secrets -from typing import Any, Dict, Optional +from typing import Any from dotenv import load_dotenv from pydantic import BaseSettings, PostgresDsn, validator @@ -20,10 +20,10 @@ class Settings(BaseSettings): POSTGRES_USER: str = "postgres" POSTGRES_PASSWORD: str = "" POSTGRES_DB: str = "" - SQLALCHEMY_DATABASE_URI: Optional[str] = None + SQLALCHEMY_DATABASE_URI: str | None = None @validator("SQLALCHEMY_DATABASE_URI", pre=True) - def assemble_db_connection(cls, v: Optional[str], values: Dict[str, Any]) -> Any: + def assemble_db_connection(cls, v: str | None, values: dict[str, Any]) -> Any: if isinstance(v, str): return v return PostgresDsn.build( diff --git a/brewman/brewman/core/security.py b/brewman/brewman/core/security.py index a24931d8..ccc69a3e 100644 --- a/brewman/brewman/core/security.py +++ b/brewman/brewman/core/security.py @@ -1,7 +1,6 @@ import uuid from datetime import datetime, timedelta -from typing import List, Optional, Tuple from fastapi import Depends, HTTPException, Security, status from fastapi.security import OAuth2PasswordBearer, SecurityScopes @@ -30,7 +29,7 @@ class Token(BaseModel): class TokenData(BaseModel): username: str - scopes: List[str] = [] + scopes: list[str] = [] def f7(seq): @@ -50,7 +49,7 @@ def create_access_token(*, data: dict, expires_delta: timedelta = None): return encoded_jwt -def get_user(username: str, id_: str, locked_out: bool, scopes: List[str]) -> UserToken: +def get_user(username: str, id_: str, locked_out: bool, scopes: list[str]) -> UserToken: return UserToken( id_=uuid.UUID(id_), name=username, @@ -60,13 +59,13 @@ def get_user(username: str, id_: str, locked_out: bool, scopes: List[str]) -> Us ) -def authenticate_user(username: str, password: str, db: Session) -> Optional[UserModel]: +def authenticate_user(username: str, password: str, db: Session) -> UserModel | None: user = UserModel.auth(username, password, db) return user -def client_allowed(user: UserModel, client_id: int, otp: Optional[int], db: Session) -> Tuple[bool, Client]: - client: Optional[Client] = ( +def client_allowed(user: UserModel, client_id: int, otp: int | None, db: Session) -> tuple[bool, Client]: + client: Client | None = ( db.execute(select(Client).where(Client.code == client_id)).scalar_one_or_none() if client_id else None ) if client is None: diff --git a/brewman/brewman/core/session.py b/brewman/brewman/core/session.py index 48caaa1f..244e8831 100644 --- a/brewman/brewman/core/session.py +++ b/brewman/brewman/core/session.py @@ -1,5 +1,4 @@ from datetime import date, timedelta -from typing import Union def get_date(session: dict) -> str: @@ -8,7 +7,7 @@ def get_date(session: dict) -> str: return session["date"] # type: ignore[no-any-return] -def set_date(date_: Union[str, date], session: dict) -> None: +def set_date(date_: str | date, session: dict) -> None: session["date"] = date_ if isinstance(date_, str) else date_.strftime("%d-%b-%Y") @@ -24,7 +23,7 @@ def get_finish_date(session: dict) -> str: return session["finish"] # type: ignore[no-any-return] -def set_period(start: Union[str, date], finish: Union[str, date], session: dict) -> None: +def set_period(start: str | date, finish: str | date, session: dict) -> None: if start is not None: session["start"] = start if isinstance(start, str) else start.strftime("%d-%b-%Y") if finish is not None: diff --git a/brewman/brewman/db/base.py b/brewman/brewman/db/base.py index 8e0f7ce3..80e41a52 100644 --- a/brewman/brewman/db/base.py +++ b/brewman/brewman/db/base.py @@ -25,11 +25,14 @@ from ..models.rate_contract import RateContract # noqa: F401 from ..models.rate_contract_item import RateContractItem # noqa: F401 from ..models.recipe import Recipe # noqa: F401 from ..models.recipe_item import RecipeItem # noqa: F401 +from ..models.recipe_tag import RecipeTag # noqa: F401 +from ..models.recipe_template import RecipeTemplate # noqa: F401 from ..models.role import Role # noqa: F401 -from ..models.role_permission import role_permission # noqa: F401 +from ..models.role_permission import RolePermission # noqa: F401 from ..models.stock_keeping_unit import StockKeepingUnit # noqa: F401 +from ..models.tag import Tag # noqa: F401 from ..models.user import User # noqa: F401 -from ..models.user_role import user_role # noqa: F401 +from ..models.user_role import UserRole # noqa: F401 from ..models.voucher import Voucher # noqa: F401 from ..models.voucher_type import VoucherType # noqa: F401 -from .base_class import Base # noqa: F401 +from .base_class import reg # noqa: F401 diff --git a/brewman/brewman/db/base_class.py b/brewman/brewman/db/base_class.py index 0c313478..72ceba81 100644 --- a/brewman/brewman/db/base_class.py +++ b/brewman/brewman/db/base_class.py @@ -1,4 +1,4 @@ -from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import registry from sqlalchemy.schema import MetaData @@ -14,4 +14,5 @@ NAMING_CONVENTION = { } metadata = MetaData(naming_convention=NAMING_CONVENTION) -Base = declarative_base(metadata=metadata) +reg = registry() +reg.metadata = metadata diff --git a/brewman/brewman/main.py b/brewman/brewman/main.py index d206ce19..e1bd64a7 100644 --- a/brewman/brewman/main.py +++ b/brewman/brewman/main.py @@ -4,7 +4,7 @@ from fastapi import FastAPI from starlette.middleware.sessions import SessionMiddleware from .core.config import settings -from .db.base import Base # noqa: F401 +from .db.base import reg # noqa: F401 from .routers import ( account, account_types, @@ -38,6 +38,7 @@ from .routers import ( rate_contract, rebase, recipe, + recipe_template, role, user, voucher, @@ -86,6 +87,7 @@ app.include_router(fingerprint.router, prefix="/api/fingerprint", tags=["employe app.include_router(product.router, prefix="/api/products", tags=["products"]) app.include_router(product_group.router, prefix="/api/product-groups", tags=["products"]) app.include_router(recipe.router, prefix="/api/recipes", tags=["products"]) +app.include_router(recipe_template.router, prefix="/api/recipe-templates", tags=["products"]) app.include_router(period.router, prefix="/api/periods", tags=["periods"]) app.include_router(client.router, prefix="/api/clients", tags=["clients"]) diff --git a/brewman/brewman/models/account.py b/brewman/brewman/models/account.py index 727b54af..c16698c9 100644 --- a/brewman/brewman/models/account.py +++ b/brewman/brewman/models/account.py @@ -1,24 +1,24 @@ -from typing import TYPE_CHECKING, List, Tuple +from typing import TYPE_CHECKING -from sqlalchemy.orm import relationship +from sqlalchemy.orm import Mapped, relationship +from ..db.base_class import reg from .account_base import AccountBase if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime from .product import Product +@reg.mapped_as_dataclass(unsafe_hash=True) class Account(AccountBase): __mapper_args__ = {"polymorphic_identity": ""} # type: ignore - products: List["Product"] = relationship( + products: Mapped[list["Product"]] = relationship( "Product", primaryjoin="Account.id==Product.account_id", back_populates="account" ) - def can_delete(self, advanced_delete: bool) -> Tuple[bool, str]: + def can_delete(self, advanced_delete: bool) -> tuple[bool, str]: if len(self.products) > 0: return False, "Account has products" return super(Account, self).can_delete(advanced_delete) diff --git a/brewman/brewman/models/account_base.py b/brewman/brewman/models/account_base.py index d0600418..47983a94 100644 --- a/brewman/brewman/models/account_base.py +++ b/brewman/brewman/models/account_base.py @@ -1,57 +1,50 @@ import uuid -from typing import TYPE_CHECKING, List, Optional, Tuple +from typing import TYPE_CHECKING -from sqlalchemy import Boolean, Column, ForeignKey, Integer, Unicode, func, select -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import Mapped, Session, relationship +from sqlalchemy import Boolean, ForeignKey, Integer, Unicode, Uuid, func, select +from sqlalchemy.orm import Mapped, Session, mapped_column, relationship -from ..db.base_class import Base +from ..db.base_class import reg from ..schemas.account import AccountLink from .account_type import AccountType if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime from .cost_centre import CostCentre from .journal import Journal from .product import Product from .rate_contract import RateContract -class AccountBase(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class AccountBase: __tablename__ = "accounts" - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - code: int = Column("code", Integer, nullable=False) - name: str = Column("name", Unicode(255), unique=True, nullable=False) - type_id: int = Column("type", Integer, ForeignKey("account_types.id"), nullable=False) - account_type: str = Column("account_type", Unicode(50), nullable=False) - is_starred: bool = Column("is_starred", Boolean, nullable=False) - is_active: bool = Column("is_active", Boolean, nullable=False) - is_reconcilable: bool = Column("is_reconcilable", Boolean, nullable=False) - cost_centre_id: uuid.UUID = Column( - "cost_centre_id", - UUID(as_uuid=True), + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + code: Mapped[int] = mapped_column(Integer, nullable=False) + name: Mapped[str] = mapped_column(Unicode, unique=True, nullable=False) + type_id: Mapped[int] = mapped_column("type", Integer, ForeignKey("account_types.id"), nullable=False) + account_type: Mapped[str] = mapped_column(Unicode, nullable=False) + is_starred: Mapped[bool] = mapped_column(Boolean, nullable=False) + is_active: Mapped[bool] = mapped_column(Boolean, nullable=False) + is_reconcilable: Mapped[bool] = mapped_column(Boolean, nullable=False) + cost_centre_id: Mapped[uuid.UUID] = mapped_column( + Uuid, ForeignKey("cost_centres.id"), nullable=False, ) - is_fixture: bool = Column("is_fixture", Boolean, nullable=False) + is_fixture: Mapped[bool] = mapped_column(Boolean, nullable=False) __mapper_args__ = {"polymorphic_on": account_type} - type_: AccountType = relationship("AccountType") + type_: Mapped[AccountType] = relationship("AccountType", back_populates="accounts") - journals: List["Journal"] = relationship("Journal", back_populates="account") + journals: Mapped[list["Journal"]] = relationship("Journal", back_populates="account") cost_centre: Mapped["CostCentre"] = relationship("CostCentre", back_populates="accounts") - products: List["Product"] = relationship("Product", back_populates="account") + products: Mapped[list["Product"]] = relationship("Product", back_populates="account") - rate_contracts: List["RateContract"] = relationship("RateContract", back_populates="vendor") - - @property - def __name__(self) -> str: - return self.name + rate_contracts: Mapped[list["RateContract"]] = relationship("RateContract", back_populates="vendor") def __init__( self, @@ -61,8 +54,8 @@ class AccountBase(Base): is_active: bool, is_reconcilable: bool, cost_centre_id: uuid.UUID, - code: Optional[int] = None, - id_: Optional[uuid.UUID] = None, + code: int | None = None, + id_: uuid.UUID | None = None, is_fixture: bool = False, ) -> None: if code is not None: @@ -84,7 +77,7 @@ class AccountBase(Base): db.add(self) return self - def can_delete(self, advanced_delete: bool) -> Tuple[bool, str]: + def can_delete(self, advanced_delete: bool) -> tuple[bool, str]: if self.is_fixture: return False, f"{self.name} is a fixture and cannot be edited or deleted." if self.is_active: diff --git a/brewman/brewman/models/account_type.py b/brewman/brewman/models/account_type.py index f3e62686..ad1c5490 100644 --- a/brewman/brewman/models/account_type.py +++ b/brewman/brewman/models/account_type.py @@ -1,15 +1,27 @@ -from sqlalchemy import Boolean, Column, Integer, Unicode +from typing import TYPE_CHECKING -from ..db.base_class import Base +from sqlalchemy import Boolean, Integer, Unicode +from sqlalchemy.orm import Mapped, mapped_column, relationship + +from ..db.base_class import reg -class AccountType(Base): +if TYPE_CHECKING: + # if the target of the relationship is in another module + # that cannot normally be imported at runtime + from .account import AccountBase + + +@reg.mapped_as_dataclass(unsafe_hash=True) +class AccountType: __tablename__ = "account_types" - id: int = Column("id", Integer, primary_key=True) - name: str = Column("name", Unicode(255), unique=True, nullable=False) - balance_sheet: bool = Column("balance_sheet", Boolean, nullable=False) - debit: bool = Column("debit", Boolean, nullable=False) - cash_flow_classification: str = Column("cash_flow_classification", Unicode(255), nullable=False) - order: int = Column("order", Integer, nullable=False) - show_in_list: bool = Column("show_in_list", Boolean, nullable=False) + id: Mapped[int] = mapped_column(Integer, primary_key=True) + name: Mapped[str] = mapped_column(Unicode, unique=True, nullable=False) + balance_sheet: Mapped[bool] = mapped_column(Boolean, nullable=False) + debit: Mapped[bool] = mapped_column(Boolean, nullable=False) + cash_flow_classification: Mapped[str] = mapped_column(Unicode, nullable=False) + order: Mapped[int] = mapped_column(Integer, nullable=False) + show_in_list: Mapped[bool] = mapped_column(Boolean, nullable=False) + + accounts: Mapped[list["AccountBase"]] = relationship("AccountBase", back_populates="type_") diff --git a/brewman/brewman/models/attendance.py b/brewman/brewman/models/attendance.py index add5927a..7dbbaf53 100644 --- a/brewman/brewman/models/attendance.py +++ b/brewman/brewman/models/attendance.py @@ -2,23 +2,22 @@ import datetime import uuid from decimal import Decimal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING from sqlalchemy import ( Boolean, - Column, Date, DateTime, ForeignKey, Index, Integer, Numeric, + Uuid, select, ) -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import Mapped, Session, relationship +from sqlalchemy.orm import Mapped, Session, mapped_column, relationship -from ..db.base_class import Base +from ..db.base_class import reg if TYPE_CHECKING: @@ -28,16 +27,17 @@ if TYPE_CHECKING: from .user import User -class Attendance(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class Attendance: __tablename__ = "attendances" - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - employee_id: uuid.UUID = Column("employee_id", UUID(as_uuid=True), ForeignKey("employees.id")) - date: datetime.date = Column("date", Date, nullable=False) - attendance_type: int = Column("attendance_type", Integer) - amount: Decimal = Column("amount", Numeric(precision=5, scale=2)) - creation_date: datetime.datetime = Column("creation_date", DateTime()) - user_id: uuid.UUID = Column("user_id", UUID(as_uuid=True), ForeignKey("users.id")) - is_valid: bool = Column("is_valid", Boolean) + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + employee_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("employees.id"), nullable=False) + date: Mapped[datetime.date] = mapped_column(Date, nullable=False) + attendance_type: Mapped[int] = mapped_column(Integer, nullable=False) + amount: Mapped[Decimal] = mapped_column(Numeric(precision=5, scale=2), nullable=False) + creation_date: Mapped[datetime.datetime] = mapped_column(DateTime(), nullable=False) + user_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("users.id"), nullable=False) + is_valid: Mapped[bool] = mapped_column(Boolean, nullable=False) __table_args__ = ( Index( @@ -58,9 +58,9 @@ class Attendance(Base): attendance_type: int, user_id: uuid.UUID, amount: Decimal = Decimal(0), - creation_date: Optional[datetime.datetime] = None, + creation_date: datetime.datetime | None = None, is_valid: bool = True, - id_: Optional[uuid.UUID] = None, + id_: uuid.UUID | None = None, ): self.employee_id = employee_id self.date = date diff --git a/brewman/brewman/models/attendance_type.py b/brewman/brewman/models/attendance_type.py index 8f974d99..a1281e3d 100644 --- a/brewman/brewman/models/attendance_type.py +++ b/brewman/brewman/models/attendance_type.py @@ -1,5 +1,4 @@ from decimal import Decimal -from typing import List class AttendanceType: @@ -13,7 +12,7 @@ class AttendanceType: self.value = value @classmethod - def list(cls) -> List["AttendanceType"]: + def list(cls) -> list["AttendanceType"]: return [ AttendanceType(0, "Not Set", Decimal(0)), AttendanceType(1, "Present", Decimal(1)), diff --git a/brewman/brewman/models/batch.py b/brewman/brewman/models/batch.py index 8556d179..f1d29aa7 100644 --- a/brewman/brewman/models/batch.py +++ b/brewman/brewman/models/batch.py @@ -1,35 +1,35 @@ +from __future__ import annotations + import uuid from datetime import date from decimal import Decimal -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING -from sqlalchemy import Column, Date, ForeignKey, Numeric -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import Mapped, relationship +from sqlalchemy import Date, ForeignKey, Numeric, Uuid +from sqlalchemy.orm import Mapped, mapped_column, relationship -from ..db.base_class import Base +from ..db.base_class import reg if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime from .inventory import Inventory from .stock_keeping_unit import StockKeepingUnit -class Batch(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class Batch: __tablename__ = "batches" - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - name: date = Column("name", Date, nullable=False) - sku_id: uuid.UUID = Column("sku_id", UUID(as_uuid=True), ForeignKey("stock_keeping_units.id"), nullable=False) - quantity_remaining: Decimal = Column("quantity_remaining", Numeric(precision=15, scale=2), nullable=False) - rate: Decimal = Column("rate", Numeric(precision=15, scale=2), nullable=False) - tax: Decimal = Column("tax", Numeric(precision=15, scale=5), nullable=False) - discount: Decimal = Column("discount", Numeric(precision=15, scale=5), nullable=False) + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + name: Mapped[date] = mapped_column(Date, nullable=False) + sku_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("stock_keeping_units.id"), nullable=False) + quantity_remaining: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=2), nullable=False) + rate: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=2), nullable=False) + tax: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=5), nullable=False) + discount: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=5), nullable=False) - inventories: List["Inventory"] = relationship("Inventory", back_populates="batch") + inventories: Mapped[list["Inventory"]] = relationship("Inventory", back_populates="batch") sku: Mapped["StockKeepingUnit"] = relationship("StockKeepingUnit", back_populates="batches") def __init__( @@ -39,8 +39,8 @@ class Batch(Base): rate: Decimal, tax: Decimal, discount: Decimal, - sku_id: Optional[uuid.UUID] = None, - sku: Optional["StockKeepingUnit"] = None, + sku_id: uuid.UUID | None = None, + sku: StockKeepingUnit | None = None, ): self.name = name if sku_id is not None and sku is None: diff --git a/brewman/brewman/models/client.py b/brewman/brewman/models/client.py index 39a224b1..6ac2f11f 100644 --- a/brewman/brewman/models/client.py +++ b/brewman/brewman/models/client.py @@ -5,27 +5,26 @@ import string import uuid from datetime import datetime -from typing import List, Optional -from sqlalchemy import Boolean, Column, DateTime, Integer, Unicode, desc -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import Session, relationship +from sqlalchemy import Boolean, DateTime, Integer, Unicode, Uuid, desc +from sqlalchemy.orm import Mapped, Session, mapped_column, relationship -from ..db.base_class import Base +from ..db.base_class import reg from .login_history import LoginHistory -class Client(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class Client: __tablename__ = "clients" - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - code: int = Column("code", Integer, unique=True, nullable=False) - name: str = Column("name", Unicode(255), unique=True, nullable=False) - enabled: bool = Column("enabled", Boolean, nullable=False) - otp: Optional[int] = Column("otp", Integer) - creation_date: datetime = Column("creation_date", DateTime(), nullable=False) + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + code: Mapped[int] = mapped_column(Integer, unique=True, nullable=False) + name: Mapped[str] = mapped_column(Unicode, unique=True, nullable=False) + enabled: Mapped[bool] = mapped_column(Boolean, nullable=False) + otp: Mapped[int | None] = mapped_column(Integer, nullable=True) + creation_date: Mapped[datetime] = mapped_column(DateTime(), nullable=False) - login_history: List["LoginHistory"] = relationship( + login_history: Mapped[list["LoginHistory"]] = relationship( "LoginHistory", order_by=desc(LoginHistory.date), back_populates="client" ) @@ -34,9 +33,9 @@ class Client(Base): code: int, name: str, enabled: bool, - otp: Optional[int], - creation_date: Optional[datetime] = None, - id_: Optional[uuid.UUID] = None, + otp: int | None, + creation_date: datetime | None = None, + id_: uuid.UUID | None = None, ) -> None: self.code = code self.name = name diff --git a/brewman/brewman/models/closing_stock.py b/brewman/brewman/models/closing_stock.py index 1471d986..6d77af3a 100644 --- a/brewman/brewman/models/closing_stock.py +++ b/brewman/brewman/models/closing_stock.py @@ -1,36 +1,35 @@ +from __future__ import annotations + import datetime import uuid from decimal import Decimal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING -from sqlalchemy import Column, Date, ForeignKey, Numeric, UniqueConstraint -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import Mapped, relationship +from sqlalchemy import Date, ForeignKey, Numeric, UniqueConstraint, Uuid +from sqlalchemy.orm import Mapped, mapped_column, relationship -from ..db.base_class import Base +from ..db.base_class import reg if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime from .cost_centre import CostCentre from .stock_keeping_unit import StockKeepingUnit -class ClosingStock(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class ClosingStock: __tablename__ = "closing_stocks" __table_args__ = (UniqueConstraint("date", "cost_centre_id", "sku_id"),) - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - date_: datetime.date = Column("date", Date, nullable=False) - cost_centre_id: uuid.UUID = Column( - "cost_centre_id", - UUID(as_uuid=True), + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + date_: Mapped[datetime.date] = mapped_column("date", Date, nullable=False) + cost_centre_id: Mapped[uuid.UUID] = mapped_column( + Uuid, ForeignKey("cost_centres.id"), nullable=False, ) - sku_id: uuid.UUID = Column("sku_id", UUID(as_uuid=True), ForeignKey("stock_keeping_units.id"), nullable=False) - quantity: Decimal = Column("quantity", Numeric(precision=15, scale=2), nullable=False) + sku_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("stock_keeping_units.id"), nullable=False) + quantity: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=2), nullable=False) cost_centre: Mapped["CostCentre"] = relationship("CostCentre") sku: Mapped["StockKeepingUnit"] = relationship("StockKeepingUnit") @@ -40,9 +39,9 @@ class ClosingStock(Base): date_: datetime.date, cost_centre_id: uuid.UUID, quantity: Decimal, - sku_id: Optional[uuid.UUID] = None, - sku: Optional["StockKeepingUnit"] = None, - id_: Optional[uuid.UUID] = None, + sku_id: uuid.UUID | None = None, + sku: "StockKeepingUnit" | None = None, + id_: uuid.UUID | None = None, ): self.date_ = date_ self.cost_centre_id = cost_centre_id diff --git a/brewman/brewman/models/cost_centre.py b/brewman/brewman/models/cost_centre.py index e70d13ac..6b732f64 100644 --- a/brewman/brewman/models/cost_centre.py +++ b/brewman/brewman/models/cost_centre.py @@ -1,12 +1,11 @@ import uuid -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING -from sqlalchemy import Boolean, Column, Unicode -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import relationship +from sqlalchemy import Boolean, Unicode, Uuid +from sqlalchemy.orm import Mapped, mapped_column, relationship -from ..db.base_class import Base +from ..db.base_class import reg from ..schemas.cost_centre import CostCentreLink @@ -17,21 +16,22 @@ if TYPE_CHECKING: from .journal import Journal -class CostCentre(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class CostCentre: __tablename__ = "cost_centres" - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - name: str = Column("name", Unicode(255), unique=True) - is_fixture: bool = Column("is_fixture", Boolean, nullable=False) + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + name: Mapped[str] = mapped_column(Unicode, unique=True, nullable=False) + is_fixture: Mapped[bool] = mapped_column(Boolean, nullable=False) - accounts: List["AccountBase"] = relationship("AccountBase", back_populates="cost_centre") - journals: List["Journal"] = relationship("Journal", back_populates="cost_centre") + accounts: Mapped[list["AccountBase"]] = relationship("AccountBase", back_populates="cost_centre") + journals: Mapped[list["Journal"]] = relationship("Journal", back_populates="cost_centre") @property def __name__(self) -> str: return self.name - def __init__(self, name: str, id_: Optional[uuid.UUID] = None, is_fixture: bool = False) -> None: + def __init__(self, name: str, id_: uuid.UUID | None = None, is_fixture: bool = False) -> None: self.name = name if id_ is not None: self.id = id_ diff --git a/brewman/brewman/models/db_image.py b/brewman/brewman/models/db_image.py index e0a4bc99..18526276 100644 --- a/brewman/brewman/models/db_image.py +++ b/brewman/brewman/models/db_image.py @@ -1,23 +1,23 @@ import uuid from datetime import datetime -from typing import Optional -from sqlalchemy import Column, DateTime, Unicode -from sqlalchemy.dialects.postgresql import BYTEA, UUID +from sqlalchemy import DateTime, LargeBinary, Unicode, Uuid +from sqlalchemy.orm import Mapped, mapped_column -from ..db.base_class import Base +from ..db.base_class import reg -class DbImage(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class DbImage: __tablename__ = "images" - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - resource_id: uuid.UUID = Column("resource_id", UUID(as_uuid=True), nullable=False) - resource_type: str = Column("resource_type", Unicode(255), nullable=False) - image: bytes = Column("image", BYTEA, nullable=False) - thumbnail: bytes = Column("thumbnail", BYTEA, nullable=False) - creation_date: datetime = Column("creation_date", DateTime(), nullable=False) + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + resource_id: Mapped[uuid.UUID] = mapped_column(Uuid, nullable=False) + resource_type: Mapped[str] = mapped_column(Unicode, nullable=False) + image: Mapped[bytes] = mapped_column(LargeBinary, nullable=False) + thumbnail: Mapped[bytes] = mapped_column(LargeBinary, nullable=False) + creation_date: Mapped[datetime] = mapped_column(DateTime(), nullable=False) def __init__( self, @@ -25,8 +25,8 @@ class DbImage(Base): resource_type: str, image: bytes, thumbnail: bytes, - creation_date: Optional[datetime] = None, - id_: Optional[uuid.UUID] = None, + creation_date: datetime | None = None, + id_: uuid.UUID | None = None, ) -> None: self.resource_id = resource_id self.resource_type = resource_type diff --git a/brewman/brewman/models/db_setting.py b/brewman/brewman/models/db_setting.py index 5ebfee9f..844f9793 100644 --- a/brewman/brewman/models/db_setting.py +++ b/brewman/brewman/models/db_setting.py @@ -1,34 +1,34 @@ import uuid from datetime import date -from typing import Optional -from sqlalchemy import Column, Date, Enum, PickleType, Unicode -from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy import Date, Enum, PickleType, Unicode, Uuid +from sqlalchemy.orm import Mapped, mapped_column -from ..db.base_class import Base +from ..db.base_class import reg from .setting_type import SettingType -class DbSetting(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class DbSetting: __tablename__ = "settings" - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - name: str = Column("name", Unicode(255), unique=True, nullable=False) - data: dict = Column("data", PickleType) - setting_type: SettingType = Column("setting_type", Enum(SettingType), nullable=False) + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + name: Mapped[str] = mapped_column(Unicode, unique=True, nullable=False) + data: Mapped[dict] = mapped_column(PickleType) + setting_type: Mapped[SettingType] = mapped_column(Enum(SettingType), nullable=False) - valid_from: Optional[date] = Column("valid_from", Date(), nullable=True) - valid_till: Optional[date] = Column("valid_till", Date(), nullable=True) + valid_from: Mapped[date | None] = mapped_column(Date(), nullable=True) + valid_till: Mapped[date | None] = mapped_column(Date(), nullable=True) def __init__( self, setting_type: SettingType, name: str, data: dict, - valid_from: Optional[date] = None, - valid_till: Optional[date] = None, - id_: Optional[uuid.UUID] = None, + valid_from: date | None = None, + valid_till: date | None = None, + id_: uuid.UUID | None = None, ) -> None: if id_ is not None: self.id = id_ diff --git a/brewman/brewman/models/employee.py b/brewman/brewman/models/employee.py index 894fb309..36605b62 100644 --- a/brewman/brewman/models/employee.py +++ b/brewman/brewman/models/employee.py @@ -2,39 +2,38 @@ import uuid from datetime import date from decimal import Decimal -from typing import TYPE_CHECKING, List, Optional, Tuple +from typing import TYPE_CHECKING -from sqlalchemy import Column, Date, ForeignKey, Integer, Numeric, Unicode, func, select -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import Session, relationship +from sqlalchemy import Date, ForeignKey, Integer, Numeric, Unicode, Uuid, func, select +from sqlalchemy.orm import Mapped, Session, mapped_column, relationship +from ..db.base_class import reg from .account_base import AccountBase if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime from .attendance import Attendance from .fingerprint import Fingerprint +@reg.mapped_as_dataclass(unsafe_hash=True) class Employee(AccountBase): __tablename__ = "employees" __mapper_args__ = {"polymorphic_identity": "employees"} # type: ignore - id: uuid.UUID = Column("id", UUID(as_uuid=True), ForeignKey("accounts.id"), primary_key=True) - designation: str = Column("designation", Unicode(255), nullable=False) - salary: int = Column("salary", Integer, nullable=False) - points: Decimal = Column("points", Numeric(precision=5, scale=2), nullable=False) - joining_date: date = Column("joining_date", Date, nullable=False) - leaving_date: Optional[date] = Column("leaving_date", Date, nullable=True) + id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("accounts.id"), primary_key=True) + designation: Mapped[str] = mapped_column(Unicode, nullable=False) + salary: Mapped[int] = mapped_column(Integer, nullable=False) + points: Mapped[Decimal] = mapped_column(Numeric(precision=5, scale=2), nullable=False) + joining_date: Mapped[date] = mapped_column(Date, nullable=False) + leaving_date: Mapped[date | None] = mapped_column(Date, nullable=True) - attendances: List["Attendance"] = relationship("Attendance", back_populates="employee") - fingerprints: List["Fingerprint"] = relationship("Fingerprint", back_populates="employee") + attendances: Mapped[list["Attendance"]] = relationship("Attendance", back_populates="employee") + fingerprints: Mapped[list["Fingerprint"]] = relationship("Fingerprint", back_populates="employee") def __init__( self, - code: Optional[int], + code: int | None, name: str, is_starred: bool, is_active: bool, @@ -43,7 +42,7 @@ class Employee(AccountBase): salary: int, points: Decimal, joining_date: date, - leaving_date: Optional[date] = None, + leaving_date: date | None = None, ) -> None: self.designation = designation self.salary = salary @@ -68,5 +67,5 @@ class Employee(AccountBase): db.add(self) return self - def can_delete(self, advanced_delete: bool) -> Tuple[bool, str]: + def can_delete(self, advanced_delete: bool) -> tuple[bool, str]: return super(Employee, self).can_delete(advanced_delete) diff --git a/brewman/brewman/models/employee_benefit.py b/brewman/brewman/models/employee_benefit.py index 29e7161f..576db009 100644 --- a/brewman/brewman/models/employee_benefit.py +++ b/brewman/brewman/models/employee_benefit.py @@ -1,31 +1,29 @@ import uuid -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING -from sqlalchemy import Column, ForeignKey, Integer -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import Mapped, relationship +from sqlalchemy import ForeignKey, Integer, Uuid +from sqlalchemy.orm import Mapped, mapped_column, relationship -from ..db.base_class import Base +from ..db.base_class import reg if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime from .journal import Journal -class EmployeeBenefit(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class EmployeeBenefit: __tablename__ = "employee_benefit" - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - voucher_id: uuid.UUID = Column("voucher_id", UUID(as_uuid=True), ForeignKey("vouchers.id"), nullable=False) - journal_id: uuid.UUID = Column("journal_id", UUID(as_uuid=True), ForeignKey("journals.id"), nullable=False) - gross_salary: int = Column("gross_salary", Integer) - days_worked: int = Column("days_worked", Integer) - esi_ee: int = Column("esi_employee", Integer) - pf_ee: int = Column("pf_employee", Integer) - esi_er: int = Column("esi_employer", Integer) - pf_er: int = Column("pf_employer", Integer) + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + voucher_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("vouchers.id"), nullable=False) + journal_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("journals.id"), nullable=False) + gross_salary: Mapped[int] = mapped_column(Integer, nullable=False) + days_worked: Mapped[int] = mapped_column(Integer, nullable=False) + esi_ee: Mapped[int] = mapped_column("esi_employee", Integer, nullable=False) + pf_ee: Mapped[int] = mapped_column("pf_employee", Integer, nullable=False) + esi_er: Mapped[int] = mapped_column("esi_employer", Integer, nullable=False) + pf_er: Mapped[int] = mapped_column("pf_employer", Integer, nullable=False) journal: Mapped["Journal"] = relationship("Journal", back_populates="employee_benefit") @@ -38,8 +36,8 @@ class EmployeeBenefit(Base): esi_er: int, pf_er: int, journal: "Journal", - voucher_id: Optional[uuid.UUID] = None, - id_: Optional[uuid.UUID] = None, + voucher_id: uuid.UUID | None = None, + id_: uuid.UUID | None = None, ) -> None: if id_ is not None: self.id = id_ diff --git a/brewman/brewman/models/fingerprint.py b/brewman/brewman/models/fingerprint.py index 89f1af51..22408635 100644 --- a/brewman/brewman/models/fingerprint.py +++ b/brewman/brewman/models/fingerprint.py @@ -1,31 +1,29 @@ import uuid from datetime import datetime -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING -from sqlalchemy import Column, DateTime, ForeignKey -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import Mapped, relationship +from sqlalchemy import DateTime, ForeignKey, Uuid +from sqlalchemy.orm import Mapped, mapped_column, relationship -from ..db.base_class import Base +from ..db.base_class import reg if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime from .employee import Employee -class Fingerprint(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class Fingerprint: __tablename__ = "fingerprints" - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - employee_id: uuid.UUID = Column("employee_id", UUID(as_uuid=True), ForeignKey("employees.id")) - date: datetime = Column("date", DateTime) + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + employee_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("employees.id"), nullable=False) + date: Mapped[datetime] = mapped_column(DateTime, nullable=False) employee: Mapped["Employee"] = relationship("Employee", back_populates="fingerprints") - def __init__(self, employee_id: uuid.UUID, date: datetime, id_: Optional[uuid.UUID] = None) -> None: + def __init__(self, employee_id: uuid.UUID, date: datetime, id_: uuid.UUID | None = None) -> None: self.employee_id = employee_id self.date = date if id_ is not None: diff --git a/brewman/brewman/models/incentive.py b/brewman/brewman/models/incentive.py index d63633c7..4b40965d 100644 --- a/brewman/brewman/models/incentive.py +++ b/brewman/brewman/models/incentive.py @@ -1,28 +1,26 @@ import uuid from decimal import Decimal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING -from sqlalchemy import Column, ForeignKey, Numeric -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import Mapped, relationship +from sqlalchemy import ForeignKey, Numeric, Uuid +from sqlalchemy.orm import Mapped, mapped_column, relationship -from ..db.base_class import Base +from ..db.base_class import reg if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime from .journal import Journal -class Incentive(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class Incentive: __tablename__ = "incentives" - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - voucher_id: uuid.UUID = Column("voucher_id", UUID(as_uuid=True), ForeignKey("vouchers.id"), nullable=False) - journal_id: uuid.UUID = Column("journal_id", UUID(as_uuid=True), ForeignKey("journals.id"), nullable=False) - days_worked: Decimal = Column("days_worked", Numeric(precision=5, scale=1), nullable=False) - points: Decimal = Column("points", Numeric(precision=5, scale=2), nullable=False) + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + voucher_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("vouchers.id"), nullable=False) + journal_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("journals.id"), nullable=False) + days_worked: Mapped[Decimal] = mapped_column(Numeric(precision=5, scale=1), nullable=False) + points: Mapped[Decimal] = mapped_column(Numeric(precision=5, scale=2), nullable=False) journal: Mapped["Journal"] = relationship("Journal", back_populates="incentive") @@ -31,8 +29,8 @@ class Incentive(Base): journal: "Journal", days_worked: Decimal, points: Decimal, - voucher_id: Optional[uuid.UUID] = None, - id_: Optional[uuid.UUID] = None, + voucher_id: uuid.UUID | None = None, + id_: uuid.UUID | None = None, ) -> None: self.journal = journal self.days_worked = days_worked diff --git a/brewman/brewman/models/inventory.py b/brewman/brewman/models/inventory.py index aea8f10b..256dc176 100644 --- a/brewman/brewman/models/inventory.py +++ b/brewman/brewman/models/inventory.py @@ -1,39 +1,36 @@ import uuid from decimal import Decimal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING -from sqlalchemy import Column, ForeignKey, Numeric, UniqueConstraint -from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy import ForeignKey, Numeric, UniqueConstraint, Uuid from sqlalchemy.ext.hybrid import hybrid_property -from sqlalchemy.orm import Mapped, relationship +from sqlalchemy.orm import Mapped, mapped_column, relationship -from ..db.base_class import Base +from ..db.base_class import reg if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime from .batch import Batch from .voucher import Voucher -class Inventory(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class Inventory: __tablename__ = "inventories" __table_args__ = (UniqueConstraint("voucher_id", "batch_id"),) - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - voucher_id: uuid.UUID = Column( - "voucher_id", - UUID(as_uuid=True), + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + voucher_id: Mapped[uuid.UUID] = mapped_column( + Uuid, ForeignKey("vouchers.id"), nullable=False, index=True, ) - batch_id: uuid.UUID = Column("batch_id", UUID(as_uuid=True), ForeignKey("batches.id"), nullable=False) - quantity: Decimal = Column("quantity", Numeric(precision=15, scale=2), nullable=False) - rate: Decimal = Column("rate", Numeric(precision=15, scale=2), nullable=False) - tax: Decimal = Column("tax", Numeric(precision=15, scale=5), nullable=False) - discount: Decimal = Column("discount", Numeric(precision=15, scale=5), nullable=False) + batch_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("batches.id"), nullable=False) + quantity: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=2), nullable=False) + rate: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=2), nullable=False) + tax: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=5), nullable=False) + discount: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=5), nullable=False) voucher: Mapped["Voucher"] = relationship("Voucher", back_populates="inventories") batch: Mapped["Batch"] = relationship("Batch", back_populates="inventories") @@ -45,8 +42,8 @@ class Inventory(Base): tax: Decimal, discount: Decimal, batch: "Batch", - voucher_id: Optional[uuid.UUID] = None, - id_: Optional[uuid.UUID] = None, + voucher_id: uuid.UUID | None = None, + id_: uuid.UUID | None = None, ) -> None: self.batch = batch self.quantity = quantity diff --git a/brewman/brewman/models/journal.py b/brewman/brewman/models/journal.py index df87e746..ca42a193 100644 --- a/brewman/brewman/models/journal.py +++ b/brewman/brewman/models/journal.py @@ -1,19 +1,16 @@ import uuid from decimal import Decimal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING -from sqlalchemy import Column, ForeignKey, Integer, Numeric -from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy import ForeignKey, Integer, Numeric, Uuid from sqlalchemy.ext.hybrid import hybrid_property -from sqlalchemy.orm import Mapped, relationship +from sqlalchemy.orm import Mapped, mapped_column, relationship -from ..db.base_class import Base +from ..db.base_class import reg if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime from .account import AccountBase from .cost_centre import CostCentre from .employee_benefit import EmployeeBenefit @@ -21,23 +18,22 @@ if TYPE_CHECKING: from .voucher import Voucher -class Journal(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class Journal: __tablename__ = "journals" - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - debit: int = Column("debit", Integer, nullable=False) - amount: Decimal = Column("amount", Numeric(precision=15, scale=2), nullable=False) - voucher_id: uuid.UUID = Column( - "voucher_id", - UUID(as_uuid=True), + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + debit: Mapped[int] = mapped_column(Integer, nullable=False) + amount: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=2), nullable=False) + voucher_id: Mapped[uuid.UUID] = mapped_column( + Uuid, ForeignKey("vouchers.id"), nullable=False, index=True, ) - account_id: uuid.UUID = Column("account_id", UUID(as_uuid=True), ForeignKey("accounts.id"), nullable=False) - cost_centre_id: uuid.UUID = Column( - "cost_centre_id", - UUID(as_uuid=True), + account_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("accounts.id"), nullable=False) + cost_centre_id: Mapped[uuid.UUID] = mapped_column( + Uuid, ForeignKey("cost_centres.id"), nullable=False, ) @@ -49,11 +45,12 @@ class Journal(Base): incentive: Mapped["Incentive"] = relationship("Incentive", back_populates="journal") @hybrid_property - def _signed_amount(self) -> Decimal: + def signed_amount(self) -> Decimal: return self.debit * self.amount - @_signed_amount.expression - def signed_amount(cls) -> Decimal: + @signed_amount.inplace.expression + @classmethod + def _signed_amount_expression(cls): return cls.debit * cls.amount def __init__( @@ -62,8 +59,8 @@ class Journal(Base): amount: Decimal, account_id: uuid.UUID, cost_centre_id: uuid.UUID, - voucher_id: Optional[uuid.UUID] = None, - id_: Optional[uuid.UUID] = None, + voucher_id: uuid.UUID | None = None, + id_: uuid.UUID | None = None, ) -> None: if id_ is not None: self.id = id_ diff --git a/brewman/brewman/models/login_history.py b/brewman/brewman/models/login_history.py index b5a98e49..fd194d53 100644 --- a/brewman/brewman/models/login_history.py +++ b/brewman/brewman/models/login_history.py @@ -1,37 +1,32 @@ -from __future__ import annotations - import datetime import uuid -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING -from sqlalchemy import Column, DateTime, UniqueConstraint -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import Mapped, relationship +from sqlalchemy import DateTime, UniqueConstraint, Uuid +from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.schema import ForeignKey -from ..db.base_class import Base +from ..db.base_class import reg if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime from .client import Client from .user import User -class LoginHistory(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class LoginHistory: __tablename__ = "login_history" __table_args__ = (UniqueConstraint("user_id", "client_id", "date"),) - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - user_id: uuid.UUID = Column("user_id", UUID(as_uuid=True), ForeignKey("users.id"), nullable=False) - client_id: uuid.UUID = Column( - "client_id", - UUID(as_uuid=True), + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + user_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("users.id"), nullable=False) + client_id: Mapped[uuid.UUID] = mapped_column( + Uuid, ForeignKey("clients.id"), nullable=False, ) - date: datetime.datetime = Column("date", DateTime(), nullable=False) + date: Mapped[datetime.datetime] = mapped_column(DateTime(), nullable=False) user: Mapped["User"] = relationship("User", back_populates="login_history") client: Mapped["Client"] = relationship("Client", back_populates="login_history") @@ -40,8 +35,8 @@ class LoginHistory(Base): self, user_id: uuid.UUID, client_id: uuid.UUID, - date: Optional[datetime.datetime] = None, - id_: Optional[uuid.UUID] = None, + date: datetime.datetime | None = None, + id_: uuid.UUID | None = None, ) -> None: self.user_id = user_id self.client_id = client_id diff --git a/brewman/brewman/models/period.py b/brewman/brewman/models/period.py index bda88690..92480e06 100644 --- a/brewman/brewman/models/period.py +++ b/brewman/brewman/models/period.py @@ -1,41 +1,32 @@ import datetime import uuid -from typing import TYPE_CHECKING, List, Optional - -from sqlalchemy import Column, Date, func, text +from sqlalchemy import Date, Uuid, func, text from sqlalchemy.dialects import postgresql -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import relationship +from sqlalchemy.orm import Mapped, mapped_column -from ..db.base_class import Base +from ..db.base_class import reg -if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime - from .recipe import Recipe - - -class Period(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class Period: __tablename__ = "periods" - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - valid_from: datetime.date = Column("valid_from", Date, nullable=False) - valid_till: datetime.date = Column("valid_till", Date, nullable=False) + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + valid_from: Mapped[datetime.date] = mapped_column(Date, nullable=False) + valid_till: Mapped[datetime.date] = mapped_column(Date, nullable=False) __table_args__ = ( postgresql.ExcludeConstraint( (func.daterange(valid_from, valid_till, text("'[]'")), "&&"), ), ) - recipes: List["Recipe"] = relationship("Recipe", back_populates="period") def __init__( self, valid_from: datetime.date, valid_till: datetime.date, - id_: Optional[uuid.UUID] = None, + id_: uuid.UUID | None = None, ) -> None: self.valid_from = valid_from self.valid_till = valid_till diff --git a/brewman/brewman/models/permission.py b/brewman/brewman/models/permission.py index 9630825d..06909688 100644 --- a/brewman/brewman/models/permission.py +++ b/brewman/brewman/models/permission.py @@ -1,27 +1,23 @@ -from __future__ import annotations - import uuid -from typing import List, Optional +from sqlalchemy import Unicode, Uuid +from sqlalchemy.orm import Mapped, mapped_column, relationship -from sqlalchemy import Column, Unicode -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import relationship - -from ..db.base_class import Base +from ..db.base_class import reg from .role import Role -from .role_permission import role_permission +from .role_permission import RolePermission -class Permission(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class Permission: __tablename__ = "permissions" - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - name: str = Column("name", Unicode(255), unique=True) + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + name: Mapped[str] = mapped_column(Unicode, unique=True, nullable=False) - roles: List[Role] = relationship("Role", secondary=role_permission, back_populates="permissions") + roles: Mapped[list[Role]] = relationship(secondary=RolePermission.__table__, back_populates="permissions") # type: ignore[attr-defined] - def __init__(self, name: str, id_: Optional[uuid.UUID] = None) -> None: + def __init__(self, name: str, id_: uuid.UUID | None = None) -> None: self.name = name if id_ is not None: self.id = id_ diff --git a/brewman/brewman/models/product.py b/brewman/brewman/models/product.py index 0b818b3d..4b8a9b54 100644 --- a/brewman/brewman/models/product.py +++ b/brewman/brewman/models/product.py @@ -1,42 +1,40 @@ import uuid -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING -from sqlalchemy import Boolean, Column, ForeignKey, Integer, Unicode -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import Mapped, relationship +from sqlalchemy import Boolean, ForeignKey, Integer, Text, Unicode, Uuid +from sqlalchemy.orm import Mapped, mapped_column, relationship -from ..db.base_class import Base +from ..db.base_class import reg if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime from .account import Account from .product_group import ProductGroup from .stock_keeping_unit import StockKeepingUnit -class Product(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class Product: __tablename__ = "products" - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - code: int = Column("code", Integer, unique=True) - name: str = Column("name", Unicode(255), nullable=False, unique=True) - fraction_units: str = Column("fraction_units", Unicode(255), nullable=False) - product_group_id: uuid.UUID = Column( - "product_group_id", - UUID(as_uuid=True), + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + code: Mapped[int] = mapped_column(Integer, unique=True, nullable=False) + name: Mapped[str] = mapped_column(Unicode, nullable=False, unique=True) + description: Mapped[str | None] = mapped_column(Text, nullable=True) + fraction_units: Mapped[str] = mapped_column(Unicode, nullable=False) + product_group_id: Mapped[uuid.UUID] = mapped_column( + Uuid, ForeignKey("product_groups.id"), nullable=False, ) - account_id: uuid.UUID = Column("account_id", UUID(as_uuid=True), ForeignKey("accounts.id"), nullable=False) - is_active: bool = Column("is_active", Boolean, nullable=False) - is_fixture: bool = Column("is_fixture", Boolean, nullable=False) - is_purchased: bool = Column("is_purchased", Boolean, nullable=False) - is_sold: bool = Column("is_sold", Boolean, nullable=False) + account_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("accounts.id"), nullable=False) + is_active: Mapped[bool] = mapped_column(Boolean, nullable=False) + is_fixture: Mapped[bool] = mapped_column(Boolean, nullable=False) + is_purchased: Mapped[bool] = mapped_column(Boolean, nullable=False) + is_sold: Mapped[bool] = mapped_column(Boolean, nullable=False) - skus: List["StockKeepingUnit"] = relationship("StockKeepingUnit", back_populates="product") + skus: Mapped[list["StockKeepingUnit"]] = relationship("StockKeepingUnit", back_populates="product") product_group: Mapped["ProductGroup"] = relationship("ProductGroup", back_populates="products") account: Mapped["Account"] = relationship("Account", back_populates="products") @@ -49,9 +47,9 @@ class Product(Base): is_active: bool, is_purchased: bool, is_sold: bool, - code: Optional[int] = None, - id_: Optional[uuid.UUID] = None, - is_fixture: Optional[bool] = False, + code: int | None = None, + id_: uuid.UUID | None = None, + is_fixture: bool | None = False, ) -> None: if code is not None: self.code = code diff --git a/brewman/brewman/models/product_group.py b/brewman/brewman/models/product_group.py index 9bc166da..312749e4 100644 --- a/brewman/brewman/models/product_group.py +++ b/brewman/brewman/models/product_group.py @@ -1,30 +1,28 @@ import uuid -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING -from sqlalchemy import Boolean, Column, Unicode -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import relationship +from sqlalchemy import Boolean, Unicode, Uuid +from sqlalchemy.orm import Mapped, mapped_column, relationship -from ..db.base_class import Base +from ..db.base_class import reg if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime from .product import Product -class ProductGroup(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class ProductGroup: __tablename__ = "product_groups" - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - name: str = Column("name", Unicode(255), unique=True) - is_fixture: bool = Column("is_fixture", Boolean, nullable=False) + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + name: Mapped[str] = mapped_column(Unicode, unique=True, nullable=False) + is_fixture: Mapped[bool] = mapped_column(Boolean, nullable=False) - products: List["Product"] = relationship("Product", back_populates="product_group") + products: Mapped[list["Product"]] = relationship("Product", back_populates="product_group") - def __init__(self, name: str, id_: Optional[uuid.UUID] = None, is_fixture: Optional[bool] = False) -> None: + def __init__(self, name: str, id_: uuid.UUID | None = None, is_fixture: bool | None = False) -> None: self.name = name if id_ is not None: self.id = id_ diff --git a/brewman/brewman/models/rate_contract.py b/brewman/brewman/models/rate_contract.py index 79c0cad3..a356abe7 100644 --- a/brewman/brewman/models/rate_contract.py +++ b/brewman/brewman/models/rate_contract.py @@ -1,52 +1,50 @@ import datetime import uuid -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING -from sqlalchemy import Column, Date, DateTime, ForeignKey, Unicode -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import Mapped, relationship +from sqlalchemy import Date, DateTime, ForeignKey, Unicode, Uuid +from sqlalchemy.orm import Mapped, mapped_column, relationship -from ..db.base_class import Base +from ..db.base_class import reg if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime from .account_base import AccountBase from .rate_contract_item import RateContractItem from .user import User -class RateContract(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class RateContract: __tablename__ = "rate_contracts" - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - date: datetime.date = Column("date", Date, nullable=False, index=True) - vendor_id: uuid.UUID = Column("vendor_id", UUID(as_uuid=True), ForeignKey("accounts.id"), nullable=False) - valid_from: Optional[datetime.date] = Column("valid_from", Date(), nullable=True) - valid_till: Optional[datetime.date] = Column("valid_till", Date(), nullable=True) - narration: str = Column("narration", Unicode(1000), nullable=False) + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + date: Mapped[datetime.date] = mapped_column(Date, nullable=False, index=True) + vendor_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("accounts.id"), nullable=False) + valid_from: Mapped[datetime.date | None] = mapped_column(Date(), nullable=True) + valid_till: Mapped[datetime.date | None] = mapped_column(Date(), nullable=True) + narration: Mapped[str] = mapped_column(Unicode, nullable=False) - user_id: uuid.UUID = Column("user_id", UUID(as_uuid=True), ForeignKey("users.id"), nullable=False) - creation_date: datetime.datetime = Column("creation_date", DateTime(), nullable=False) - last_edit_date: datetime.datetime = Column("last_edit_date", DateTime(), nullable=False) + user_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("users.id"), nullable=False) + creation_date: Mapped[datetime.datetime] = mapped_column(DateTime(), nullable=False) + last_edit_date: Mapped[datetime.datetime] = mapped_column(DateTime(), nullable=False) user: Mapped["User"] = relationship("User") vendor: Mapped["AccountBase"] = relationship("AccountBase", back_populates="rate_contracts") - items: List["RateContractItem"] = relationship("RateContractItem", back_populates="rate_contract") + items: Mapped[list["RateContractItem"]] = relationship("RateContractItem", back_populates="rate_contract") def __init__( self, date: datetime.date, vendor_id: uuid.UUID, - valid_from: Optional[datetime.date], - valid_till: Optional[datetime.date], + valid_from: datetime.date | None, + valid_till: datetime.date | None, user_id: uuid.UUID, narration: str = "", - creation_date: Optional[datetime.datetime] = None, - last_edit_date: Optional[datetime.datetime] = None, - id_: Optional[uuid.UUID] = None, + creation_date: datetime.datetime | None = None, + last_edit_date: datetime.datetime | None = None, + id_: uuid.UUID | None = None, ): self.date = date self.vendor_id = vendor_id diff --git a/brewman/brewman/models/rate_contract_item.py b/brewman/brewman/models/rate_contract_item.py index 5dde0cbf..ccd513fc 100644 --- a/brewman/brewman/models/rate_contract_item.py +++ b/brewman/brewman/models/rate_contract_item.py @@ -1,36 +1,33 @@ import uuid from decimal import Decimal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING -from sqlalchemy import Column, ForeignKey, Numeric, UniqueConstraint -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import Mapped, relationship +from sqlalchemy import ForeignKey, Numeric, UniqueConstraint, Uuid +from sqlalchemy.orm import Mapped, mapped_column, relationship -from ..db.base_class import Base +from ..db.base_class import reg if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime from .rate_contract import RateContract from .stock_keeping_unit import StockKeepingUnit -class RateContractItem(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class RateContractItem: __tablename__ = "rate_contract_items" __table_args__ = (UniqueConstraint("rate_contract_id", "sku_id"),) - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - rate_contract_id: uuid.UUID = Column( - "rate_contract_id", - UUID(as_uuid=True), + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + rate_contract_id: Mapped[uuid.UUID] = mapped_column( + Uuid, ForeignKey("rate_contracts.id"), nullable=False, index=True, ) - sku_id: uuid.UUID = Column("sku_id", UUID(as_uuid=True), ForeignKey("stock_keeping_units.id"), nullable=False) - price: Decimal = Column("cost_price", Numeric(precision=15, scale=2), nullable=False) + sku_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("stock_keeping_units.id"), nullable=False) + price: Mapped[Decimal] = mapped_column("cost_price", Numeric(precision=15, scale=2), nullable=False) rate_contract: Mapped["RateContract"] = relationship("RateContract", back_populates="items") sku: Mapped["StockKeepingUnit"] = relationship("StockKeepingUnit") @@ -40,7 +37,7 @@ class RateContractItem(Base): rate_contract_id: uuid.UUID, sku_id: uuid.UUID, price: Decimal, - id_: Optional[uuid.UUID] = None, + id_: uuid.UUID | None = None, ) -> None: self.rate_contract_id = rate_contract_id self.sku_id = sku_id diff --git a/brewman/brewman/models/recipe.py b/brewman/brewman/models/recipe.py index 4a7bce29..40a85fa2 100644 --- a/brewman/brewman/models/recipe.py +++ b/brewman/brewman/models/recipe.py @@ -1,66 +1,42 @@ +from __future__ import annotations + +import datetime import uuid from decimal import Decimal -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING -from sqlalchemy import Column, ForeignKey, Numeric, Unicode, UniqueConstraint -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import Mapped, relationship +from sqlalchemy import Date, ForeignKey, Numeric, Text, UniqueConstraint, Uuid +from sqlalchemy.orm import Mapped, mapped_column, relationship -from ..db.base_class import Base +from ..db.base_class import reg +from .recipe_tag import RecipeTag if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime - from .period import Period from .recipe_item import RecipeItem from .stock_keeping_unit import StockKeepingUnit + from .tag import Tag -class Recipe(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class Recipe: __tablename__ = "recipes" - __table_args__ = (UniqueConstraint("sku_id", "period_id"),) + __table_args__ = (UniqueConstraint("sku_id", "date"),) - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - sku_id: uuid.UUID = Column("sku_id", UUID(as_uuid=True), ForeignKey("stock_keeping_units.id"), nullable=False) + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + sku_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("stock_keeping_units.id"), nullable=False) + date: Mapped[datetime.date] = mapped_column(Date, nullable=False, index=True) + source: Mapped[str] = mapped_column(Text, nullable=False) + instructions: Mapped[str] = mapped_column(Text, nullable=False) + garnishing: Mapped[str] = mapped_column(Text, nullable=False) + plating: Mapped[str] = mapped_column(Text, nullable=False) + notes: Mapped[str] = mapped_column(Text, nullable=False) - recipe_yield: Decimal = Column("recipe_yield", Numeric(precision=15, scale=2), nullable=False) - cost_price: Decimal = Column("cost_price", Numeric(precision=15, scale=2), nullable=False) - sale_price: Decimal = Column("sale_price", Numeric(precision=15, scale=2), nullable=False) - notes: str = Column("notes", Unicode(255)) + recipe_yield: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=2), nullable=False) - period_id: uuid.UUID = Column("period_id", UUID(as_uuid=True), ForeignKey("periods.id"), nullable=False) - - items: List["RecipeItem"] = relationship("RecipeItem", back_populates="recipe") + items: Mapped[list["RecipeItem"]] = relationship("RecipeItem", back_populates="recipe") sku: Mapped["StockKeepingUnit"] = relationship("StockKeepingUnit", back_populates="recipes") - period: Mapped["Period"] = relationship("Period", back_populates="recipes") - - def __init__( - self, - recipe_yield: Decimal, - cost_price: Decimal, - sale_price: Decimal, - period_id: Optional[uuid.UUID] = None, - period: Optional["Period"] = None, - notes: str = "", - sku_id: Optional[uuid.UUID] = None, - sku: Optional["StockKeepingUnit"] = None, - id_: Optional[uuid.UUID] = None, - ) -> None: - if sku_id is not None: - self.sku_id = sku_id - self.recipe_yield = recipe_yield - self.cost_price = cost_price - self.sale_price = sale_price - if period_id is not None: - self.period_id = period_id - if period is not None: - self.period_id = period.id - self.period = period - self.notes = notes - if id_ is not None: - self.id = id_ - if sku is not None: - self.sku_id = sku.id - self.sku = sku + tags: Mapped[list["Tag"]] = relationship( + "Tag", secondary=RecipeTag.__table__, order_by="Tag.name", back_populates="recipes" + ) diff --git a/brewman/brewman/models/recipe_item.py b/brewman/brewman/models/recipe_item.py index 14c32c07..4d66c20f 100644 --- a/brewman/brewman/models/recipe_item.py +++ b/brewman/brewman/models/recipe_item.py @@ -1,51 +1,31 @@ +from __future__ import annotations + import uuid from decimal import Decimal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING -from sqlalchemy import Column, ForeignKey, Numeric, UniqueConstraint -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import Mapped, relationship +from sqlalchemy import ForeignKey, Numeric, Text, UniqueConstraint, Uuid +from sqlalchemy.orm import Mapped, mapped_column, relationship -from ..db.base_class import Base +from ..db.base_class import reg if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime from .product import Product from .recipe import Recipe -class RecipeItem(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class RecipeItem: __tablename__ = "recipe_items" __table_args__ = (UniqueConstraint("recipe_id", "product_id"),) - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - recipe_id: uuid.UUID = Column("recipe_id", UUID(as_uuid=True), ForeignKey("recipes.id"), nullable=False) - product_id: uuid.UUID = Column("product_id", UUID(as_uuid=True), ForeignKey("products.id"), nullable=False) - quantity: Decimal = Column("quantity", Numeric(precision=15, scale=2), nullable=False) - price: Decimal = Column("price", Numeric(precision=15, scale=5), nullable=False) + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + recipe_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("recipes.id"), nullable=False) + product_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("products.id"), nullable=False) + quantity: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=2), nullable=False) + description: Mapped[str] = mapped_column(Text, nullable=False) recipe: Mapped["Recipe"] = relationship("Recipe", back_populates="items") product: Mapped["Product"] = relationship("Product") - - def __init__( - self, - product_id: uuid.UUID, - quantity: Decimal, - price: Decimal, - recipe: Optional["Recipe"] = None, - recipe_id: Optional[uuid.UUID] = None, - id_: Optional[uuid.UUID] = None, - ) -> None: - if recipe_id is not None: - self.recipe_id = recipe_id - self.product_id = product_id - self.quantity = quantity - self.price = price - if recipe is not None: - self.recipe_id = recipe.id - self.recipe = recipe - if id_ is not None: - self.id = id_ diff --git a/brewman/brewman/models/recipe_tag.py b/brewman/brewman/models/recipe_tag.py new file mode 100644 index 00000000..79a9230d --- /dev/null +++ b/brewman/brewman/models/recipe_tag.py @@ -0,0 +1,17 @@ +import uuid + +from sqlalchemy import UniqueConstraint, Uuid +from sqlalchemy.orm import Mapped, mapped_column +from sqlalchemy.schema import ForeignKey + +from ..db.base_class import reg + + +@reg.mapped_as_dataclass(unsafe_hash=True) +class RecipeTag: + __tablename__ = "recipe_tags" + __table_args__ = (UniqueConstraint("recipe_id", "tag_id"),) + + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + recipe_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("recipes.id")) + tag_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("tags.id")) diff --git a/brewman/brewman/models/recipe_template.py b/brewman/brewman/models/recipe_template.py new file mode 100644 index 00000000..9a57e638 --- /dev/null +++ b/brewman/brewman/models/recipe_template.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +import uuid + +from datetime import datetime + +from sqlalchemy import Boolean, Date, Index, Unicode, Uuid +from sqlalchemy.orm import Mapped, mapped_column + +from ..db.base_class import reg + + +@reg.mapped_as_dataclass(unsafe_hash=True) +class RecipeTemplate: + __tablename__ = "recipe_templates" + + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + name: Mapped[str] = mapped_column(Unicode, unique=True, nullable=False) + date: Mapped[datetime.date] = mapped_column(Date, nullable=False) + text: Mapped[str] = mapped_column(Unicode, nullable=False) + selected: Mapped[bool] = mapped_column(Boolean, nullable=False) + + __table_args__ = ( + Index( + "only_one_selected_template", + "selected", + unique=True, + postgresql_where=(selected == True), # noqa: E712 + ), + ) diff --git a/brewman/brewman/models/role.py b/brewman/brewman/models/role.py index 50bed0e7..c5c1075e 100644 --- a/brewman/brewman/models/role.py +++ b/brewman/brewman/models/role.py @@ -1,32 +1,25 @@ -from __future__ import annotations - import uuid -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING -from sqlalchemy import Column, Unicode -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import relationship +from sqlalchemy import Unicode, Uuid +from sqlalchemy.orm import Mapped, mapped_column, relationship -from ..db.base_class import Base -from .role_permission import role_permission +from ..db.base_class import reg +from .role_permission import RolePermission if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime from .permission import Permission -class Role(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class Role: __tablename__ = "roles" - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - name: str = Column("name", Unicode(255), unique=True) + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + name: Mapped[str] = mapped_column(Unicode, unique=True) - permissions: List["Permission"] = relationship("Permission", secondary=role_permission, back_populates="roles") - - def __init__(self, name: str, id_: Optional[uuid.UUID] = None) -> None: - self.name = name - if id_ is not None: - self.id = id_ + permissions: Mapped[list["Permission"]] = relationship( + "Permission", secondary=RolePermission.__table__, back_populates="roles" # type: ignore[attr-defined] + ) diff --git a/brewman/brewman/models/role_permission.py b/brewman/brewman/models/role_permission.py index 158a6a26..3def6050 100644 --- a/brewman/brewman/models/role_permission.py +++ b/brewman/brewman/models/role_permission.py @@ -1,18 +1,16 @@ -from __future__ import annotations - import uuid -from sqlalchemy import Column -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.schema import ForeignKey, Table +from sqlalchemy import ForeignKey, UniqueConstraint, Uuid +from sqlalchemy.orm import Mapped, mapped_column -from ..db.base_class import Base +from ..db.base_class import reg -role_permission = Table( - "role_permissions", - Base.metadata, - Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4), - Column("permission_id", UUID(as_uuid=True), ForeignKey("permissions.id")), - Column("role_id", UUID(as_uuid=True), ForeignKey("roles.id")), -) +@reg.mapped_as_dataclass(unsafe_hash=True) +class RolePermission: + __tablename__ = "role_permissions" + __table_args__ = (UniqueConstraint("permission_id", "role_id"),) + + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + permission_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("permissions.id"), nullable=False) + role_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("roles.id"), nullable=False) diff --git a/brewman/brewman/models/setting_type.py b/brewman/brewman/models/setting_type.py index fa51f656..a0e76e75 100644 --- a/brewman/brewman/models/setting_type.py +++ b/brewman/brewman/models/setting_type.py @@ -4,3 +4,4 @@ import enum class SettingType(enum.IntEnum): VOUCHER_LOCK = 0 MAINTENANCE_MODE = 1 + RECIPE_TEMPLATE = 2 diff --git a/brewman/brewman/models/stock_keeping_unit.py b/brewman/brewman/models/stock_keeping_unit.py index aea11041..550c1c36 100644 --- a/brewman/brewman/models/stock_keeping_unit.py +++ b/brewman/brewman/models/stock_keeping_unit.py @@ -1,38 +1,38 @@ +from __future__ import annotations + import uuid from decimal import Decimal -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING -from sqlalchemy import Column, ForeignKey, Numeric, Unicode, UniqueConstraint -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import Mapped, relationship +from sqlalchemy import ForeignKey, Numeric, Unicode, UniqueConstraint, Uuid +from sqlalchemy.orm import Mapped, mapped_column, relationship -from ..db.base_class import Base +from ..db.base_class import reg if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime from .batch import Batch from .product import Product from .recipe import Recipe -class StockKeepingUnit(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class StockKeepingUnit: __tablename__ = "stock_keeping_units" __table_args__ = (UniqueConstraint("product_id", "units"),) - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - product_id: uuid.UUID = Column("product_id", UUID(as_uuid=True), ForeignKey("products.id"), nullable=False) - units: str = Column("units", Unicode(255), nullable=False) - fraction: Decimal = Column("fraction", Numeric(precision=15, scale=5), nullable=False) - product_yield: Decimal = Column("product_yield", Numeric(precision=15, scale=5), nullable=False) - cost_price: Decimal = Column("cost_price", Numeric(precision=15, scale=2), nullable=False) - sale_price: Decimal = Column("sale_price", Numeric(precision=15, scale=2), nullable=False) + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + product_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("products.id"), nullable=False) + units: Mapped[str] = mapped_column(Unicode, nullable=False) + fraction: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=5), nullable=False) + product_yield: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=5), nullable=False) + cost_price: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=2), nullable=False) + sale_price: Mapped[Decimal] = mapped_column(Numeric(precision=15, scale=2), nullable=False) product: Mapped["Product"] = relationship("Product", back_populates="skus") - batches: List["Batch"] = relationship("Batch", back_populates="sku") - recipes: List["Recipe"] = relationship("Recipe", back_populates="sku") + batches: Mapped[list["Batch"]] = relationship("Batch", back_populates="sku") + recipes: Mapped[list["Recipe"]] = relationship("Recipe", back_populates="sku") def __init__( self, @@ -41,9 +41,9 @@ class StockKeepingUnit(Base): product_yield: Decimal, cost_price: Decimal, sale_price: Decimal, - product_id: Optional[uuid.UUID] = None, - product: Optional["Product"] = None, - id_: Optional[uuid.UUID] = None, + product_id: uuid.UUID | None = None, + product: "Product" | None = None, + id_: uuid.UUID | None = None, ) -> None: if product_id is not None: self.product_id = product_id diff --git a/brewman/brewman/models/tag.py b/brewman/brewman/models/tag.py new file mode 100644 index 00000000..cc697965 --- /dev/null +++ b/brewman/brewman/models/tag.py @@ -0,0 +1,25 @@ +import uuid + +from typing import TYPE_CHECKING + +from sqlalchemy import Unicode, Uuid +from sqlalchemy.orm import Mapped, mapped_column, relationship + +from ..db.base_class import reg +from .recipe_tag import RecipeTag + + +if TYPE_CHECKING: + from .recipe import Recipe + + +@reg.mapped_as_dataclass(unsafe_hash=True) +class Tag: + __tablename__ = "tags" + + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + name: Mapped[str] = mapped_column(Unicode, unique=True, nullable=False) + + recipes: Mapped[list["Recipe"]] = relationship( + "Recipe", secondary=RecipeTag.__table__, order_by="Recipe.date", back_populates="tags" + ) diff --git a/brewman/brewman/models/tzinfoutc.py b/brewman/brewman/models/tzinfoutc.py deleted file mode 100644 index 736c765b..00000000 --- a/brewman/brewman/models/tzinfoutc.py +++ /dev/null @@ -1,47 +0,0 @@ -from datetime import datetime, timedelta, tzinfo - - -ZERO = timedelta(0) -HOUR = timedelta(hours=1) - - -# A UTC class. - - -class UTC(tzinfo): - """UTC""" - - def utcoffset(self, dt): - return ZERO - - def tzname(self, dt): - return "UTC" - - def dst(self, dt): - return ZERO - - -utc = UTC() - - -def get_age(old_date, now=None) -> str: - def is_naive(date) -> bool: - return date.tzinfo is None or date.tzinfo.utcoffset(date) is None - - if now is None: - now = datetime.utcnow().replace(tzinfo=utc) - if is_naive(old_date) == is_naive(now): - pass - elif is_naive(old_date): - old_date = old_date.replace(tzinfo=utc) - else: - now = now.replace(tzinfo=utc) - - delta = now - old_date - if delta.days > 0: - return "{0} days".format(delta.days) - if delta.seconds > 3600: - return "{0} hours".format(delta.seconds // 3600) - if delta.seconds > 60: - return "{0} minutes".format(delta.seconds // 60) - return "{0} seconds".format(delta.seconds) diff --git a/brewman/brewman/models/user.py b/brewman/brewman/models/user.py index c8eedadf..b69cd3de 100644 --- a/brewman/brewman/models/user.py +++ b/brewman/brewman/models/user.py @@ -3,34 +3,38 @@ from __future__ import annotations import uuid from hashlib import md5 -from typing import List, Optional +from typing import TYPE_CHECKING -from sqlalchemy import Boolean, Column, Unicode, desc, select -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import Session, relationship, synonym +from sqlalchemy import Boolean, Unicode, Uuid, desc, select +from sqlalchemy.ext.hybrid import hybrid_property +from sqlalchemy.orm import Mapped, Session, mapped_column, relationship -from ..db.base_class import Base +from ..db.base_class import reg from .login_history import LoginHistory -from .role import Role -from .user_role import user_role +from .user_role import UserRole + + +if TYPE_CHECKING: + from .role import Role + from .voucher import Voucher def encrypt(val: str) -> str: return md5(val.encode("utf-8") + "Salt".encode("utf-8")).hexdigest() -class User(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class User: __tablename__ = "users" - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - name: str = Column("username", Unicode(255), unique=True) - _password: str = Column("password", Unicode(60)) - locked_out: bool = Column("disabled", Boolean) + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + name: Mapped[str] = mapped_column("username", Unicode, unique=True, nullable=False) + _password: Mapped[str] = mapped_column("password", Unicode, nullable=False) + locked_out: Mapped[bool] = mapped_column("disabled", Boolean, nullable=False) - roles: List[Role] = relationship("Role", secondary=user_role) - login_history: List[LoginHistory] = relationship( - "LoginHistory", order_by=desc(LoginHistory.date), back_populates="user" - ) + roles: Mapped[list[Role]] = relationship(secondary=UserRole.__table__) # type: ignore[attr-defined] + login_history: Mapped[list[LoginHistory]] = relationship(order_by=desc(LoginHistory.date), back_populates="user") + vouchers: Mapped[list[Voucher]] = relationship("Voucher", foreign_keys="Voucher.user_id", back_populates="user") def _get_password(self) -> str: return self._password @@ -38,14 +42,15 @@ class User(Base): def _set_password(self, password: str) -> None: self._password = encrypt(password) - password = property(_get_password, _set_password) # type: ignore - password = synonym("_password", descriptor=password) + @hybrid_property + def password(self) -> str: + return self._password - @property - def __name__(self) -> str: - return self.name + @password.inplace.setter + def _password_setter(self, password: str) -> None: + self._password = encrypt(password) - def __init__(self, name: str, password: str, locked_out: bool, id_: Optional[uuid.UUID] = None) -> None: + def __init__(self, name: str, password: str, locked_out: bool, id_: uuid.UUID | None = None) -> None: self.name = name self.password = password self.locked_out = locked_out @@ -53,10 +58,10 @@ class User(Base): self.id = id_ @classmethod - def auth(cls, name: str, password: str, db: Session) -> Optional[User]: + def auth(cls, name: str, password: str, db: Session) -> "User" | None: if password is None: return None - user: Optional[User] = db.execute(select(cls).where(cls.name.ilike(name))).scalars().one_or_none() + user: User | None = db.execute(select(cls).where(cls.name.ilike(name))).scalars().one_or_none() if not user: return None if user.password != encrypt(password) or user.locked_out: diff --git a/brewman/brewman/models/user_role.py b/brewman/brewman/models/user_role.py index e0d5a436..3c06b939 100644 --- a/brewman/brewman/models/user_role.py +++ b/brewman/brewman/models/user_role.py @@ -1,18 +1,16 @@ -from __future__ import annotations - import uuid -from sqlalchemy import Column -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.schema import ForeignKey, Table +from sqlalchemy import ForeignKey, UniqueConstraint, Uuid +from sqlalchemy.orm import Mapped, mapped_column -from ..db.base_class import Base +from ..db.base_class import reg -user_role = Table( - "user_roles", - Base.metadata, - Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4), - Column("user_id", UUID(as_uuid=True), ForeignKey("users.id")), - Column("role_id", UUID(as_uuid=True), ForeignKey("roles.id")), -) +@reg.mapped_as_dataclass(unsafe_hash=True) +class UserRole: + __tablename__ = "user_roles" + __table_args__ = (UniqueConstraint("user_id", "role_id"),) + + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + user_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("users.id"), nullable=False) + role_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("roles.id"), nullable=False) diff --git a/brewman/brewman/models/voucher.py b/brewman/brewman/models/voucher.py index 390d07f2..e27e1f10 100644 --- a/brewman/brewman/models/voucher.py +++ b/brewman/brewman/models/voucher.py @@ -1,19 +1,16 @@ import datetime import uuid -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING, Optional -from sqlalchemy import Boolean, Column, Date, DateTime, Enum, ForeignKey, Unicode -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import Mapped, relationship +from sqlalchemy import Boolean, Date, DateTime, Enum, ForeignKey, Unicode, Uuid +from sqlalchemy.orm import Mapped, mapped_column, relationship -from ..db.base_class import Base +from ..db.base_class import reg from .voucher_type import VoucherType if TYPE_CHECKING: - # if the target of the relationship is in another module - # that cannot normally be imported at runtime from .employee_benefit import EmployeeBenefit from .incentive import Incentive from .inventory import Inventory @@ -21,34 +18,39 @@ if TYPE_CHECKING: from .user import User -class Voucher(Base): +@reg.mapped_as_dataclass(unsafe_hash=True) +class Voucher: __tablename__ = "vouchers" - id: uuid.UUID = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - date: datetime.date = Column("date", Date, nullable=False, index=True) - narration: str = Column("narration", Unicode(1000), nullable=False) - is_reconciled: bool = Column("is_reconciled", Boolean, nullable=False) - reconcile_date: datetime.date = Column("reconcile_date", Date, nullable=False) - is_starred: bool = Column("is_starred", Boolean, nullable=False) - creation_date: datetime.datetime = Column("creation_date", DateTime(), nullable=False) - last_edit_date: datetime.datetime = Column("last_edit_date", DateTime(), nullable=False) - voucher_type: VoucherType = Column("voucher_type", Enum(VoucherType), nullable=False) - user_id: uuid.UUID = Column("user_id", UUID(as_uuid=True), ForeignKey("users.id"), nullable=False) - posted: bool = Column("is_posted", Boolean, nullable=False) - poster_id: uuid.UUID = Column("poster_id", UUID(as_uuid=True), ForeignKey("users.id")) + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, insert_default=uuid.uuid4) + date: Mapped[datetime.date] = mapped_column(Date, nullable=False, index=True) + narration: Mapped[str] = mapped_column(Unicode, nullable=False) + is_reconciled: Mapped[bool] = mapped_column(Boolean, nullable=False) + reconcile_date: Mapped[datetime.date] = mapped_column(Date, nullable=False) + is_starred: Mapped[bool] = mapped_column(Boolean, nullable=False) + creation_date: Mapped[datetime.datetime] = mapped_column(DateTime(), nullable=False) + last_edit_date: Mapped[datetime.datetime] = mapped_column(DateTime(), nullable=False) + voucher_type: Mapped[VoucherType] = mapped_column(Enum(VoucherType), nullable=False) + user_id: Mapped[uuid.UUID] = mapped_column(Uuid, ForeignKey("users.id"), nullable=False) + posted: Mapped[bool] = mapped_column("is_posted", Boolean, nullable=False) + poster_id: Mapped[uuid.UUID | None] = mapped_column(Uuid, ForeignKey("users.id"), nullable=True) - user: Mapped["User"] = relationship("User", primaryjoin="User.id==Voucher.user_id") - poster: Mapped["User"] = relationship("User", primaryjoin="User.id==Voucher.poster_id") + user: Mapped["User"] = relationship("User", foreign_keys=[user_id], back_populates="vouchers") + poster: Mapped[Optional["User"]] = relationship("User", foreign_keys=[poster_id]) - journals: List["Journal"] = relationship("Journal", cascade="delete, delete-orphan", back_populates="voucher") + journals: Mapped[list["Journal"]] = relationship( + "Journal", cascade="delete, delete-orphan", back_populates="voucher" + ) - inventories: List["Inventory"] = relationship( + inventories: Mapped[list["Inventory"]] = relationship( "Inventory", cascade="delete, delete-orphan", back_populates="voucher" ) - employee_benefits: List["EmployeeBenefit"] = relationship( + employee_benefits: Mapped[list["EmployeeBenefit"]] = relationship( "EmployeeBenefit", cascade="delete, delete-orphan", backref="voucher" ) - incentives: List["Incentive"] = relationship("Incentive", cascade="delete, delete-orphan", backref="voucher") + incentives: Mapped[list["Incentive"]] = relationship( + "Incentive", cascade="delete, delete-orphan", backref="voucher" + ) def __init__( self, @@ -59,10 +61,10 @@ class Voucher(Base): is_starred: bool = False, posted: bool = False, is_reconciled: bool = False, - reconcile_date: Optional[datetime.date] = None, - poster_id: Optional[uuid.UUID] = None, - creation_date: Optional[datetime.datetime] = None, - last_edit_date: Optional[datetime.datetime] = None, + reconcile_date: datetime.date | None = None, + poster_id: uuid.UUID | None = None, + creation_date: datetime.datetime | None = None, + last_edit_date: datetime.datetime | None = None, ): self.date = date self.is_reconciled = is_reconciled diff --git a/brewman/brewman/routers/__init__.py b/brewman/brewman/routers/__init__.py index 67485a32..ae17816d 100644 --- a/brewman/brewman/routers/__init__.py +++ b/brewman/brewman/routers/__init__.py @@ -1,5 +1,4 @@ from datetime import date, timedelta -from typing import List, Optional, Tuple from sqlalchemy import or_, select from sqlalchemy.orm import Session @@ -16,12 +15,12 @@ from ..schemas.settings import ( def get_lock_info( - voucher_dates: List[date], voucher_type: VoucherType, account_types: List[int], db: Session -) -> Tuple[bool, str]: + voucher_dates: list[date], voucher_type: VoucherType, account_types: list[int], db: Session +) -> tuple[bool, str]: date_ = date.today() - start: Optional[date] - finish: Optional[date] - data: List = ( + start: date | None + finish: date | None + data: list = ( db.execute( select(DbSetting.data).where( DbSetting.setting_type == SettingType.VOUCHER_LOCK, diff --git a/brewman/brewman/routers/account.py b/brewman/brewman/routers/account.py index db1b4638..6219a6a3 100644 --- a/brewman/brewman/routers/account.py +++ b/brewman/brewman/routers/account.py @@ -2,7 +2,6 @@ import uuid from datetime import date, datetime from decimal import Decimal -from typing import List, Optional import brewman.schemas.account as schemas @@ -111,8 +110,8 @@ def show_blank( return account_blank(db) -@router.get("/list", response_model=List[schemas.Account]) -async def show_list(user: UserToken = Depends(get_user)) -> List[schemas.Account]: +@router.get("/list", response_model=list[schemas.Account]) +async def show_list(user: UserToken = Depends(get_user)) -> list[schemas.Account]: with SessionFuture() as db: return [ account_info(item) @@ -124,16 +123,16 @@ async def show_list(user: UserToken = Depends(get_user)) -> List[schemas.Account ] -@router.get("/query", response_model=List[schemas.AccountLink]) +@router.get("/query", response_model=list[schemas.AccountLink]) async def show_term( q: str, - t: Optional[int] = None, # AccountType - r: Optional[bool] = None, # Reconcilable - a: Optional[bool] = None, # Active - c: Optional[int] = None, # Count + t: int | None = None, # AccountType + r: bool | None = None, # Reconcilable + a: bool | None = None, # Active + c: int | None = None, # Count current_user: UserToken = Depends(get_user), -) -> List[schemas.AccountLink]: - list_: List[schemas.AccountLink] = [] +) -> list[schemas.AccountLink]: + list_: list[schemas.AccountLink] = [] with SessionFuture() as db: query_ = select(AccountBase) if t is not None: @@ -148,7 +147,7 @@ async def show_term( query_ = query_.order_by(AccountBase.name) if c is not None: query_ = query_.limit(c) - data: List[AccountBase] = db.execute(query_).scalars().all() + data: list[AccountBase] = db.execute(query_).scalars().all() for item in data: list_.append(schemas.AccountLink(id=item.id, name=item.name)) @@ -158,7 +157,7 @@ async def show_term( @router.get("/{id_}/balance", response_model=AccountBalance) async def show_balance( id_: uuid.UUID, - d: Optional[str] = None, + d: str | None = None, user: UserToken = Depends(get_user), ) -> AccountBalance: date_ = None if d is None or d == "" else datetime.strptime(d, "%d-%b-%Y") @@ -176,7 +175,7 @@ def show_id( return account_info(item) -def balance(id_: uuid.UUID, date_: Optional[date], db: Session) -> Decimal: +def balance(id_: uuid.UUID, date_: date | None, db: Session) -> Decimal: account: AccountBase = db.execute(select(AccountBase).where(AccountBase.id == id_)).scalar_one() if not account.type_.balance_sheet: return Decimal(0) @@ -186,7 +185,7 @@ def balance(id_: uuid.UUID, date_: Optional[date], db: Session) -> Decimal: bal = bal.where(Voucher.date <= date_) bal = bal.where(Voucher.voucher_type != VoucherType.ISSUE).where(Journal.account_id == id_) - result: Optional[Decimal] = db.execute(bal).scalar_one_or_none() + result: Decimal | None = db.execute(bal).scalar_one_or_none() return Decimal(0) if result is None else result @@ -221,7 +220,7 @@ def account_blank(db: Session) -> schemas.AccountBlank: def delete_with_data(account: Account, db: Session) -> None: suspense_account = db.execute(select(Account).where(Account.id == Account.suspense())).scalar_one() - query: List[Voucher] = ( + query: list[Voucher] = ( db.execute( select(Voucher) .options(joinedload(Voucher.journals, innerjoin=True).joinedload(Journal.account, innerjoin=True)) diff --git a/brewman/brewman/routers/account_types.py b/brewman/brewman/routers/account_types.py index 63969552..853f9b17 100644 --- a/brewman/brewman/routers/account_types.py +++ b/brewman/brewman/routers/account_types.py @@ -1,5 +1,3 @@ -from typing import List - import brewman.schemas.account_type as schemas from fastapi import APIRouter, Depends @@ -14,8 +12,8 @@ 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]: +@router.get("", response_model=list[schemas.AccountType]) +def account_type_list(user: UserToken = Depends(get_user)) -> list[schemas.AccountType]: with SessionFuture() as db: return [ schemas.AccountType(id=item.id, name=item.name) for item in db.execute(select(AccountType)).scalars().all() diff --git a/brewman/brewman/routers/attendance.py b/brewman/brewman/routers/attendance.py index 340b1116..5c8699cd 100644 --- a/brewman/brewman/routers/attendance.py +++ b/brewman/brewman/routers/attendance.py @@ -1,5 +1,5 @@ from datetime import date, datetime, timedelta -from typing import Generator, List +from typing import Generator import brewman.schemas.attendance as schemas @@ -41,8 +41,8 @@ def attendance_date( ) -def attendance_date_report(date_: date, db: Session) -> List[schemas.AttendanceItem]: - body: List[schemas.AttendanceItem] = [] +def attendance_date_report(date_: date, db: Session) -> list[schemas.AttendanceItem]: + body: list[schemas.AttendanceItem] = [] employees = ( db.execute( select(Employee) diff --git a/brewman/brewman/routers/attendance_types.py b/brewman/brewman/routers/attendance_types.py index 240ead13..12d94298 100644 --- a/brewman/brewman/routers/attendance_types.py +++ b/brewman/brewman/routers/attendance_types.py @@ -1,5 +1,3 @@ -from typing import List - import brewman.schemas.attendance_type as schemas from fastapi import APIRouter, Depends @@ -12,10 +10,10 @@ from ..schemas.user import UserToken router = APIRouter() -@router.get("", response_model=List[schemas.AttendanceType]) -async def show_list(user: UserToken = Depends(get_user)) -> List[schemas.AttendanceType]: +@router.get("", response_model=list[schemas.AttendanceType]) +async def show_list(user: UserToken = Depends(get_user)) -> list[schemas.AttendanceType]: list_ = AttendanceType.list() - attendance_types: List[schemas.AttendanceType] = [] + attendance_types: list[schemas.AttendanceType] = [] for item in list_: attendance_types.append(schemas.AttendanceType(id=item.id, name=item.name, value=item.value)) return attendance_types diff --git a/brewman/brewman/routers/batch.py b/brewman/brewman/routers/batch.py index 94b68648..084f8d8b 100644 --- a/brewman/brewman/routers/batch.py +++ b/brewman/brewman/routers/batch.py @@ -1,7 +1,5 @@ import datetime -from typing import List - import brewman.schemas.batch as schemas from fastapi import APIRouter, Depends @@ -19,14 +17,14 @@ from ..schemas.user import UserToken router = APIRouter() -@router.get("", response_model=List[schemas.Batch]) +@router.get("", response_model=list[schemas.Batch]) def batch_term( q: str, d: str, current_user: UserToken = Depends(get_user), -) -> List[schemas.Batch]: +) -> list[schemas.Batch]: date_ = datetime.datetime.strptime(d, "%d-%b-%Y") - list_: List[schemas.Batch] = [] + list_: list[schemas.Batch] = [] with SessionFuture() as db: query = ( select(Batch) diff --git a/brewman/brewman/routers/batch_integrity.py b/brewman/brewman/routers/batch_integrity.py index 0d5afb00..227be3c1 100644 --- a/brewman/brewman/routers/batch_integrity.py +++ b/brewman/brewman/routers/batch_integrity.py @@ -1,7 +1,5 @@ import uuid -from typing import List - import brewman.schemas.batch_integrity as schemas from fastapi import APIRouter, Security @@ -24,11 +22,10 @@ from .issue import refresh_voucher router = APIRouter() -@router.post("", response_model=List[schemas.BatchIntegrity]) +@router.post("", response_model=list[schemas.BatchIntegrity]) def post_check_batch_integrity( user: UserToken = Security(get_user, scopes=["product-ledger"]) -) -> List[schemas.BatchIntegrity]: - +) -> list[schemas.BatchIntegrity]: with SessionFuture() as db: info = negative_batches(db) + batch_dates(db) fix_batch_prices(db) @@ -36,7 +33,7 @@ def post_check_batch_integrity( return info -def negative_batches(db: Session) -> List[schemas.BatchIntegrity]: +def negative_batches(db: Session) -> list[schemas.BatchIntegrity]: inv_sum = func.sum(Inventory.quantity * Journal.debit).label("quantity") list_ = db.execute( select(Batch, Product.name, inv_sum) @@ -71,7 +68,7 @@ def negative_batches(db: Session) -> List[schemas.BatchIntegrity]: return issue -def batch_details(batch_id: uuid.UUID, db: Session) -> List[schemas.BatchIntegrityItem]: +def batch_details(batch_id: uuid.UUID, db: Session) -> list[schemas.BatchIntegrityItem]: list_ = db.execute( select(Voucher.id, Voucher.date, Voucher.voucher_type, Inventory.quantity, Inventory.rate) .join(Inventory.voucher) @@ -93,7 +90,7 @@ def batch_details(batch_id: uuid.UUID, db: Session) -> List[schemas.BatchIntegri ] -def batch_dates(db: Session) -> List[schemas.BatchIntegrity]: +def batch_dates(db: Session) -> list[schemas.BatchIntegrity]: list_ = ( db.execute( select(Batch) diff --git a/brewman/brewman/routers/client.py b/brewman/brewman/routers/client.py index fb947bff..b659b08c 100644 --- a/brewman/brewman/routers/client.py +++ b/brewman/brewman/routers/client.py @@ -1,7 +1,5 @@ import uuid -from typing import List - import brewman.schemas.client as schemas from fastapi import APIRouter, HTTPException, Security, status @@ -57,10 +55,10 @@ def delete_route( ) -@router.get("/list", response_model=List[schemas.ClientList]) +@router.get("/list", response_model=list[schemas.ClientList]) async def show_list( user: UserToken = Security(get_user, scopes=["clients"]), -) -> List[schemas.ClientList]: +) -> list[schemas.ClientList]: with SessionFuture() as db: list_ = db.execute(select(Client).order_by(Client.name)).scalars().all() clients = [] diff --git a/brewman/brewman/routers/cost_centre.py b/brewman/brewman/routers/cost_centre.py index bb11ddb4..7649bec9 100644 --- a/brewman/brewman/routers/cost_centre.py +++ b/brewman/brewman/routers/cost_centre.py @@ -1,7 +1,5 @@ import uuid -from typing import List - import brewman.schemas.cost_centre as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status @@ -94,8 +92,8 @@ def show_blank( return cost_centre_blank() -@router.get("/list", response_model=List[schemas.CostCentre]) -async def show_list(user: UserToken = Depends(get_user)) -> List[schemas.CostCentre]: +@router.get("/list", response_model=list[schemas.CostCentre]) +async def show_list(user: UserToken = Depends(get_user)) -> list[schemas.CostCentre]: with SessionFuture() as db: return [ cost_centre_info(item) for item in db.execute(select(CostCentre).order_by(CostCentre.name)).scalars().all() diff --git a/brewman/brewman/routers/credit_salary.py b/brewman/brewman/routers/credit_salary.py index 513681bf..0b5f6396 100644 --- a/brewman/brewman/routers/credit_salary.py +++ b/brewman/brewman/routers/credit_salary.py @@ -1,7 +1,6 @@ from calendar import monthrange from datetime import date, datetime from decimal import Decimal -from typing import List from fastapi import APIRouter, Body, HTTPException, Security, status from sqlalchemy import or_, select @@ -49,10 +48,10 @@ def credit_salary( return Message(message="Salary Entry created") -def salary_journals(start_date: date, finish_date: date, db: Session) -> List[Journal]: +def salary_journals(start_date: date, finish_date: date, db: Session) -> list[Journal]: days = monthrange(start_date.year, start_date.month)[1] amount = Decimal(0) - journals: List[Journal] = [] + journals: list[Journal] = [] employees = ( db.execute( select(Employee) diff --git a/brewman/brewman/routers/db_image.py b/brewman/brewman/routers/db_image.py index ce80643e..178366d3 100644 --- a/brewman/brewman/routers/db_image.py +++ b/brewman/brewman/routers/db_image.py @@ -1,7 +1,6 @@ import uuid from io import BytesIO -from typing import List import brewman.schemas.image_upload as output @@ -28,7 +27,7 @@ def db_image(id_: uuid.UUID, type_: str) -> StreamingResponse: return StreamingResponse(item, media_type="image/jpeg") -def save_files(voucher_id: uuid.UUID, i: List[bytes], t: List[bytes], db: Session) -> None: +def save_files(voucher_id: uuid.UUID, i: list[bytes], t: list[bytes], db: Session) -> None: i = i or [] t = t or [] for index, value in enumerate(i): @@ -37,9 +36,9 @@ def save_files(voucher_id: uuid.UUID, i: List[bytes], t: List[bytes], db: Sessio def update_files( voucher_id: uuid.UUID, - data: List[output.ImageUpload], - i: List[bytes], - t: List[bytes], + data: list[output.ImageUpload], + i: list[bytes], + t: list[bytes], db: Session, ) -> None: i = i or [] diff --git a/brewman/brewman/routers/employee.py b/brewman/brewman/routers/employee.py index b1c1c44c..c9abda80 100644 --- a/brewman/brewman/routers/employee.py +++ b/brewman/brewman/routers/employee.py @@ -1,7 +1,6 @@ import uuid from datetime import datetime -from typing import List, Optional import brewman.schemas.employee as schemas @@ -111,8 +110,8 @@ def show_blank( return employee_blank() -@router.get("/list", response_model=List[schemas.Employee]) -async def show_list(user: UserToken = Depends(get_user)) -> List[schemas.Employee]: +@router.get("/list", response_model=list[schemas.Employee]) +async def show_list(user: UserToken = Depends(get_user)) -> list[schemas.Employee]: with SessionFuture() as db: return [ employee_info(item) @@ -128,13 +127,13 @@ async def show_list(user: UserToken = Depends(get_user)) -> List[schemas.Employe ] -@router.get("/query", response_model=List[schemas.EmployeeLink]) +@router.get("/query", response_model=list[schemas.EmployeeLink]) async def show_term( q: str, - c: Optional[int] = None, + c: int | None = None, current_user: UserToken = Depends(get_user), -) -> List[schemas.EmployeeLink]: - list_: List[schemas.EmployeeLink] = [] +) -> list[schemas.EmployeeLink]: + list_: list[schemas.EmployeeLink] = [] with SessionFuture() as db: query_ = select(Employee) if q is not None: @@ -143,7 +142,7 @@ async def show_term( query_ = query_.order_by(Employee.name) if c is not None: query_ = query_.limit(c) - data: List[Employee] = db.execute(query_).scalars().all() + data: list[Employee] = db.execute(query_).scalars().all() for item in data: list_.append( diff --git a/brewman/brewman/routers/employee_attendance.py b/brewman/brewman/routers/employee_attendance.py index da62df71..4b778d48 100644 --- a/brewman/brewman/routers/employee_attendance.py +++ b/brewman/brewman/routers/employee_attendance.py @@ -1,7 +1,6 @@ import uuid from datetime import date, datetime -from typing import List, Optional import brewman.schemas.employee_attendance as schemas @@ -39,8 +38,8 @@ def show_blank( def employee_attendance_report( id_: uuid.UUID, request: Request, - s: Optional[str] = None, - f: Optional[str] = None, + s: str | None = None, + f: str | None = None, user: UserToken = Security(get_user, scopes=["attendance"]), ) -> schemas.EmployeeAttendance: with SessionFuture() as db: @@ -62,7 +61,7 @@ def employee_attendance_report( def employee_attendance( employee: Employee, start_date: date, finish_date: date, db: Session -) -> List[schemas.EmployeeAttendanceItem]: +) -> list[schemas.EmployeeAttendanceItem]: list_ = [] for item in date_range(start_date, finish_date, inclusive=True): att = ( diff --git a/brewman/brewman/routers/employee_benefit.py b/brewman/brewman/routers/employee_benefit.py index 8135270e..9f707372 100644 --- a/brewman/brewman/routers/employee_benefit.py +++ b/brewman/brewman/routers/employee_benefit.py @@ -3,7 +3,6 @@ import uuid from datetime import date, datetime from decimal import Decimal from math import ceil -from typing import List, Tuple import brewman.schemas.input as schema_in import brewman.schemas.voucher as output @@ -38,8 +37,8 @@ router = APIRouter() def save_route( request: Request, data: schema_in.EmployeeBenefitIn = Depends(schema_in.EmployeeBenefitIn.load_form), - i: List[bytes] = File(None), - t: List[bytes] = File(None), + i: list[bytes] = File(None), + t: list[bytes] = File(None), user: UserToken = Security(get_user, scopes=["employee-benefit"]), ) -> output.Voucher: try: @@ -91,10 +90,10 @@ def save(data: schema_in.EmployeeBenefitIn, date_: date, user: UserToken, db: Se def save_employee_benefits( voucher: Voucher, - employee_benefits: List[EmployeeBenefitSchema], + employee_benefits: list[EmployeeBenefitSchema], days_in_month: int, db: Session, -) -> Tuple[int, int]: +) -> tuple[int, int]: total_exp, total_total = 0, 0 for item in employee_benefits: account = db.execute(select(Employee).where(Employee.id == item.employee.id_)).scalar_one() @@ -152,8 +151,8 @@ def update_route( id_: uuid.UUID, request: Request, data: schema_in.EmployeeBenefitIn = Depends(schema_in.EmployeeBenefitIn.load_form), - i: List[bytes] = File(None), - t: List[bytes] = File(None), + i: list[bytes] = File(None), + t: list[bytes] = File(None), user: UserToken = Security(get_user, scopes=["employee-benefit"]), ) -> output.Voucher: try: @@ -210,10 +209,10 @@ def update_voucher(id_: uuid.UUID, data: schema_in.EmployeeBenefitIn, user: User def update_employee_benefits( voucher: Voucher, - employee_benefits: List[EmployeeBenefitSchema], + employee_benefits: list[EmployeeBenefitSchema], days_in_month: int, db: Session, -) -> Tuple[int, int]: +) -> tuple[int, int]: exp, total = 0, 0 for i in range(len(voucher.employee_benefits), 0, -1): item = voucher.employee_benefits[i - 1] @@ -262,7 +261,7 @@ def show_blank( return blank_voucher(additional_info, db) -def esi_contribution(gross_salary: int, days_worked: int, days_in_month: int) -> Tuple[int, int, int]: +def esi_contribution(gross_salary: int, days_worked: int, days_in_month: int) -> tuple[int, int, int]: limit = 21000 employee_rate = 0.0175 employer_rate = 0.0475 @@ -271,7 +270,7 @@ def esi_contribution(gross_salary: int, days_worked: int, days_in_month: int) -> return employee, employer, employee + employer -def pf_contribution(gross_salary: int, days_worked: int, days_in_month: int) -> Tuple[int, int, int]: +def pf_contribution(gross_salary: int, days_worked: int, days_in_month: int) -> tuple[int, int, int]: limit = 15000 employee_rate = 0.12 employer_rate = 0.12 + 0.011 + 0.005 + 0.0001 diff --git a/brewman/brewman/routers/fingerprint.py b/brewman/brewman/routers/fingerprint.py index f5f229fc..975f83e6 100644 --- a/brewman/brewman/routers/fingerprint.py +++ b/brewman/brewman/routers/fingerprint.py @@ -4,7 +4,6 @@ import uuid from datetime import date, datetime, time, timedelta from io import StringIO -from typing import Dict, List, Tuple import brewman.schemas.fingerprint as schemas @@ -33,7 +32,7 @@ def upload_prints( with SessionFuture() as db: start = date.today() - timedelta(days=90) finish = date.today() + timedelta(days=7) - employees: Dict[int, uuid.UUID] = {} + employees: dict[int, uuid.UUID] = {} for id_, code in db.execute(select(Employee.id, Employee.code)).all(): employees[code] = id_ file_data = read_file(fingerprints) @@ -80,8 +79,8 @@ def read_file(input_file: UploadFile) -> io.StringIO: return StringIO(output.decode(encoding)) -def fp(file_data: StringIO, employees: Dict[int, uuid.UUID]) -> List[schemas.Fingerprint]: - fingerprints: List[schemas.Fingerprint] = [] +def fp(file_data: StringIO, employees: dict[int, uuid.UUID]) -> list[schemas.Fingerprint]: + fingerprints: list[schemas.Fingerprint] = [] reader = csv.reader(file_data, delimiter="\t") header = next(reader) employee_column = 2 @@ -104,7 +103,7 @@ def fp(file_data: StringIO, employees: Dict[int, uuid.UUID]) -> List[schemas.Fin return fingerprints -def get_prints(employee_id: uuid.UUID, date_: date, db: Session) -> Tuple[str, str, bool]: +def get_prints(employee_id: uuid.UUID, date_: date, db: Session) -> tuple[str, str, bool]: prints = ( db.execute( select(Fingerprint) @@ -144,7 +143,7 @@ def get_prints(employee_id: uuid.UUID, date_: date, db: Session) -> Tuple[str, s ) -def working_hours(delta: timedelta) -> Tuple[str, bool]: +def working_hours(delta: timedelta) -> tuple[str, bool]: minutes = (delta.seconds // 60) % 60 minutes = int(5 * round(float(minutes) / 5)) hours = delta.seconds // 3600 diff --git a/brewman/brewman/routers/incentive.py b/brewman/brewman/routers/incentive.py index 024c76bf..59a9b71d 100644 --- a/brewman/brewman/routers/incentive.py +++ b/brewman/brewman/routers/incentive.py @@ -2,7 +2,6 @@ import uuid from datetime import date, datetime from decimal import Decimal -from typing import List, Optional import brewman.schemas.input as schema_in import brewman.schemas.voucher as output @@ -88,7 +87,7 @@ def save(data: schema_in.IncentiveIn, user: UserToken, db: Session) -> Voucher: def save_incentives( voucher: Voucher, - employees: List[schema_in.IncentiveEmployee], + employees: list[schema_in.IncentiveEmployee], point_value: Decimal, db: Session, ) -> None: @@ -170,7 +169,7 @@ def update_voucher(id_: uuid.UUID, data: schema_in.IncentiveIn, user: UserToken, def update_incentives( voucher: Voucher, - employees: List[schema_in.IncentiveEmployee], + employees: list[schema_in.IncentiveEmployee], point_value: Decimal, db: Session, ) -> None: @@ -206,7 +205,7 @@ def get_id( @router.get("", response_model=output.Voucher) def show_blank( request: Request, - d: Optional[str] = None, + d: str | None = None, user: UserToken = Security(get_user, scopes=["incentive"]), ) -> output.Voucher: additional_info = BlankVoucherInfo(date=d or get_date(request.session), type=VoucherType.INCENTIVE) @@ -217,10 +216,10 @@ def show_blank( def get_employees( start_date: date, finish_date: date, - incentives: List[IncentiveSchema], - journals: Optional[List[Journal]], + incentives: list[IncentiveSchema], + journals: list[Journal] | None, db: Session, -) -> List[schema_in.IncentiveEmployee]: +) -> list[schema_in.IncentiveEmployee]: details = [] employees = ( db.execute( @@ -260,7 +259,7 @@ def get_employees( return details -def balance(date_: date, voucher_id: Optional[uuid.UUID], db: Session) -> Decimal: +def balance(date_: date, voucher_id: uuid.UUID | None, db: Session) -> Decimal: amount = ( select(func.sum(Journal.amount * Journal.debit)) .join(Journal.voucher) @@ -278,14 +277,14 @@ def balance(date_: date, voucher_id: Optional[uuid.UUID], db: Session) -> Decima ) if voucher_id is not None: amount = amount.where(Voucher.id != voucher_id) - result: Optional[Decimal] = db.execute(amount).scalar_one_or_none() + result: Decimal | None = db.execute(amount).scalar_one_or_none() return Decimal(0) if result is None else result * -1 def check_if_employees_changed( - json: List[IncentiveSchema], - db: List[Employee], - voucher: Optional[List[Journal]], + json: list[IncentiveSchema], + db: list[Employee], + voucher: list[Journal] | None, ) -> None: json_ids = set(x.employee_id for x in json) db_ids = set(x.id for x in db) diff --git a/brewman/brewman/routers/issue.py b/brewman/brewman/routers/issue.py index 68829278..f8b16dd6 100644 --- a/brewman/brewman/routers/issue.py +++ b/brewman/brewman/routers/issue.py @@ -2,7 +2,6 @@ import uuid from datetime import datetime from decimal import Decimal -from typing import List, Optional, Tuple import brewman.schemas.input as schema_in import brewman.schemas.voucher as output @@ -46,8 +45,8 @@ router = APIRouter() def save_route( request: Request, data: schema_in.IssueIn = Depends(schema_in.IssueIn.load_form), - i: List[bytes] = File(None), - t: List[bytes] = File(None), + i: list[bytes] = File(None), + t: list[bytes] = File(None), user: UserToken = Security(get_user, scopes=["issue"]), ) -> output.Voucher: try: @@ -69,7 +68,7 @@ def save_route( ) -def save(data: schema_in.IssueIn, user: UserToken, db: Session) -> Tuple[Voucher, Optional[bool]]: +def save(data: schema_in.IssueIn, user: UserToken, db: Session) -> tuple[Voucher, bool | None]: product_accounts = ( select(Product.account_id) .join(Product.skus) @@ -98,7 +97,7 @@ def save(data: schema_in.IssueIn, user: UserToken, db: Session) -> Tuple[Voucher detail="Source cannot be the same as destination", ) - batch_consumed: Optional[bool] + batch_consumed: bool | None if data.source.id_ == CostCentre.cost_centre_purchase(): batch_consumed = True elif data.destination.id_ == CostCentre.cost_centre_purchase(): @@ -110,8 +109,8 @@ def save(data: schema_in.IssueIn, user: UserToken, db: Session) -> Tuple[Voucher def save_inventories( voucher: Voucher, - inventories: List[InventorySchema], - batch_consumed: Optional[bool], + inventories: list[InventorySchema], + batch_consumed: bool | None, db: Session, ) -> Decimal: amount: Decimal = Decimal(0) @@ -179,8 +178,8 @@ def update_route( id_: uuid.UUID, request: Request, data: schema_in.IssueIn = Depends(schema_in.IssueIn.load_form), - i: List[bytes] = File(None), - t: List[bytes] = File(None), + i: list[bytes] = File(None), + t: list[bytes] = File(None), user: UserToken = Security(get_user, scopes=["issue"]), ) -> output.Voucher: try: @@ -204,7 +203,7 @@ def update_route( def update_voucher( id_: uuid.UUID, data: schema_in.IssueIn, user: UserToken, db: Session -) -> Tuple[Voucher, Optional[bool]]: +) -> tuple[Voucher, bool | None]: voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one() product_accounts = ( select(Product.account_id) @@ -243,7 +242,7 @@ def update_voucher( else: source = item.cost_centre_id - old_batch_consumed: Optional[bool] + old_batch_consumed: bool | None if source == CostCentre.cost_centre_purchase(): old_batch_consumed = True elif destination == CostCentre.cost_centre_purchase(): @@ -251,7 +250,7 @@ def update_voucher( else: old_batch_consumed = None - new_batch_consumed: Optional[bool] + new_batch_consumed: bool | None if data.source.id_ == CostCentre.cost_centre_purchase(): new_batch_consumed = True elif data.destination.id_ == CostCentre.cost_centre_purchase(): @@ -269,8 +268,8 @@ def update_voucher( def update_inventories( voucher: Voucher, - inventories: List[InventorySchema], - batch_consumed: Optional[bool], + inventories: list[InventorySchema], + batch_consumed: bool | None, db: Session, ) -> Decimal: amount: Decimal = Decimal(0) @@ -381,9 +380,9 @@ def get_id( @router.get("", response_model=output.Voucher) def show_blank( request: Request, - date: Optional[str] = None, - source: Optional[uuid.UUID] = None, - destination: Optional[uuid.UUID] = None, + date: str | None = None, + source: uuid.UUID | None = None, + destination: uuid.UUID | None = None, user: UserToken = Security(get_user, scopes=["issue"]), ) -> output.Voucher: date_ = date or get_date(request.session) diff --git a/brewman/brewman/routers/issue_grid.py b/brewman/brewman/routers/issue_grid.py index bc9e9ad0..ae2f30c6 100644 --- a/brewman/brewman/routers/issue_grid.py +++ b/brewman/brewman/routers/issue_grid.py @@ -1,5 +1,4 @@ from datetime import date, datetime -from typing import List from fastapi import APIRouter, Security from sqlalchemy import select @@ -16,18 +15,18 @@ from ..schemas.user import UserToken router = APIRouter() -@router.get("/{date_}", response_model=List[IssueGridItem]) +@router.get("/{date_}", response_model=list[IssueGridItem]) def grid_date( date_: str, user: UserToken = Security(get_user, scopes=["issue"]), -) -> List[IssueGridItem]: +) -> list[IssueGridItem]: date_obj = datetime.strptime(date_, "%d-%b-%Y") with SessionFuture() as db: return get_grid(date_obj, db) -def get_grid(date_: date, db: Session) -> List[IssueGridItem]: - list_: List[IssueGridItem] = [] +def get_grid(date_: date, db: Session) -> list[IssueGridItem]: + list_: list[IssueGridItem] = [] query = ( db.execute( select(Voucher) diff --git a/brewman/brewman/routers/journal.py b/brewman/brewman/routers/journal.py index 54a39199..0c2f1dfa 100644 --- a/brewman/brewman/routers/journal.py +++ b/brewman/brewman/routers/journal.py @@ -1,7 +1,6 @@ import uuid from datetime import datetime -from typing import List, Optional import brewman.schemas.input as schema_in import brewman.schemas.voucher as output @@ -34,8 +33,8 @@ router = APIRouter() def save_route( request: Request, data: schema_in.JournalIn = Depends(schema_in.JournalIn.load_form), - i: List[bytes] = File(None), - t: List[bytes] = File(None), + i: list[bytes] = File(None), + t: list[bytes] = File(None), user: UserToken = Security(get_user, scopes=["journal"]), ) -> output.Voucher: try: @@ -95,8 +94,8 @@ def update_route( id_: uuid.UUID, request: Request, data: schema_in.JournalIn = Depends(schema_in.JournalIn.load_form), - i: List[bytes] = File(None), - t: List[bytes] = File(None), + i: list[bytes] = File(None), + t: list[bytes] = File(None), user: UserToken = Security(get_user, scopes=["journal"]), ) -> output.Voucher: try: @@ -190,7 +189,7 @@ def get_id( @router.get("", response_model=output.Voucher) def show_blank( request: Request, - a: Optional[uuid.UUID] = None, + a: uuid.UUID | None = None, user: UserToken = Security(get_user, scopes=["journal"]), ) -> output.Voucher: if request.scope.get("path") == "/api/payment": diff --git a/brewman/brewman/routers/lock_information.py b/brewman/brewman/routers/lock_information.py index a075d581..42254ddc 100644 --- a/brewman/brewman/routers/lock_information.py +++ b/brewman/brewman/routers/lock_information.py @@ -1,7 +1,5 @@ import uuid -from typing import List - from fastapi import APIRouter, Security from sqlalchemy import delete, select from sqlalchemy.orm import Session @@ -24,11 +22,11 @@ from ..schemas.user import UserToken router = APIRouter() -@router.post("", response_model=List[LockInformation]) +@router.post("", response_model=list[LockInformation]) def post( data_in: LockInformation, user: UserToken = Security(get_user, scopes=["lock-date"]), -) -> List[LockInformation]: +) -> list[LockInformation]: id_ = uuid.uuid4() data_in.id_ = id_ data_dict = data_in.dict() @@ -49,26 +47,26 @@ def post( return get_info(db) -@router.delete("/{id_}", response_model=List[LockInformation]) +@router.delete("/{id_}", response_model=list[LockInformation]) def delete_route( id_: uuid.UUID, user: UserToken = Security(get_user, scopes=["lock-date"]), -) -> List[LockInformation]: +) -> list[LockInformation]: with SessionFuture() as db: db.execute(delete(DbSetting).where(DbSetting.id == id_)) db.commit() return get_info(db) -@router.get("", response_model=List[LockInformation]) +@router.get("", response_model=list[LockInformation]) def get( user: UserToken = Security(get_user), -) -> List[LockInformation]: +) -> list[LockInformation]: with SessionFuture() as db: return get_info(db) -def get_info(db: Session) -> List[LockInformation]: +def get_info(db: Session) -> list[LockInformation]: data = db.execute(select(DbSetting.data).where(DbSetting.setting_type == SettingType.VOUCHER_LOCK)).scalars().all() return [ LockInformation( diff --git a/brewman/brewman/routers/login.py b/brewman/brewman/routers/login.py index f1983f3a..552ee2c1 100644 --- a/brewman/brewman/routers/login.py +++ b/brewman/brewman/routers/login.py @@ -1,5 +1,4 @@ from datetime import datetime, timedelta -from typing import Union from fastapi import ( APIRouter, @@ -39,7 +38,7 @@ async def login_for_access_token( form_data: OAuth2PasswordRequestForm = Depends(), client_id: int = Cookie(None), otp: int = Form(None), -) -> Union[Token, JSONResponse]: +) -> Token | JSONResponse: with SessionFuture() as db: user = authenticate_user(form_data.username, form_data.password, db) if not user: diff --git a/brewman/brewman/routers/maintenance.py b/brewman/brewman/routers/maintenance.py index 9257d894..bcced6b6 100644 --- a/brewman/brewman/routers/maintenance.py +++ b/brewman/brewman/routers/maintenance.py @@ -1,5 +1,3 @@ -from typing import Optional - from fastapi import APIRouter, Security from sqlalchemy import select from sqlalchemy.orm import Session @@ -31,7 +29,7 @@ def set_maintenance( user: UserToken = Security(get_user, scopes=["maintenance"]), ) -> Maintenance: with SessionFuture() as db: - maintenance: Optional[DbSetting] = db.execute( + maintenance: DbSetting | None = db.execute( select(DbSetting).where(DbSetting.name == "Maintenance") ).scalar_one_or_none() if maintenance is None: @@ -51,7 +49,7 @@ def set_maintenance( return info(maintenance, db) -def info(data: Optional[DbSetting], db: Session) -> Maintenance: +def info(data: DbSetting | None, db: Session) -> Maintenance: if data is None: return Maintenance(enabled=False, user="") user = db.execute(select(User).where(User.id == data.data["id"])).scalar_one() diff --git a/brewman/brewman/routers/period.py b/brewman/brewman/routers/period.py index bd826abc..71955f49 100644 --- a/brewman/brewman/routers/period.py +++ b/brewman/brewman/routers/period.py @@ -1,7 +1,5 @@ import uuid -from typing import List - import brewman.schemas.period as schemas from fastapi import APIRouter, Depends, HTTPException, Request, Security, status @@ -75,8 +73,8 @@ def show_blank( return period_blank(request) -@router.get("/list", response_model=List[schemas.Period]) -async def show_list(user: UserToken = Depends(get_user)) -> List[schemas.Period]: +@router.get("/list", response_model=list[schemas.Period]) +async def show_list(user: UserToken = Depends(get_user)) -> list[schemas.Period]: with SessionFuture() as db: return [ period_info(item) for item in db.execute(select(Period).order_by(desc(Period.valid_from))).scalars().all() diff --git a/brewman/brewman/routers/product.py b/brewman/brewman/routers/product.py index 0038feec..74c468ff 100644 --- a/brewman/brewman/routers/product.py +++ b/brewman/brewman/routers/product.py @@ -2,14 +2,13 @@ import uuid from datetime import datetime from decimal import Decimal -from typing import List, Optional import brewman.schemas.product as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status from sqlalchemy import delete, desc, func, or_, select from sqlalchemy.exc import SQLAlchemyError -from sqlalchemy.orm import Session, contains_eager, joinedload +from sqlalchemy.orm import Session, contains_eager from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture @@ -166,8 +165,8 @@ def show_blank( return product_blank() -@router.get("/list", response_model=List[schemas.Product]) -def show_list(user: UserToken = Depends(get_user)) -> List[schemas.Product]: +@router.get("/list", response_model=list[schemas.Product]) +def show_list(user: UserToken = Depends(get_user)) -> list[schemas.Product]: with SessionFuture() as db: return [ product_info(item) @@ -179,8 +178,6 @@ def show_list(user: UserToken = Depends(get_user)) -> List[schemas.Product]: .order_by(Product.product_group_id) .order_by(Product.name) .options( - joinedload(Product.skus, innerjoin=True), - joinedload(Product.product_group, innerjoin=True), contains_eager(Product.skus), contains_eager(Product.product_group), ) @@ -191,15 +188,15 @@ def show_list(user: UserToken = Depends(get_user)) -> List[schemas.Product]: ] -@router.get("/q-sku", response_model=List[ProductSku]) +@router.get("/q-sku", response_model=list[ProductSku]) async def show_term_sku( - q: Optional[str] = None, # Query - a: Optional[bool] = None, # Active - p: Optional[bool] = None, # Is Purchased? - v: Optional[uuid.UUID] = None, # Vendor - d: Optional[str] = None, # Date + q: str | None = None, # Query + a: bool | None = None, # Active + p: bool | None = None, # Is Purchased? + v: uuid.UUID | None = None, # Vendor + d: str | None = None, # Date current_user: UserToken = Depends(get_user), -) -> List[ProductSku]: +) -> list[ProductSku]: list_ = [] with SessionFuture() as db: query_ = select(Product).join(Product.skus).options(contains_eager(Product.skus)) @@ -231,13 +228,13 @@ async def show_term_sku( return list_ -@router.get("/q-product", response_model=List[ProductSku]) +@router.get("/q-product", response_model=list[ProductSku]) async def show_term_product( q: str | None = None, # Query a: bool | None = None, # Active p: bool | None = None, # Is Purchased? current_user: UserToken = Depends(get_user), -) -> List[ProductSku]: +) -> list[ProductSku]: list_ = [] with SessionFuture() as db: query_ = select(Product) @@ -312,7 +309,7 @@ def product_blank() -> schemas.ProductBlank: ) -def get_rc_price(id_: uuid.UUID, d: Optional[str], v: Optional[uuid.UUID], db: Session) -> Optional[Decimal]: +def get_rc_price(id_: uuid.UUID, d: str | None, v: uuid.UUID | None, db: Session) -> Decimal | None: if d is None or v is None: return None date_ = datetime.strptime(d, "%d-%b-%Y") diff --git a/brewman/brewman/routers/product_group.py b/brewman/brewman/routers/product_group.py index daac417c..4e6580d3 100644 --- a/brewman/brewman/routers/product_group.py +++ b/brewman/brewman/routers/product_group.py @@ -1,7 +1,5 @@ import uuid -from typing import List - import brewman.schemas.product_group as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status @@ -91,8 +89,8 @@ def show_blank( return product_group_blank() -@router.get("/list", response_model=List[schemas.ProductGroup]) -async def show_list(user: UserToken = Depends(get_user)) -> List[schemas.ProductGroup]: +@router.get("/list", response_model=list[schemas.ProductGroup]) +async def show_list(user: UserToken = Depends(get_user)) -> list[schemas.ProductGroup]: with SessionFuture() as db: return [ product_group_info(item) diff --git a/brewman/brewman/routers/purchase.py b/brewman/brewman/routers/purchase.py index fd657e16..4bcb14d2 100644 --- a/brewman/brewman/routers/purchase.py +++ b/brewman/brewman/routers/purchase.py @@ -2,7 +2,6 @@ import uuid from datetime import date, datetime from decimal import Decimal -from typing import Dict, List, Optional import brewman.schemas.input as schema_in import brewman.schemas.voucher as output @@ -47,8 +46,8 @@ router = APIRouter() def save_route( request: Request, data: schema_in.PurchaseIn = Depends(schema_in.PurchaseIn.load_form), - i: List[bytes] = File(None), - t: List[bytes] = File(None), + i: list[bytes] = File(None), + t: list[bytes] = File(None), user: UserToken = Security(get_user, scopes=["purchase"]), ) -> output.Voucher: try: @@ -105,7 +104,7 @@ def save(data: schema_in.PurchaseIn, user: UserToken, db: Session) -> Voucher: return voucher -def save_inventories(voucher: Voucher, vendor_id: uuid.UUID, inventories: List[InventorySchema], db: Session) -> None: +def save_inventories(voucher: Voucher, vendor_id: uuid.UUID, inventories: list[InventorySchema], db: Session) -> None: for item in inventories: sku: StockKeepingUnit = db.execute( select(StockKeepingUnit).where(StockKeepingUnit.id == item.batch.sku.id_) @@ -143,7 +142,7 @@ def save_inventories(voucher: Voucher, vendor_id: uuid.UUID, inventories: List[I def save_journals(voucher: Voucher, ven: schema_in.AccountLink, db: Session) -> None: vendor = db.execute(select(AccountBase).where(AccountBase.id == ven.id_)).scalar_one() - journals: Dict[uuid.UUID, Journal] = {} + journals: dict[uuid.UUID, Journal] = {} amount = 0 for i_item in voucher.inventories: account_id, cc_id = db.execute( @@ -178,8 +177,8 @@ def update_route( id_: uuid.UUID, request: Request, data: schema_in.PurchaseIn = Depends(schema_in.PurchaseIn.load_form), - i: List[bytes] = File(None), - t: List[bytes] = File(None), + i: list[bytes] = File(None), + t: list[bytes] = File(None), user: UserToken = Security(get_user, scopes=["purchase"]), ) -> output.Voucher: try: @@ -236,7 +235,7 @@ def update_voucher(id_: uuid.UUID, data: schema_in.PurchaseIn, user: UserToken, return voucher -def update_inventory(voucher: Voucher, vendor_id: uuid.UUID, inventories: List[InventorySchema], db: Session) -> None: +def update_inventory(voucher: Voucher, vendor_id: uuid.UUID, inventories: list[InventorySchema], db: Session) -> None: for it in range(len(voucher.inventories), 0, -1): item = voucher.inventories[it - 1] quantity_consumed = -1 * get_batch_quantity(item.batch_id, voucher.id, db) @@ -298,7 +297,7 @@ def update_inventory(voucher: Voucher, vendor_id: uuid.UUID, inventories: List[I def update_journals(voucher: Voucher, ven: schema_in.AccountLink, db: Session) -> None: vendor = db.execute(select(AccountBase).where(AccountBase.id == ven.id_)).scalar_one() - journals: Dict[uuid.UUID, Journal] = {} + journals: dict[uuid.UUID, Journal] = {} amount = Decimal(0) for i_item in voucher.inventories: account_id, cc_id = db.execute( @@ -365,7 +364,7 @@ def show_blank( return blank_voucher(additional_info, db) -def rate_contract_price(id_: uuid.UUID, vendor_id: uuid.UUID, date_: date, db: Session) -> Optional[Decimal]: +def rate_contract_price(id_: uuid.UUID, vendor_id: uuid.UUID, date_: date, db: Session) -> Decimal | None: contracts = select(RateContract.id).where( RateContract.vendor_id == vendor_id, RateContract.valid_from <= date_, RateContract.valid_till >= date_ ) diff --git a/brewman/brewman/routers/purchase_return.py b/brewman/brewman/routers/purchase_return.py index 8eca629b..232a37b5 100644 --- a/brewman/brewman/routers/purchase_return.py +++ b/brewman/brewman/routers/purchase_return.py @@ -2,7 +2,6 @@ import uuid from datetime import datetime from decimal import Decimal -from typing import Dict, List import brewman.schemas.input as schema_in import brewman.schemas.voucher as output @@ -44,8 +43,8 @@ router = APIRouter() def save_route( request: Request, data: schema_in.PurchaseIn = Depends(schema_in.PurchaseIn.load_form), - i: List[bytes] = File(None), - t: List[bytes] = File(None), + i: list[bytes] = File(None), + t: list[bytes] = File(None), user: UserToken = Security(get_user, scopes=["purchase-return"]), ) -> output.Voucher: try: @@ -102,7 +101,7 @@ def save(data: schema_in.PurchaseIn, user: UserToken, db: Session) -> Voucher: return voucher -def save_inventories(voucher: Voucher, inventories: List[InventorySchema], db: Session) -> None: +def save_inventories(voucher: Voucher, inventories: list[InventorySchema], db: Session) -> None: for d_item in inventories: batch = db.execute(select(Batch).where(Batch.id == d_item.batch.id_)).scalar_one() @@ -133,7 +132,7 @@ def save_inventories(voucher: Voucher, inventories: List[InventorySchema], db: S def save_journals(voucher: Voucher, ven: schema_in.AccountLink, db: Session) -> None: vendor = db.execute(select(AccountBase).where(AccountBase.id == ven.id_)).scalar_one() - journals: Dict[uuid.UUID, Journal] = {} + journals: dict[uuid.UUID, Journal] = {} amount = Decimal(0) for v_item in voucher.inventories: account_id, cc_id = db.execute( @@ -168,8 +167,8 @@ def update_route( id_: uuid.UUID, request: Request, data: schema_in.PurchaseIn = Depends(schema_in.PurchaseIn.load_form), - i: List[bytes] = File(None), - t: List[bytes] = File(None), + i: list[bytes] = File(None), + t: list[bytes] = File(None), user: UserToken = Security(get_user, scopes=["purchase-return"]), ) -> output.Voucher: try: @@ -229,7 +228,7 @@ def update_voucher(id_: uuid.UUID, data: schema_in.PurchaseIn, user: UserToken, return voucher -def update_inventory(voucher: Voucher, new_inventories: List[InventorySchema], db: Session) -> None: +def update_inventory(voucher: Voucher, new_inventories: list[InventorySchema], db: Session) -> None: for it in range(len(voucher.inventories), 0, -1): item = voucher.inventories[it - 1] batch = db.execute(select(Batch).where(Batch.id == item.batch_id)).scalar_one() @@ -264,7 +263,7 @@ def update_inventory(voucher: Voucher, new_inventories: List[InventorySchema], d def update_journals(voucher: Voucher, ven: schema_in.AccountLink, db: Session) -> None: vendor = db.execute(select(AccountBase).where(AccountBase.id == ven.id_)).scalar_one() - journals: Dict[uuid.UUID, Journal] = {} + journals: dict[uuid.UUID, Journal] = {} amount = Decimal(0) for v_item in voucher.inventories: account_id, cc_id = db.execute( diff --git a/brewman/brewman/routers/rate_contract.py b/brewman/brewman/routers/rate_contract.py index 94cba2bf..047342e0 100644 --- a/brewman/brewman/routers/rate_contract.py +++ b/brewman/brewman/routers/rate_contract.py @@ -1,7 +1,5 @@ import uuid -from typing import List - from fastapi import APIRouter, HTTPException, Request, Security, status from sqlalchemy import delete, select from sqlalchemy.exc import SQLAlchemyError @@ -59,7 +57,7 @@ async def save( ) -def add_items(rate_contract: RateContract, items: List[RateContractItemSchema], db: Session) -> None: +def add_items(rate_contract: RateContract, items: list[RateContractItemSchema], db: Session) -> None: for item in items: rci = RateContractItem(rate_contract_id=rate_contract.id, sku_id=item.sku.id_, price=item.price) rate_contract.items.append(rci) @@ -94,7 +92,7 @@ async def update_route( ) -def update_items(rate_contract: RateContract, items: List[RateContractItemSchema], db: Session) -> None: +def update_items(rate_contract: RateContract, items: list[RateContractItemSchema], db: Session) -> None: for it in range(len(rate_contract.items), 0, -1): item = rate_contract.items[it - 1] index = next((idx for (idx, d) in enumerate(items) if d.id_ == item.id), None) @@ -129,10 +127,10 @@ async def show_blank( return rate_contract_blank(request.session) -@router.get("/list", response_model=List[RateContractSchema]) +@router.get("/list", response_model=list[RateContractSchema]) async def show_list( user: UserToken = Security(get_user), -) -> List[RateContractSchema]: +) -> list[RateContractSchema]: with SessionFuture() as db: return [ rate_contract_info(item) diff --git a/brewman/brewman/routers/rebase.py b/brewman/brewman/routers/rebase.py index 25c15bd4..e6ecc0af 100644 --- a/brewman/brewman/routers/rebase.py +++ b/brewman/brewman/routers/rebase.py @@ -2,7 +2,6 @@ import uuid from datetime import date, datetime, timedelta from decimal import Decimal -from typing import List from fastapi import APIRouter, Security from sqlalchemy import and_, delete, distinct, func, select @@ -57,7 +56,7 @@ def rebase( db.commit() -def save_starred(date_: date, db: Session) -> List[Voucher]: +def save_starred(date_: date, db: Session) -> list[Voucher]: accounts = [ i.id for i in db.execute(select(AccountBase.id).where(AccountBase.is_starred == True)).all() # noqa: E712 ] @@ -205,7 +204,7 @@ def opening_batches(date_: date, user_id: uuid.UUID, db: Session) -> Voucher: return voucher -def delete_data(date_: date, vouchers: List[Voucher], db: Session) -> None: +def delete_data(date_: date, vouchers: list[Voucher], db: Session) -> None: sub_voucher = aliased(Voucher) sub_query = select(sub_voucher.id).where(sub_voucher.date < date_).subquery() diff --git a/brewman/brewman/routers/recipe.py b/brewman/brewman/routers/recipe.py index fe000db7..0419fd75 100644 --- a/brewman/brewman/routers/recipe.py +++ b/brewman/brewman/routers/recipe.py @@ -1,30 +1,30 @@ +import subprocess +import tempfile import uuid +from collections import defaultdict from datetime import date -from decimal import Decimal -from typing import List, Optional +from io import BytesIO import brewman.schemas.recipe as schemas -import brewman.schemas.recipe_item +import brewman.schemas.recipe_item as rischemas from fastapi import APIRouter, Depends, HTTPException, Request, Security, status -from sqlalchemy import delete, desc, func, select +from fastapi.responses import FileResponse, StreamingResponse +from openpyxl import Workbook +from openpyxl.styles import Alignment, Border, Font, NamedStyle, PatternFill, Side +from sqlalchemy import delete, func, select from sqlalchemy.exc import SQLAlchemyError -from sqlalchemy.orm import Session, contains_eager +from sqlalchemy.orm import Session, contains_eager, joinedload from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture -from ..models.batch import Batch -from ..models.inventory import Inventory -from ..models.period import Period from ..models.product import Product from ..models.recipe import Recipe from ..models.recipe_item import RecipeItem +from ..models.recipe_template import RecipeTemplate from ..models.stock_keeping_unit import StockKeepingUnit -from ..models.voucher import Voucher -from ..schemas.product_sku import ProductSku from ..schemas.user import UserToken -from .reports import report_finish_date, report_start_date router = APIRouter() @@ -38,9 +38,7 @@ def save( ) -> schemas.Recipe: try: with SessionFuture() as db: - recipe_sku: StockKeepingUnit = db.execute( - select(StockKeepingUnit).where(StockKeepingUnit.id == data.sku.id_) - ).scalar_one() + recipe_sku = db.execute(select(StockKeepingUnit).where(StockKeepingUnit.id == data.sku.id_)).scalar_one() if recipe_sku.product_id in set(i.product.id_ for i in data.items): raise HTTPException( @@ -52,37 +50,26 @@ def save( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Recipe has no ingredients", ) - period: Period = db.execute(select(Period).where(Period.id == data.period.id_)).scalar_one() recipe = Recipe( + date=data.date_, + source=data.source, + instructions=data.instructions, + garnishing=data.garnishing, + plating=data.plating, sku=recipe_sku, recipe_yield=round(data.recipe_yield, 2), - sale_price=round(data.sale_price, 2), - period_id=data.period.id_, - cost_price=Decimal(0), ) - recipe_cost = Decimal(0) for item in data.items: product: Product = db.execute(select(Product).where(Product.id == item.product.id_)).scalar_one() quantity = round(item.quantity, 2) - if product.is_purchased: - ingredient_cost = get_purchased_product_cost(product, period.valid_from, period.valid_till, db) - else: - ingredient_cost = get_sub_product_cost(product, period.id, db) - # ingredient_cost = get_purchased_product_cost(product.id, period.valid_from, period.valid_till, db) - recipe_cost += round(ingredient_cost * quantity, 2) - recipe.items.append(RecipeItem(product.id, quantity, ingredient_cost)) - - recipe.cost_price = round(recipe_cost / recipe.recipe_yield, 2) - recipe_sku.cost_price = round(recipe.cost_price / recipe.recipe_yield, 2) - if recipe_sku.product.is_sold: - recipe_sku.sale_price = recipe.sale_price + recipe.items.append(RecipeItem(product_id=product.id, quantity=quantity, description=item.description)) db.add(recipe) for r_item in recipe.items: r_item.recipe_id = recipe.id db.add(r_item) - check_recursion(recipe_sku.product_id, recipe, db) + check_recursion(set([recipe_sku.product_id]), set(), recipe, db) db.commit() return recipe_info(recipe) except SQLAlchemyError as e: @@ -112,14 +99,13 @@ async def update_route( detail="Product cannot also be an ingredient", ) - period: Period = db.execute(select(Period).where(Period.id == data.period.id_)).scalar_one() recipe.sku = sku recipe.recipe_yield = round(data.recipe_yield, 2) - recipe.sale_price = round(data.sale_price, 2) - recipe.period = period - - recipe_cost = Decimal(0) - + recipe.date = data.date_ + recipe.source = data.source + recipe.instructions = data.instructions + recipe.garnishing = data.garnishing + recipe.plating = data.plating for i in range(len(recipe.items), 0, -1): item = recipe.items[i - 1] index = next((idx for (idx, d) in enumerate(data.items) if d.id_ == item.id), None) @@ -129,31 +115,18 @@ async def update_route( product = db.execute(select(Product).where(Product.id == new_item.product.id_)).scalar_one() item.product_id = new_item.product.id_ item.quantity = round(new_item.quantity, 2) - if product.is_purchased: - ingredient_cost = get_purchased_product_cost(product, period.valid_from, period.valid_till, db) - else: - ingredient_cost = get_sub_product_cost(product, period.id, db) - # ingredient_cost = get_purchased_product_cost(product.id, period.valid_from, period.valid_till, db) - item.price = ingredient_cost - recipe_cost += round(ingredient_cost * item.quantity, 2) + item.description = new_item.description else: recipe.items.remove(item) for d_item in data.items: product = db.execute(select(Product).where(Product.id == d_item.product.id_)).scalar_one() quantity = round(d_item.quantity, 2) - if product.is_purchased: - ingredient_cost = get_purchased_product_cost(product, period.valid_from, period.valid_till, db) - else: - ingredient_cost = get_sub_product_cost(product, period.id, db) - # ingredient_cost = get_purchased_product_cost(product.id, period.valid_from, period.valid_till, db) + recipe.items.append( + RecipeItem(product_id=product.id, quantity=quantity, description=d_item.description) + ) - recipe_cost += round(ingredient_cost * quantity, 2) - recipe.items.append(RecipeItem(product.id, quantity, ingredient_cost)) - - recipe.cost_price = round(recipe_cost / recipe.recipe_yield, 2) - sku.cost_price = round(recipe.cost_price / recipe.recipe_yield, 2) - check_recursion(sku.product_id, recipe, db) + check_recursion(set([sku.product_id]), set(), db) db.commit() return recipe_info(recipe) except SQLAlchemyError as e: @@ -163,75 +136,26 @@ async def update_route( ) -def check_recursion(product_id: uuid.UUID, recipe: Recipe, db: Session) -> None: - for item in recipe.items: - item_recipes: List[Recipe] = ( - db.execute( - select(Recipe) - .join(Recipe.sku) - .join(Recipe.items) - .where( - StockKeepingUnit.product_id == item.product_id, - Recipe.period_id == recipe.period_id, - ) - .options(contains_eager(Recipe.sku), contains_eager(Recipe.items)) - ) - .unique() - .scalars() - .all() - ) - for sub_recipe in item_recipes: - if sub_recipe.sku.product_id == product_id: - raise HTTPException( - status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, - detail="Recipe recursion. Some ingredient recipe contains parent recipe.", - ) - check_recursion(product_id, sub_recipe, db) - - -def get_purchased_product_cost(product: Product, start_date: date, finish_date: date, db: Session) -> Decimal: - query = ( - select(Batch) - .join(Batch.inventories) - .join(Inventory.voucher) - .join(Batch.sku) - .join(StockKeepingUnit.product) - .where(Product.id == product.id, Voucher.date >= start_date, Voucher.date <= finish_date) +def check_recursion(products: set[uuid.UUID], visited: set[uuid.UUID], db: Session) -> None: + sq = ( + select(func.distinct(RecipeItem.product_id)) + .join(Recipe.items) + .join(Recipe.sku) + .where(StockKeepingUnit.product_id.in_(products)) ) - batches: List[Batch] = db.execute(query).unique().scalars().all() - skus: List[StockKeepingUnit] = ( - db.execute(select(StockKeepingUnit).where(StockKeepingUnit.product_id == product.id)).scalars().all() - ) - price: List[Decimal] = [] - for batch in batches: - price.append(batch.rate / batch.sku.fraction / batch.sku.product_yield) - else: - for sku in skus: - price.append(sku.cost_price / sku.fraction / sku.product_yield) - return round(sum(price) / len(price), 5) # type: ignore[return-value] - - -def get_sub_product_cost(product: Product, period_id: uuid.UUID, db: Session) -> Decimal: - recipe: Optional[Recipe] = ( - db.execute( - select(Recipe) - .join(Recipe.sku) - .where( - StockKeepingUnit.product_id == product.id, - Recipe.period_id == period_id, - ) - ) + ingredient_product_ids = ( + db.execute(select(StockKeepingUnit.product_id).join(Recipe.sku).where(StockKeepingUnit.product_id.in_(sq))) .scalars() - .one_or_none() + .all() ) - - if recipe is None: + if len(ingredient_product_ids) == 0: + return + if (visited | products) & set(ingredient_product_ids): raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, - detail=f"Ingredient {product.name} is not bought and also does not have a recipe for the period.", + detail="Recipe recursion. Some ingredient recipe contains parent recipe.", ) - - return round(recipe.cost_price / recipe.sku.fraction / recipe.sku.product_yield, 5) + check_recursion(set(ingredient_product_ids), visited | products, db) @router.delete("/{id_}", response_model=None) @@ -242,14 +166,13 @@ def delete_route( ) -> None: with SessionFuture() as db: recipe: Recipe = db.execute(select(Recipe).where(Recipe.id == id_)).scalar_one() - recipe_ids: List[uuid.UUID] = ( + recipe_ids: list[uuid.UUID] = ( db.execute( select(func.distinct(RecipeItem.recipe_id)).where( RecipeItem.product_id == select(StockKeepingUnit.product_id) .where(StockKeepingUnit.id == select(Recipe.sku_id).where(Recipe.id == id_).scalar_subquery()) .scalar_subquery(), - RecipeItem.recipe_id.in_(select(Recipe.id).where(Recipe.period_id == recipe.period_id)), ) ) .scalars() @@ -272,73 +195,168 @@ def show_blank( request: Request, user: UserToken = Security(get_user, scopes=["recipes"]), ) -> schemas.RecipeBlank: - with SessionFuture() as db: - period_id: uuid.UUID = db.execute(select(Period.id).order_by(desc(Period.valid_till)).limit(1)).scalar_one() - return recipe_blank(period_id) + return recipe_blank() -@router.get("/list", response_model=List[schemas.Recipe]) +@router.get("/list", response_model=list[schemas.Recipe]) async def show_list( - p: uuid.UUID, # period_id user: UserToken = Depends(get_user), -) -> List[schemas.Recipe]: +) -> list[schemas.Recipe]: with SessionFuture() as db: - list_: List[Recipe] = ( - db.execute(select(Recipe).join(Recipe.sku).where(Recipe.period_id == p).order_by(Recipe.sku_id)) + list_: list[Recipe] = ( + db.execute( + select(Recipe) + .options(joinedload(Recipe.sku, innerjoin=True).joinedload(StockKeepingUnit.product, innerjoin=True)) + .order_by(Recipe.sku_id) + ) .scalars() .all() ) return [ schemas.Recipe( id=item.id, - period=schemas.PeriodLink(id=item.period.id), - sku=schemas.ProductLink(id=item.sku.id, name=f"{item.sku.product.name} ({item.sku.units})"), + sku=schemas.ProductLink(id=item.sku.id, name=item.sku.product.name), + date=item.date, + source=item.source, + instructions=item.instructions, + garnishing=item.garnishing, + plating=item.plating, recipeYield=item.recipe_yield, - costPrice=item.cost_price, - salePrice=item.sale_price, - notes=str(item.sku.product.product_group_id), + notes=item.notes, + productGroupId=item.sku.product.product_group_id, + units=item.sku.units, items=[], ) for item in list_ ] -@router.get("/ingredient-details/{id_}", response_model=ProductSku) -def show_ingredient_id( +# @router.get("/ingredient-details/{id_}", response_model=ProductSku) +# def show_ingredient_id( +# id_: uuid.UUID, +# user: UserToken = Security(get_user, scopes=["recipes"]), +# ) -> ProductSku: +# with SessionFuture() as db: +# product: Product = db.execute(select(Product).join(Product.skus).where(Product.id == id_)).unique().scalar_one() +# return ProductSku( +# id=id_, +# name=product.name, +# fractionUnits=product.fraction_units, +# isRateContracted=False, +# ) + + +@router.get("/pdf/{id_}", response_class=FileResponse) +def show_pdf( id_: uuid.UUID, - start_date: Optional[date] = Depends(report_start_date), - finish_date: Optional[date] = Depends(report_finish_date), user: UserToken = Security(get_user, scopes=["recipes"]), -) -> ProductSku: +) -> schemas.Recipe: with SessionFuture() as db: - query = ( - select(Batch) - .join(Batch.inventories) - .join(Inventory.voucher) - .join(Batch.sku) + # template = db.execute(select(RecipeTemplate.text).where(RecipeTemplate.selected == True)).scalar_one() + # with tempfile.TemporaryDirectory() as tmpdir: + # with open(tmpdir + "/recipe.tex", 'w') as rt: + # rt.write(template) + # subprocess.run(f"docker run -i --rm --name latex --volume \"{tmpdir}\":/data -w /data --user $(id -u):$(id -g) texlive/texlive:latest pdflatex recipe.tex") + item: Recipe = db.execute(select(Recipe).where(Recipe.id == id_)).scalar_one() + return recipe_info(item) + + +@router.get("/xlsx", response_class=StreamingResponse) +def get_report( + p: uuid.UUID | None = None, +) -> StreamingResponse: + with SessionFuture() as db: + q = ( + select(Recipe) + .join(Recipe.sku) .join(StockKeepingUnit.product) - .where(Product.id == id_) - ) - if start_date is not None: - query = query.where(Voucher.date >= start_date) - if finish_date is not None: - query = query.where(Voucher.date <= finish_date) - batches: List[Batch] = db.execute(query).unique().scalars().all() - product: Product = db.execute(select(Product).join(Product.skus).where(Product.id == id_)).unique().scalar_one() - price: List[Decimal] = [] - for batch in batches: - price.append(batch.rate / batch.sku.fraction / batch.sku.product_yield) - else: - for sku in product.skus: - price.append(sku.cost_price / sku.fraction / sku.product_yield) - return ProductSku( - id=id_, - name=product.name, - fractionUnits=product.fraction_units, - costPrice=round(sum(price) / len(price), 2), - salePrice=0, - isRateContracted=False, + .join(Product.product_group) + .options( + joinedload(Recipe.items, innerjoin=True).joinedload(RecipeItem.product, innerjoin=True), + contains_eager(Recipe.sku, StockKeepingUnit.product, Product.product_group), + ) ) + if p is not None: + q = q.where(Recipe.sku, StockKeepingUnit.product, Product.product_group_id == p) + list_: list[Recipe] = db.execute(q).unique().scalars().all() + e = excel(sorted(list_, key=lambda r: r.sku.product.name)) + e.seek(0) + + headers = {"Content-Disposition": "attachment; filename = recipe.xlsx"} + return StreamingResponse(e, media_type="text/xlsx", headers=headers) + + +def excel(recipes: list[Recipe]) -> None: + wb = Workbook() + wb.active.title = "Rate List" + pgs = set([x.sku.product.product_group.name for x in recipes]) + for pg in pgs: + wb.create_sheet(pg) + rows = defaultdict(lambda: 1) + register_styles(wb) + for recipe in recipes: + ws = wb[recipe.sku.product.product_group.name] + row = rows[recipe.sku.product.product_group.name] + ings = len(recipe.items) + ing_from = row + 2 + ing_till = ing_from + ings - 1 + ws.cell(row=row, column=1, value=recipe.sku.product.name).style = "recipe_name" + # ws.cell(row=row, column=3, value=f"Yeild = {recipe.recipe_yield} {recipe.sku.units}") + ws.cell(row=row, column=2, value=recipe.sku.units).style = "recipe_unit" + ws.cell(row=row, column=3, value=recipe.recipe_yield).style = "recipe_name" + ws.cell(row=row, column=4).style = "recipe_name" + ws.cell(row=row, column=5, value=f"=SUM(E{ing_from}:E{ing_till})").style = "recipe_name" + row += 1 + ws.cell(row=row, column=1, value="Ingredients").style = "header" + ws.cell(row=row, column=2, value="Unit").style = "header" + ws.cell(row=row, column=3, value="Qty").style = "header" + ws.cell(row=row, column=4, value="Rate").style = "header" + ws.cell(row=row, column=5, value="Amount").style = "header" + for item in recipe.items: + row += 1 + ws.cell(row=row, column=1, value=item.product.name).style = "ing" + ws.cell(row=row, column=2, value=item.product.fraction_units).style = "unit" + ws.cell(row=row, column=3, value=item.quantity).style = "ing" + ws.cell(row=row, column=4, value="=VLOOKUP(A:A,'Rate List'!A:G,7,0)").style = "ing" + ws.cell(row=row, column=5, value=f"=C{row}*D{row}").style = "ing" + rows[recipe.sku.product.product_group.name] = row + 1 + virtual_workbook = BytesIO() + wb.save(virtual_workbook) + return virtual_workbook + + +def register_styles(wb: Workbook): + bd = Side(style="thin", color="000000") + + recipe_name = NamedStyle(name="recipe_name") + recipe_name.font = Font(bold=True) + recipe_name.border = Border(left=bd, top=bd, right=bd, bottom=bd) + recipe_name.fill = PatternFill(fill_type=None, start_color="FFFF00", end_color="FFFF00", patternType="solid") + wb.add_named_style(recipe_name) + + recipe_unit = NamedStyle(name="recipe_unit") + recipe_unit.font = Font(bold=True) + recipe_unit.border = Border(left=bd, top=bd, right=bd, bottom=bd) + recipe_unit.fill = PatternFill(fill_type=None, start_color="FFFF00", end_color="FFFF00", patternType="solid") + recipe_unit.alignment = Alignment(horizontal="center") + wb.add_named_style(recipe_unit) + + header = NamedStyle(name="header") + header.font = Font(bold=True) + header.border = Border(left=bd, top=bd, right=bd, bottom=bd) + header.fill = PatternFill(fill_type=None, start_color="92D050", end_color="92D050", patternType="solid") + header.alignment = Alignment(horizontal="center") + wb.add_named_style(header) + + ing = NamedStyle(name="ing") + ing.border = Border(left=bd, top=bd, right=bd, bottom=bd) + wb.add_named_style(ing) + + unit = NamedStyle(name="unit") + unit.border = Border(left=bd, top=bd, right=bd, bottom=bd) + unit.alignment = Alignment(horizontal="center") + wb.add_named_style(unit) + return recipe_name, recipe_unit, header, ing, unit @router.get("/{id_}", response_model=schemas.Recipe) @@ -358,32 +376,36 @@ def recipe_info(recipe: Recipe) -> schemas.Recipe: id=recipe.sku_id, name=f"{recipe.sku.product.name} ({recipe.sku.units})", ), - period=schemas.PeriodLink(id=recipe.period_id), + date=recipe.date, + source=recipe.source, + instructions=recipe.instructions, + garnishing=recipe.garnishing, + plating=recipe.plating, recipeYield=recipe.recipe_yield, - salePrice=recipe.sale_price, - costPrice=recipe.cost_price, notes="", items=[ - brewman.schemas.recipe_item.RecipeItem( + rischemas.RecipeItem( id=item.id, product=schemas.ProductLink( id=item.product.id, name=f"{item.product.name} ({item.product.fraction_units})", ), quantity=round(item.quantity, 2), - price=round(item.price, 5), + description=item.description, ) for item in recipe.items ], ) -def recipe_blank(period_id: uuid.UUID) -> schemas.RecipeBlank: +def recipe_blank() -> schemas.RecipeBlank: return schemas.RecipeBlank( - period=schemas.PeriodLink(id=period_id), + date=date.today(), + source="", + instructions="", + garnishing="", + plating="", recipeYield=1, - costPrice=0, - salePrice=0, isLocked=False, notes="", items=[], diff --git a/brewman/brewman/routers/recipe_template.py b/brewman/brewman/routers/recipe_template.py new file mode 100644 index 00000000..dddec3a7 --- /dev/null +++ b/brewman/brewman/routers/recipe_template.py @@ -0,0 +1,109 @@ +import datetime +import uuid + +import brewman.schemas.recipe_template as schemas + +from fastapi import APIRouter, HTTPException, Security, status +from sqlalchemy import delete, select, update +from sqlalchemy.exc import SQLAlchemyError + +from ..core.security import get_current_active_user as get_user +from ..db.session import SessionFuture +from ..models.recipe_template import RecipeTemplate +from ..schemas.user import UserToken + + +router = APIRouter() + + +@router.post("", response_model=None) +def save( + data: schemas.RecipeTemplateIn, + user: UserToken = Security(get_user, scopes=["recipes"]), +) -> None: + try: + with SessionFuture() as db: + date_ = data.date_ if data.date_ is not None else datetime.date.today() + if data.selected: + db.execute(update(RecipeTemplate).values(selected=False)) + item = RecipeTemplate(id=None, name=data.name, date=date_, text=data.text, selected=data.selected) + db.add(item) + db.commit() + return None + except SQLAlchemyError as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=str(e), + ) + + +@router.put("/{id_}", response_model=None) +def update_route( + id_: uuid.UUID, + data: schemas.RecipeTemplateIn, + user: UserToken = Security(get_user, scopes=["recipes"]), +) -> None: + try: + with SessionFuture() as db: + date_ = data.date_ if data.date_ is not None else datetime.date.today() + item = db.execute(select(RecipeTemplate).where(RecipeTemplate.id == id_)).scalar_one() + if data.selected and not item.selected: + db.execute(update(RecipeTemplate).where(RecipeTemplate.id != id_).values(selected=False)) + item.name = data.name + item.text = data.text + item.selected = data.selected + item.date = date_ + db.commit() + return None + except SQLAlchemyError as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=str(e), + ) + + +@router.delete("/{id_}", response_model=None) +def delete_route( + id_: uuid.UUID, + user: UserToken = Security(get_user, scopes=["recipes"]), +) -> None: + try: + with SessionFuture() as db: + db.execute(delete(RecipeTemplate).where(RecipeTemplate.id == id_)) + db.commit() + return None + except SQLAlchemyError as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=str(e), + ) + + +@router.get("", response_model=schemas.RecipeTemplateIn) +def show_blank( + user: UserToken = Security(get_user, scopes=["recipes"]), +) -> schemas.RecipeTemplateIn: + return schemas.RecipeTemplateIn(name="", date=datetime.date.today(), text="", selected=False) + + +@router.get("/list", response_model=list[schemas.RecipeTemplate]) +async def show_list( + user: UserToken = Security(get_user, scopes=["recipes"]), +) -> list[schemas.RecipeTemplate]: + with SessionFuture() as db: + list_ = db.execute(select(RecipeTemplate).order_by(RecipeTemplate.name)).scalars().all() + return [ + schemas.RecipeTemplate(id=i.id, name=i.name, date=i.date, text=i.text, selected=i.selected) for i in list_ + ] + + +@router.get("/{id_}", response_model=schemas.RecipeTemplate) +def show_id( + id_: uuid.UUID, + user: UserToken = Security(get_user, scopes=["recipes"]), +) -> schemas.RecipeTemplate: + with SessionFuture() as db: + item = db.execute(select(RecipeTemplate).where(RecipeTemplate.id == id_)).scalar_one() + return schemas.RecipeTemplate( + id=item.id, name=item.name, date=item.date, text=item.text, selected=item.selected + ) diff --git a/brewman/brewman/routers/reports/__init__.py b/brewman/brewman/routers/reports/__init__.py index f3767712..0c095104 100644 --- a/brewman/brewman/routers/reports/__init__.py +++ b/brewman/brewman/routers/reports/__init__.py @@ -1,12 +1,11 @@ from datetime import date, datetime -from typing import Optional -def report_start_date(s: Optional[str] = None) -> Optional[date]: +def report_start_date(s: str | None = None) -> date | None: return None if s is None else datetime.strptime(s, "%d-%b-%Y").date() -def report_finish_date(f: Optional[str] = None) -> Optional[date]: +def report_finish_date(f: str | None = None) -> date | None: return None if f is None else datetime.strptime(f, "%d-%b-%Y").date() diff --git a/brewman/brewman/routers/reports/balance_sheet.py b/brewman/brewman/routers/reports/balance_sheet.py index 13cd026a..e6747fa5 100644 --- a/brewman/brewman/routers/reports/balance_sheet.py +++ b/brewman/brewman/routers/reports/balance_sheet.py @@ -1,6 +1,5 @@ from datetime import date, datetime from decimal import Decimal -from typing import Dict, List, Tuple import brewman.schemas.balance_sheet as schemas @@ -45,12 +44,12 @@ def report_data( return schemas.BalanceSheet(date=date_, body=body, footer=footer) -def build_balance_sheet(date_: date, db: Session) -> Tuple[List[schemas.BalanceSheetItem], schemas.BalanceSheetItem]: +def build_balance_sheet(date_: date, db: Session) -> tuple[list[schemas.BalanceSheetItem], schemas.BalanceSheetItem]: type_list = ( db.execute(select(AccountType.id).where(AccountType.balance_sheet == True)).scalars().all() # noqa: E712 ) - report: List[schemas.BalanceSheetItem] = [] - groups: Dict[int, schemas.BalanceSheetItem] = {} + report: list[schemas.BalanceSheetItem] = [] + groups: dict[int, schemas.BalanceSheetItem] = {} # Add Net Profit / Loss closing_stock = round(get_closing_stock(date_, db), 2) net_profit = round(get_accumulated_profit(date_, db), 2) - closing_stock diff --git a/brewman/brewman/routers/reports/cash_flow.py b/brewman/brewman/routers/reports/cash_flow.py index b5e9eb68..78262246 100644 --- a/brewman/brewman/routers/reports/cash_flow.py +++ b/brewman/brewman/routers/reports/cash_flow.py @@ -1,5 +1,4 @@ from datetime import date, datetime -from typing import List, Tuple import brewman.schemas.cash_flow as schemas @@ -78,7 +77,7 @@ def report_id( def build_report( start_date: date, finish_date: date, db: Session -) -> Tuple[schemas.CashFlowBody, List[schemas.CashFlowItem]]: +) -> tuple[schemas.CashFlowBody, list[schemas.CashFlowItem]]: sub_query = ( select(Voucher.id) .join(Voucher.journals) @@ -158,7 +157,7 @@ def build_report( def build_report_id( account_type: int, start_date: date, finish_date: date, db: Session -) -> Tuple[schemas.CashFlowBody, List[schemas.CashFlowItem]]: +) -> tuple[schemas.CashFlowBody, list[schemas.CashFlowItem]]: cf: schemas.CashFlowBody = schemas.CashFlowBody(operating=[], investing=[], financing=[], details=[]) details = cf.details sub_query = ( diff --git a/brewman/brewman/routers/reports/closing_stock.py b/brewman/brewman/routers/reports/closing_stock.py index 736899f1..36dbe431 100644 --- a/brewman/brewman/routers/reports/closing_stock.py +++ b/brewman/brewman/routers/reports/closing_stock.py @@ -2,7 +2,6 @@ import uuid from datetime import date, datetime from decimal import Decimal -from typing import List, Optional import brewman.schemas.closing_stock as schemas @@ -50,7 +49,7 @@ def report_blank( def report_data( date_: str, request: Request, - d: Optional[uuid.UUID] = None, + d: uuid.UUID | None = None, user: UserToken = Security(get_user, scopes=["closing-stock"]), ) -> schemas.ClosingStock: set_period(get_start_date(request.session), date_, request.session) @@ -61,7 +60,7 @@ def report_data( @router.delete("/{date_}") def delete_voucher( date_: str, - d: Optional[uuid.UUID] = None, + d: uuid.UUID | None = None, user: UserToken = Security(get_user, scopes=["closing-stock"]), ) -> None: try: @@ -136,7 +135,7 @@ def full_report(date_: date, cost_centre_id: uuid.UUID, db: Session) -> schemas. ) -def build_report(date_: date, cost_centre_id: uuid.UUID, db: Session) -> List[schemas.ClosingStockItem]: +def build_report(date_: date, cost_centre_id: uuid.UUID, db: Session) -> list[schemas.ClosingStockItem]: amount_sum = func.sum(Journal.debit * Inventory.quantity * Inventory.rate * (1 + Inventory.tax)).label("amount") quantity_sum = func.sum(Journal.debit * Inventory.quantity).label("quantity") query = db.execute( @@ -200,7 +199,7 @@ def build_report(date_: date, cost_centre_id: uuid.UUID, db: Session) -> List[sc def get_opening_stock(date_: date, db: Session) -> Decimal: - opening_stock: Optional[Decimal] = db.execute( + opening_stock: Decimal | None = db.execute( select(func.sum(Inventory.quantity * Inventory.rate * (1 + Inventory.tax) * Journal.debit)) .join(Journal.voucher) .join(Journal.account) @@ -211,7 +210,7 @@ def get_opening_stock(date_: date, db: Session) -> Decimal: def get_closing_stock(date_: date, db: Session) -> Decimal: - closing_stock: Optional[Decimal] = db.execute( + closing_stock: Decimal | None = db.execute( select(func.sum(Inventory.quantity * Inventory.rate * (1 + Inventory.tax) * Journal.debit)) .join(Journal.voucher) .join(Journal.account) @@ -286,7 +285,7 @@ def save_cs(data: schemas.ClosingStock, db: Session) -> None: db.add(cs) -def save(date_: date, items: List[schemas.ClosingStockItem], user: UserToken, db: Session) -> Voucher: +def save(date_: date, items: list[schemas.ClosingStockItem], user: UserToken, db: Session) -> Voucher: product_accounts = ( select(Product.account_id).join(Product.skus).where(StockKeepingUnit.id.in_([i.product.id_ for i in items])) ) @@ -310,7 +309,7 @@ def save(date_: date, items: List[schemas.ClosingStockItem], user: UserToken, db return voucher -def save_inventories(voucher: Voucher, items: List[schemas.ClosingStockItem], db: Session) -> None: +def save_inventories(voucher: Voucher, items: list[schemas.ClosingStockItem], db: Session) -> None: for item in items: batches = ( db.execute( @@ -353,7 +352,7 @@ def save_journals( db.add(d) -def update_cs(date_: date, cost_centre_id: uuid.UUID, items: List[schemas.ClosingStockItem], db: Session) -> None: +def update_cs(date_: date, cost_centre_id: uuid.UUID, items: list[schemas.ClosingStockItem], db: Session) -> None: old = ( db.execute( select(ClosingStock).where(ClosingStock.date_ == date_, ClosingStock.cost_centre_id == cost_centre_id) diff --git a/brewman/brewman/routers/reports/daybook.py b/brewman/brewman/routers/reports/daybook.py index de58bc49..2a58c0f4 100644 --- a/brewman/brewman/routers/reports/daybook.py +++ b/brewman/brewman/routers/reports/daybook.py @@ -1,5 +1,4 @@ from datetime import date, datetime -from typing import List import brewman.schemas.daybook as schemas @@ -46,7 +45,7 @@ def report_data( return schemas.Daybook(startDate=start_date, finishDate=finish_date, body=body) -def build_report(start_date: date, finish_date: date, db: Session) -> List[schemas.DaybookItem]: +def build_report(start_date: date, finish_date: date, db: Session) -> list[schemas.DaybookItem]: body = [] query = ( diff --git a/brewman/brewman/routers/reports/entries.py b/brewman/brewman/routers/reports/entries.py index b3791c78..774fb3d0 100644 --- a/brewman/brewman/routers/reports/entries.py +++ b/brewman/brewman/routers/reports/entries.py @@ -1,11 +1,10 @@ from datetime import date, datetime, time, timedelta -from typing import List, Optional, Tuple import brewman.schemas.entries as schemas from fastapi import APIRouter, Depends, Security from sqlalchemy import desc, not_, or_, select -from sqlalchemy.orm import Session, contains_eager, joinedload +from sqlalchemy.orm import Session, contains_eager from sqlalchemy.sql.functions import count from ...core.security import get_current_active_user as get_user @@ -22,22 +21,22 @@ router = APIRouter() def build_report( - start_date: Optional[date], - finish_date: Optional[date], - posted: Optional[bool], + start_date: date | None, + finish_date: date | None, + posted: bool | None, issue: bool, page_size: int, page_index: int, active_sort: str, sort_direction: str, db: Session, -) -> Tuple[int, List[Voucher]]: +) -> tuple[int, list[Voucher]]: query = ( select(Voucher) .join(Voucher.user) + .join(Voucher.journals) + .join(Journal.account) .options( - joinedload(Voucher.user, innerjoin=True), - joinedload(Voucher.journals, innerjoin=True).joinedload(Journal.account, innerjoin=True), contains_eager(Voucher.user), contains_eager(Voucher.journals, Journal.account), ) @@ -78,19 +77,20 @@ def build_report( sq = sq.limit(page_size) if page_index: sq = sq.offset(page_size * page_index) + print("Counts:", counts, "\n\n\n\n", "Query:", query) return db.execute(counts).scalar_one(), db.execute(query.where(Voucher.id.in_(sq))).unique().scalars().all() @router.get("", response_model=schemas.Report) def report_data( - start_date: Optional[date] = Depends(report_start_date), - finish_date: Optional[date] = Depends(report_finish_date), - p: Optional[bool] = None, # Posted - ps: Optional[int] = 50, # Page Size - pi: Optional[int] = 0, # Page Index - a: Optional[str] = "date", # Active Sort - d: Optional[str] = "desc", # Sort Direction - i: Optional[bool] = False, # Show issue vouchers + start_date: date | None = Depends(report_start_date), + finish_date: date | None = Depends(report_finish_date), + p: bool | None = None, # Posted + ps: int | None = 50, # Page Size + pi: int | None = 0, # Page Index + a: str | None = "date", # Active Sort + d: str | None = "desc", # Sort Direction + i: bool | None = False, # Show issue vouchers user: UserToken = Security(get_user, scopes=["ledger"]), ) -> schemas.Report: assert i is not None and ps is not None and pi is not None and a is not None and d is not None diff --git a/brewman/brewman/routers/reports/ledger.py b/brewman/brewman/routers/reports/ledger.py index ffc34615..62105794 100644 --- a/brewman/brewman/routers/reports/ledger.py +++ b/brewman/brewman/routers/reports/ledger.py @@ -1,8 +1,6 @@ import datetime import uuid -from typing import List, Optional - import brewman.schemas.ledger as schemas from fastapi import APIRouter, Request, Security @@ -40,8 +38,8 @@ def show_blank( def show_data( id_: uuid.UUID, request: Request, - s: Optional[str] = None, - f: Optional[str] = None, + s: str | None = None, + f: str | None = None, user: UserToken = Security(get_user, scopes=["ledger"]), ) -> schemas.Ledger: with SessionFuture() as db: @@ -58,7 +56,7 @@ def show_data( ) -def build_report(account_id: uuid.UUID, start_date: str, finish_date: str, db: Session) -> List[schemas.LedgerItem]: +def build_report(account_id: uuid.UUID, start_date: str, finish_date: str, db: Session) -> list[schemas.LedgerItem]: body = [] opening = opening_balance(account_id, start_date, db) body.append(opening) diff --git a/brewman/brewman/routers/reports/net_transactions.py b/brewman/brewman/routers/reports/net_transactions.py index cf22924f..b8ecadcd 100644 --- a/brewman/brewman/routers/reports/net_transactions.py +++ b/brewman/brewman/routers/reports/net_transactions.py @@ -1,5 +1,4 @@ from datetime import date, datetime -from typing import List import brewman.schemas.net_transactions as schemas @@ -53,7 +52,7 @@ def show_data( ) -def build_report(start_date: date, finish_date: date, db: Session) -> List[schemas.NetTransactionsItem]: +def build_report(start_date: date, finish_date: date, db: Session) -> list[schemas.NetTransactionsItem]: amount_sum = func.sum(Journal.amount * Journal.debit).label("amount") query = db.execute( select(AccountBase, amount_sum) diff --git a/brewman/brewman/routers/reports/non_contract_purchase.py b/brewman/brewman/routers/reports/non_contract_purchase.py index 54a29c16..8f12ebf6 100644 --- a/brewman/brewman/routers/reports/non_contract_purchase.py +++ b/brewman/brewman/routers/reports/non_contract_purchase.py @@ -1,5 +1,3 @@ -from typing import List - import brewman.schemas.non_contract_purchase as schemas from brewman.core.security import get_current_active_user as get_user @@ -21,17 +19,16 @@ from sqlalchemy.orm import Session, contains_eager, joinedload router = APIRouter() -@router.post("", response_model=List[schemas.NonContractPurchase]) +@router.post("", response_model=list[schemas.NonContractPurchase]) def non_contract_purchases( user: UserToken = Security(get_user, scopes=["product-ledger"]) -) -> List[schemas.NonContractPurchase]: - +) -> list[schemas.NonContractPurchase]: with SessionFuture() as db: return report(db) -def report(db: Session) -> List[schemas.NonContractPurchase]: - rcs: List[RateContract] = ( +def report(db: Session) -> list[schemas.NonContractPurchase]: + rcs: list[RateContract] = ( db.execute( select(RateContract) .join(RateContract.vendor) @@ -47,7 +44,7 @@ def report(db: Session) -> List[schemas.NonContractPurchase]: .scalars() .all() ) - list_: List[schemas.NonContractPurchase] = [] + list_: list[schemas.NonContractPurchase] = [] for rc in rcs: for item in rc.items: invs = db.execute( diff --git a/brewman/brewman/routers/reports/product_ledger.py b/brewman/brewman/routers/reports/product_ledger.py index b19f1b41..8dfa520f 100644 --- a/brewman/brewman/routers/reports/product_ledger.py +++ b/brewman/brewman/routers/reports/product_ledger.py @@ -2,7 +2,6 @@ import uuid from datetime import date, datetime from decimal import Decimal -from typing import List, Optional, Tuple import brewman.schemas.product_ledger as schemas @@ -44,8 +43,8 @@ def show_blank( def show_data( id_: uuid.UUID, request: Request, - s: Optional[str] = None, - f: Optional[str] = None, + s: str | None = None, + f: str | None = None, user: UserToken = Security(get_user, scopes=["product-ledger"]), ) -> schemas.ProductLedger: with SessionFuture() as db: @@ -69,7 +68,7 @@ def show_data( def build_report( product_id: uuid.UUID, start_date: date, finish_date: date, db: Session -) -> List[schemas.ProductLedgerItem]: +) -> list[schemas.ProductLedgerItem]: running_total_q, running_total_a, opening = opening_balance(product_id, start_date, db) body = opening @@ -141,7 +140,7 @@ def build_report( def opening_balance( product_id: uuid.UUID, start_date: date, db: Session -) -> Tuple[Decimal, Decimal, List[schemas.ProductLedgerItem]]: +) -> tuple[Decimal, Decimal, list[schemas.ProductLedgerItem]]: row = db.execute( select( StockKeepingUnit.units, diff --git a/brewman/brewman/routers/reports/profit_loss.py b/brewman/brewman/routers/reports/profit_loss.py index 7863236f..fa836bb9 100644 --- a/brewman/brewman/routers/reports/profit_loss.py +++ b/brewman/brewman/routers/reports/profit_loss.py @@ -1,6 +1,5 @@ from datetime import date, datetime from decimal import Decimal -from typing import List, Optional, Tuple import brewman.schemas.profit_loss as schemas @@ -59,11 +58,11 @@ def report_data( def build_profit_loss( start_date: date, finish_date: date, db: Session -) -> Tuple[List[schemas.ProfitLossItem], schemas.ProfitLossItem]: +) -> tuple[list[schemas.ProfitLossItem], schemas.ProfitLossItem]: profit_type_list = ( db.execute(select(AccountType.id).where(AccountType.balance_sheet == False)).scalars().all() # noqa: E712 ) - report: List[schemas.ProfitLossItem] = [] + report: list[schemas.ProfitLossItem] = [] groups = {} amount_sum = func.sum(Journal.amount * Journal.debit) @@ -139,7 +138,7 @@ def get_accumulated_profit(finish_date: date, db: Session) -> Decimal: db.execute(select(AccountType.id).where(AccountType.balance_sheet == False)).scalars().all() # noqa: E712 ) - accumulated_profit: Optional[Decimal] = db.execute( + accumulated_profit: Decimal | None = db.execute( select(func.sum(Journal.amount * Journal.debit)) .join(Journal.voucher) .join(Journal.account) diff --git a/brewman/brewman/routers/reports/purchase_entries.py b/brewman/brewman/routers/reports/purchase_entries.py index a6a5bc5c..ea476770 100644 --- a/brewman/brewman/routers/reports/purchase_entries.py +++ b/brewman/brewman/routers/reports/purchase_entries.py @@ -1,5 +1,4 @@ from datetime import date, datetime -from typing import List import brewman.schemas.purchase_entries as schemas @@ -49,9 +48,9 @@ def report_data( return schemas.PurchaseEntries(startDate=start, finishDate=finish, body=body) -def build_report(start_date: date, finish_date: date, db: Session) -> List[schemas.PurchaseEntriesItem]: +def build_report(start_date: date, finish_date: date, db: Session) -> list[schemas.PurchaseEntriesItem]: body = [] - query: List[Voucher] = ( + query: list[Voucher] = ( db.execute( select(Voucher) .join(Voucher.journals) diff --git a/brewman/brewman/routers/reports/purchases.py b/brewman/brewman/routers/reports/purchases.py index 3c1effd0..370c37ea 100644 --- a/brewman/brewman/routers/reports/purchases.py +++ b/brewman/brewman/routers/reports/purchases.py @@ -1,5 +1,4 @@ from datetime import datetime -from typing import List, Tuple import brewman.schemas.purchases as schemas @@ -58,7 +57,7 @@ def report_data( def build_report( start_date: str, finish_date: str, db: Session -) -> Tuple[List[schemas.PurchasesItem], schemas.PurchasesItem]: +) -> tuple[list[schemas.PurchasesItem], schemas.PurchasesItem]: body = [] quantity_sum = func.sum(Journal.debit * Inventory.quantity).label("quantity") amount_sum = func.sum(Journal.debit * Inventory.quantity * Inventory.rate * (1 + Inventory.tax)).label("amount") diff --git a/brewman/brewman/routers/reports/raw_material_cost.py b/brewman/brewman/routers/reports/raw_material_cost.py index 38768a22..a94cbcf5 100644 --- a/brewman/brewman/routers/reports/raw_material_cost.py +++ b/brewman/brewman/routers/reports/raw_material_cost.py @@ -1,7 +1,6 @@ import uuid from datetime import date, datetime -from typing import Dict, List, Tuple import brewman.schemas.raw_material_cost as schemas @@ -84,10 +83,10 @@ def report_id( def build_report( start_date: date, finish_date: date, db: Session -) -> Tuple[List[schemas.RawMaterialCostItem], schemas.RawMaterialCostItem]: +) -> tuple[list[schemas.RawMaterialCostItem], schemas.RawMaterialCostItem]: body = [] - sum_issue = func.sum(case([(AccountBase.type_id == 2, Journal.signed_amount)], else_=0)).label("issue") - sum_sale = func.sum(case([(AccountBase.type_id == 3, Journal.signed_amount * -1)], else_=0)).label("sale") + sum_issue = func.sum(case((AccountBase.type_id == 2, Journal.signed_amount), else_=0)).label("issue") + sum_sale = func.sum(case((AccountBase.type_id == 3, Journal.signed_amount * -1), else_=0)).label("sale") query = db.execute( select(CostCentre, sum_issue, sum_sale) @@ -127,7 +126,7 @@ def build_report( def build_report_id( cost_centre_id: uuid.UUID, start_date: date, finish_date: date, db: Session -) -> List[schemas.RawMaterialCostItem]: +) -> list[schemas.RawMaterialCostItem]: sum_quantity = func.sum(Inventory.quantity * Journal.debit).label("quantity") sum_net = func.sum(Inventory.rate * Inventory.quantity * Journal.debit).label("net") sum_gross = func.sum(Inventory.amount * Journal.debit).label("gross") @@ -150,9 +149,9 @@ def build_report_id( .order_by(ProductGroup.name, sum_net.desc()) ).all() - groups: Dict[uuid.UUID, schemas.RawMaterialCostItem] = {} + groups: dict[uuid.UUID, schemas.RawMaterialCostItem] = {} counter = 0 - list_: List[schemas.RawMaterialCostItem] = [] + list_: list[schemas.RawMaterialCostItem] = [] for product, pg, quantity, net, gross in query: if pg.id in groups: group = groups[pg.id] diff --git a/brewman/brewman/routers/reports/reconcile.py b/brewman/brewman/routers/reports/reconcile.py index d820b610..4e42e8f6 100644 --- a/brewman/brewman/routers/reports/reconcile.py +++ b/brewman/brewman/routers/reports/reconcile.py @@ -1,7 +1,6 @@ import uuid from datetime import date, datetime -from typing import List, Optional import brewman.schemas.reconcile as schemas @@ -40,8 +39,8 @@ def show_blank( def show_data( id_: uuid.UUID, request: Request, - s: Optional[str] = None, - f: Optional[str] = None, + s: str | None = None, + f: str | None = None, user: UserToken = Security(get_user, scopes=["reconcile"]), ) -> schemas.Reconcile: with SessionFuture() as db: @@ -60,7 +59,7 @@ def show_data( def build_report( account_id: uuid.UUID, start_date: date, finish_date: date, db: Session -) -> List[schemas.ReconcileItem]: +) -> list[schemas.ReconcileItem]: opening = opening_balance(account_id, start_date, db) body = [opening] @@ -152,8 +151,8 @@ def save( id_: uuid.UUID, data: schemas.Reconcile, request: Request, - s: Optional[str] = None, - f: Optional[str] = None, + s: str | None = None, + f: str | None = None, user: UserToken = Security(get_user, scopes=["reconcile"]), ) -> schemas.Reconcile: with SessionFuture() as db: diff --git a/brewman/brewman/routers/reports/stock_movement.py b/brewman/brewman/routers/reports/stock_movement.py index 4571b856..e35de5a3 100644 --- a/brewman/brewman/routers/reports/stock_movement.py +++ b/brewman/brewman/routers/reports/stock_movement.py @@ -1,6 +1,5 @@ from datetime import date, datetime from decimal import Decimal -from typing import List import brewman.schemas.stock_movement as schemas @@ -52,7 +51,7 @@ def report_data( return schemas.StockMovement(startDate=start, finishDate=finish, body=body) -def build_stock_movement(start_date: date, finish_date: date, db: Session) -> List[schemas.StockMovementItem]: +def build_stock_movement(start_date: date, finish_date: date, db: Session) -> list[schemas.StockMovementItem]: dict_ = {} quantity_sum = func.sum(Journal.debit * Inventory.quantity).label("quantity") openings = db.execute( diff --git a/brewman/brewman/routers/reports/trial_balance.py b/brewman/brewman/routers/reports/trial_balance.py index 1a90f7b3..9c1a3fbc 100644 --- a/brewman/brewman/routers/reports/trial_balance.py +++ b/brewman/brewman/routers/reports/trial_balance.py @@ -1,5 +1,4 @@ from datetime import date, datetime -from typing import List import brewman.schemas.trial_balance as schemas @@ -44,7 +43,7 @@ def report_data( ) -def build_report(date_: date, db: Session) -> List[schemas.TrialBalanceItem]: +def build_report(date_: date, db: Session) -> list[schemas.TrialBalanceItem]: amount_sum = func.sum(Journal.amount * Journal.debit).label("amount") query = db.execute( select(AccountBase, amount_sum) diff --git a/brewman/brewman/routers/role.py b/brewman/brewman/routers/role.py index 18f2d3c7..09af84e9 100644 --- a/brewman/brewman/routers/role.py +++ b/brewman/brewman/routers/role.py @@ -1,7 +1,5 @@ import uuid -from typing import List - import brewman.schemas.role as schemas from fastapi import APIRouter, HTTPException, Security, status @@ -14,8 +12,8 @@ from ..core.security import get_current_active_user as get_user from ..db.session import SessionFuture from ..models.permission import Permission from ..models.role import Role -from ..models.role_permission import role_permission -from ..models.user_role import user_role +from ..models.role_permission import RolePermission +from ..models.user_role import UserRole from ..schemas.user import UserToken @@ -61,7 +59,7 @@ def update_route( ) -def add_permissions(role: Role, permissions: List[schemas.PermissionItem], db: Session) -> None: +def add_permissions(role: Role, permissions: list[schemas.PermissionItem], db: Session) -> None: for permission in permissions: gp = next((p for p in role.permissions if p.id == permission.id_), None) if permission.enabled and gp is None: @@ -76,12 +74,12 @@ def delete_route( user: UserToken = Security(get_user, scopes=["users"]), ) -> schemas.RoleBlank: with SessionFuture() as db: - if db.execute(select(count(user_role.c.id)).where(user_role.c.role_id == id_)).scalar_one() > 0: + if db.execute(select(count(UserRole.id)).where(UserRole.role_id == id_)).scalar_one() > 0: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="This Role has been assigned to users and cannot be deleted.", ) - if db.execute(select(count(role_permission.c.id)).where(role_permission.c.role_id == id_)).scalar_one() > 0: + if db.execute(select(count(RolePermission.id)).where(RolePermission.role_id == id_)).scalar_one() > 0: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="This Role has permissions and cannot be deleted.", @@ -101,12 +99,12 @@ def show_blank( return role_blank(db) -@router.get("/list", response_model=List[schemas.RoleList]) +@router.get("/list", response_model=list[schemas.RoleList]) async def show_list( user: UserToken = Security(get_user, scopes=["users"]), -) -> List[schemas.RoleList]: +) -> list[schemas.RoleList]: with SessionFuture() as db: - roles: List[Role] = db.execute(select(Role).order_by(Role.name)).scalars().all() + roles: list[Role] = db.execute(select(Role).order_by(Role.name)).scalars().all() return [ schemas.RoleList( id=item.id, diff --git a/brewman/brewman/routers/user.py b/brewman/brewman/routers/user.py index e8765d9d..b49d9ce4 100644 --- a/brewman/brewman/routers/user.py +++ b/brewman/brewman/routers/user.py @@ -1,7 +1,5 @@ import uuid -from typing import List - import brewman.schemas.user as schemas from fastapi import APIRouter, Depends, HTTPException, Security, status @@ -15,7 +13,7 @@ from ..models.attendance import Attendance from ..models.login_history import LoginHistory from ..models.role import Role from ..models.user import User -from ..models.user_role import user_role +from ..models.user_role import UserRole from ..models.voucher import Voucher from ..schemas.user import UserToken @@ -97,7 +95,7 @@ def update_route( ) -def add_roles(user: User, roles: List[schemas.RoleItem], db: Session) -> None: +def add_roles(user: User, roles: list[schemas.RoleItem], db: Session) -> None: for role in roles: ug = next((g for g in user.roles if g.id == role.id_), None) if role.enabled and ug is None: @@ -122,7 +120,7 @@ def delete_route( db.execute(update(Voucher).where(Voucher.user_id == item.id).values(user_id=admin.id)) db.execute(update(Attendance).where(Attendance.user_id == item.id).values(user_id=admin.id)) db.execute(delete(LoginHistory).where(LoginHistory.user_id == item.id)) - db.execute(delete(user_role).where(user_role.c.user_id == item.id)) + db.execute(delete(UserRole).where(UserRole.user_id == item.id)) db.execute(delete(User).where(User.id == item.id)) db.commit() @@ -135,12 +133,12 @@ def show_blank( return user_blank(db) -@router.get("/list", response_model=List[schemas.UserList]) +@router.get("/list", response_model=list[schemas.UserList]) async def show_list( user: UserToken = Security(get_user, scopes=["users"]), -) -> List[schemas.UserList]: +) -> list[schemas.UserList]: with SessionFuture() as db: - users: List[User] = db.execute(select(User).order_by(User.name)).scalars().all() + users: list[User] = db.execute(select(User).order_by(User.name)).scalars().all() return [ schemas.UserList( id=item.id, @@ -154,8 +152,8 @@ async def show_list( ] -@router.get("/active", response_model=List[schemas.UserBlank]) -async def show_active(user: UserToken = Depends(get_user)) -> List[schemas.UserBlank]: +@router.get("/active", response_model=list[schemas.UserBlank]) +async def show_active(user: UserToken = Depends(get_user)) -> list[schemas.UserBlank]: with SessionFuture() as db: return [ schemas.UserBlank(name=name) diff --git a/brewman/brewman/routers/voucher.py b/brewman/brewman/routers/voucher.py index cec4f2b9..30be00d4 100644 --- a/brewman/brewman/routers/voucher.py +++ b/brewman/brewman/routers/voucher.py @@ -2,7 +2,6 @@ import uuid from datetime import date from decimal import Decimal -from typing import List, Optional, Tuple import brewman.schemas.voucher as output @@ -121,7 +120,7 @@ def delete_voucher( else: source = j_item.cost_centre_id - batch_consumed: Optional[bool] + batch_consumed: bool | None if source == CostCentre.cost_centre_purchase(): batch_consumed = True elif destination == CostCentre.cost_centre_purchase(): @@ -357,10 +356,10 @@ def blank_voucher(info: BlankVoucherInfo, db: Session) -> output.Voucher: return json_voucher -def incentive_employees(date_: date, db: Session) -> Tuple[List[output.Incentive], Decimal]: +def incentive_employees(date_: date, db: Session) -> tuple[list[output.Incentive], Decimal]: start_date = get_first_day(date_) finish_date = date_ - details: List[output.Incentive] = [] + details: list[output.Incentive] = [] employees = ( db.execute( select(Employee) @@ -374,7 +373,7 @@ def incentive_employees(date_: date, db: Session) -> Tuple[List[output.Incentive .all() ) for employee in employees: - att: List[Attendance] = ( + att: list[Attendance] = ( db.execute( select(Attendance).where( Attendance.employee_id == employee.id, @@ -398,7 +397,7 @@ def incentive_employees(date_: date, db: Session) -> Tuple[List[output.Incentive ) ) - amount: Optional[Decimal] = db.execute( + amount: Decimal | None = db.execute( select(func.sum(Journal.amount * Journal.debit)) .join(Journal.voucher) .where( @@ -430,7 +429,7 @@ def check_voucher_edit_allowed(voucher: Voucher, user: UserToken) -> None: ) -def get_batch_quantity(id_: uuid.UUID, voucher_id: Optional[uuid.UUID], db: Session) -> Decimal: +def get_batch_quantity(id_: uuid.UUID, voucher_id: uuid.UUID | None, db: Session) -> Decimal: query = ( select(func.sum(Inventory.quantity * Journal.debit)) .join(Inventory.voucher) diff --git a/brewman/brewman/routers/voucher_types.py b/brewman/brewman/routers/voucher_types.py index 361c24c4..7a92cf9f 100644 --- a/brewman/brewman/routers/voucher_types.py +++ b/brewman/brewman/routers/voucher_types.py @@ -1,5 +1,3 @@ -from typing import List - import brewman.schemas.account_type as schemas from fastapi import APIRouter, Depends @@ -12,6 +10,6 @@ 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]: +@router.get("", response_model=list[schemas.AccountType]) +def account_type_list(user: UserToken = Depends(get_user)) -> list[schemas.AccountType]: return [schemas.AccountType(id=item.value, name=item.name) for item in list(VoucherType)] diff --git a/brewman/brewman/schemas/account.py b/brewman/brewman/schemas/account.py index 8cbb5941..1253f2d6 100644 --- a/brewman/brewman/schemas/account.py +++ b/brewman/brewman/schemas/account.py @@ -1,7 +1,5 @@ import uuid -from typing import Optional - from pydantic import BaseModel, Field from . import to_camel @@ -10,7 +8,7 @@ from .cost_centre import CostCentreLink class AccountLink(BaseModel): id_: uuid.UUID = Field(...) - name: Optional[str] + name: str | None class Config: fields = {"id_": "id"} diff --git a/brewman/brewman/schemas/attendance.py b/brewman/brewman/schemas/attendance.py index cbaf35f5..b14c43ea 100644 --- a/brewman/brewman/schemas/attendance.py +++ b/brewman/brewman/schemas/attendance.py @@ -1,7 +1,6 @@ import uuid from datetime import date, datetime -from typing import List, Optional, Union from brewman.schemas import to_camel from brewman.schemas.attendance_type import AttendanceType @@ -17,18 +16,18 @@ class AttendanceItem(BaseModel): attendance_type: AttendanceType prints: str hours_worked: str - full_day: Optional[bool] + full_day: bool | None class Config: alias_generator = to_camel class Attendance(BaseModel): - date_: Optional[date] - body: List[AttendanceItem] + date_: date | None + body: list[AttendanceItem] @validator("date_", pre=True) - def parse_date(cls, value: Union[date, str]) -> date: + def parse_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/attendance_type.py b/brewman/brewman/schemas/attendance_type.py index 2ca091bc..d4c80d24 100644 --- a/brewman/brewman/schemas/attendance_type.py +++ b/brewman/brewman/schemas/attendance_type.py @@ -1,5 +1,4 @@ from decimal import Decimal -from typing import Optional from brewman.schemas import to_camel from pydantic import BaseModel @@ -7,8 +6,8 @@ from pydantic import BaseModel class AttendanceType(BaseModel): id_: int - name: Optional[str] - value: Optional[Decimal] + name: str | None + value: Decimal | None class Config: alias_generator = to_camel diff --git a/brewman/brewman/schemas/balance_sheet.py b/brewman/brewman/schemas/balance_sheet.py index 08bfb66a..abdc62d2 100644 --- a/brewman/brewman/schemas/balance_sheet.py +++ b/brewman/brewman/schemas/balance_sheet.py @@ -1,6 +1,5 @@ from datetime import date, datetime from decimal import Decimal -from typing import List, Optional, Union from pydantic import validator from pydantic.main import BaseModel @@ -9,10 +8,10 @@ from . import to_camel class BalanceSheetItem(BaseModel): - name: Optional[str] - group: Optional[str] - amount: Optional[Decimal] - sub_amount: Optional[Decimal] + name: str | None + group: str | None + amount: Decimal | None + sub_amount: Decimal | None order: int class Config: @@ -22,8 +21,8 @@ class BalanceSheetItem(BaseModel): class BalanceSheet(BaseModel): date_: date - body: List[BalanceSheetItem] - footer: Optional[BalanceSheetItem] + body: list[BalanceSheetItem] + footer: BalanceSheetItem | None class Config: anystr_strip_whitespace = True @@ -31,7 +30,7 @@ class BalanceSheet(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} @validator("date_", pre=True) - def parse_date(cls, value: Union[date, str]) -> date: + def parse_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/batch.py b/brewman/brewman/schemas/batch.py index 7cbee326..79f61f9d 100644 --- a/brewman/brewman/schemas/batch.py +++ b/brewman/brewman/schemas/batch.py @@ -1,7 +1,6 @@ import uuid from decimal import Decimal -from typing import Optional from brewman.schemas import to_camel from brewman.schemas.product import ProductLink @@ -9,7 +8,7 @@ from pydantic import BaseModel class Batch(BaseModel): - id_: Optional[uuid.UUID] + id_: uuid.UUID | None name: str sku: ProductLink quantity_remaining: Decimal diff --git a/brewman/brewman/schemas/batch_integrity.py b/brewman/brewman/schemas/batch_integrity.py index 0a9f9374..5d0da60a 100644 --- a/brewman/brewman/schemas/batch_integrity.py +++ b/brewman/brewman/schemas/batch_integrity.py @@ -2,7 +2,6 @@ import uuid from datetime import date, datetime from decimal import Decimal -from typing import List, Union from pydantic import validator from pydantic.main import BaseModel @@ -13,7 +12,7 @@ from . import to_camel class BatchIntegrityItem(BaseModel): date_: date type_: str - url: List[str] + url: list[str] quantity: Decimal price: Decimal @@ -23,7 +22,7 @@ class BatchIntegrityItem(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} @validator("date_", pre=True) - def parse_start_date(cls, value: Union[date, str]) -> date: + def parse_start_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @@ -36,7 +35,7 @@ class BatchIntegrity(BaseModel): price: Decimal showing: Decimal actual: Decimal - details: List[BatchIntegrityItem] + details: list[BatchIntegrityItem] class Config: anystr_strip_whitespace = True @@ -44,7 +43,7 @@ class BatchIntegrity(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} @validator("date_", pre=True) - def parse_start_date(cls, value: Union[date, str]) -> date: + def parse_start_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/blank_voucher_info.py b/brewman/brewman/schemas/blank_voucher_info.py index a8a1cafe..634d3e83 100644 --- a/brewman/brewman/schemas/blank_voucher_info.py +++ b/brewman/brewman/schemas/blank_voucher_info.py @@ -1,5 +1,4 @@ from datetime import date, datetime -from typing import Optional, Union from pydantic import BaseModel, validator @@ -12,16 +11,16 @@ from .cost_centre import CostCentreLink class BlankVoucherInfo(BaseModel): type_: VoucherType date_: date - account: Optional[AccountLink] - source: Optional[CostCentreLink] - destination: Optional[CostCentreLink] + account: AccountLink | None + source: CostCentreLink | None + destination: CostCentreLink | None class Config: alias_generator = to_camel json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} @validator("date_", pre=True) - def parse_date(cls, value: Union[date, str]) -> date: + def parse_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/cash_flow.py b/brewman/brewman/schemas/cash_flow.py index 05e0ff81..db300e7c 100644 --- a/brewman/brewman/schemas/cash_flow.py +++ b/brewman/brewman/schemas/cash_flow.py @@ -1,6 +1,5 @@ from datetime import date, datetime from decimal import Decimal -from typing import List, Optional, Union from pydantic import validator from pydantic.main import BaseModel @@ -10,7 +9,7 @@ from . import to_camel class CashFlowItem(BaseModel): name: str - url: Optional[List[str]] + url: list[str] | None amount: Decimal class Config: @@ -19,10 +18,10 @@ class CashFlowItem(BaseModel): class CashFlowBody(BaseModel): - operating: List[CashFlowItem] - investing: List[CashFlowItem] - financing: List[CashFlowItem] - details: List[CashFlowItem] + operating: list[CashFlowItem] + investing: list[CashFlowItem] + financing: list[CashFlowItem] + details: list[CashFlowItem] class Config: anystr_strip_whitespace = True @@ -33,7 +32,7 @@ class CashFlow(BaseModel): start_date: date finish_date: date body: CashFlowBody - footer: List[CashFlowItem] + footer: list[CashFlowItem] class Config: anystr_strip_whitespace = True @@ -41,13 +40,13 @@ class CashFlow(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} @validator("start_date", pre=True) - def parse_start_date(cls, value: Union[date, str]) -> date: + def parse_start_date(cls, value: date | str) -> date: 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: Union[date, str]) -> date: + def parse_finish_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/client.py b/brewman/brewman/schemas/client.py index a4d9b387..d1af972c 100644 --- a/brewman/brewman/schemas/client.py +++ b/brewman/brewman/schemas/client.py @@ -1,7 +1,6 @@ import uuid from datetime import datetime -from typing import Optional from pydantic.main import BaseModel @@ -11,7 +10,7 @@ from . import to_camel class ClientIn(BaseModel): name: str enabled: bool - otp: Optional[int] + otp: int | None class Client(ClientIn): @@ -26,7 +25,7 @@ class Client(ClientIn): class ClientList(Client): last_user: str - last_date: Optional[datetime] + last_date: datetime | None class Config: anystr_strip_whitespace = True diff --git a/brewman/brewman/schemas/closing_stock.py b/brewman/brewman/schemas/closing_stock.py index ef8bc4c8..11a2112b 100644 --- a/brewman/brewman/schemas/closing_stock.py +++ b/brewman/brewman/schemas/closing_stock.py @@ -2,7 +2,6 @@ import uuid from datetime import date, datetime from decimal import Decimal -from typing import List, Optional, Union from pydantic import validator from pydantic.main import BaseModel @@ -14,13 +13,13 @@ from .user_link import UserLink class ClosingStockItem(BaseModel): - id_: Optional[uuid.UUID] + id_: uuid.UUID | None product: ProductLink group: str quantity: Decimal amount: Decimal physical: Decimal - cost_centre: Optional[CostCentreLink] + cost_centre: CostCentreLink | None class Config: anystr_strip_whitespace = True @@ -30,12 +29,12 @@ class ClosingStockItem(BaseModel): class ClosingStock(BaseModel): date_: date cost_centre: CostCentreLink - items: List[ClosingStockItem] - creation_date: Optional[datetime] - last_edit_date: Optional[datetime] - user: Optional[UserLink] + items: list[ClosingStockItem] + creation_date: datetime | None + last_edit_date: datetime | None + user: UserLink | None posted: bool - poster: Optional[UserLink] + poster: UserLink | None class Config: anystr_strip_whitespace = True @@ -43,13 +42,13 @@ class ClosingStock(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y"), datetime: lambda v: v.strftime("%d-%b-%Y %H:%M")} @validator("date_", pre=True) - def parse_date(cls, value: Union[date, str]) -> date: + def parse_date(cls, value: date | str) -> date: 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: Union[None, datetime, str]) -> Optional[datetime]: + def parse_creation_date(cls, value: None | datetime | str) -> datetime | None: if value is None: return None if isinstance(value, datetime): @@ -57,7 +56,7 @@ class ClosingStock(BaseModel): return datetime.strptime(value, "%d-%b-%Y %H:%M") @validator("last_edit_date", pre=True) - def parse_last_edit_date(cls, value: Union[None, datetime, str]) -> Optional[datetime]: + def parse_last_edit_date(cls, value: None | datetime | str) -> datetime | None: if value is None: return None if isinstance(value, datetime): diff --git a/brewman/brewman/schemas/cost_centre.py b/brewman/brewman/schemas/cost_centre.py index 96b74d4d..aaa8e3dd 100644 --- a/brewman/brewman/schemas/cost_centre.py +++ b/brewman/brewman/schemas/cost_centre.py @@ -1,7 +1,5 @@ import uuid -from typing import Optional - from pydantic import BaseModel, Field from . import to_camel @@ -25,7 +23,7 @@ class CostCentre(CostCentreIn): class CostCentreLink(BaseModel): id_: uuid.UUID = Field(...) - name: Optional[str] + name: str | None class Config: fields = {"id_": "id"} diff --git a/brewman/brewman/schemas/daybook.py b/brewman/brewman/schemas/daybook.py index d15cf42c..1c0d0058 100644 --- a/brewman/brewman/schemas/daybook.py +++ b/brewman/brewman/schemas/daybook.py @@ -2,7 +2,6 @@ import uuid from datetime import date, datetime from decimal import Decimal -from typing import List, Optional, Union from pydantic import validator from pydantic.main import BaseModel @@ -11,19 +10,19 @@ from . import to_camel class DaybookItem(BaseModel): - id_: Optional[uuid.UUID] + id_: uuid.UUID | None date_: date - url: List[str] + url: list[str] type_: str narration: str - debit_text: Optional[str] - debit_amount: Optional[Decimal] - credit_text: Optional[str] - credit_amount: Optional[Decimal] + debit_text: str | None + debit_amount: Decimal | None + credit_text: str | None + credit_amount: Decimal | None posted: bool @validator("date_", pre=True) - def parse_date(cls, value: Union[date, str]) -> date: + def parse_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @@ -37,7 +36,7 @@ class DaybookItem(BaseModel): class Daybook(BaseModel): start_date: date finish_date: date - body: List[DaybookItem] + body: list[DaybookItem] class Config: anystr_strip_whitespace = True @@ -45,13 +44,13 @@ class Daybook(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} @validator("start_date", pre=True) - def parse_start_date(cls, value: Union[date, str]) -> date: + def parse_start_date(cls, value: date | str) -> date: 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: Union[date, str]) -> date: + def parse_finish_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/employee.py b/brewman/brewman/schemas/employee.py index 4dea08c8..d3b68fbb 100644 --- a/brewman/brewman/schemas/employee.py +++ b/brewman/brewman/schemas/employee.py @@ -2,7 +2,7 @@ import uuid from datetime import date, datetime from decimal import Decimal -from typing import Any, Optional, Union +from typing import Any from pydantic import BaseModel, Field, validator @@ -16,16 +16,16 @@ class EmployeeIn(AccountBase): salary: int = Field(ge=0) points: Decimal = Field(ge=0, lt=1000, multiple_of=0.01) joining_date: date - leaving_date: Optional[date] + leaving_date: date | None @validator("joining_date", pre=True) - def parse_joining_date(cls, value: Union[date, str]) -> date: + def parse_joining_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @validator("leaving_date", pre=True) - def parse_leaving_date(cls, value: Union[None, date, str]) -> Optional[date]: + def parse_leaving_date(cls, value: None | date | str) -> date | None: if isinstance(value, date): return value if value is None: @@ -33,7 +33,7 @@ class EmployeeIn(AccountBase): return datetime.strptime(value, "%d-%b-%Y").date() @validator("leaving_date") - def leaving_date_more_than_joining_date(cls, v: Optional[date], values: dict, **kwargs: Any) -> Optional[date]: + def leaving_date_more_than_joining_date(cls, v: date | None, values: dict, **kwargs: Any) -> date | None: if values["is_active"]: return None if v < values["joining_date"]: @@ -63,8 +63,8 @@ class EmployeeBlank(EmployeeIn): class EmployeeLink(BaseModel): id_: uuid.UUID = Field(...) - name: Optional[str] - designation: Optional[str] + name: str | None + designation: str | None cost_centre: CostCentreLink class Config: diff --git a/brewman/brewman/schemas/employee_attendance.py b/brewman/brewman/schemas/employee_attendance.py index 36d94eda..b64cc166 100644 --- a/brewman/brewman/schemas/employee_attendance.py +++ b/brewman/brewman/schemas/employee_attendance.py @@ -1,5 +1,4 @@ from datetime import date, datetime -from typing import List, Optional, Union from brewman.schemas import to_camel from brewman.schemas.account import AccountLink @@ -12,10 +11,10 @@ class EmployeeAttendanceItem(BaseModel): attendance_type: AttendanceType prints: str hours_worked: str - full_day: Optional[bool] + full_day: bool | None @validator("date_", pre=True) - def parse_date(cls, value: Union[date, str]) -> date: + def parse_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @@ -27,19 +26,19 @@ class EmployeeAttendanceItem(BaseModel): class EmployeeAttendance(BaseModel): - start_date: Optional[date] - finish_date: Optional[date] - employee: Optional[AccountLink] - body: List[EmployeeAttendanceItem] + start_date: date | None + finish_date: date | None + employee: AccountLink | None + body: list[EmployeeAttendanceItem] @validator("start_date", pre=True) - def parse_start_date(cls, value: Union[date, str]) -> date: + def parse_start_date(cls, value: date | str) -> date: 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: Union[date, str]) -> date: + def parse_finish_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/employee_benefit.py b/brewman/brewman/schemas/employee_benefit.py index 67e99b55..44b51414 100644 --- a/brewman/brewman/schemas/employee_benefit.py +++ b/brewman/brewman/schemas/employee_benefit.py @@ -1,21 +1,19 @@ 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] + id_: uuid.UUID | None employee: 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) + esi_employee: int | None = Field(ge=0) + pf_employee: int | None = Field(ge=0) + esi_employer: int | None = Field(ge=0) + pf_employer: int | None = Field(ge=0) class Config: alias_generator = to_camel diff --git a/brewman/brewman/schemas/entries.py b/brewman/brewman/schemas/entries.py index a85277dc..9dc366cf 100644 --- a/brewman/brewman/schemas/entries.py +++ b/brewman/brewman/schemas/entries.py @@ -2,7 +2,6 @@ import uuid from datetime import date, datetime from decimal import Decimal -from typing import List, Union from pydantic import BaseModel, validator @@ -13,12 +12,12 @@ from .user_link import UserLink class Entries(BaseModel): id_: uuid.UUID date_: date - url: List[str] + url: list[str] type_: str posted: bool narration: str - debit_names: List[str] - credit_names: List[str] + debit_names: list[str] + credit_names: list[str] amount: Decimal creation_date: datetime last_edit_date: datetime @@ -33,19 +32,19 @@ class Entries(BaseModel): } @validator("date_", pre=True) - def parse_date(cls, value: Union[date, str]) -> date: + def parse_date(cls, value: date | str) -> date: 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: Union[datetime, str]) -> datetime: + def parse_creation_date(cls, value: datetime | str) -> datetime: 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: Union[datetime, str]) -> datetime: + def parse_last_edit_date(cls, value: datetime | str) -> datetime: if isinstance(value, datetime): return value return datetime.strptime(value, "%d-%b-%Y %H:%M") @@ -53,7 +52,7 @@ class Entries(BaseModel): class Report(BaseModel): counts: int - report: List[Entries] + report: list[Entries] class Config: anystr_strip_whitespace = True diff --git a/brewman/brewman/schemas/image_upload.py b/brewman/brewman/schemas/image_upload.py index f7363f32..0cafd615 100644 --- a/brewman/brewman/schemas/image_upload.py +++ b/brewman/brewman/schemas/image_upload.py @@ -1,15 +1,13 @@ 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] + id_: uuid.UUID | None + resized: str | None + thumbnail: str | None class Config: alias_generator = to_camel diff --git a/brewman/brewman/schemas/input.py b/brewman/brewman/schemas/input.py index ab969ad8..59e52f7d 100644 --- a/brewman/brewman/schemas/input.py +++ b/brewman/brewman/schemas/input.py @@ -3,7 +3,6 @@ import uuid from datetime import date, datetime from decimal import Decimal -from typing import List, Optional, Union from fastapi import Form from pydantic import BaseModel, Field, validator @@ -19,7 +18,7 @@ from .voucher import VoucherIn class JournalIn(VoucherIn): - journals: List[Journal] + journals: list[Journal] class Config: anystr_strip_whitespace = True @@ -30,7 +29,7 @@ class JournalIn(VoucherIn): } @validator("date_", pre=True) - def parse_date(cls, value: Union[date, str]) -> date: + def parse_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @@ -43,7 +42,7 @@ class JournalIn(VoucherIn): class PurchaseIn(VoucherIn): vendor: AccountLink - inventories: List[Inventory] + inventories: list[Inventory] class Config: anystr_strip_whitespace = True @@ -54,7 +53,7 @@ class PurchaseIn(VoucherIn): } @validator("date_", pre=True) - def parse_date(cls, value: Union[date, str]) -> date: + def parse_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @@ -68,7 +67,7 @@ class PurchaseIn(VoucherIn): class IssueIn(VoucherIn): source: CostCentreLink destination: CostCentreLink - inventories: List[Inventory] + inventories: list[Inventory] class Config: anystr_strip_whitespace = True @@ -79,7 +78,7 @@ class IssueIn(VoucherIn): } @validator("date_", pre=True) - def parse_date(cls, value: Union[date, str]) -> date: + def parse_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @@ -97,7 +96,7 @@ class IssueIn(VoucherIn): class EmployeeBenefitIn(VoucherIn): - employee_benefits: List[EmployeeBenefit] + employee_benefits: list[EmployeeBenefit] class Config: anystr_strip_whitespace = True @@ -108,7 +107,7 @@ class EmployeeBenefitIn(VoucherIn): } @validator("date_", pre=True) - def parse_date(cls, value: Union[date, str]) -> date: + def parse_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @@ -120,7 +119,7 @@ class EmployeeBenefitIn(VoucherIn): class IncentiveIn(VoucherIn): - incentives: List[Incentive] + incentives: list[Incentive] class Config: anystr_strip_whitespace = True @@ -131,7 +130,7 @@ class IncentiveIn(VoucherIn): } @validator("date_", pre=True) - def parse_date(cls, value: Union[date, str]) -> date: + def parse_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @@ -143,9 +142,9 @@ class IncentiveIn(VoucherIn): class IncentiveEmployee(BaseModel): - id_: Optional[uuid.UUID] + id_: uuid.UUID | None employee_id: uuid.UUID cost_centre_id: uuid.UUID - journal: Optional[Journal] + journal: Journal | None days_worked: Decimal = Field(ge=0, multiple_of=0.5) points: Decimal = Field(ge=0, multiple_of=0.01) diff --git a/brewman/brewman/schemas/inventory.py b/brewman/brewman/schemas/inventory.py index e16358f0..3e9f1146 100644 --- a/brewman/brewman/schemas/inventory.py +++ b/brewman/brewman/schemas/inventory.py @@ -1,7 +1,6 @@ import uuid from decimal import Decimal -from typing import Optional from brewman.schemas import to_camel from brewman.schemas.batch import Batch @@ -9,13 +8,13 @@ from pydantic import BaseModel, Field class Inventory(BaseModel): - id_: Optional[uuid.UUID] + id_: uuid.UUID | None batch: 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] + amount: Decimal | None class Config: alias_generator = to_camel diff --git a/brewman/brewman/schemas/journal.py b/brewman/brewman/schemas/journal.py index 82e414a9..87ef9560 100644 --- a/brewman/brewman/schemas/journal.py +++ b/brewman/brewman/schemas/journal.py @@ -1,7 +1,6 @@ import uuid from decimal import Decimal -from typing import Optional from brewman.schemas import to_camel from brewman.schemas.account import AccountLink @@ -10,11 +9,11 @@ from pydantic import BaseModel, Field class Journal(BaseModel): - id_: Optional[uuid.UUID] + id_: uuid.UUID | None debit: int = Field(ge=-1, le=1, multiple_of=1) amount: Decimal = Field(ge=0) account: AccountLink - cost_centre: Optional[CostCentreLink] + cost_centre: CostCentreLink | None class Config: anystr_strip_whitespace = True diff --git a/brewman/brewman/schemas/ledger.py b/brewman/brewman/schemas/ledger.py index c8593d88..3aa7fe98 100644 --- a/brewman/brewman/schemas/ledger.py +++ b/brewman/brewman/schemas/ledger.py @@ -2,7 +2,6 @@ import uuid from datetime import date, datetime from decimal import Decimal -from typing import List, Optional, Union from pydantic import validator from pydantic.main import BaseModel @@ -12,10 +11,10 @@ from .account import AccountLink class LedgerItem(BaseModel): - id_: Optional[uuid.UUID] + id_: uuid.UUID | None date_: date name: str - url: List[str] + url: list[str] type_: str narration: str debit: Decimal @@ -23,7 +22,7 @@ class LedgerItem(BaseModel): posted: bool @validator("date_", pre=True) - def parse_date(cls, value: Union[date, str]) -> date: + def parse_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @@ -37,8 +36,8 @@ class LedgerItem(BaseModel): class Ledger(BaseModel): start_date: date finish_date: date - account: Optional[AccountLink] - body: List[LedgerItem] + account: AccountLink | None + body: list[LedgerItem] class Config: anystr_strip_whitespace = True @@ -46,13 +45,13 @@ class Ledger(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} @validator("start_date", pre=True) - def parse_start_date(cls, value: Union[date, str]) -> date: + def parse_start_date(cls, value: date | str) -> date: 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: Union[date, str]) -> date: + def parse_finish_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/net_transactions.py b/brewman/brewman/schemas/net_transactions.py index e898b714..68c4a2d1 100644 --- a/brewman/brewman/schemas/net_transactions.py +++ b/brewman/brewman/schemas/net_transactions.py @@ -1,6 +1,5 @@ from datetime import date, datetime from decimal import Decimal -from typing import List, Optional, Union from pydantic import validator from pydantic.main import BaseModel @@ -11,8 +10,8 @@ from . import to_camel class NetTransactionsItem(BaseModel): type_: str name: str - debit: Optional[Decimal] - credit: Optional[Decimal] + debit: Decimal | None + credit: Decimal | None class Config: anystr_strip_whitespace = True @@ -23,7 +22,7 @@ class NetTransactionsItem(BaseModel): class NetTransactions(BaseModel): start_date: date finish_date: date - body: List[NetTransactionsItem] + body: list[NetTransactionsItem] class Config: anystr_strip_whitespace = True @@ -31,13 +30,13 @@ class NetTransactions(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} @validator("start_date", pre=True) - def parse_start_date(cls, value: Union[date, str]) -> date: + def parse_start_date(cls, value: date | str) -> date: 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: Union[date, str]) -> date: + def parse_finish_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/non_contract_purchase.py b/brewman/brewman/schemas/non_contract_purchase.py index adc38cfa..d532d2ab 100644 --- a/brewman/brewman/schemas/non_contract_purchase.py +++ b/brewman/brewman/schemas/non_contract_purchase.py @@ -1,6 +1,5 @@ from datetime import date, datetime from decimal import Decimal -from typing import List, Union from pydantic import validator from pydantic.main import BaseModel @@ -12,7 +11,7 @@ class NonContractPurchase(BaseModel): date_: date vendor: str product: str - url: List[str] + url: list[str] contract_price: Decimal purchase_price: Decimal @@ -22,7 +21,7 @@ class NonContractPurchase(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} @validator("date_", pre=True) - def parse_start_date(cls, value: Union[date, str]) -> date: + def parse_start_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/period.py b/brewman/brewman/schemas/period.py index 49dc4da9..28700a09 100644 --- a/brewman/brewman/schemas/period.py +++ b/brewman/brewman/schemas/period.py @@ -1,7 +1,6 @@ import uuid from datetime import date, datetime -from typing import Union from pydantic import BaseModel, Field, validator @@ -18,13 +17,13 @@ class PeriodIn(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y") if v is not None else None} @validator("valid_from", pre=True) - def parse_valid_from(cls, value: Union[date, str]) -> date: + def parse_valid_from(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @validator("valid_till", pre=True) - def parse_valid_till(cls, value: Union[date, str]) -> date: + def parse_valid_till(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/product.py b/brewman/brewman/schemas/product.py index 93b47ec1..a3667f35 100644 --- a/brewman/brewman/schemas/product.py +++ b/brewman/brewman/schemas/product.py @@ -1,7 +1,5 @@ import uuid -from typing import List, Optional - from pydantic import BaseModel, Field from . import to_camel @@ -11,7 +9,7 @@ from .stock_keeping_unit import StockKeepingUnit class ProductLink(BaseModel): id_: uuid.UUID = Field(...) - name: Optional[str] + name: str | None class Config: fields = {"id_": "id"} @@ -20,7 +18,7 @@ class ProductLink(BaseModel): class ProductIn(BaseModel): name: str = Field(..., min_length=1) fraction_units: str = Field(..., min_length=1) - skus: List[StockKeepingUnit] + skus: list[StockKeepingUnit] product_group: ProductGroupLink = Field(...) is_active: bool is_purchased: bool @@ -40,8 +38,8 @@ class Product(ProductIn): class ProductBlank(ProductIn): name: str fraction_units: str - skus: List[StockKeepingUnit] - product_group: Optional[ProductGroupLink] # type: ignore[assignment] + skus: list[StockKeepingUnit] + product_group: ProductGroupLink | None # type: ignore[assignment] is_fixture: bool class Config: diff --git a/brewman/brewman/schemas/product_group.py b/brewman/brewman/schemas/product_group.py index 56181a1e..0cdbbde2 100644 --- a/brewman/brewman/schemas/product_group.py +++ b/brewman/brewman/schemas/product_group.py @@ -1,7 +1,5 @@ import uuid -from typing import Optional - from pydantic import BaseModel, Field from . import to_camel @@ -31,7 +29,7 @@ class ProductGroupBlank(ProductGroupIn): class ProductGroupLink(BaseModel): id_: uuid.UUID = Field(...) - name: Optional[str] + name: str | None class Config: fields = {"id_": "id"} diff --git a/brewman/brewman/schemas/product_ledger.py b/brewman/brewman/schemas/product_ledger.py index 143fe490..8f2084a1 100644 --- a/brewman/brewman/schemas/product_ledger.py +++ b/brewman/brewman/schemas/product_ledger.py @@ -2,7 +2,6 @@ import uuid from datetime import date, datetime from decimal import Decimal -from typing import List, Optional, Union from pydantic import validator from pydantic.main import BaseModel @@ -12,24 +11,24 @@ from .product import ProductLink class ProductLedgerItem(BaseModel): - id_: Optional[uuid.UUID] + id_: uuid.UUID | None date_: date name: str - url: List[str] + url: list[str] type_: str narration: str - debit_quantity: Optional[Decimal] - debit_amount: Optional[Decimal] - debit_unit: Optional[str] - credit_quantity: Optional[Decimal] - credit_amount: Optional[Decimal] - credit_unit: Optional[str] + debit_quantity: Decimal | None + debit_amount: Decimal | None + debit_unit: str | None + credit_quantity: Decimal | None + credit_amount: Decimal | None + credit_unit: str | None running_quantity: Decimal running_amount: Decimal posted: bool @validator("date_", pre=True) - def parse_date(cls, value: Union[date, str]) -> date: + def parse_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @@ -43,8 +42,8 @@ class ProductLedgerItem(BaseModel): class ProductLedger(BaseModel): start_date: date finish_date: date - product: Optional[ProductLink] - body: List[ProductLedgerItem] + product: ProductLink | None + body: list[ProductLedgerItem] class Config: anystr_strip_whitespace = True @@ -52,13 +51,13 @@ class ProductLedger(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} @validator("start_date", pre=True) - def parse_start_date(cls, value: Union[date, str]) -> date: + def parse_start_date(cls, value: date | str) -> date: 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: Union[date, str]) -> date: + def parse_finish_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/profit_loss.py b/brewman/brewman/schemas/profit_loss.py index e23d39a6..c4a8c42d 100644 --- a/brewman/brewman/schemas/profit_loss.py +++ b/brewman/brewman/schemas/profit_loss.py @@ -1,6 +1,5 @@ from datetime import date, datetime from decimal import Decimal -from typing import List, Optional, Union from pydantic import validator from pydantic.main import BaseModel @@ -9,10 +8,10 @@ from . import to_camel class ProfitLossItem(BaseModel): - group: Optional[str] - name: Optional[str] - amount: Optional[Decimal] - total: Optional[Decimal] + group: str | None + name: str | None + amount: Decimal | None + total: Decimal | None order: int class Config: @@ -24,7 +23,7 @@ class ProfitLossItem(BaseModel): class ProfitLoss(BaseModel): start_date: date finish_date: date - body: List[ProfitLossItem] + body: list[ProfitLossItem] footer: ProfitLossItem class Config: @@ -33,13 +32,13 @@ class ProfitLoss(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} @validator("start_date", pre=True) - def parse_start_date(cls, value: Union[date, str]) -> date: + def parse_start_date(cls, value: date | str) -> date: 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: Union[date, str]) -> date: + def parse_finish_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/purchase_entries.py b/brewman/brewman/schemas/purchase_entries.py index 652d9d6a..e61c39fa 100644 --- a/brewman/brewman/schemas/purchase_entries.py +++ b/brewman/brewman/schemas/purchase_entries.py @@ -1,6 +1,5 @@ from datetime import date, datetime from decimal import Decimal -from typing import List, Union from pydantic import validator from pydantic.main import BaseModel @@ -10,7 +9,7 @@ from . import to_camel class PurchaseEntriesItem(BaseModel): date_: date - url: List[str] + url: list[str] supplier: str product: str quantity: Decimal @@ -20,7 +19,7 @@ class PurchaseEntriesItem(BaseModel): amount: Decimal @validator("date_", pre=True) - def parse_date(cls, value: Union[date, str]) -> date: + def parse_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @@ -33,7 +32,7 @@ class PurchaseEntriesItem(BaseModel): class PurchaseEntries(BaseModel): start_date: date finish_date: date - body: List[PurchaseEntriesItem] + body: list[PurchaseEntriesItem] class Config: anystr_strip_whitespace = True @@ -41,13 +40,13 @@ class PurchaseEntries(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} @validator("start_date", pre=True) - def parse_start_date(cls, value: Union[date, str]) -> date: + def parse_start_date(cls, value: date | str) -> date: 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: Union[date, str]) -> date: + def parse_finish_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/purchases.py b/brewman/brewman/schemas/purchases.py index 739f270e..f1dd0a7f 100644 --- a/brewman/brewman/schemas/purchases.py +++ b/brewman/brewman/schemas/purchases.py @@ -1,6 +1,5 @@ from datetime import date, datetime from decimal import Decimal -from typing import List, Union from pydantic import validator from pydantic.main import BaseModel @@ -13,7 +12,7 @@ class PurchasesItem(BaseModel): quantity: Decimal rate: Decimal amount: Decimal - url: List[str] + url: list[str] class Config: anystr_strip_whitespace = True @@ -23,7 +22,7 @@ class PurchasesItem(BaseModel): class Purchases(BaseModel): start_date: date finish_date: date - body: List[PurchasesItem] + body: list[PurchasesItem] footer: PurchasesItem class Config: @@ -32,13 +31,13 @@ class Purchases(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} @validator("start_date", pre=True) - def parse_start_date(cls, value: Union[date, str]) -> date: + def parse_start_date(cls, value: date | str) -> date: 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: Union[date, str]) -> date: + def parse_finish_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/rate_contract.py b/brewman/brewman/schemas/rate_contract.py index 9eeafc3b..dc27cbd9 100644 --- a/brewman/brewman/schemas/rate_contract.py +++ b/brewman/brewman/schemas/rate_contract.py @@ -1,7 +1,6 @@ import uuid from datetime import date, datetime -from typing import List, Union from pydantic import BaseModel, validator @@ -16,7 +15,7 @@ class RateContractBlank(BaseModel): valid_from: date valid_till: date narration: str - items: List[RateContractItem] + items: list[RateContractItem] class Config: anystr_strip_whitespace = True @@ -27,19 +26,19 @@ class RateContractBlank(BaseModel): } @validator("date_", pre=True) - def parse_date(cls, value: Union[date, str]) -> date: + def parse_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @validator("valid_from", pre=True) - def parse_valid_from(cls, value: Union[date, str]) -> date: + def parse_valid_from(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @validator("valid_till", pre=True) - def parse_valid_till(cls, value: Union[date, str]) -> date: + def parse_valid_till(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @@ -57,7 +56,7 @@ class RateContractIn(RateContractBlank): } @validator("date_", pre=True) - def parse_date(cls, value: Union[date, str]) -> date: + def parse_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @@ -78,13 +77,13 @@ class RateContract(RateContractIn): } @validator("creation_date", pre=True) - def parse_creation_date(cls, value: Union[datetime, str]) -> datetime: + def parse_creation_date(cls, value: datetime | str) -> datetime: 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: Union[datetime, str]) -> datetime: + def parse_last_edit_date(cls, value: datetime | str) -> datetime: if isinstance(value, datetime): return value return datetime.strptime(value, "%d-%b-%Y %H:%M") diff --git a/brewman/brewman/schemas/rate_contract_item.py b/brewman/brewman/schemas/rate_contract_item.py index dd24e9f4..b1670ff7 100644 --- a/brewman/brewman/schemas/rate_contract_item.py +++ b/brewman/brewman/schemas/rate_contract_item.py @@ -1,7 +1,6 @@ import uuid from decimal import Decimal -from typing import Optional from brewman.schemas import to_camel from brewman.schemas.product import ProductLink @@ -9,7 +8,7 @@ from pydantic import BaseModel, Field class RateContractItem(BaseModel): - id_: Optional[uuid.UUID] + id_: uuid.UUID | None sku: ProductLink price: Decimal = Field(ge=0, multiple_of=0.01) diff --git a/brewman/brewman/schemas/raw_material_cost.py b/brewman/brewman/schemas/raw_material_cost.py index da0f46ff..4baff878 100644 --- a/brewman/brewman/schemas/raw_material_cost.py +++ b/brewman/brewman/schemas/raw_material_cost.py @@ -2,7 +2,6 @@ import uuid from datetime import date, datetime from decimal import Decimal -from typing import List, Optional, Union from pydantic import validator from pydantic.main import BaseModel @@ -11,19 +10,19 @@ from . import to_camel class RawMaterialCostItem(BaseModel): - name: Optional[str] - issue: Optional[Decimal] - sale: Optional[Decimal] - rmc: Optional[Decimal] - url: Optional[List[str]] + name: str | None + issue: Decimal | None + sale: Decimal | None + rmc: Decimal | None + url: list[str] | None - group: Optional[str] - quantity: Optional[Decimal] - net: Optional[Decimal] - gross: Optional[Decimal] + group: str | None + quantity: Decimal | None + net: Decimal | None + gross: Decimal | None order: int - heading: Optional[bool] + heading: bool | None class Config: anystr_strip_whitespace = True @@ -31,11 +30,11 @@ class RawMaterialCostItem(BaseModel): class RawMaterialCost(BaseModel): - id_: Optional[uuid.UUID] + id_: uuid.UUID | None start_date: date finish_date: date - body: List[RawMaterialCostItem] - footer: Optional[RawMaterialCostItem] + body: list[RawMaterialCostItem] + footer: RawMaterialCostItem | None class Config: anystr_strip_whitespace = True @@ -43,13 +42,13 @@ class RawMaterialCost(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} @validator("start_date", pre=True) - def parse_start_date(cls, value: Union[date, str]) -> date: + def parse_start_date(cls, value: date | str) -> date: 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: Union[date, str]) -> date: + def parse_finish_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/recipe.py b/brewman/brewman/schemas/recipe.py index 1bce260f..8e035f50 100644 --- a/brewman/brewman/schemas/recipe.py +++ b/brewman/brewman/schemas/recipe.py @@ -1,35 +1,43 @@ import uuid +from datetime import date, datetime from decimal import Decimal -from typing import List, Optional -from pydantic import BaseModel +from pydantic import BaseModel, validator from . import to_camel -from .period import PeriodLink from .product import ProductLink from .recipe_item import RecipeItem class RecipeIn(BaseModel): sku: ProductLink - + date_: date + source: str + instructions: str + garnishing: str + plating: str recipe_yield: Decimal - cost_price: Decimal - sale_price: Decimal notes: str - period: PeriodLink - - items: List[RecipeItem] + items: list[RecipeItem] class Config: anystr_strip_whitespace = True alias_generator = to_camel + json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} + + @validator("date_", pre=True) + def parse_start_date(cls, value: date | str) -> date: + if isinstance(value, date): + return value + return datetime.strptime(value, "%d-%b-%Y").date() class Recipe(RecipeIn): id_: uuid.UUID + product_group_id: uuid.UUID | None + units: str | None class Config: anystr_strip_whitespace = True @@ -37,7 +45,7 @@ class Recipe(RecipeIn): class RecipeBlank(RecipeIn): - sku: Optional[ProductLink] # type: ignore[assignment] + sku: ProductLink | None # type: ignore[assignment] class Config: anystr_strip_whitespace = True diff --git a/brewman/brewman/schemas/recipe_item.py b/brewman/brewman/schemas/recipe_item.py index 66678217..dad716e3 100644 --- a/brewman/brewman/schemas/recipe_item.py +++ b/brewman/brewman/schemas/recipe_item.py @@ -1,17 +1,16 @@ import uuid from decimal import Decimal -from typing import Optional from brewman.schemas.product import ProductLink from pydantic import BaseModel class RecipeItem(BaseModel): - id_: Optional[uuid.UUID] + id_: uuid.UUID | None product: ProductLink quantity: Decimal - price: Decimal + description: str class Config: fields = {"id_": "id"} diff --git a/brewman/brewman/schemas/recipe_template.py b/brewman/brewman/schemas/recipe_template.py new file mode 100644 index 00000000..404a505b --- /dev/null +++ b/brewman/brewman/schemas/recipe_template.py @@ -0,0 +1,37 @@ +import uuid + +from datetime import date, datetime + +from pydantic import validator +from pydantic.main import BaseModel + +from . import to_camel + + +class RecipeTemplateIn(BaseModel): + name: str + date_: date | None + text: str + selected: bool + + @validator("date_", pre=True) + def parse_valid_from(cls, value: date | str | None) -> date: + if value is None: + return None + 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") if v is not None else None} + + +class RecipeTemplate(RecipeTemplateIn): + id_: uuid.UUID + + class Config: + anystr_strip_whitespace = True + alias_generator = to_camel + json_encoders = {date: lambda v: v.strftime("%d-%b-%Y") if v is not None else None} diff --git a/brewman/brewman/schemas/reconcile.py b/brewman/brewman/schemas/reconcile.py index 942bad55..5db7e65e 100644 --- a/brewman/brewman/schemas/reconcile.py +++ b/brewman/brewman/schemas/reconcile.py @@ -2,7 +2,6 @@ import uuid from datetime import date, datetime from decimal import Decimal -from typing import List, Optional, Union from pydantic import validator from pydantic.main import BaseModel @@ -12,26 +11,26 @@ from .account import AccountLink class ReconcileItem(BaseModel): - id_: Optional[uuid.UUID] + id_: uuid.UUID | None date_: date name: str - url: List[str] + url: list[str] type_: str narration: str debit: Decimal credit: Decimal posted: bool is_reconciled: bool - reconcile_date: Optional[date] + reconcile_date: date | None @validator("date_", pre=True) - def parse_date(cls, value: Union[date, str]) -> date: + def parse_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() @validator("reconcile_date", pre=True) - def parse_reconcile_date(cls, value: Union[None, date, str]) -> Optional[date]: + def parse_reconcile_date(cls, value: None | date | str) -> date | None: if value is None: return None if isinstance(value, date): @@ -47,8 +46,8 @@ class ReconcileItem(BaseModel): class Reconcile(BaseModel): start_date: date finish_date: date - account: Optional[AccountLink] - body: List[ReconcileItem] + account: AccountLink | None + body: list[ReconcileItem] class Config: anystr_strip_whitespace = True @@ -56,13 +55,13 @@ class Reconcile(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} @validator("start_date", pre=True) - def parse_start_date(cls, value: Union[date, str]) -> date: + def parse_start_date(cls, value: date | str) -> date: 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: Union[date, str]) -> date: + def parse_finish_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/role.py b/brewman/brewman/schemas/role.py index 93c90cf4..73ecf6de 100644 --- a/brewman/brewman/schemas/role.py +++ b/brewman/brewman/schemas/role.py @@ -1,7 +1,5 @@ import uuid -from typing import List - from pydantic import Field from pydantic.main import BaseModel @@ -10,7 +8,7 @@ from .permission import PermissionItem class RoleIn(BaseModel): name: str = Field(..., min_length=1) - permissions: List[PermissionItem] + permissions: list[PermissionItem] class Config: anystr_strip_whitespace = True @@ -27,7 +25,7 @@ class Role(RoleIn): class RoleList(BaseModel): id_: uuid.UUID name: str - permissions: List[str] + permissions: list[str] class Config: fields = {"id_": "id"} diff --git a/brewman/brewman/schemas/settings.py b/brewman/brewman/schemas/settings.py index 7ce4eda7..16b5aaab 100644 --- a/brewman/brewman/schemas/settings.py +++ b/brewman/brewman/schemas/settings.py @@ -1,7 +1,6 @@ import uuid from datetime import date, datetime -from typing import List, Optional, Union from pydantic import BaseModel, validator @@ -9,8 +8,8 @@ from . import to_camel class LockDate(BaseModel): - days: Optional[int] - date_: Optional[date] + days: int | None + date_: date | None class Config: fields = {"date_": "date"} @@ -19,7 +18,7 @@ class LockDate(BaseModel): } @validator("date_", pre=True) - def parse_date(cls, value: Union[None, date, str]) -> Optional[date]: + def parse_date(cls, value: None | date | str) -> date | None: if value is None: return None if isinstance(value, date): @@ -44,13 +43,13 @@ class AccountTypesSelected(BaseModel): class LockInformation(BaseModel): - id_: Optional[uuid.UUID] + id_: uuid.UUID | None - valid_from: Optional[date] - valid_till: Optional[date] + valid_from: date | None + valid_till: date | None - voucher_types: List[VoucherTypesSelected] - account_types: List[AccountTypesSelected] + voucher_types: list[VoucherTypesSelected] + account_types: list[AccountTypesSelected] start: LockDate finish: LockDate @@ -64,7 +63,7 @@ class LockInformation(BaseModel): } @validator("valid_from", pre=True) - def parse_valid_from(cls, value: Union[None, date, str]) -> Optional[date]: + def parse_valid_from(cls, value: None | date | str) -> date | None: if value is None: return None if isinstance(value, date): @@ -72,7 +71,7 @@ class LockInformation(BaseModel): return datetime.strptime(value, "%d-%b-%Y").date() @validator("valid_till", pre=True) - def parse_valid_till(cls, value: Union[None, date, str]) -> Optional[date]: + def parse_valid_till(cls, value: None | date | str) -> date | None: if value is None: return None if isinstance(value, date): diff --git a/brewman/brewman/schemas/stock_keeping_unit.py b/brewman/brewman/schemas/stock_keeping_unit.py index 9cdd2079..cbf43353 100644 --- a/brewman/brewman/schemas/stock_keeping_unit.py +++ b/brewman/brewman/schemas/stock_keeping_unit.py @@ -1,7 +1,6 @@ import uuid from decimal import Decimal -from typing import Optional from pydantic import BaseModel, Field @@ -9,7 +8,7 @@ from . import to_camel class StockKeepingUnit(BaseModel): - id_: Optional[uuid.UUID] + id_: uuid.UUID | None units: str = Field(..., min_length=1) fraction: Decimal = Field(ge=1, default=1) product_yield: Decimal = Field(gt=0, le=1, default=1) diff --git a/brewman/brewman/schemas/stock_movement.py b/brewman/brewman/schemas/stock_movement.py index c0eb1ccc..bfef314f 100644 --- a/brewman/brewman/schemas/stock_movement.py +++ b/brewman/brewman/schemas/stock_movement.py @@ -2,7 +2,6 @@ import uuid from datetime import date, datetime from decimal import Decimal -from typing import List, Union from pydantic import validator from pydantic.main import BaseModel @@ -18,7 +17,7 @@ class StockMovementItem(BaseModel): purchase: Decimal issue: Decimal closing: Decimal - url: List[str] + url: list[str] class Config: anystr_strip_whitespace = True @@ -28,7 +27,7 @@ class StockMovementItem(BaseModel): class StockMovement(BaseModel): start_date: date finish_date: date - body: List[StockMovementItem] + body: list[StockMovementItem] class Config: anystr_strip_whitespace = True @@ -36,13 +35,13 @@ class StockMovement(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} @validator("start_date", pre=True) - def parse_start_date(cls, value: Union[date, str]) -> date: + def parse_start_date(cls, value: date | str) -> date: 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: Union[date, str]) -> date: + def parse_finish_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/trial_balance.py b/brewman/brewman/schemas/trial_balance.py index 7d7838b0..e25da93a 100644 --- a/brewman/brewman/schemas/trial_balance.py +++ b/brewman/brewman/schemas/trial_balance.py @@ -1,6 +1,5 @@ from datetime import date, datetime from decimal import Decimal -from typing import List, Optional, Union from pydantic import validator from pydantic.main import BaseModel @@ -9,10 +8,10 @@ from . import to_camel class TrialBalanceItem(BaseModel): - type_: Optional[str] - name: Optional[str] - debit: Optional[Decimal] - credit: Optional[Decimal] + type_: str | None + name: str | None + debit: Decimal | None + credit: Decimal | None class Config: anystr_strip_whitespace = True @@ -21,7 +20,7 @@ class TrialBalanceItem(BaseModel): class TrialBalance(BaseModel): date_: date - body: List[TrialBalanceItem] + body: list[TrialBalanceItem] class Config: anystr_strip_whitespace = True @@ -29,7 +28,7 @@ class TrialBalance(BaseModel): json_encoders = {date: lambda v: v.strftime("%d-%b-%Y")} @validator("date_", pre=True) - def parse_date(cls, value: Union[date, str]) -> date: + def parse_date(cls, value: date | str) -> date: if isinstance(value, date): return value return datetime.strptime(value, "%d-%b-%Y").date() diff --git a/brewman/brewman/schemas/user.py b/brewman/brewman/schemas/user.py index 6eaa179c..3955c89a 100644 --- a/brewman/brewman/schemas/user.py +++ b/brewman/brewman/schemas/user.py @@ -1,7 +1,6 @@ import uuid from datetime import datetime -from typing import List, Optional from pydantic import Field from pydantic.main import BaseModel @@ -14,7 +13,7 @@ class UserIn(BaseModel): name: str = Field(..., min_length=1) password: str locked_out: bool - roles: List[RoleItem] + roles: list[RoleItem] class Config: anystr_strip_whitespace = True @@ -32,9 +31,9 @@ class User(UserIn): class UserList(BaseModel): id_: uuid.UUID name: str - roles: List[str] + roles: list[str] last_device: str - last_date: Optional[datetime] + last_date: datetime | None class Config: anystr_strip_whitespace = True @@ -47,7 +46,7 @@ class UserToken(BaseModel): name: str locked_out: bool password: str - permissions: List[str] + permissions: list[str] class UserBlank(UserIn): diff --git a/brewman/brewman/schemas/voucher.py b/brewman/brewman/schemas/voucher.py index e72040ff..628d3ca2 100644 --- a/brewman/brewman/schemas/voucher.py +++ b/brewman/brewman/schemas/voucher.py @@ -3,7 +3,6 @@ import uuid from datetime import date, datetime from decimal import Decimal -from typing import List, Optional, Union from fastapi import Form from pydantic import BaseModel, validator @@ -27,7 +26,7 @@ class VoucherIn(BaseModel): narration: str is_starred: bool type_: str - files: List[ImageUpload] + files: list[ImageUpload] @classmethod def load_form(cls, data: str = Form(...)) -> BaseModel: @@ -36,23 +35,23 @@ class VoucherIn(BaseModel): 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: Optional[str] - journals: List[Journal] - inventories: List[Inventory] - vendor: Optional[AccountLink] - source: Optional[CostCentreLink] - destination: Optional[CostCentreLink] - incentives: List[Incentive] - incentive: Optional[Decimal] - employee_benefits: List[EmployeeBenefit] - files: List[ImageUpload] + id_: uuid.UUID | None + is_reconciled: bool | None + reconcile_date: date | None + creation_date: datetime | None + last_edit_date: datetime | None + user: UserLink | None + posted: bool | None + poster: str | None + journals: list[Journal] + inventories: list[Inventory] + vendor: AccountLink | None + source: CostCentreLink | None + destination: CostCentreLink | None + incentives: list[Incentive] + incentive: Decimal | None + employee_benefits: list[EmployeeBenefit] + files: list[ImageUpload] class Config: anystr_strip_whitespace = True @@ -63,13 +62,13 @@ class Voucher(VoucherIn): } @validator("date_", pre=True) - def parse_date(cls, value: Union[date, str]) -> date: + def parse_date(cls, value: date | str) -> date: 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: Union[None, datetime, str]) -> Optional[datetime]: + def parse_creation_date(cls, value: None | datetime | str) -> datetime | None: if value is None or value == "": return None if isinstance(value, datetime): @@ -77,7 +76,7 @@ class Voucher(VoucherIn): return datetime.strptime(value, "%d-%b-%Y %H:%M") @validator("last_edit_date", pre=True) - def parse_last_edit_date(cls, value: Union[None, datetime, str]) -> Optional[datetime]: + def parse_last_edit_date(cls, value: None | datetime | str) -> datetime | None: if value is None or value == "": return None if isinstance(value, datetime): @@ -85,7 +84,7 @@ class Voucher(VoucherIn): return datetime.strptime(value, "%d-%b-%Y %H:%M") @validator("reconcile_date", pre=True) - def parse_reconcile_date(cls, value: Union[None, date, str]) -> Optional[date]: + def parse_reconcile_date(cls, value: None | date | str) -> date | None: if value is None or value == "": return None if isinstance(value, date): diff --git a/brewman/pyproject.toml b/brewman/pyproject.toml index 313fc95f..751adf13 100644 --- a/brewman/pyproject.toml +++ b/brewman/pyproject.toml @@ -5,27 +5,28 @@ description = "Accounting plus inventory management for a restaurant." authors = ["tanshu "] [tool.poetry.dependencies] -python = "^3.10" -uvicorn = {extras = ["standard"], version = "^0.20.0"} -fastapi = "^0.88.0" +python = "^3.11" +uvicorn = {extras = ["standard"], version = "^0.21.1"} +fastapi = "^0.95.0" python-jose = {extras = ["cryptography"], version = "^3.3.0"} passlib = {extras = ["bcrypt"], version = "^1.7.4"} psycopg2-binary = "^2.9.5" -SQLAlchemy = {extras = ["mypy"], version = "^1.4.45"} -python-multipart = "^0.0.5" +SQLAlchemy = "^2.0.7" +python-multipart = "^0.0.6" PyJWT = "^2.6.0" -alembic = "^1.8.1" +alembic = "^1.10.2" itsdangerous = "^2.1.2" -python-dotenv = "^0.21.0" -pydantic = {extras = ["dotenv"], version = "^1.10.2"} -starlette = "^0.22.0" +python-dotenv = "^1.0.0" +pydantic = {extras = ["dotenv"], version = "^1.10.7"} +starlette = "^0.26.1" +openpyxl = "^3.1.2" [tool.poetry.dev-dependencies] -flake8 = "^5.0.4" -black = "^22.10.0" -isort = {extras = ["toml"], version = "^5.10.1"} -pre-commit = "^2.20.0" -mypy = "^0.991" +flake8 = "^6.0.0" +black = "^23.1.0" +isort = {extras = ["toml"], version = "^5.12.0"} +pre-commit = "^3.2.0" +mypy = "^1.1.1" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/lint.sh b/lint.sh index 1bbf89e3..1a7513dd 100755 --- a/lint.sh +++ b/lint.sh @@ -8,6 +8,6 @@ cd "$parent_path/overlord" || exit npx prettier --write src/app npx ng lint --fix cd "$parent_path/brewman" || exit -poetry run isort brewman -poetry run black brewman -poetry run flake8 brewman +isort brewman +black brewman +flake8 brewman diff --git a/overlord/.eslintrc.json b/overlord/.eslintrc.json index 1685e316..80c6b5af 100644 --- a/overlord/.eslintrc.json +++ b/overlord/.eslintrc.json @@ -8,16 +8,13 @@ "files": [ "*.ts" ], - "parserOptions": { - "project": [ - "tsconfig.json" - ], - "createDefaultProgram": true - }, "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", "plugin:@angular-eslint/recommended", "plugin:@angular-eslint/template/process-inline-templates", - "plugin:@angular-eslint/recommended--extra" + "plugin:@angular-eslint/recommended--extra", + "prettier" ], "plugins": [ "import", @@ -60,7 +57,8 @@ "@typescript-eslint/no-explicit-any": [ "error" ], - "unused-imports/no-unused-imports": "error" + "unused-imports/no-unused-imports": "error", + "@typescript-eslint/no-empty-function": "off" } }, { diff --git a/overlord/angular.json b/overlord/angular.json index 0d85ee28..907ced64 100644 --- a/overlord/angular.json +++ b/overlord/angular.json @@ -20,14 +20,16 @@ "outputPath": "../frontend", "index": "src/index.html", "main": "src/main.ts", - "polyfills": "src/polyfills.ts", + "polyfills": [ + "zone.js" + ], "tsConfig": "tsconfig.app.json", "assets": [ "src/favicon.ico", "src/assets" ], "styles": [ - "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", + "@angular/material/prebuilt-themes/indigo-pink.css", "src/styles.css" ], "scripts": [] @@ -90,15 +92,17 @@ "builder": "@angular-devkit/build-angular:karma", "options": { "main": "src/test.ts", - "polyfills": "src/polyfills.ts", + "polyfills": [ + "zone.js", + "zone.js/testing" + ], "tsConfig": "tsconfig.spec.json", - "karmaConfig": "karma.conf.js", "assets": [ "src/favicon.ico", "src/assets" ], "styles": [ - "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", + "@angular/material/prebuilt-themes/indigo-pink.css", "src/styles.css" ], "scripts": [] diff --git a/overlord/karma.conf.js b/overlord/karma.conf.js deleted file mode 100644 index 1d4956c2..00000000 --- a/overlord/karma.conf.js +++ /dev/null @@ -1,44 +0,0 @@ -// Karma configuration file, see link for more information -// https://karma-runner.github.io/1.0/config/configuration-file.html - -module.exports = function (config) { - config.set({ - basePath: '', - frameworks: ['jasmine', '@angular-devkit/build-angular'], - plugins: [ - require('karma-jasmine'), - require('karma-chrome-launcher'), - require('karma-jasmine-html-reporter'), - require('karma-coverage'), - require('@angular-devkit/build-angular/plugins/karma') - ], - client: { - jasmine: { - // you can add configuration options for Jasmine here - // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html - // for example, you can disable the random execution with `random: false` - // or set a specific seed with `seed: 4321` - }, - clearContext: false // leave Jasmine Spec Runner output visible in browser - }, - jasmineHtmlReporter: { - suppressAll: true // removes the duplicated traces - }, - coverageReporter: { - dir: require('path').join(__dirname, './coverage/ele'), - subdir: '.', - reporters: [ - { type: 'html' }, - { type: 'text-summary' } - ] - }, - reporters: ['progress', 'kjhtml'], - port: 9876, - colors: true, - logLevel: config.LOG_INFO, - autoWatch: true, - browsers: ['Chrome'], - singleRun: false, - restartOnFileChange: true - }); -}; diff --git a/overlord/package.json b/overlord/package.json index ebedfd90..30972a29 100644 --- a/overlord/package.json +++ b/overlord/package.json @@ -7,52 +7,52 @@ "build": "ng build", "test": "ng test", "lint": "ng lint", - "e2e": "ng e2e", + "watch": "ng build --watch --configuration development", "eslint": "eslint", "prettier": "prettier", "husky": "husky" }, "private": true, "dependencies": { - "@angular/animations": "^15.0.3", - "@angular/cdk": "^15.0.2", - "@angular/common": "^15.0.3", - "@angular/compiler": "^15.0.3", - "@angular/core": "^15.0.3", - "@angular/forms": "^15.0.3", - "@angular/material": "^15.0.2", - "@angular/material-moment-adapter": "^15.0.2", - "@angular/platform-browser": "^15.0.3", - "@angular/platform-browser-dynamic": "^15.0.3", - "@angular/router": "^15.0.3", + "@angular/animations": "^15.2.6", + "@angular/cdk": "^15.2.6", + "@angular/common": "^15.2.6", + "@angular/compiler": "^15.2.6", + "@angular/core": "^15.2.6", + "@angular/forms": "^15.2.6", + "@angular/material": "^15.2.6", + "@angular/material-moment-adapter": "^15.2.6", + "@angular/platform-browser": "^15.2.6", + "@angular/platform-browser-dynamic": "^15.2.6", + "@angular/router": "^15.2.6", "@ngx-loading-bar/core": "^6.0.2", "@ngx-loading-bar/http-client": "^6.0.2", "@ngx-loading-bar/router": "^6.0.2", "@types/mousetrap": "1.6.11", - "angular2-hotkeys": "^13.1.0", - "mathjs": "^11.5.0", + "angular2-hotkeys": "^13.2.0", + "mathjs": "^11.8.0", "moment": "^2.29.4", - "rxjs": "^6.6.7", - "tslib": "^2.1.0", - "zone.js": "~0.11.4" + "rxjs": "~7.8.0", + "tslib": "^2.5.0", + "zone.js": "~0.12.0" }, "devDependencies": { - "@angular-devkit/build-angular": "^15.0.3", - "@angular-eslint/builder": "^15.1.0", - "@angular-eslint/eslint-plugin": "^15.1.0", - "@angular-eslint/eslint-plugin-template": "^15.1.0", - "@angular-eslint/schematics": "^15.1.0", - "@angular-eslint/template-parser": "^15.1.0", - "@angular/cli": "^15.0.3", - "@angular/compiler-cli": "^15.0.3", - "@angular/language-service": "^15.0.3", + "@angular-devkit/build-angular": "^15.2.5", + "@angular-eslint/builder": "^15.2.1", + "@angular-eslint/eslint-plugin": "^15.2.1", + "@angular-eslint/eslint-plugin-template": "^15.2.1", + "@angular-eslint/schematics": "^15.2.1", + "@angular-eslint/template-parser": "^15.2.1", + "@angular/cli": "^15.2.5", + "@angular/compiler-cli": "^15.2.6", + "@angular/language-service": "^15.2.6", "@types/jasmine": "~4.3.0", - "@types/node": "^18.11.13", - "@typescript-eslint/eslint-plugin": "^5.46.0", - "@typescript-eslint/parser": "^5.46.0", - "autoprefixer": "^10.4.13", - "eslint": "^8.28.0", - "eslint-plugin-import": "2.26.0", + "@typescript-eslint/eslint-plugin": "^5.57.1", + "@typescript-eslint/parser": "^5.57.1", + "autoprefixer": "^10.4.14", + "eslint": "^8.37.0", + "eslint-config-prettier": "^8.7.0", + "eslint-plugin-import": "^2.27.5", "eslint-plugin-unused-imports": "^2.0.0", "husky": "^8.0.2", "jasmine-core": "~4.5.0", @@ -63,12 +63,12 @@ "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "^2.0.0", "lint-staged": "^13.1.0", - "postcss": "^8.4.19", - "prettier": "^2.8.1", + "postcss": "^8.4.21", + "prettier": "^2.8.7", "standard-version": "^9.5.0", - "tailwindcss": "^3.2.4", + "tailwindcss": "^3.3.1", "ts-node": "^9.1.1", - "typescript": "~4.8.4" + "typescript": "~4.9.4" }, "husky": { "hooks": { diff --git a/overlord/src/app/account/account-list-resolver.service.ts b/overlord/src/app/account/account-list-resolver.service.ts index ae94ef33..fb09a6f1 100644 --- a/overlord/src/app/account/account-list-resolver.service.ts +++ b/overlord/src/app/account/account-list-resolver.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { Resolve } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Account } from '../core/account'; @@ -8,7 +7,7 @@ import { AccountService } from '../core/account.service'; @Injectable({ providedIn: 'root', }) -export class AccountListResolver implements Resolve { +export class AccountListResolver { constructor(private ser: AccountService) {} resolve(): Observable { diff --git a/overlord/src/app/account/account-list/account-list-datasource.ts b/overlord/src/app/account/account-list/account-list-datasource.ts index 99bc6149..1da0b3ce 100644 --- a/overlord/src/app/account/account-list/account-list-datasource.ts +++ b/overlord/src/app/account/account-list/account-list-datasource.ts @@ -28,12 +28,7 @@ export class AccountListDataSource extends DataSource { } connect(): Observable { - const dataMutations: ( - | Observable - | Observable - | EventEmitter - | EventEmitter - )[] = [observableOf(this.data), this.filter]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -41,7 +36,7 @@ export class AccountListDataSource extends DataSource { dataMutations.push((this.sort as MatSort).sortChange); } - return merge(...dataMutations) + return merge(observableOf(this.data), this.filter, ...dataMutations) .pipe( map(() => this.getFilteredData([...this.data])), tap((x: Account[]) => { diff --git a/overlord/src/app/account/account-resolver.service.ts b/overlord/src/app/account/account-resolver.service.ts index 086b20c3..35fe496a 100644 --- a/overlord/src/app/account/account-resolver.service.ts +++ b/overlord/src/app/account/account-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Account } from '../core/account'; @@ -8,7 +8,7 @@ import { AccountService } from '../core/account.service'; @Injectable({ providedIn: 'root', }) -export class AccountResolver implements Resolve { +export class AccountResolver { constructor(private ser: AccountService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/account/account-routing.module.ts b/overlord/src/app/account/account-routing.module.ts index c6718f0c..bc36711f 100644 --- a/overlord/src/app/account/account-routing.module.ts +++ b/overlord/src/app/account/account-routing.module.ts @@ -1,13 +1,13 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; import { CostCentreListResolver } from '../cost-centre/cost-centre-list-resolver.service'; import { AccountDetailComponent } from './account-detail/account-detail.component'; -import { AccountListResolver } from './account-list-resolver.service'; import { AccountListComponent } from './account-list/account-list.component'; +import { AccountListResolver } from './account-list-resolver.service'; import { AccountResolver } from './account-resolver.service'; import { AccountTypeResolver } from './account-type-resolver.service'; @@ -15,39 +15,48 @@ const accountRoutes: Routes = [ { path: '', component: AccountListComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Accounts', }, resolve: { - list: AccountListResolver, - accountTypes: AccountTypeResolver, + list: () => inject(AccountListResolver).resolve(), + accountTypes: () => inject(AccountTypeResolver).resolve(), }, }, { path: 'new', component: AccountDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Accounts', }, resolve: { - item: AccountResolver, - accountTypes: AccountTypeResolver, - costCentres: CostCentreListResolver, + item: (route: ActivatedRouteSnapshot) => inject(AccountResolver).resolve(route), + accountTypes: () => inject(AccountTypeResolver).resolve(), + costCentres: () => inject(CostCentreListResolver).resolve(), }, }, { path: ':id', component: AccountDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Accounts', }, resolve: { - item: AccountResolver, - accountTypes: AccountTypeResolver, - costCentres: CostCentreListResolver, + item: (route: ActivatedRouteSnapshot) => inject(AccountResolver).resolve(route), + accountTypes: () => inject(AccountTypeResolver).resolve(), + costCentres: () => inject(CostCentreListResolver).resolve(), }, }, ]; diff --git a/overlord/src/app/account/account-type-resolver.service.ts b/overlord/src/app/account/account-type-resolver.service.ts index 8e5ec27a..fdbd4a62 100644 --- a/overlord/src/app/account/account-type-resolver.service.ts +++ b/overlord/src/app/account/account-type-resolver.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { Resolve } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { AccountType } from '../core/account-type'; @@ -9,7 +8,7 @@ import { AccountTypeService } from './account-type.service'; @Injectable({ providedIn: 'root', }) -export class AccountTypeResolver implements Resolve { +export class AccountTypeResolver { constructor(private ser: AccountTypeService) {} resolve(): Observable { diff --git a/overlord/src/app/app-routing.module.ts b/overlord/src/app/app-routing.module.ts index b7974021..ba05bbab 100644 --- a/overlord/src/app/app-routing.module.ts +++ b/overlord/src/app/app-routing.module.ts @@ -84,6 +84,11 @@ const appRoutes: Routes = [ path: 'recipes', loadChildren: () => import('./recipe/recipe.module').then((mod) => mod.RecipeModule), }, + { + path: 'recipe-templates', + loadChildren: () => + import('./recipe-template/recipe-template.module').then((mod) => mod.RecipeTemplateModule), + }, { path: 'roles', loadChildren: () => import('./role/role.module').then((mod) => mod.RoleModule), diff --git a/overlord/src/app/attendance/attendance-resolver.service.ts b/overlord/src/app/attendance/attendance-resolver.service.ts index c7490923..616c0b7d 100644 --- a/overlord/src/app/attendance/attendance-resolver.service.ts +++ b/overlord/src/app/attendance/attendance-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Attendance } from './attendance'; @@ -8,7 +8,7 @@ import { AttendanceService } from './attendance.service'; @Injectable({ providedIn: 'root', }) -export class AttendanceResolver implements Resolve { +export class AttendanceResolver { constructor(private ser: AttendanceService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/attendance/attendance-routing.module.ts b/overlord/src/app/attendance/attendance-routing.module.ts index d72ff202..94f58be9 100644 --- a/overlord/src/app/attendance/attendance-routing.module.ts +++ b/overlord/src/app/attendance/attendance-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -12,26 +12,32 @@ const attendanceRoutes: Routes = [ { path: '', component: AttendanceComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Attendance', }, resolve: { - info: AttendanceResolver, - attendanceTypes: AttendanceTypeResolver, + info: (route: ActivatedRouteSnapshot) => inject(AttendanceResolver).resolve(route), + attendanceTypes: () => inject(AttendanceTypeResolver).resolve(), }, runGuardsAndResolvers: 'always', }, { path: ':date', component: AttendanceComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Attendance', }, resolve: { - info: AttendanceResolver, - attendanceTypes: AttendanceTypeResolver, + info: (route: ActivatedRouteSnapshot) => inject(AttendanceResolver).resolve(route), + attendanceTypes: () => inject(AttendanceTypeResolver).resolve(), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/attendance/attendance-type-resolver.service.ts b/overlord/src/app/attendance/attendance-type-resolver.service.ts index 1acadce2..68a7f8b8 100644 --- a/overlord/src/app/attendance/attendance-type-resolver.service.ts +++ b/overlord/src/app/attendance/attendance-type-resolver.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { Resolve } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { AttendanceType } from './attendance-type'; @@ -8,7 +7,7 @@ import { AttendanceTypeService } from './attendance-type.service'; @Injectable({ providedIn: 'root', }) -export class AttendanceTypeResolver implements Resolve { +export class AttendanceTypeResolver { constructor(private ser: AttendanceTypeService) {} resolve(): Observable { diff --git a/overlord/src/app/attendance/attendance.module.ts b/overlord/src/app/attendance/attendance.module.ts index 7b9d4602..41c91b65 100644 --- a/overlord/src/app/attendance/attendance.module.ts +++ b/overlord/src/app/attendance/attendance.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -25,6 +24,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSelectModule } from '@angular/material/select'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/auth/auth-guard.service.ts b/overlord/src/app/auth/auth-guard.service.ts index 7df8c607..365713ff 100644 --- a/overlord/src/app/auth/auth-guard.service.ts +++ b/overlord/src/app/auth/auth-guard.service.ts @@ -1,12 +1,12 @@ import { Injectable } from '@angular/core'; -import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; +import { Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { ToasterService } from '../core/toaster.service'; import { AuthService } from './auth.service'; @Injectable({ providedIn: 'root' }) -export class AuthGuard implements CanActivate { +export class AuthGuard { constructor( private router: Router, private authService: AuthService, @@ -16,9 +16,9 @@ export class AuthGuard implements CanActivate { canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { const { user } = this.authService; const permission = - route.data.permission === undefined - ? route.data.permission - : route.data.permission.replace(/ /g, '-').toLowerCase(); + route.data['permission'] === undefined + ? route.data['permission'] + : route.data['permission'].replace(/ /g, '-').toLowerCase(); if (!user) { // not logged in so redirect to login page with the return url this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } }); diff --git a/overlord/src/app/auth/auth.service.ts b/overlord/src/app/auth/auth.service.ts index 30e5b482..d495ef91 100644 --- a/overlord/src/app/auth/auth.service.ts +++ b/overlord/src/app/auth/auth.service.ts @@ -76,11 +76,11 @@ export class AuthService { return this.currentUserSubject.value; } - login(username: string, password: string, otp: string) { + login(username: string, password: string, otp: number) { const formData: FormData = new FormData(); formData.append('username', username); formData.append('password', password); - formData.append('otp', otp); + formData.append('otp', '' + otp); formData.append('grant_type', 'password'); return this.http .post<{ access_token: string; token_type: string }>(loginUrl, formData) diff --git a/overlord/src/app/auth/login/login.component.css b/overlord/src/app/auth/login/login.component.css index cfd4505c..e69de29b 100644 --- a/overlord/src/app/auth/login/login.component.css +++ b/overlord/src/app/auth/login/login.component.css @@ -1,8 +0,0 @@ -.example-container { - display: flex; - flex-direction: column; -} - -.example-container > * { - width: 100%; -} diff --git a/overlord/src/app/auth/login/login.component.ts b/overlord/src/app/auth/login/login.component.ts index 5b288ab5..1c38aac8 100644 --- a/overlord/src/app/auth/login/login.component.ts +++ b/overlord/src/app/auth/login/login.component.ts @@ -13,7 +13,12 @@ import { AuthService } from '../auth.service'; }) export class LoginComponent implements OnInit, AfterViewInit { @ViewChild('nameElement', { static: true }) nameElement?: ElementRef; - form: FormGroup; + form: FormGroup<{ + username: FormControl; + password: FormControl; + otp: FormControl; + }>; + hide: boolean; showOtp: boolean; clientId = ''; @@ -36,7 +41,7 @@ export class LoginComponent implements OnInit, AfterViewInit { } ngOnInit() { - this.returnUrl = this.route.snapshot.queryParams.returnUrl || '/'; + this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/'; } ngAfterViewInit() { @@ -49,9 +54,9 @@ export class LoginComponent implements OnInit, AfterViewInit { login(): void { const formModel = this.form.value; - const { username } = formModel; - const { password } = formModel; - const { otp } = formModel; + const username = formModel.username ?? ''; + const password = formModel.password ?? ''; + const otp = formModel.otp ?? 0; this.auth .login(username, password, otp) // .pipe(first()) diff --git a/overlord/src/app/balance-sheet/balance-sheet-datasource.ts b/overlord/src/app/balance-sheet/balance-sheet-datasource.ts index 190c3fd0..20209714 100644 --- a/overlord/src/app/balance-sheet/balance-sheet-datasource.ts +++ b/overlord/src/app/balance-sheet/balance-sheet-datasource.ts @@ -20,11 +20,7 @@ export class BalanceSheetDataSource extends DataSource { } connect(): Observable { - const dataMutations: ( - | Observable - | EventEmitter - | EventEmitter - )[] = [observableOf(this.data)]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -37,7 +33,7 @@ export class BalanceSheetDataSource extends DataSource { this.paginator.length = this.data.length; } - return merge(...dataMutations).pipe( + return merge(observableOf(this.data), ...dataMutations).pipe( map(() => this.getPagedData(this.getSortedData([...this.data]))), ); } diff --git a/overlord/src/app/balance-sheet/balance-sheet-resolver.service.ts b/overlord/src/app/balance-sheet/balance-sheet-resolver.service.ts index c3b6facb..374dfb12 100644 --- a/overlord/src/app/balance-sheet/balance-sheet-resolver.service.ts +++ b/overlord/src/app/balance-sheet/balance-sheet-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { BalanceSheet } from './balance-sheet'; @@ -8,7 +8,7 @@ import { BalanceSheetService } from './balance-sheet.service'; @Injectable({ providedIn: 'root', }) -export class BalanceSheetResolver implements Resolve { +export class BalanceSheetResolver { constructor(private ser: BalanceSheetService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/balance-sheet/balance-sheet-routing.module.ts b/overlord/src/app/balance-sheet/balance-sheet-routing.module.ts index 95d8d024..8c29140b 100644 --- a/overlord/src/app/balance-sheet/balance-sheet-routing.module.ts +++ b/overlord/src/app/balance-sheet/balance-sheet-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -11,24 +11,30 @@ const balanceSheetRoutes: Routes = [ { path: '', component: BalanceSheetComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Balance Sheet', }, resolve: { - info: BalanceSheetResolver, + info: (route: ActivatedRouteSnapshot) => inject(BalanceSheetResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, { path: ':date', component: BalanceSheetComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Balance Sheet', }, resolve: { - info: BalanceSheetResolver, + info: (route: ActivatedRouteSnapshot) => inject(BalanceSheetResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/balance-sheet/balance-sheet.module.ts b/overlord/src/app/balance-sheet/balance-sheet.module.ts index 9a418f62..aff406ad 100644 --- a/overlord/src/app/balance-sheet/balance-sheet.module.ts +++ b/overlord/src/app/balance-sheet/balance-sheet.module.ts @@ -2,7 +2,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -19,6 +18,7 @@ import { MatPaginatorModule } from '@angular/material/paginator'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/batch-integrity-report/batch-integrity-report-routing.module.ts b/overlord/src/app/batch-integrity-report/batch-integrity-report-routing.module.ts index 6942e82e..fa3a2af5 100644 --- a/overlord/src/app/batch-integrity-report/batch-integrity-report-routing.module.ts +++ b/overlord/src/app/batch-integrity-report/batch-integrity-report-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -11,12 +11,15 @@ const batchIntegrityRoutes: Routes = [ { path: '', component: BatchIntegrityReportComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Product Ledger', }, resolve: { - info: BatchIntegrityResolverService, + info: () => inject(BatchIntegrityResolverService).resolve(), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/batch-integrity-report/batch-integrity-report.module.ts b/overlord/src/app/batch-integrity-report/batch-integrity-report.module.ts index 495d1c6f..17df11b8 100644 --- a/overlord/src/app/batch-integrity-report/batch-integrity-report.module.ts +++ b/overlord/src/app/batch-integrity-report/batch-integrity-report.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -22,6 +21,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatRadioModule } from '@angular/material/radio'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/batch-integrity-report/batch-integrity-resolver.service.ts b/overlord/src/app/batch-integrity-report/batch-integrity-resolver.service.ts index 40a32a90..5134a186 100644 --- a/overlord/src/app/batch-integrity-report/batch-integrity-resolver.service.ts +++ b/overlord/src/app/batch-integrity-report/batch-integrity-resolver.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { BatchIntegrity } from './batch-integrity'; @@ -8,10 +7,10 @@ import { BatchIntegrityReportService } from './batch-integrity-report.service'; @Injectable({ providedIn: 'root', }) -export class BatchIntegrityResolverService implements Resolve { +export class BatchIntegrityResolverService { constructor(private ser: BatchIntegrityReportService) {} - resolve(route: ActivatedRouteSnapshot): Observable { + resolve(): Observable { return this.ser.batchIntegrity(); } } diff --git a/overlord/src/app/cash-flow/cash-flow-resolver.service.ts b/overlord/src/app/cash-flow/cash-flow-resolver.service.ts index a284f60c..cff86b9f 100644 --- a/overlord/src/app/cash-flow/cash-flow-resolver.service.ts +++ b/overlord/src/app/cash-flow/cash-flow-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { CashFlow } from './cash-flow'; @@ -8,7 +8,7 @@ import { CashFlowService } from './cash-flow.service'; @Injectable({ providedIn: 'root', }) -export class CashFlowResolver implements Resolve { +export class CashFlowResolver { constructor(private ser: CashFlowService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/cash-flow/cash-flow-routing.module.ts b/overlord/src/app/cash-flow/cash-flow-routing.module.ts index 865066b3..d3dd24b2 100644 --- a/overlord/src/app/cash-flow/cash-flow-routing.module.ts +++ b/overlord/src/app/cash-flow/cash-flow-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -11,24 +11,30 @@ const cashFlowRoutes: Routes = [ { path: '', component: CashFlowComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Cash Flow', }, resolve: { - info: CashFlowResolver, + info: (route: ActivatedRouteSnapshot) => inject(CashFlowResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, { path: ':id', component: CashFlowComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Cash Flow', }, resolve: { - info: CashFlowResolver, + info: (route: ActivatedRouteSnapshot) => inject(CashFlowResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/cash-flow/cash-flow.module.ts b/overlord/src/app/cash-flow/cash-flow.module.ts index c307e48d..c5049b09 100644 --- a/overlord/src/app/cash-flow/cash-flow.module.ts +++ b/overlord/src/app/cash-flow/cash-flow.module.ts @@ -2,7 +2,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -19,6 +18,7 @@ import { MatPaginatorModule } from '@angular/material/paginator'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/client/client-list-resolver.service.ts b/overlord/src/app/client/client-list-resolver.service.ts index 0685046c..dbe9771c 100644 --- a/overlord/src/app/client/client-list-resolver.service.ts +++ b/overlord/src/app/client/client-list-resolver.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { Resolve } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Client } from './client'; @@ -8,7 +7,7 @@ import { ClientService } from './client.service'; @Injectable({ providedIn: 'root', }) -export class ClientListResolver implements Resolve { +export class ClientListResolver { constructor(private ser: ClientService) {} resolve(): Observable { diff --git a/overlord/src/app/client/client-list/client-list-datasource.ts b/overlord/src/app/client/client-list/client-list-datasource.ts index ddf807e3..3ac5a5fa 100644 --- a/overlord/src/app/client/client-list/client-list-datasource.ts +++ b/overlord/src/app/client/client-list/client-list-datasource.ts @@ -16,9 +16,7 @@ export class ClientListDataSource extends DataSource { } connect(): Observable { - const dataMutations: (Observable | EventEmitter | EventEmitter)[] = [ - observableOf(this.data), - ]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -31,7 +29,7 @@ export class ClientListDataSource extends DataSource { this.paginator.length = this.data.length; } - return merge(...dataMutations).pipe( + return merge(observableOf(this.data), ...dataMutations).pipe( map(() => this.getPagedData(this.getSortedData([...this.data]))), ); } diff --git a/overlord/src/app/client/client-list/client-list.component.html b/overlord/src/app/client/client-list/client-list.component.html index 851d6702..dc4674a9 100644 --- a/overlord/src/app/client/client-list/client-list.component.html +++ b/overlord/src/app/client/client-list/client-list.component.html @@ -6,7 +6,7 @@ - Name + Code {{ row.code }} diff --git a/overlord/src/app/client/client-resolver.service.ts b/overlord/src/app/client/client-resolver.service.ts index 062729ab..1f264247 100644 --- a/overlord/src/app/client/client-resolver.service.ts +++ b/overlord/src/app/client/client-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Client } from './client'; @@ -8,7 +8,7 @@ import { ClientService } from './client.service'; @Injectable({ providedIn: 'root', }) -export class ClientResolver implements Resolve { +export class ClientResolver { constructor(private ser: ClientService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/client/client-routing.module.ts b/overlord/src/app/client/client-routing.module.ts index a48a36ff..b91fd00d 100644 --- a/overlord/src/app/client/client-routing.module.ts +++ b/overlord/src/app/client/client-routing.module.ts @@ -1,35 +1,41 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; import { ClientDetailComponent } from './client-detail/client-detail.component'; -import { ClientListResolver } from './client-list-resolver.service'; import { ClientListComponent } from './client-list/client-list.component'; +import { ClientListResolver } from './client-list-resolver.service'; import { ClientResolver } from './client-resolver.service'; const clientRoutes: Routes = [ { path: '', component: ClientListComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Cost Centres', }, resolve: { - list: ClientListResolver, + list: () => inject(ClientListResolver).resolve(), }, }, { path: ':id', component: ClientDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Cost Centres', }, resolve: { - item: ClientResolver, + item: (route: ActivatedRouteSnapshot) => inject(ClientResolver).resolve(route), }, }, ]; diff --git a/overlord/src/app/closing-stock/closing-stock-datasource.ts b/overlord/src/app/closing-stock/closing-stock-datasource.ts index d8bbc30b..aa81605f 100644 --- a/overlord/src/app/closing-stock/closing-stock-datasource.ts +++ b/overlord/src/app/closing-stock/closing-stock-datasource.ts @@ -20,11 +20,7 @@ export class ClosingStockDataSource extends DataSource { } connect(): Observable { - const dataMutations: ( - | Observable - | EventEmitter - | EventEmitter - )[] = [observableOf(this.data)]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -37,7 +33,7 @@ export class ClosingStockDataSource extends DataSource { this.paginator.length = this.data.length; } - return merge(...dataMutations).pipe( + return merge(observableOf(this.data), ...dataMutations).pipe( map(() => this.getPagedData(this.getSortedData([...this.data]))), ); } diff --git a/overlord/src/app/closing-stock/closing-stock-resolver.service.ts b/overlord/src/app/closing-stock/closing-stock-resolver.service.ts index f1afef9e..39db789f 100644 --- a/overlord/src/app/closing-stock/closing-stock-resolver.service.ts +++ b/overlord/src/app/closing-stock/closing-stock-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { ClosingStock } from './closing-stock'; @@ -8,7 +8,7 @@ import { ClosingStockService } from './closing-stock.service'; @Injectable({ providedIn: 'root', }) -export class ClosingStockResolver implements Resolve { +export class ClosingStockResolver { constructor(private ser: ClosingStockService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/closing-stock/closing-stock-routing.module.ts b/overlord/src/app/closing-stock/closing-stock-routing.module.ts index 38403d78..1659598b 100644 --- a/overlord/src/app/closing-stock/closing-stock-routing.module.ts +++ b/overlord/src/app/closing-stock/closing-stock-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; import { CostCentreListResolver } from '../cost-centre/cost-centre-list-resolver.service'; @@ -12,26 +12,32 @@ const closingStockRoutes: Routes = [ { path: '', component: ClosingStockComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Closing Stock', }, resolve: { - info: ClosingStockResolver, - costCentres: CostCentreListResolver, + info: (route: ActivatedRouteSnapshot) => inject(ClosingStockResolver).resolve(route), + costCentres: () => inject(CostCentreListResolver).resolve(), }, runGuardsAndResolvers: 'always', }, { path: ':date', component: ClosingStockComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Closing Stock', }, resolve: { - info: ClosingStockResolver, - costCentres: CostCentreListResolver, + info: (route: ActivatedRouteSnapshot) => inject(ClosingStockResolver).resolve(route), + costCentres: () => inject(CostCentreListResolver).resolve(), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/closing-stock/closing-stock.component.ts b/overlord/src/app/closing-stock/closing-stock.component.ts index bde95cc2..d7412188 100644 --- a/overlord/src/app/closing-stock/closing-stock.component.ts +++ b/overlord/src/app/closing-stock/closing-stock.component.ts @@ -189,8 +189,7 @@ export class ClosingStockComponent implements OnInit { post() { this.ser.post(this.info.date, this.info.costCentre.id as string).subscribe( - (result) => { - // this.loadVoucher(result); + () => { this.toaster.show('Success', 'Voucher Posted'); }, (error) => { diff --git a/overlord/src/app/closing-stock/closing-stock.module.ts b/overlord/src/app/closing-stock/closing-stock.module.ts index 7f404661..81d8a83a 100644 --- a/overlord/src/app/closing-stock/closing-stock.module.ts +++ b/overlord/src/app/closing-stock/closing-stock.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -21,6 +20,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSelectModule } from '@angular/material/select'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/core/error-logger.service.ts b/overlord/src/app/core/error-logger.service.ts index 1bc41280..b96cdbec 100644 --- a/overlord/src/app/core/error-logger.service.ts +++ b/overlord/src/app/core/error-logger.service.ts @@ -17,10 +17,10 @@ export class ErrorLoggerService { * @param operation - name of the operation that failed * @param result - optional value to return as the observable result */ - public handleError(serviceName = 'error-logger', operation = 'operation', result?: T) { + public handleError(serviceName = 'error-logger', operation = 'operation') { return (error: unknown): Observable => { ErrorLoggerService.log(serviceName, `${operation} failed: ${error}`); - return throwError(error); + return throwError(() => error); }; } } diff --git a/overlord/src/app/core/nav-bar/nav-bar.component.html b/overlord/src/app/core/nav-bar/nav-bar.component.html index 73f3be95..7ec899a6 100644 --- a/overlord/src/app/core/nav-bar/nav-bar.component.html +++ b/overlord/src/app/core/nav-bar/nav-bar.component.html @@ -54,6 +54,7 @@ Products Product Groups Recipes + Recipe Templates Periods diff --git a/overlord/src/app/cost-centre/cost-centre-list-resolver.service.ts b/overlord/src/app/cost-centre/cost-centre-list-resolver.service.ts index b65ec7e7..ca92d31d 100644 --- a/overlord/src/app/cost-centre/cost-centre-list-resolver.service.ts +++ b/overlord/src/app/cost-centre/cost-centre-list-resolver.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { Resolve } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { CostCentre } from '../core/cost-centre'; @@ -9,7 +8,7 @@ import { CostCentreService } from './cost-centre.service'; @Injectable({ providedIn: 'root', }) -export class CostCentreListResolver implements Resolve { +export class CostCentreListResolver { constructor(private ser: CostCentreService) {} resolve(): Observable { diff --git a/overlord/src/app/cost-centre/cost-centre-list/cost-centre-list-datasource.ts b/overlord/src/app/cost-centre/cost-centre-list/cost-centre-list-datasource.ts index 8fe687fa..1ec7c19b 100644 --- a/overlord/src/app/cost-centre/cost-centre-list/cost-centre-list-datasource.ts +++ b/overlord/src/app/cost-centre/cost-centre-list/cost-centre-list-datasource.ts @@ -16,11 +16,7 @@ export class CostCentreListDataSource extends DataSource { } connect(): Observable { - const dataMutations: ( - | Observable - | EventEmitter - | EventEmitter - )[] = [observableOf(this.data)]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -33,7 +29,7 @@ export class CostCentreListDataSource extends DataSource { this.paginator.length = this.data.length; } - return merge(...dataMutations).pipe( + return merge(observableOf(this.data), ...dataMutations).pipe( map(() => this.getPagedData(this.getSortedData([...this.data]))), ); } diff --git a/overlord/src/app/cost-centre/cost-centre-resolver.service.ts b/overlord/src/app/cost-centre/cost-centre-resolver.service.ts index 4c20c935..256e7b0e 100644 --- a/overlord/src/app/cost-centre/cost-centre-resolver.service.ts +++ b/overlord/src/app/cost-centre/cost-centre-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { CostCentre } from '../core/cost-centre'; @@ -9,7 +9,7 @@ import { CostCentreService } from './cost-centre.service'; @Injectable({ providedIn: 'root', }) -export class CostCentreResolver implements Resolve { +export class CostCentreResolver { constructor(private ser: CostCentreService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/cost-centre/cost-centre-routing.module.ts b/overlord/src/app/cost-centre/cost-centre-routing.module.ts index 8a583638..9f2c4264 100644 --- a/overlord/src/app/cost-centre/cost-centre-routing.module.ts +++ b/overlord/src/app/cost-centre/cost-centre-routing.module.ts @@ -1,46 +1,55 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; import { CostCentreDetailComponent } from './cost-centre-detail/cost-centre-detail.component'; -import { CostCentreListResolver } from './cost-centre-list-resolver.service'; import { CostCentreListComponent } from './cost-centre-list/cost-centre-list.component'; +import { CostCentreListResolver } from './cost-centre-list-resolver.service'; import { CostCentreResolver } from './cost-centre-resolver.service'; const costCentreRoutes: Routes = [ { path: '', component: CostCentreListComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Cost Centres', }, resolve: { - list: CostCentreListResolver, + list: () => inject(CostCentreListResolver).resolve(), }, }, { path: 'new', component: CostCentreDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Cost Centres', }, resolve: { - item: CostCentreResolver, + item: (route: ActivatedRouteSnapshot) => inject(CostCentreResolver).resolve(route), }, }, { path: ':id', component: CostCentreDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Cost Centres', }, resolve: { - item: CostCentreResolver, + item: (route: ActivatedRouteSnapshot) => inject(CostCentreResolver).resolve(route), }, }, ]; diff --git a/overlord/src/app/daybook/daybook-datasource.ts b/overlord/src/app/daybook/daybook-datasource.ts index 3dd60e5d..e8173003 100644 --- a/overlord/src/app/daybook/daybook-datasource.ts +++ b/overlord/src/app/daybook/daybook-datasource.ts @@ -20,11 +20,7 @@ export class DaybookDataSource extends DataSource { } connect(): Observable { - const dataMutations: ( - | Observable - | EventEmitter - | EventEmitter - )[] = [observableOf(this.data)]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -37,7 +33,7 @@ export class DaybookDataSource extends DataSource { this.paginator.length = this.data.length; } - return merge(...dataMutations).pipe( + return merge(observableOf(this.data), ...dataMutations).pipe( map(() => this.getPagedData(this.getSortedData([...this.data]))), ); } diff --git a/overlord/src/app/daybook/daybook-resolver.service.ts b/overlord/src/app/daybook/daybook-resolver.service.ts index 58efa7b6..c9db98d6 100644 --- a/overlord/src/app/daybook/daybook-resolver.service.ts +++ b/overlord/src/app/daybook/daybook-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Daybook } from './daybook'; @@ -8,7 +8,7 @@ import { DaybookService } from './daybook.service'; @Injectable({ providedIn: 'root', }) -export class DaybookResolver implements Resolve { +export class DaybookResolver { constructor(private ser: DaybookService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/daybook/daybook-routing.module.ts b/overlord/src/app/daybook/daybook-routing.module.ts index 9e1694e2..98a75d5b 100644 --- a/overlord/src/app/daybook/daybook-routing.module.ts +++ b/overlord/src/app/daybook/daybook-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -11,12 +11,15 @@ const daybookRoutes: Routes = [ { path: '', component: DaybookComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Daybook', }, resolve: { - info: DaybookResolver, + info: (route: ActivatedRouteSnapshot) => inject(DaybookResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/daybook/daybook.module.ts b/overlord/src/app/daybook/daybook.module.ts index c75e47ab..a2b5c990 100644 --- a/overlord/src/app/daybook/daybook.module.ts +++ b/overlord/src/app/daybook/daybook.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -22,6 +21,7 @@ import { MatPaginatorModule } from '@angular/material/paginator'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/employee-attendance/employee-attendance-resolver.service.ts b/overlord/src/app/employee-attendance/employee-attendance-resolver.service.ts index 38156703..833cf7ab 100644 --- a/overlord/src/app/employee-attendance/employee-attendance-resolver.service.ts +++ b/overlord/src/app/employee-attendance/employee-attendance-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { EmployeeAttendance } from './employee-attendance'; @@ -8,7 +8,7 @@ import { EmployeeAttendanceService } from './employee-attendance.service'; @Injectable({ providedIn: 'root', }) -export class EmployeeAttendanceResolver implements Resolve { +export class EmployeeAttendanceResolver { constructor(private ser: EmployeeAttendanceService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/employee-attendance/employee-attendance-routing.module.ts b/overlord/src/app/employee-attendance/employee-attendance-routing.module.ts index ff1d2d39..679884cc 100644 --- a/overlord/src/app/employee-attendance/employee-attendance-routing.module.ts +++ b/overlord/src/app/employee-attendance/employee-attendance-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AttendanceTypeResolver } from '../attendance/attendance-type-resolver.service'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -12,26 +12,32 @@ const employeeEmployeeAttendanceRoutes: Routes = [ { path: '', component: EmployeeAttendanceComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Attendance', }, resolve: { - info: EmployeeAttendanceResolver, - attendanceTypes: AttendanceTypeResolver, + info: (route: ActivatedRouteSnapshot) => inject(EmployeeAttendanceResolver).resolve(route), + attendanceTypes: () => inject(AttendanceTypeResolver).resolve(), }, runGuardsAndResolvers: 'always', }, { path: ':id', component: EmployeeAttendanceComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Attendance', }, resolve: { - info: EmployeeAttendanceResolver, - attendanceTypes: AttendanceTypeResolver, + info: (route: ActivatedRouteSnapshot) => inject(EmployeeAttendanceResolver).resolve(route), + attendanceTypes: () => inject(AttendanceTypeResolver).resolve(), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/employee-attendance/employee-attendance.module.ts b/overlord/src/app/employee-attendance/employee-attendance.module.ts index 9c4db49b..201dc4c5 100644 --- a/overlord/src/app/employee-attendance/employee-attendance.module.ts +++ b/overlord/src/app/employee-attendance/employee-attendance.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -25,6 +24,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSelectModule } from '@angular/material/select'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/employee-benefits/employee-benefits-resolver.service.ts b/overlord/src/app/employee-benefits/employee-benefits-resolver.service.ts index 8288c77b..80c6c70f 100644 --- a/overlord/src/app/employee-benefits/employee-benefits-resolver.service.ts +++ b/overlord/src/app/employee-benefits/employee-benefits-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Voucher } from '../core/voucher'; @@ -8,7 +8,7 @@ import { VoucherService } from '../core/voucher.service'; @Injectable({ providedIn: 'root', }) -export class EmployeeBenefitsResolver implements Resolve { +export class EmployeeBenefitsResolver { constructor(private ser: VoucherService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/employee-benefits/employee-benefits-routing.module.ts b/overlord/src/app/employee-benefits/employee-benefits-routing.module.ts index 86e55bc6..b8c25a6d 100644 --- a/overlord/src/app/employee-benefits/employee-benefits-routing.module.ts +++ b/overlord/src/app/employee-benefits/employee-benefits-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -11,24 +11,30 @@ const employeeBenefitsRoutes: Routes = [ { path: '', component: EmployeeBenefitsComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'employee-benefit', }, resolve: { - voucher: EmployeeBenefitsResolver, + voucher: (route: ActivatedRouteSnapshot) => inject(EmployeeBenefitsResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, { path: ':id', component: EmployeeBenefitsComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'employee-benefit', }, resolve: { - voucher: EmployeeBenefitsResolver, + voucher: (route: ActivatedRouteSnapshot) => inject(EmployeeBenefitsResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/employee-benefits/employee-benefits.module.ts b/overlord/src/app/employee-benefits/employee-benefits.module.ts index 30ba674d..fbad22c0 100644 --- a/overlord/src/app/employee-benefits/employee-benefits.module.ts +++ b/overlord/src/app/employee-benefits/employee-benefits.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -24,6 +23,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSelectModule } from '@angular/material/select'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/employee-functions/employee-functions-routing.module.ts b/overlord/src/app/employee-functions/employee-functions-routing.module.ts index e6308452..b74509ab 100644 --- a/overlord/src/app/employee-functions/employee-functions-routing.module.ts +++ b/overlord/src/app/employee-functions/employee-functions-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -10,7 +10,10 @@ const employeeFunctionsRoutes: Routes = [ { path: '', component: EmployeeFunctionsComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'employee-benefit', }, diff --git a/overlord/src/app/employee-functions/employee-functions.component.html b/overlord/src/app/employee-functions/employee-functions.component.html index 971049c3..99025332 100644 --- a/overlord/src/app/employee-functions/employee-functions.component.html +++ b/overlord/src/app/employee-functions/employee-functions.component.html @@ -88,10 +88,7 @@ -
+
- +
diff --git a/overlord/src/app/employee-functions/employee-functions.component.ts b/overlord/src/app/employee-functions/employee-functions.component.ts index d0b4a532..58e8297c 100644 --- a/overlord/src/app/employee-functions/employee-functions.component.ts +++ b/overlord/src/app/employee-functions/employee-functions.component.ts @@ -1,12 +1,9 @@ import { Component } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { MatDatepicker } from '@angular/material/datepicker'; -import { MatDialog } from '@angular/material/dialog'; -import { ActivatedRoute, Router } from '@angular/router'; import * as moment from 'moment'; import { Moment } from 'moment'; -import { AuthService } from '../auth/auth.service'; import { ToasterService } from '../core/toaster.service'; import { EmployeeFunctionsService } from './employee-functions.service'; @@ -26,17 +23,9 @@ export class EmployeeFunctionsComponent { finishDate: FormControl; }>; - fingerprintForm: FormGroup<{}>; fingerprintFile: File | null = null; - constructor( - private route: ActivatedRoute, - private router: Router, - private dialog: MatDialog, - private toaster: ToasterService, - private auth: AuthService, - private ser: EmployeeFunctionsService, - ) { + constructor(private toaster: ToasterService, private ser: EmployeeFunctionsService) { const startDate = moment().date(1); const finishDate = moment().date(startDate.daysInMonth()); this.creditSalaryForm = new FormGroup({ @@ -46,7 +35,6 @@ export class EmployeeFunctionsComponent { startDate: new FormControl(moment(), { nonNullable: true }), finishDate: new FormControl(moment(), { nonNullable: true }), }); - this.fingerprintForm = new FormGroup({}); } chosenYearHandler(normalizedYear: Moment) { diff --git a/overlord/src/app/employee-functions/employee-functions.module.ts b/overlord/src/app/employee-functions/employee-functions.module.ts index a7e0b873..d995f435 100644 --- a/overlord/src/app/employee-functions/employee-functions.module.ts +++ b/overlord/src/app/employee-functions/employee-functions.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -25,6 +24,7 @@ import { MatSelectModule } from '@angular/material/select'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { MatTabsModule } from '@angular/material/tabs'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/employee/employee-list-resolver.service.ts b/overlord/src/app/employee/employee-list-resolver.service.ts index 304a1142..815bf8c2 100644 --- a/overlord/src/app/employee/employee-list-resolver.service.ts +++ b/overlord/src/app/employee/employee-list-resolver.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { Resolve } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Employee } from './employee'; @@ -8,7 +7,7 @@ import { EmployeeService } from './employee.service'; @Injectable({ providedIn: 'root', }) -export class EmployeeListResolver implements Resolve { +export class EmployeeListResolver { constructor(private ser: EmployeeService) {} resolve(): Observable { diff --git a/overlord/src/app/employee/employee-list/employee-list-datasource.ts b/overlord/src/app/employee/employee-list/employee-list-datasource.ts index 8ead3558..42837303 100644 --- a/overlord/src/app/employee/employee-list/employee-list-datasource.ts +++ b/overlord/src/app/employee/employee-list/employee-list-datasource.ts @@ -28,12 +28,7 @@ export class EmployeeListDataSource extends DataSource { } connect(): Observable { - const dataMutations: ( - | Observable - | Observable - | EventEmitter - | EventEmitter - )[] = [observableOf(this.data), this.filter]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -41,7 +36,7 @@ export class EmployeeListDataSource extends DataSource { dataMutations.push((this.sort as MatSort).sortChange); } - return merge(...dataMutations) + return merge(observableOf(this.data), this.filter, ...dataMutations) .pipe( map(() => this.getFilteredData([...this.data])), tap((x: Employee[]) => { diff --git a/overlord/src/app/employee/employee-resolver.service.ts b/overlord/src/app/employee/employee-resolver.service.ts index 605fbb45..71a2104b 100644 --- a/overlord/src/app/employee/employee-resolver.service.ts +++ b/overlord/src/app/employee/employee-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve, Router } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Employee } from './employee'; @@ -8,8 +8,8 @@ import { EmployeeService } from './employee.service'; @Injectable({ providedIn: 'root', }) -export class EmployeeResolver implements Resolve { - constructor(private ser: EmployeeService, private router: Router) {} +export class EmployeeResolver { + constructor(private ser: EmployeeService) {} resolve(route: ActivatedRouteSnapshot): Observable { const id = route.paramMap.get('id'); diff --git a/overlord/src/app/employee/employee-routing.module.ts b/overlord/src/app/employee/employee-routing.module.ts index 29e6ee12..7cc7517d 100644 --- a/overlord/src/app/employee/employee-routing.module.ts +++ b/overlord/src/app/employee/employee-routing.module.ts @@ -1,49 +1,58 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; import { CostCentreListResolver } from '../cost-centre/cost-centre-list-resolver.service'; import { EmployeeDetailComponent } from './employee-detail/employee-detail.component'; -import { EmployeeListResolver } from './employee-list-resolver.service'; import { EmployeeListComponent } from './employee-list/employee-list.component'; +import { EmployeeListResolver } from './employee-list-resolver.service'; import { EmployeeResolver } from './employee-resolver.service'; const employeeRoutes: Routes = [ { path: '', component: EmployeeListComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Employees', }, resolve: { - list: EmployeeListResolver, + list: () => inject(EmployeeListResolver).resolve(), }, }, { path: 'new', component: EmployeeDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Employees', }, resolve: { - item: EmployeeResolver, - costCentres: CostCentreListResolver, + item: (route: ActivatedRouteSnapshot) => inject(EmployeeResolver).resolve(route), + costCentres: () => inject(CostCentreListResolver).resolve(), }, }, { path: ':id', component: EmployeeDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Employees', }, resolve: { - item: EmployeeResolver, - costCentres: CostCentreListResolver, + item: (route: ActivatedRouteSnapshot) => inject(EmployeeResolver).resolve(route), + costCentres: () => inject(CostCentreListResolver).resolve(), }, }, ]; diff --git a/overlord/src/app/employee/employee.module.ts b/overlord/src/app/employee/employee.module.ts index a073e0f3..f53db773 100644 --- a/overlord/src/app/employee/employee.module.ts +++ b/overlord/src/app/employee/employee.module.ts @@ -2,7 +2,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -17,6 +16,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSelectModule } from '@angular/material/select'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { EmployeeDetailComponent } from './employee-detail/employee-detail.component'; import { EmployeeListComponent } from './employee-list/employee-list.component'; diff --git a/overlord/src/app/entries/entries-resolver.service.ts b/overlord/src/app/entries/entries-resolver.service.ts index 3f89c72a..455e4f02 100644 --- a/overlord/src/app/entries/entries-resolver.service.ts +++ b/overlord/src/app/entries/entries-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { EntriesService } from './entries.service'; @@ -8,7 +8,7 @@ import { Report } from './report'; @Injectable({ providedIn: 'root', }) -export class EntriesResolver implements Resolve { +export class EntriesResolver { constructor(private ser: EntriesService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/entries/entries-routing.module.ts b/overlord/src/app/entries/entries-routing.module.ts index ac6b2c50..e1336d50 100644 --- a/overlord/src/app/entries/entries-routing.module.ts +++ b/overlord/src/app/entries/entries-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -11,12 +11,15 @@ const entriesRoutes: Routes = [ { path: '', component: EntriesComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Ledger', }, resolve: { - info: EntriesResolver, + info: (route: ActivatedRouteSnapshot) => inject(EntriesResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/entries/entries.component.ts b/overlord/src/app/entries/entries.component.ts index 7f29287c..3ca9abcc 100644 --- a/overlord/src/app/entries/entries.component.ts +++ b/overlord/src/app/entries/entries.component.ts @@ -88,14 +88,14 @@ export class EntriesComponent implements OnInit { sortDirection, ); }); - this.sort?.sortChange.subscribe((x) => + this.sort?.sortChange.subscribe(() => this.router.navigate([], { relativeTo: this.route, queryParams: { a: this.sort?.active ?? 'date', d: this.sort?.direction }, queryParamsHandling: 'merge', }), ); - this.paginator?.page.subscribe((x) => + this.paginator?.page.subscribe(() => this.router.navigate([], { relativeTo: this.route, queryParams: { ps: this.paginator?.pageSize, pi: this.paginator?.pageIndex }, diff --git a/overlord/src/app/entries/entries.module.ts b/overlord/src/app/entries/entries.module.ts index f9241b43..45402c28 100644 --- a/overlord/src/app/entries/entries.module.ts +++ b/overlord/src/app/entries/entries.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -22,6 +21,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatRadioModule } from '@angular/material/radio'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/incentive/incentive-resolver.service.ts b/overlord/src/app/incentive/incentive-resolver.service.ts index 65162ef3..58b914f9 100644 --- a/overlord/src/app/incentive/incentive-resolver.service.ts +++ b/overlord/src/app/incentive/incentive-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Voucher } from '../core/voucher'; @@ -8,7 +8,7 @@ import { VoucherService } from '../core/voucher.service'; @Injectable({ providedIn: 'root', }) -export class IncentiveResolver implements Resolve { +export class IncentiveResolver { constructor(private ser: VoucherService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/incentive/incentive-routing.module.ts b/overlord/src/app/incentive/incentive-routing.module.ts index ce106a17..19ec2320 100644 --- a/overlord/src/app/incentive/incentive-routing.module.ts +++ b/overlord/src/app/incentive/incentive-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -11,24 +11,30 @@ const incentiveRoutes: Routes = [ { path: '', component: IncentiveComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'incentive', }, resolve: { - voucher: IncentiveResolver, + voucher: (route: ActivatedRouteSnapshot) => inject(IncentiveResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, { path: ':id', component: IncentiveComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'incentive', }, resolve: { - voucher: IncentiveResolver, + voucher: (route: ActivatedRouteSnapshot) => inject(IncentiveResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/incentive/incentive.module.ts b/overlord/src/app/incentive/incentive.module.ts index 9056c1eb..4672c51f 100644 --- a/overlord/src/app/incentive/incentive.module.ts +++ b/overlord/src/app/incentive/incentive.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -24,6 +23,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSelectModule } from '@angular/material/select'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/issue/issue-resolver.service.ts b/overlord/src/app/issue/issue-resolver.service.ts index 73edc5e4..50a0287f 100644 --- a/overlord/src/app/issue/issue-resolver.service.ts +++ b/overlord/src/app/issue/issue-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Voucher } from '../core/voucher'; @@ -8,7 +8,7 @@ import { VoucherService } from '../core/voucher.service'; @Injectable({ providedIn: 'root', }) -export class IssueResolver implements Resolve { +export class IssueResolver { constructor(private ser: VoucherService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/issue/issue-routing.module.ts b/overlord/src/app/issue/issue-routing.module.ts index 837a1f8d..9bd75ce5 100644 --- a/overlord/src/app/issue/issue-routing.module.ts +++ b/overlord/src/app/issue/issue-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; import { CostCentreListResolver } from '../cost-centre/cost-centre-list-resolver.service'; @@ -12,26 +12,32 @@ const issueRoutes: Routes = [ { path: '', component: IssueComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Issue', }, resolve: { - voucher: IssueResolver, - costCentres: CostCentreListResolver, + voucher: (route: ActivatedRouteSnapshot) => inject(IssueResolver).resolve(route), + costCentres: () => inject(CostCentreListResolver).resolve(), }, runGuardsAndResolvers: 'always', }, { path: ':id', component: IssueComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Issue', }, resolve: { - voucher: IssueResolver, - costCentres: CostCentreListResolver, + voucher: (route: ActivatedRouteSnapshot) => inject(IssueResolver).resolve(route), + costCentres: () => inject(CostCentreListResolver).resolve(), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/issue/issue.module.ts b/overlord/src/app/issue/issue.module.ts index 4b8b5419..e1ee27bf 100644 --- a/overlord/src/app/issue/issue.module.ts +++ b/overlord/src/app/issue/issue.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -24,6 +23,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSelectModule } from '@angular/material/select'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { HotkeyModule } from 'angular2-hotkeys'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/journal/journal-resolver.service.ts b/overlord/src/app/journal/journal-resolver.service.ts index 5707e537..6c116930 100644 --- a/overlord/src/app/journal/journal-resolver.service.ts +++ b/overlord/src/app/journal/journal-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Voucher } from '../core/voucher'; @@ -8,7 +8,7 @@ import { VoucherService } from '../core/voucher.service'; @Injectable({ providedIn: 'root', }) -export class JournalResolver implements Resolve { +export class JournalResolver { constructor(private ser: VoucherService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/journal/journal-routing.module.ts b/overlord/src/app/journal/journal-routing.module.ts index 19231ba8..74a96538 100644 --- a/overlord/src/app/journal/journal-routing.module.ts +++ b/overlord/src/app/journal/journal-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -11,24 +11,30 @@ const journalRoutes: Routes = [ { path: '', component: JournalComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Journal', }, resolve: { - voucher: JournalResolver, + voucher: (route: ActivatedRouteSnapshot) => inject(JournalResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, { path: ':id', component: JournalComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Journal', }, resolve: { - voucher: JournalResolver, + voucher: (route: ActivatedRouteSnapshot) => inject(JournalResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/journal/journal.module.ts b/overlord/src/app/journal/journal.module.ts index f737033d..f1f5972f 100644 --- a/overlord/src/app/journal/journal.module.ts +++ b/overlord/src/app/journal/journal.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -24,6 +23,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSelectModule } from '@angular/material/select'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { HotkeyModule } from 'angular2-hotkeys'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/ledger/ledger-datasource.ts b/overlord/src/app/ledger/ledger-datasource.ts index a2e967cf..b6c74094 100644 --- a/overlord/src/app/ledger/ledger-datasource.ts +++ b/overlord/src/app/ledger/ledger-datasource.ts @@ -16,11 +16,7 @@ export class LedgerDataSource extends DataSource { } connect(): Observable { - const dataMutations: ( - | Observable - | EventEmitter - | EventEmitter - )[] = [observableOf(this.data)]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -33,7 +29,7 @@ export class LedgerDataSource extends DataSource { this.paginator.length = this.data.length; } - return merge(...dataMutations).pipe( + return merge(observableOf(this.data), ...dataMutations).pipe( map(() => this.getPagedData(this.getSortedData([...this.data]))), ); } diff --git a/overlord/src/app/ledger/ledger-resolver.service.ts b/overlord/src/app/ledger/ledger-resolver.service.ts index 5fdecba6..e8a800ed 100644 --- a/overlord/src/app/ledger/ledger-resolver.service.ts +++ b/overlord/src/app/ledger/ledger-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Ledger } from './ledger'; @@ -8,7 +8,7 @@ import { LedgerService } from './ledger.service'; @Injectable({ providedIn: 'root', }) -export class LedgerResolver implements Resolve { +export class LedgerResolver { constructor(private ser: LedgerService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/ledger/ledger-routing.module.ts b/overlord/src/app/ledger/ledger-routing.module.ts index 5bdd8c6a..085345b4 100644 --- a/overlord/src/app/ledger/ledger-routing.module.ts +++ b/overlord/src/app/ledger/ledger-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -11,24 +11,30 @@ const ledgerRoutes: Routes = [ { path: '', component: LedgerComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Ledger', }, resolve: { - info: LedgerResolver, + info: (route: ActivatedRouteSnapshot) => inject(LedgerResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, { path: ':id', component: LedgerComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Ledger', }, resolve: { - info: LedgerResolver, + info: (route: ActivatedRouteSnapshot) => inject(LedgerResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/ledger/ledger.module.ts b/overlord/src/app/ledger/ledger.module.ts index f2ea8109..ef2bad5f 100644 --- a/overlord/src/app/ledger/ledger.module.ts +++ b/overlord/src/app/ledger/ledger.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -22,6 +21,7 @@ import { MatPaginatorModule } from '@angular/material/paginator'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/net-transactions/net-transactions-datasource.ts b/overlord/src/app/net-transactions/net-transactions-datasource.ts index edad6dcf..8d1ac35e 100644 --- a/overlord/src/app/net-transactions/net-transactions-datasource.ts +++ b/overlord/src/app/net-transactions/net-transactions-datasource.ts @@ -20,11 +20,7 @@ export class NetTransactionsDataSource extends DataSource { } connect(): Observable { - const dataMutations: ( - | Observable - | EventEmitter - | EventEmitter - )[] = [observableOf(this.data)]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -37,7 +33,7 @@ export class NetTransactionsDataSource extends DataSource { this.paginator.length = this.data.length; } - return merge(...dataMutations).pipe( + return merge(observableOf(this.data), ...dataMutations).pipe( map(() => this.getPagedData(this.getSortedData([...this.data]))), ); } diff --git a/overlord/src/app/net-transactions/net-transactions-resolver.service.ts b/overlord/src/app/net-transactions/net-transactions-resolver.service.ts index 505e6d08..46bec321 100644 --- a/overlord/src/app/net-transactions/net-transactions-resolver.service.ts +++ b/overlord/src/app/net-transactions/net-transactions-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { NetTransactions } from './net-transactions'; @@ -8,7 +8,7 @@ import { NetTransactionsService } from './net-transactions.service'; @Injectable({ providedIn: 'root', }) -export class NetTransactionsResolver implements Resolve { +export class NetTransactionsResolver { constructor(private ser: NetTransactionsService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/net-transactions/net-transactions-routing.module.ts b/overlord/src/app/net-transactions/net-transactions-routing.module.ts index 63fb03f7..8be2b613 100644 --- a/overlord/src/app/net-transactions/net-transactions-routing.module.ts +++ b/overlord/src/app/net-transactions/net-transactions-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -11,12 +11,15 @@ const netTransactionsRoutes: Routes = [ { path: '', component: NetTransactionsComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Net Transactions', }, resolve: { - info: NetTransactionsResolver, + info: (route: ActivatedRouteSnapshot) => inject(NetTransactionsResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/net-transactions/net-transactions.module.ts b/overlord/src/app/net-transactions/net-transactions.module.ts index 5e1c8adc..1e340775 100644 --- a/overlord/src/app/net-transactions/net-transactions.module.ts +++ b/overlord/src/app/net-transactions/net-transactions.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -22,6 +21,7 @@ import { MatPaginatorModule } from '@angular/material/paginator'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/non-contact-purchase/non-contract-purchase-resolver.service.ts b/overlord/src/app/non-contact-purchase/non-contract-purchase-resolver.service.ts index 208cddf0..a51aca88 100644 --- a/overlord/src/app/non-contact-purchase/non-contract-purchase-resolver.service.ts +++ b/overlord/src/app/non-contact-purchase/non-contract-purchase-resolver.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { NonContractPurchase } from './non-contract-purchase'; @@ -8,10 +7,10 @@ import { NonContractPurchaseService } from './non-contract-purchase.service'; @Injectable({ providedIn: 'root', }) -export class NonContractPurchaseResolverService implements Resolve { +export class NonContractPurchaseResolverService { constructor(private ser: NonContractPurchaseService) {} - resolve(route: ActivatedRouteSnapshot): Observable { + resolve(): Observable { return this.ser.nonContractPurchase(); } } diff --git a/overlord/src/app/non-contact-purchase/non-contract-purchase-routing.module.ts b/overlord/src/app/non-contact-purchase/non-contract-purchase-routing.module.ts index ca47e838..9ae4d4c4 100644 --- a/overlord/src/app/non-contact-purchase/non-contract-purchase-routing.module.ts +++ b/overlord/src/app/non-contact-purchase/non-contract-purchase-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -11,12 +11,15 @@ const nonContractPurchaseRoutes: Routes = [ { path: '', component: NonContractPurchaseComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Product Ledger', }, resolve: { - info: NonContractPurchaseResolverService, + info: () => inject(NonContractPurchaseResolverService).resolve(), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/payment/payment-accounts-resolver.service.ts b/overlord/src/app/payment/payment-accounts-resolver.service.ts index f2075489..3a4fa91a 100644 --- a/overlord/src/app/payment/payment-accounts-resolver.service.ts +++ b/overlord/src/app/payment/payment-accounts-resolver.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { Resolve } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Account } from '../core/account'; @@ -8,7 +7,7 @@ import { AccountService } from '../core/account.service'; @Injectable({ providedIn: 'root', }) -export class PaymentAccountsResolver implements Resolve { +export class PaymentAccountsResolver { constructor(private ser: AccountService) {} resolve(): Observable { diff --git a/overlord/src/app/payment/payment-resolver.service.ts b/overlord/src/app/payment/payment-resolver.service.ts index e72556a6..b10388ec 100644 --- a/overlord/src/app/payment/payment-resolver.service.ts +++ b/overlord/src/app/payment/payment-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Voucher } from '../core/voucher'; @@ -8,7 +8,7 @@ import { VoucherService } from '../core/voucher.service'; @Injectable({ providedIn: 'root', }) -export class PaymentResolver implements Resolve { +export class PaymentResolver { constructor(private ser: VoucherService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/payment/payment-routing.module.ts b/overlord/src/app/payment/payment-routing.module.ts index 813bfb3b..38f1ef58 100644 --- a/overlord/src/app/payment/payment-routing.module.ts +++ b/overlord/src/app/payment/payment-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -12,26 +12,32 @@ const paymentRoutes: Routes = [ { path: '', component: PaymentComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'journal', }, resolve: { - paymentAccounts: PaymentAccountsResolver, - voucher: PaymentResolver, + paymentAccounts: () => inject(PaymentAccountsResolver).resolve(), + voucher: (route: ActivatedRouteSnapshot) => inject(PaymentResolver).resolve(route), }, runGuardsAndResolvers: 'paramsChange', }, { path: ':id', component: PaymentComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'journal', }, resolve: { - paymentAccounts: PaymentAccountsResolver, - voucher: PaymentResolver, + paymentAccounts: () => inject(PaymentAccountsResolver).resolve(), + voucher: (route: ActivatedRouteSnapshot) => inject(PaymentResolver).resolve(route), }, runGuardsAndResolvers: 'paramsChange', }, diff --git a/overlord/src/app/payment/payment.module.ts b/overlord/src/app/payment/payment.module.ts index c0aec7cc..3b826a99 100644 --- a/overlord/src/app/payment/payment.module.ts +++ b/overlord/src/app/payment/payment.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -24,6 +23,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSelectModule } from '@angular/material/select'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { HotkeyModule } from 'angular2-hotkeys'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/period/period-list-resolver.service.ts b/overlord/src/app/period/period-list-resolver.service.ts index 3b6b1e1b..8d0eb98b 100644 --- a/overlord/src/app/period/period-list-resolver.service.ts +++ b/overlord/src/app/period/period-list-resolver.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { Resolve } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Period } from './period'; @@ -8,7 +7,7 @@ import { PeriodService } from './period.service'; @Injectable({ providedIn: 'root', }) -export class PeriodListResolver implements Resolve { +export class PeriodListResolver { constructor(private ser: PeriodService) {} resolve(): Observable { diff --git a/overlord/src/app/period/period-resolver.service.ts b/overlord/src/app/period/period-resolver.service.ts index 3f1fbda6..2f7092ba 100644 --- a/overlord/src/app/period/period-resolver.service.ts +++ b/overlord/src/app/period/period-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve, Router } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Period } from './period'; @@ -8,8 +8,8 @@ import { PeriodService } from './period.service'; @Injectable({ providedIn: 'root', }) -export class PeriodResolver implements Resolve { - constructor(private ser: PeriodService, private router: Router) {} +export class PeriodResolver { + constructor(private ser: PeriodService) {} resolve(route: ActivatedRouteSnapshot): Observable { const id = route.paramMap.get('id'); diff --git a/overlord/src/app/period/period-routing.module.ts b/overlord/src/app/period/period-routing.module.ts index 9d1de9fa..5a7a5252 100644 --- a/overlord/src/app/period/period-routing.module.ts +++ b/overlord/src/app/period/period-routing.module.ts @@ -1,49 +1,58 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; import { CostCentreListResolver } from '../cost-centre/cost-centre-list-resolver.service'; import { PeriodDetailComponent } from './period-detail/period-detail.component'; -import { PeriodListResolver } from './period-list-resolver.service'; import { PeriodListComponent } from './period-list/period-list.component'; +import { PeriodListResolver } from './period-list-resolver.service'; import { PeriodResolver } from './period-resolver.service'; const periodRoutes: Routes = [ { path: '', component: PeriodListComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Recipes', }, resolve: { - list: PeriodListResolver, + list: () => inject(PeriodListResolver).resolve(), }, }, { path: 'new', component: PeriodDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Recipes', }, resolve: { - item: PeriodResolver, - costCentres: CostCentreListResolver, + item: (route: ActivatedRouteSnapshot) => inject(PeriodResolver).resolve(route), + costCentres: () => inject(CostCentreListResolver).resolve(), }, }, { path: ':id', component: PeriodDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Recipes', }, resolve: { - item: PeriodResolver, - costCentres: CostCentreListResolver, + item: (route: ActivatedRouteSnapshot) => inject(PeriodResolver).resolve(route), + costCentres: () => inject(CostCentreListResolver).resolve(), }, }, ]; diff --git a/overlord/src/app/period/period.module.ts b/overlord/src/app/period/period.module.ts index 3ace72b3..13df30b9 100644 --- a/overlord/src/app/period/period.module.ts +++ b/overlord/src/app/period/period.module.ts @@ -2,7 +2,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -17,6 +16,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSelectModule } from '@angular/material/select'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { PeriodDetailComponent } from './period-detail/period-detail.component'; import { PeriodListComponent } from './period-list/period-list.component'; diff --git a/overlord/src/app/product-group/product-group-list-resolver.service.ts b/overlord/src/app/product-group/product-group-list-resolver.service.ts index bb3df980..30aa24b2 100644 --- a/overlord/src/app/product-group/product-group-list-resolver.service.ts +++ b/overlord/src/app/product-group/product-group-list-resolver.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { Resolve } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { ProductGroup } from '../core/product-group'; @@ -9,7 +8,7 @@ import { ProductGroupService } from './product-group.service'; @Injectable({ providedIn: 'root', }) -export class ProductGroupListResolver implements Resolve { +export class ProductGroupListResolver { constructor(private ser: ProductGroupService) {} resolve(): Observable { diff --git a/overlord/src/app/product-group/product-group-list/product-group-list-datasource.ts b/overlord/src/app/product-group/product-group-list/product-group-list-datasource.ts index d4cdccee..afa990b1 100644 --- a/overlord/src/app/product-group/product-group-list/product-group-list-datasource.ts +++ b/overlord/src/app/product-group/product-group-list/product-group-list-datasource.ts @@ -20,11 +20,7 @@ export class ProductGroupListDataSource extends DataSource { } connect(): Observable { - const dataMutations: ( - | Observable - | EventEmitter - | EventEmitter - )[] = [observableOf(this.data)]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -37,7 +33,7 @@ export class ProductGroupListDataSource extends DataSource { this.paginator.length = this.data.length; } - return merge(...dataMutations).pipe( + return merge(observableOf(this.data), ...dataMutations).pipe( map(() => this.getPagedData(this.getSortedData([...this.data]))), ); } diff --git a/overlord/src/app/product-group/product-group-resolver.service.ts b/overlord/src/app/product-group/product-group-resolver.service.ts index 86999295..9385f37b 100644 --- a/overlord/src/app/product-group/product-group-resolver.service.ts +++ b/overlord/src/app/product-group/product-group-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { ProductGroup } from '../core/product-group'; @@ -9,7 +9,7 @@ import { ProductGroupService } from './product-group.service'; @Injectable({ providedIn: 'root', }) -export class ProductGroupResolver implements Resolve { +export class ProductGroupResolver { constructor(private ser: ProductGroupService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/product-group/product-group-routing.module.ts b/overlord/src/app/product-group/product-group-routing.module.ts index 014b5ee7..0830f7f8 100644 --- a/overlord/src/app/product-group/product-group-routing.module.ts +++ b/overlord/src/app/product-group/product-group-routing.module.ts @@ -1,46 +1,55 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; import { ProductGroupDetailComponent } from './product-group-detail/product-group-detail.component'; -import { ProductGroupListResolver } from './product-group-list-resolver.service'; import { ProductGroupListComponent } from './product-group-list/product-group-list.component'; +import { ProductGroupListResolver } from './product-group-list-resolver.service'; import { ProductGroupResolver } from './product-group-resolver.service'; const productGroupRoutes: Routes = [ { path: '', component: ProductGroupListComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Cost Centres', }, resolve: { - list: ProductGroupListResolver, + list: () => inject(ProductGroupListResolver).resolve(), }, }, { path: 'new', component: ProductGroupDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Cost Centres', }, resolve: { - item: ProductGroupResolver, + item: (route: ActivatedRouteSnapshot) => inject(ProductGroupResolver).resolve(route), }, }, { path: ':id', component: ProductGroupDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Cost Centres', }, resolve: { - item: ProductGroupResolver, + item: (route: ActivatedRouteSnapshot) => inject(ProductGroupResolver).resolve(route), }, }, ]; diff --git a/overlord/src/app/product-ledger/product-ledger-datasource.ts b/overlord/src/app/product-ledger/product-ledger-datasource.ts index f46a287b..ddf08515 100644 --- a/overlord/src/app/product-ledger/product-ledger-datasource.ts +++ b/overlord/src/app/product-ledger/product-ledger-datasource.ts @@ -20,11 +20,7 @@ export class ProductLedgerDataSource extends DataSource { } connect(): Observable { - const dataMutations: ( - | Observable - | EventEmitter - | EventEmitter - )[] = [observableOf(this.data)]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -36,10 +32,10 @@ export class ProductLedgerDataSource extends DataSource { if (this.paginator) { this.paginator.length = this.data.length; } - - return merge(...dataMutations).pipe( + const a = merge(observableOf(this.data), ...dataMutations).pipe( map(() => this.getPagedData(this.getSortedData([...this.data]))), ); + return a; } disconnect() {} diff --git a/overlord/src/app/product-ledger/product-ledger-resolver.service.ts b/overlord/src/app/product-ledger/product-ledger-resolver.service.ts index acf2e519..250cf783 100644 --- a/overlord/src/app/product-ledger/product-ledger-resolver.service.ts +++ b/overlord/src/app/product-ledger/product-ledger-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { ProductLedger } from './product-ledger'; @@ -8,7 +8,7 @@ import { ProductLedgerService } from './product-ledger.service'; @Injectable({ providedIn: 'root', }) -export class ProductLedgerResolver implements Resolve { +export class ProductLedgerResolver { constructor(private ser: ProductLedgerService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/product-ledger/product-ledger-routing.module.ts b/overlord/src/app/product-ledger/product-ledger-routing.module.ts index 54ca9027..81e229c1 100644 --- a/overlord/src/app/product-ledger/product-ledger-routing.module.ts +++ b/overlord/src/app/product-ledger/product-ledger-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -11,24 +11,30 @@ const productLedgerRoutes: Routes = [ { path: '', component: ProductLedgerComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Product Ledger', }, resolve: { - info: ProductLedgerResolver, + info: (route: ActivatedRouteSnapshot) => inject(ProductLedgerResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, { path: ':id', component: ProductLedgerComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Product Ledger', }, resolve: { - info: ProductLedgerResolver, + info: (route: ActivatedRouteSnapshot) => inject(ProductLedgerResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/product-ledger/product-ledger.module.ts b/overlord/src/app/product-ledger/product-ledger.module.ts index 2547ec1a..1a0ed1d8 100644 --- a/overlord/src/app/product-ledger/product-ledger.module.ts +++ b/overlord/src/app/product-ledger/product-ledger.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -22,6 +21,7 @@ import { MatPaginatorModule } from '@angular/material/paginator'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/product/product-list-resolver.service.ts b/overlord/src/app/product/product-list-resolver.service.ts index 05707afc..f2be3370 100644 --- a/overlord/src/app/product/product-list-resolver.service.ts +++ b/overlord/src/app/product/product-list-resolver.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { Resolve } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Product } from '../core/product'; @@ -9,7 +8,7 @@ import { ProductService } from './product.service'; @Injectable({ providedIn: 'root', }) -export class ProductListResolver implements Resolve { +export class ProductListResolver { constructor(private ser: ProductService) {} resolve(): Observable { diff --git a/overlord/src/app/product/product-list/product-list-datasource.ts b/overlord/src/app/product/product-list/product-list-datasource.ts index b3efa68e..5755a2d8 100644 --- a/overlord/src/app/product/product-list/product-list-datasource.ts +++ b/overlord/src/app/product/product-list/product-list-datasource.ts @@ -26,12 +26,7 @@ export class ProductListDataSource extends DataSource { } connect(): Observable { - const dataMutations: ( - | Observable - | Observable - | EventEmitter - | EventEmitter - )[] = [observableOf(this.data), this.filter]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -39,7 +34,7 @@ export class ProductListDataSource extends DataSource { dataMutations.push((this.sort as MatSort).sortChange); } - return merge(...dataMutations).pipe( + return merge(observableOf(this.data), this.filter, ...dataMutations).pipe( map(() => this.getFilteredData([...this.data])), tap((x: Product[]) => { if (this.paginator) { diff --git a/overlord/src/app/product/product-resolver.service.ts b/overlord/src/app/product/product-resolver.service.ts index 4c74b9fd..8e7fd4b3 100644 --- a/overlord/src/app/product/product-resolver.service.ts +++ b/overlord/src/app/product/product-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Product } from '../core/product'; @@ -9,7 +9,7 @@ import { ProductService } from './product.service'; @Injectable({ providedIn: 'root', }) -export class ProductResolver implements Resolve { +export class ProductResolver { constructor(private ser: ProductService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/product/product-routing.module.ts b/overlord/src/app/product/product-routing.module.ts index 0e51b8ed..46b86fca 100644 --- a/overlord/src/app/product/product-routing.module.ts +++ b/overlord/src/app/product/product-routing.module.ts @@ -1,49 +1,58 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; import { ProductGroupListResolver } from '../product-group/product-group-list-resolver.service'; import { ProductDetailComponent } from './product-detail/product-detail.component'; -import { ProductListResolver } from './product-list-resolver.service'; import { ProductListComponent } from './product-list/product-list.component'; +import { ProductListResolver } from './product-list-resolver.service'; import { ProductResolver } from './product-resolver.service'; const productRoutes: Routes = [ { path: '', component: ProductListComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Products', }, resolve: { - list: ProductListResolver, + list: () => inject(ProductListResolver).resolve(), }, }, { path: 'new', component: ProductDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Products', }, resolve: { - item: ProductResolver, - productGroups: ProductGroupListResolver, + item: (route: ActivatedRouteSnapshot) => inject(ProductResolver).resolve(route), + productGroups: () => inject(ProductGroupListResolver).resolve(), }, }, { path: ':id', component: ProductDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Products', }, resolve: { - item: ProductResolver, - productGroups: ProductGroupListResolver, + item: (route: ActivatedRouteSnapshot) => inject(ProductResolver).resolve(route), + productGroups: () => inject(ProductGroupListResolver).resolve(), }, }, ]; diff --git a/overlord/src/app/profit-loss/profit-loss-datasource.ts b/overlord/src/app/profit-loss/profit-loss-datasource.ts index 2347f80d..c422fe65 100644 --- a/overlord/src/app/profit-loss/profit-loss-datasource.ts +++ b/overlord/src/app/profit-loss/profit-loss-datasource.ts @@ -20,11 +20,7 @@ export class ProfitLossDataSource extends DataSource { } connect(): Observable { - const dataMutations: ( - | Observable - | EventEmitter - | EventEmitter - )[] = [observableOf(this.data)]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -37,7 +33,7 @@ export class ProfitLossDataSource extends DataSource { this.paginator.length = this.data.length; } - return merge(...dataMutations).pipe( + return merge(observableOf(this.data), ...dataMutations).pipe( map(() => this.getPagedData(this.getSortedData([...this.data]))), ); } diff --git a/overlord/src/app/profit-loss/profit-loss-resolver.service.ts b/overlord/src/app/profit-loss/profit-loss-resolver.service.ts index 719ddbf6..6ee18be4 100644 --- a/overlord/src/app/profit-loss/profit-loss-resolver.service.ts +++ b/overlord/src/app/profit-loss/profit-loss-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { ProfitLoss } from './profit-loss'; @@ -8,7 +8,7 @@ import { ProfitLossService } from './profit-loss.service'; @Injectable({ providedIn: 'root', }) -export class ProfitLossResolver implements Resolve { +export class ProfitLossResolver { constructor(private ser: ProfitLossService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/profit-loss/profit-loss-routing.module.ts b/overlord/src/app/profit-loss/profit-loss-routing.module.ts index eb83c9ad..fa0f6ef7 100644 --- a/overlord/src/app/profit-loss/profit-loss-routing.module.ts +++ b/overlord/src/app/profit-loss/profit-loss-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -11,12 +11,15 @@ const profitLossRoutes: Routes = [ { path: '', component: ProfitLossComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Profit & Loss', }, resolve: { - info: ProfitLossResolver, + info: (route: ActivatedRouteSnapshot) => inject(ProfitLossResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/profit-loss/profit-loss.module.ts b/overlord/src/app/profit-loss/profit-loss.module.ts index 17254c6d..ac503ef0 100644 --- a/overlord/src/app/profit-loss/profit-loss.module.ts +++ b/overlord/src/app/profit-loss/profit-loss.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -22,6 +21,7 @@ import { MatPaginatorModule } from '@angular/material/paginator'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/purchase-entries/purchase-entries-datasource.ts b/overlord/src/app/purchase-entries/purchase-entries-datasource.ts index 52c9696e..d61ae810 100644 --- a/overlord/src/app/purchase-entries/purchase-entries-datasource.ts +++ b/overlord/src/app/purchase-entries/purchase-entries-datasource.ts @@ -20,11 +20,7 @@ export class PurchaseEntriesDataSource extends DataSource { } connect(): Observable { - const dataMutations: ( - | Observable - | EventEmitter - | EventEmitter - )[] = [observableOf(this.data)]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -37,7 +33,7 @@ export class PurchaseEntriesDataSource extends DataSource { this.paginator.length = this.data.length; } - return merge(...dataMutations).pipe( + return merge(observableOf(this.data), ...dataMutations).pipe( map(() => this.getPagedData(this.getSortedData([...this.data]))), ); } diff --git a/overlord/src/app/purchase-entries/purchase-entries-resolver.service.ts b/overlord/src/app/purchase-entries/purchase-entries-resolver.service.ts index 34287c91..218acd03 100644 --- a/overlord/src/app/purchase-entries/purchase-entries-resolver.service.ts +++ b/overlord/src/app/purchase-entries/purchase-entries-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { PurchaseEntries } from './purchase-entries'; @@ -8,7 +8,7 @@ import { PurchaseEntriesService } from './purchase-entries.service'; @Injectable({ providedIn: 'root', }) -export class PurchaseEntriesResolver implements Resolve { +export class PurchaseEntriesResolver { constructor(private ser: PurchaseEntriesService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/purchase-entries/purchase-entries-routing.module.ts b/overlord/src/app/purchase-entries/purchase-entries-routing.module.ts index c73ea759..885985a8 100644 --- a/overlord/src/app/purchase-entries/purchase-entries-routing.module.ts +++ b/overlord/src/app/purchase-entries/purchase-entries-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -11,12 +11,15 @@ const purchaseEntriesRoutes: Routes = [ { path: '', component: PurchaseEntriesComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Purchase Entries', }, resolve: { - info: PurchaseEntriesResolver, + info: (route: ActivatedRouteSnapshot) => inject(PurchaseEntriesResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/purchase-entries/purchase-entries.module.ts b/overlord/src/app/purchase-entries/purchase-entries.module.ts index 24172c0c..f49b1be8 100644 --- a/overlord/src/app/purchase-entries/purchase-entries.module.ts +++ b/overlord/src/app/purchase-entries/purchase-entries.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -22,6 +21,7 @@ import { MatPaginatorModule } from '@angular/material/paginator'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/purchase-return/purchase-return-resolver.service.ts b/overlord/src/app/purchase-return/purchase-return-resolver.service.ts index 42373e9e..2d955c7b 100644 --- a/overlord/src/app/purchase-return/purchase-return-resolver.service.ts +++ b/overlord/src/app/purchase-return/purchase-return-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Voucher } from '../core/voucher'; @@ -8,7 +8,7 @@ import { VoucherService } from '../core/voucher.service'; @Injectable({ providedIn: 'root', }) -export class PurchaseReturnResolver implements Resolve { +export class PurchaseReturnResolver { constructor(private ser: VoucherService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/purchase-return/purchase-return-routing.module.ts b/overlord/src/app/purchase-return/purchase-return-routing.module.ts index 8547590d..810ac80d 100644 --- a/overlord/src/app/purchase-return/purchase-return-routing.module.ts +++ b/overlord/src/app/purchase-return/purchase-return-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -11,24 +11,30 @@ const purchaseReturnRoutes: Routes = [ { path: '', component: PurchaseReturnComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Purchase Return', }, resolve: { - voucher: PurchaseReturnResolver, + voucher: (route: ActivatedRouteSnapshot) => inject(PurchaseReturnResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, { path: ':id', component: PurchaseReturnComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Purchase Return', }, resolve: { - voucher: PurchaseReturnResolver, + voucher: (route: ActivatedRouteSnapshot) => inject(PurchaseReturnResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/purchase-return/purchase-return.module.ts b/overlord/src/app/purchase-return/purchase-return.module.ts index b0d42d12..12389758 100644 --- a/overlord/src/app/purchase-return/purchase-return.module.ts +++ b/overlord/src/app/purchase-return/purchase-return.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -24,6 +23,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSelectModule } from '@angular/material/select'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { HotkeyModule } from 'angular2-hotkeys'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/purchase/purchase-resolver.service.ts b/overlord/src/app/purchase/purchase-resolver.service.ts index 83182fda..2df3cafb 100644 --- a/overlord/src/app/purchase/purchase-resolver.service.ts +++ b/overlord/src/app/purchase/purchase-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Voucher } from '../core/voucher'; @@ -8,7 +8,7 @@ import { VoucherService } from '../core/voucher.service'; @Injectable({ providedIn: 'root', }) -export class PurchaseResolver implements Resolve { +export class PurchaseResolver { constructor(private ser: VoucherService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/purchase/purchase-routing.module.ts b/overlord/src/app/purchase/purchase-routing.module.ts index 70a3ec5a..2b1f4e7b 100644 --- a/overlord/src/app/purchase/purchase-routing.module.ts +++ b/overlord/src/app/purchase/purchase-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -11,24 +11,30 @@ const purchaseRoutes: Routes = [ { path: '', component: PurchaseComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Purchase', }, resolve: { - voucher: PurchaseResolver, + voucher: (route: ActivatedRouteSnapshot) => inject(PurchaseResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, { path: ':id', component: PurchaseComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Purchase', }, resolve: { - voucher: PurchaseResolver, + voucher: (route: ActivatedRouteSnapshot) => inject(PurchaseResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/purchase/purchase.module.ts b/overlord/src/app/purchase/purchase.module.ts index f1476c36..68de665a 100644 --- a/overlord/src/app/purchase/purchase.module.ts +++ b/overlord/src/app/purchase/purchase.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -24,6 +23,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSelectModule } from '@angular/material/select'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { HotkeyModule } from 'angular2-hotkeys'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/purchases/purchases-datasource.ts b/overlord/src/app/purchases/purchases-datasource.ts index 610f2b6c..e164d038 100644 --- a/overlord/src/app/purchases/purchases-datasource.ts +++ b/overlord/src/app/purchases/purchases-datasource.ts @@ -20,11 +20,7 @@ export class PurchasesDataSource extends DataSource { } connect(): Observable { - const dataMutations: ( - | Observable - | EventEmitter - | EventEmitter - )[] = [observableOf(this.data)]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -37,7 +33,7 @@ export class PurchasesDataSource extends DataSource { this.paginator.length = this.data.length; } - return merge(...dataMutations).pipe( + return merge(observableOf(this.data), ...dataMutations).pipe( map(() => this.getPagedData(this.getSortedData([...this.data]))), ); } diff --git a/overlord/src/app/purchases/purchases-resolver.service.ts b/overlord/src/app/purchases/purchases-resolver.service.ts index eefd4fc7..c17ccba9 100644 --- a/overlord/src/app/purchases/purchases-resolver.service.ts +++ b/overlord/src/app/purchases/purchases-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Purchases } from './purchases'; @@ -8,7 +8,7 @@ import { PurchasesService } from './purchases.service'; @Injectable({ providedIn: 'root', }) -export class PurchasesResolver implements Resolve { +export class PurchasesResolver { constructor(private ser: PurchasesService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/purchases/purchases-routing.module.ts b/overlord/src/app/purchases/purchases-routing.module.ts index e57279d4..a65c541a 100644 --- a/overlord/src/app/purchases/purchases-routing.module.ts +++ b/overlord/src/app/purchases/purchases-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -11,12 +11,15 @@ const purchasesRoutes: Routes = [ { path: '', component: PurchasesComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Purchases', }, resolve: { - info: PurchasesResolver, + info: (route: ActivatedRouteSnapshot) => inject(PurchasesResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/purchases/purchases.module.ts b/overlord/src/app/purchases/purchases.module.ts index 620209f8..04baf734 100644 --- a/overlord/src/app/purchases/purchases.module.ts +++ b/overlord/src/app/purchases/purchases.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -22,6 +21,7 @@ import { MatPaginatorModule } from '@angular/material/paginator'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/rate-contract/rate-contract-list-resolver.service.ts b/overlord/src/app/rate-contract/rate-contract-list-resolver.service.ts index 00ef5188..5e0bbc25 100644 --- a/overlord/src/app/rate-contract/rate-contract-list-resolver.service.ts +++ b/overlord/src/app/rate-contract/rate-contract-list-resolver.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { Resolve } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { RateContract } from './rate-contract'; @@ -8,7 +7,7 @@ import { RateContractService } from './rate-contract.service'; @Injectable({ providedIn: 'root', }) -export class RateContractListResolver implements Resolve { +export class RateContractListResolver { constructor(private ser: RateContractService) {} resolve(): Observable { diff --git a/overlord/src/app/rate-contract/rate-contract-list/rate-contract-list-datasource.ts b/overlord/src/app/rate-contract/rate-contract-list/rate-contract-list-datasource.ts index b3b45b12..2468fb06 100644 --- a/overlord/src/app/rate-contract/rate-contract-list/rate-contract-list-datasource.ts +++ b/overlord/src/app/rate-contract/rate-contract-list/rate-contract-list-datasource.ts @@ -20,11 +20,7 @@ export class RateContractListDatasource extends DataSource { } connect(): Observable { - const dataMutations: ( - | Observable - | EventEmitter - | EventEmitter - )[] = [observableOf(this.data)]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -37,7 +33,7 @@ export class RateContractListDatasource extends DataSource { this.paginator.length = this.data.length; } - return merge(...dataMutations).pipe( + return merge(observableOf(this.data), ...dataMutations).pipe( map(() => this.getPagedData(this.getSortedData([...this.data]))), ); } diff --git a/overlord/src/app/rate-contract/rate-contract-resolver.service.ts b/overlord/src/app/rate-contract/rate-contract-resolver.service.ts index c2320cda..5d17b654 100644 --- a/overlord/src/app/rate-contract/rate-contract-resolver.service.ts +++ b/overlord/src/app/rate-contract/rate-contract-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { RateContract } from './rate-contract'; @@ -8,7 +8,7 @@ import { RateContractService } from './rate-contract.service'; @Injectable({ providedIn: 'root', }) -export class RateContractResolver implements Resolve { +export class RateContractResolver { constructor(private ser: RateContractService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/rate-contract/rate-contract-routing.module.ts b/overlord/src/app/rate-contract/rate-contract-routing.module.ts index 5e1396ac..1fcb9066 100644 --- a/overlord/src/app/rate-contract/rate-contract-routing.module.ts +++ b/overlord/src/app/rate-contract/rate-contract-routing.module.ts @@ -1,46 +1,55 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; import { RateContractDetailComponent } from './rate-contract-detail/rate-contract-detail.component'; -import { RateContractListResolver } from './rate-contract-list-resolver.service'; import { RateContractListComponent } from './rate-contract-list/rate-contract-list.component'; +import { RateContractListResolver } from './rate-contract-list-resolver.service'; import { RateContractResolver } from './rate-contract-resolver.service'; const rateContractRoutes: Routes = [ { path: '', component: RateContractListComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Rate Contracts', }, resolve: { - list: RateContractListResolver, + list: () => inject(RateContractListResolver).resolve(), }, }, { path: 'new', component: RateContractDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Rate Contracts', }, resolve: { - item: RateContractResolver, + item: (route: ActivatedRouteSnapshot) => inject(RateContractResolver).resolve(route), }, }, { path: ':id', component: RateContractDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Rate Contracts', }, resolve: { - item: RateContractResolver, + item: (route: ActivatedRouteSnapshot) => inject(RateContractResolver).resolve(route), }, }, ]; diff --git a/overlord/src/app/rate-contract/rate-contract.module.ts b/overlord/src/app/rate-contract/rate-contract.module.ts index fa67216d..87ac1557 100644 --- a/overlord/src/app/rate-contract/rate-contract.module.ts +++ b/overlord/src/app/rate-contract/rate-contract.module.ts @@ -2,7 +2,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -16,6 +15,7 @@ import { MatPaginatorModule } from '@angular/material/paginator'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/raw-material-cost/raw-material-cost-datasource.ts b/overlord/src/app/raw-material-cost/raw-material-cost-datasource.ts index 74b2fa64..3e8c6a76 100644 --- a/overlord/src/app/raw-material-cost/raw-material-cost-datasource.ts +++ b/overlord/src/app/raw-material-cost/raw-material-cost-datasource.ts @@ -20,11 +20,7 @@ export class RawMaterialCostDataSource extends DataSource { } connect(): Observable { - const dataMutations: ( - | Observable - | EventEmitter - | EventEmitter - )[] = [observableOf(this.data)]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -37,7 +33,7 @@ export class RawMaterialCostDataSource extends DataSource { this.paginator.length = this.data.length; } - return merge(...dataMutations).pipe( + return merge(observableOf(this.data), ...dataMutations).pipe( map(() => this.getPagedData(this.getSortedData([...this.data]))), ); } diff --git a/overlord/src/app/raw-material-cost/raw-material-cost-resolver.service.ts b/overlord/src/app/raw-material-cost/raw-material-cost-resolver.service.ts index 70449e15..cfb9997f 100644 --- a/overlord/src/app/raw-material-cost/raw-material-cost-resolver.service.ts +++ b/overlord/src/app/raw-material-cost/raw-material-cost-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { RawMaterialCost } from './raw-material-cost'; @@ -8,7 +8,7 @@ import { RawMaterialCostService } from './raw-material-cost.service'; @Injectable({ providedIn: 'root', }) -export class RawMaterialCostResolver implements Resolve { +export class RawMaterialCostResolver { constructor(private ser: RawMaterialCostService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/raw-material-cost/raw-material-cost-routing.module.ts b/overlord/src/app/raw-material-cost/raw-material-cost-routing.module.ts index 9f8d92fb..646340d1 100644 --- a/overlord/src/app/raw-material-cost/raw-material-cost-routing.module.ts +++ b/overlord/src/app/raw-material-cost/raw-material-cost-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -11,24 +11,30 @@ const rawMaterialCostRoutes: Routes = [ { path: '', component: RawMaterialCostComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Raw Material Cost', }, resolve: { - info: RawMaterialCostResolver, + info: (route: ActivatedRouteSnapshot) => inject(RawMaterialCostResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, { path: ':id', component: RawMaterialCostComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Raw Material Cost', }, resolve: { - info: RawMaterialCostResolver, + info: (route: ActivatedRouteSnapshot) => inject(RawMaterialCostResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/raw-material-cost/raw-material-cost.module.ts b/overlord/src/app/raw-material-cost/raw-material-cost.module.ts index a3db9936..94c37bac 100644 --- a/overlord/src/app/raw-material-cost/raw-material-cost.module.ts +++ b/overlord/src/app/raw-material-cost/raw-material-cost.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -22,6 +21,7 @@ import { MatPaginatorModule } from '@angular/material/paginator'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/receipt/receipt-accounts-resolver.service.ts b/overlord/src/app/receipt/receipt-accounts-resolver.service.ts index abd9784a..5562a1f5 100644 --- a/overlord/src/app/receipt/receipt-accounts-resolver.service.ts +++ b/overlord/src/app/receipt/receipt-accounts-resolver.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { Resolve } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Account } from '../core/account'; @@ -8,7 +7,7 @@ import { AccountService } from '../core/account.service'; @Injectable({ providedIn: 'root', }) -export class ReceiptAccountsResolver implements Resolve { +export class ReceiptAccountsResolver { constructor(private ser: AccountService) {} resolve(): Observable { diff --git a/overlord/src/app/receipt/receipt-resolver.service.ts b/overlord/src/app/receipt/receipt-resolver.service.ts index 998e70be..7c5ab154 100644 --- a/overlord/src/app/receipt/receipt-resolver.service.ts +++ b/overlord/src/app/receipt/receipt-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Voucher } from '../core/voucher'; @@ -8,7 +8,7 @@ import { VoucherService } from '../core/voucher.service'; @Injectable({ providedIn: 'root', }) -export class ReceiptResolver implements Resolve { +export class ReceiptResolver { constructor(private ser: VoucherService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/receipt/receipt-routing.module.ts b/overlord/src/app/receipt/receipt-routing.module.ts index 6b89c849..b084fae4 100644 --- a/overlord/src/app/receipt/receipt-routing.module.ts +++ b/overlord/src/app/receipt/receipt-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -12,26 +12,32 @@ const receiptRoutes: Routes = [ { path: '', component: ReceiptComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'journal', }, resolve: { - receiptAccounts: ReceiptAccountsResolver, - voucher: ReceiptResolver, + receiptAccounts: () => inject(ReceiptAccountsResolver).resolve(), + voucher: (route: ActivatedRouteSnapshot) => inject(ReceiptResolver).resolve(route), }, runGuardsAndResolvers: 'paramsChange', }, { path: ':id', component: ReceiptComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'journal', }, resolve: { - receiptAccounts: ReceiptAccountsResolver, - voucher: ReceiptResolver, + receiptAccounts: () => inject(ReceiptAccountsResolver).resolve(), + voucher: (route: ActivatedRouteSnapshot) => inject(ReceiptResolver).resolve(route), }, runGuardsAndResolvers: 'paramsChange', }, diff --git a/overlord/src/app/receipt/receipt.module.ts b/overlord/src/app/receipt/receipt.module.ts index d94399e3..a8a8a3a9 100644 --- a/overlord/src/app/receipt/receipt.module.ts +++ b/overlord/src/app/receipt/receipt.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -24,6 +23,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSelectModule } from '@angular/material/select'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { HotkeyModule } from 'angular2-hotkeys'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/recipe-template/recipe-template-detail/recipe-template-detail.component.css b/overlord/src/app/recipe-template/recipe-template-detail/recipe-template-detail.component.css new file mode 100644 index 00000000..e69de29b diff --git a/overlord/src/app/recipe-template/recipe-template-detail/recipe-template-detail.component.html b/overlord/src/app/recipe-template/recipe-template-detail/recipe-template-detail.component.html new file mode 100644 index 00000000..641d328c --- /dev/null +++ b/overlord/src/app/recipe-template/recipe-template-detail/recipe-template-detail.component.html @@ -0,0 +1,41 @@ +
+ + + RecipeTemplate + + +
+
+ + Name + + + + Date + + + + + Is Selected? +
+
+ + Text + + +
+
+
+ + + + +
+
diff --git a/overlord/src/app/recipe-template/recipe-template-detail/recipe-template-detail.component.spec.ts b/overlord/src/app/recipe-template/recipe-template-detail/recipe-template-detail.component.spec.ts new file mode 100644 index 00000000..6b22c93e --- /dev/null +++ b/overlord/src/app/recipe-template/recipe-template-detail/recipe-template-detail.component.spec.ts @@ -0,0 +1,28 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { ReactiveFormsModule } from '@angular/forms'; +import { MatDialogModule } from '@angular/material/dialog'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { RecipeTemplateDetailComponent } from './recipe-template-detail.component'; + +describe('RecipeTemplateDetailComponent', () => { + let component: RecipeTemplateDetailComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [MatDialogModule, ReactiveFormsModule, RouterTestingModule], + declarations: [RecipeTemplateDetailComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(RecipeTemplateDetailComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/overlord/src/app/recipe-template/recipe-template-detail/recipe-template-detail.component.ts b/overlord/src/app/recipe-template/recipe-template-detail/recipe-template-detail.component.ts new file mode 100644 index 00000000..1d93c979 --- /dev/null +++ b/overlord/src/app/recipe-template/recipe-template-detail/recipe-template-detail.component.ts @@ -0,0 +1,114 @@ +import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core'; +import { FormControl, FormGroup } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute, Router } from '@angular/router'; +import * as moment from 'moment'; + +import { ToasterService } from '../../core/toaster.service'; +import { ConfirmDialogComponent } from '../../shared/confirm-dialog/confirm-dialog.component'; +import { RecipeTemplate } from '../recipe-template'; +import { RecipeTemplateService } from '../recipe-template.service'; + +@Component({ + selector: 'app-recipe-template-detail', + templateUrl: './recipe-template-detail.component.html', + styleUrls: ['./recipe-template-detail.component.css'], +}) +export class RecipeTemplateDetailComponent implements OnInit, AfterViewInit { + @ViewChild('nameElement', { static: true }) nameElement?: ElementRef; + form: FormGroup<{ + name: FormControl; + date: FormControl; + text: FormControl; + selected: FormControl; + }>; + + item: RecipeTemplate = new RecipeTemplate(); + + constructor( + private route: ActivatedRoute, + private router: Router, + private dialog: MatDialog, + private toaster: ToasterService, + private ser: RecipeTemplateService, + ) { + this.form = new FormGroup({ + name: new FormControl('', { nonNullable: true }), + date: new FormControl(new Date(), { nonNullable: true }), + text: new FormControl('', { nonNullable: true }), + selected: new FormControl(false, { nonNullable: true }), + }); + } + + ngOnInit() { + this.route.data.subscribe((value) => { + const data = value as { item: RecipeTemplate }; + + this.showItem(data.item); + }); + } + + showItem(item: RecipeTemplate) { + this.item = item; + this.form.setValue({ + name: this.item.name, + date: moment(this.item.date, 'DD-MMM-YYYY').toDate(), + text: this.item.text, + selected: this.item.selected, + }); + } + + ngAfterViewInit() { + setTimeout(() => { + if (this.nameElement) { + this.nameElement.nativeElement.focus(); + } + }, 0); + } + + save() { + this.ser.saveOrUpdate(this.getItem()).subscribe( + () => { + this.toaster.show('Success', ''); + this.router.navigateByUrl('/recipe-templates'); + }, + (error) => { + this.toaster.show('Danger', error); + }, + ); + } + + delete() { + this.ser.delete(this.item.id).subscribe( + () => { + this.toaster.show('Success', ''); + this.router.navigateByUrl('/recipe-templates'); + }, + (error) => { + this.toaster.show('Danger', error); + }, + ); + } + + confirmDelete(): void { + const dialogRef = this.dialog.open(ConfirmDialogComponent, { + width: '250px', + data: { title: 'Delete Recipe Template?', content: 'Are you sure? This cannot be undone.' }, + }); + + dialogRef.afterClosed().subscribe((result: boolean) => { + if (result) { + this.delete(); + } + }); + } + + getItem(): RecipeTemplate { + const formModel = this.form.value; + this.item.name = formModel.name ?? ''; + this.item.date = moment(formModel.date).format('DD-MMM-YYYY'); + this.item.text = formModel.text ?? ''; + this.item.selected = formModel.selected ?? false; + return this.item; + } +} diff --git a/overlord/src/app/recipe-template/recipe-template-list-resolver.service.spec.ts b/overlord/src/app/recipe-template/recipe-template-list-resolver.service.spec.ts new file mode 100644 index 00000000..f387186f --- /dev/null +++ b/overlord/src/app/recipe-template/recipe-template-list-resolver.service.spec.ts @@ -0,0 +1,21 @@ +import { HttpClientModule } from '@angular/common/http'; +import { inject, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { RecipeTemplateListResolver } from './recipe-template-list-resolver.service'; + +describe('RecipeTemplateListResolver', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientModule, RouterTestingModule], + providers: [RecipeTemplateListResolver], + }); + }); + + it('should be created', inject( + [RecipeTemplateListResolver], + (service: RecipeTemplateListResolver) => { + expect(service).toBeTruthy(); + }, + )); +}); diff --git a/overlord/src/app/recipe-template/recipe-template-list-resolver.service.ts b/overlord/src/app/recipe-template/recipe-template-list-resolver.service.ts new file mode 100644 index 00000000..aeea2eb4 --- /dev/null +++ b/overlord/src/app/recipe-template/recipe-template-list-resolver.service.ts @@ -0,0 +1,16 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs/internal/Observable'; + +import { RecipeTemplate } from './recipe-template'; +import { RecipeTemplateService } from './recipe-template.service'; + +@Injectable({ + providedIn: 'root', +}) +export class RecipeTemplateListResolver { + constructor(private ser: RecipeTemplateService) {} + + resolve(): Observable { + return this.ser.list(); + } +} diff --git a/overlord/src/app/recipe-template/recipe-template-list/recipe-template-list-datasource.ts b/overlord/src/app/recipe-template/recipe-template-list/recipe-template-list-datasource.ts new file mode 100644 index 00000000..8090dc2d --- /dev/null +++ b/overlord/src/app/recipe-template/recipe-template-list/recipe-template-list-datasource.ts @@ -0,0 +1,72 @@ +import { DataSource } from '@angular/cdk/collections'; +import { EventEmitter } from '@angular/core'; +import { MatPaginator, PageEvent } from '@angular/material/paginator'; +import { MatSort, Sort } from '@angular/material/sort'; +import { merge, Observable, of as observableOf } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { RecipeTemplate } from '../recipe-template'; + +/** Simple sort comparator for example ID/Name columns (for client-side sorting). */ +const compare = (a: string | number, b: string | number, isAsc: boolean) => + (a < b ? -1 : 1) * (isAsc ? 1 : -1); +export class RecipeTemplateListDataSource extends DataSource { + constructor( + public data: RecipeTemplate[], + private paginator?: MatPaginator, + private sort?: MatSort, + ) { + super(); + } + + connect(): Observable { + const dataMutations: (EventEmitter | EventEmitter)[] = []; + if (this.paginator) { + dataMutations.push((this.paginator as MatPaginator).page); + } + if (this.sort) { + dataMutations.push((this.sort as MatSort).sortChange); + } + + // Set the paginators length + if (this.paginator) { + this.paginator.length = this.data.length; + } + + return merge(observableOf(this.data), ...dataMutations).pipe( + map(() => this.getPagedData(this.getSortedData([...this.data]))), + ); + } + + disconnect() {} + + private getPagedData(data: RecipeTemplate[]) { + if (this.paginator === undefined) { + return data; + } + const startIndex = this.paginator.pageIndex * this.paginator.pageSize; + return data.splice(startIndex, this.paginator.pageSize); + } + + private getSortedData(data: RecipeTemplate[]) { + if (this.sort === undefined) { + return data; + } + if (!this.sort.active || this.sort.direction === '') { + return data; + } + + const sort = this.sort as MatSort; + return data.sort((a, b) => { + const isAsc = sort.direction === 'asc'; + switch (sort.active) { + case 'name': + return compare(a.name, b.name, isAsc); + case 'id': + return compare(+a.id, +b.id, isAsc); + default: + return 0; + } + }); + } +} diff --git a/overlord/src/app/recipe-template/recipe-template-list/recipe-template-list.component.css b/overlord/src/app/recipe-template/recipe-template-list/recipe-template-list.component.css new file mode 100644 index 00000000..e69de29b diff --git a/overlord/src/app/recipe-template/recipe-template-list/recipe-template-list.component.html b/overlord/src/app/recipe-template/recipe-template-list/recipe-template-list.component.html new file mode 100644 index 00000000..74a84e50 --- /dev/null +++ b/overlord/src/app/recipe-template/recipe-template-list/recipe-template-list.component.html @@ -0,0 +1,46 @@ + + + + Recipe Templates + + add_box + Add + + + + + + + + Name + {{ row.name }} + + + + + Is Selected? + {{ row.selected }} + + + + + Date + {{ row.date }} + + + + + + + + + + diff --git a/overlord/src/app/recipe-template/recipe-template-list/recipe-template-list.component.spec.ts b/overlord/src/app/recipe-template/recipe-template-list/recipe-template-list.component.spec.ts new file mode 100644 index 00000000..6688b089 --- /dev/null +++ b/overlord/src/app/recipe-template/recipe-template-list/recipe-template-list.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { RecipeTemplateListComponent } from './recipe-template-list.component'; + +describe('RecipeTemplateListComponent', () => { + let component: RecipeTemplateListComponent; + let fixture: ComponentFixture; + + beforeEach(fakeAsync(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + declarations: [RecipeTemplateListComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(RecipeTemplateListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should compile', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/overlord/src/app/recipe-template/recipe-template-list/recipe-template-list.component.ts b/overlord/src/app/recipe-template/recipe-template-list/recipe-template-list.component.ts new file mode 100644 index 00000000..48a0791f --- /dev/null +++ b/overlord/src/app/recipe-template/recipe-template-list/recipe-template-list.component.ts @@ -0,0 +1,33 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort } from '@angular/material/sort'; +import { ActivatedRoute } from '@angular/router'; + +import { RecipeTemplate } from '../recipe-template'; + +import { RecipeTemplateListDataSource } from './recipe-template-list-datasource'; + +@Component({ + selector: 'app-recipe-template-list', + templateUrl: './recipe-template-list.component.html', + styleUrls: ['./recipe-template-list.component.css'], +}) +export class RecipeTemplateListComponent implements OnInit { + @ViewChild(MatPaginator, { static: true }) paginator?: MatPaginator; + @ViewChild(MatSort, { static: true }) sort?: MatSort; + list: RecipeTemplate[] = []; + dataSource: RecipeTemplateListDataSource = new RecipeTemplateListDataSource(this.list); + /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ + displayedColumns = ['name', 'selected', 'date']; + + constructor(private route: ActivatedRoute) {} + + ngOnInit() { + this.route.data.subscribe((value) => { + const data = value as { list: RecipeTemplate[] }; + + this.list = data.list; + }); + this.dataSource = new RecipeTemplateListDataSource(this.list, this.paginator, this.sort); + } +} diff --git a/overlord/src/app/recipe-template/recipe-template-resolver.service.spec.ts b/overlord/src/app/recipe-template/recipe-template-resolver.service.spec.ts new file mode 100644 index 00000000..296ba168 --- /dev/null +++ b/overlord/src/app/recipe-template/recipe-template-resolver.service.spec.ts @@ -0,0 +1,18 @@ +import { HttpClientModule } from '@angular/common/http'; +import { inject, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { RecipeTemplateResolver } from './recipe-template-resolver.service'; + +describe('RecipeTemplateResolver', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientModule, RouterTestingModule], + providers: [RecipeTemplateResolver], + }); + }); + + it('should be created', inject([RecipeTemplateResolver], (service: RecipeTemplateResolver) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/overlord/src/app/recipe-template/recipe-template-resolver.service.ts b/overlord/src/app/recipe-template/recipe-template-resolver.service.ts new file mode 100644 index 00000000..af9dda55 --- /dev/null +++ b/overlord/src/app/recipe-template/recipe-template-resolver.service.ts @@ -0,0 +1,18 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot } from '@angular/router'; +import { Observable } from 'rxjs/internal/Observable'; + +import { RecipeTemplate } from './recipe-template'; +import { RecipeTemplateService } from './recipe-template.service'; + +@Injectable({ + providedIn: 'root', +}) +export class RecipeTemplateResolver { + constructor(private ser: RecipeTemplateService) {} + + resolve(route: ActivatedRouteSnapshot): Observable { + const id = route.paramMap.get('id'); + return this.ser.get(id); + } +} diff --git a/overlord/src/app/recipe-template/recipe-template-routing.module.spec.ts b/overlord/src/app/recipe-template/recipe-template-routing.module.spec.ts new file mode 100644 index 00000000..6cdc5ab7 --- /dev/null +++ b/overlord/src/app/recipe-template/recipe-template-routing.module.spec.ts @@ -0,0 +1,13 @@ +import { RecipeTemplateRoutingModule } from './recipe-template-routing.module'; + +describe('RecipeTemplateRoutingModule', () => { + let recipeTemplateRoutingModule: RecipeTemplateRoutingModule; + + beforeEach(() => { + recipeTemplateRoutingModule = new RecipeTemplateRoutingModule(); + }); + + it('should create an instance', () => { + expect(recipeTemplateRoutingModule).toBeTruthy(); + }); +}); diff --git a/overlord/src/app/recipe-template/recipe-template-routing.module.ts b/overlord/src/app/recipe-template/recipe-template-routing.module.ts new file mode 100644 index 00000000..4b79b32e --- /dev/null +++ b/overlord/src/app/recipe-template/recipe-template-routing.module.ts @@ -0,0 +1,62 @@ +import { CommonModule } from '@angular/common'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; + +import { AuthGuard } from '../auth/auth-guard.service'; + +import { RecipeTemplateDetailComponent } from './recipe-template-detail/recipe-template-detail.component'; +import { RecipeTemplateListComponent } from './recipe-template-list/recipe-template-list.component'; +import { RecipeTemplateListResolver } from './recipe-template-list-resolver.service'; +import { RecipeTemplateResolver } from './recipe-template-resolver.service'; + +const recipeTemplateRoutes: Routes = [ + { + path: '', + component: RecipeTemplateListComponent, + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], + data: { + permission: 'Recipes', + }, + resolve: { + list: () => inject(RecipeTemplateListResolver).resolve(), + }, + }, + { + path: 'new', + component: RecipeTemplateDetailComponent, + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], + data: { + permission: 'Recipes', + }, + resolve: { + item: (route: ActivatedRouteSnapshot) => inject(RecipeTemplateResolver).resolve(route), + }, + }, + { + path: ':id', + component: RecipeTemplateDetailComponent, + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], + data: { + permission: 'Recipes', + }, + resolve: { + item: (route: ActivatedRouteSnapshot) => inject(RecipeTemplateResolver).resolve(route), + }, + }, +]; + +@NgModule({ + imports: [CommonModule, RouterModule.forChild(recipeTemplateRoutes)], + exports: [RouterModule], + providers: [RecipeTemplateListResolver, RecipeTemplateResolver], +}) +export class RecipeTemplateRoutingModule {} diff --git a/overlord/src/app/recipe-template/recipe-template.module.spec.ts b/overlord/src/app/recipe-template/recipe-template.module.spec.ts new file mode 100644 index 00000000..27a6ca17 --- /dev/null +++ b/overlord/src/app/recipe-template/recipe-template.module.spec.ts @@ -0,0 +1,13 @@ +import { RecipeTemplateModule } from './recipe-template.module'; + +describe('RecipeTemplateModule', () => { + let recipeTemplateModule: RecipeTemplateModule; + + beforeEach(() => { + recipeTemplateModule = new RecipeTemplateModule(); + }); + + it('should create an instance', () => { + expect(recipeTemplateModule).toBeTruthy(); + }); +}); diff --git a/overlord/src/app/recipe-template/recipe-template.module.ts b/overlord/src/app/recipe-template/recipe-template.module.ts new file mode 100644 index 00000000..585743fb --- /dev/null +++ b/overlord/src/app/recipe-template/recipe-template.module.ts @@ -0,0 +1,66 @@ +import { CdkTableModule } from '@angular/cdk/table'; +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { + DateAdapter, + MAT_DATE_FORMATS, + MAT_DATE_LOCALE, + MatNativeDateModule, +} from '@angular/material/core'; +import { MatDatepickerModule } from '@angular/material/datepicker'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatSortModule } from '@angular/material/sort'; +import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; + +import { SharedModule } from '../shared/shared.module'; + +import { RecipeTemplateDetailComponent } from './recipe-template-detail/recipe-template-detail.component'; +import { RecipeTemplateListComponent } from './recipe-template-list/recipe-template-list.component'; +import { RecipeTemplateRoutingModule } from './recipe-template-routing.module'; + +export const MY_FORMATS = { + parse: { + dateInput: 'DD-MMM-YYYY', + }, + display: { + dateInput: 'DD-MMM-YYYY', + monthYearLabel: 'MMM YYYY', + dateA11yLabel: 'DD-MMM-YYYY', + monthYearA11yLabel: 'MMM YYYY', + }, +}; + +@NgModule({ + imports: [ + CommonModule, + CdkTableModule, + MatButtonModule, + MatCardModule, + MatCheckboxModule, + MatDatepickerModule, + MatIconModule, + MatInputModule, + MatNativeDateModule, + MatPaginatorModule, + MatProgressSpinnerModule, + MatSortModule, + MatTableModule, + ReactiveFormsModule, + SharedModule, + RecipeTemplateRoutingModule, + ], + declarations: [RecipeTemplateListComponent, RecipeTemplateDetailComponent], + providers: [ + { provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE] }, + { provide: MAT_DATE_FORMATS, useValue: MY_FORMATS }, + ], +}) +export class RecipeTemplateModule {} diff --git a/overlord/src/app/recipe-template/recipe-template.service.spec.ts b/overlord/src/app/recipe-template/recipe-template.service.spec.ts new file mode 100644 index 00000000..8a4cf405 --- /dev/null +++ b/overlord/src/app/recipe-template/recipe-template.service.spec.ts @@ -0,0 +1,17 @@ +import { HttpClientModule } from '@angular/common/http'; +import { inject, TestBed } from '@angular/core/testing'; + +import { RecipeTemplateService } from './recipe-template.service'; + +describe('RecipeTemplateService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientModule], + providers: [RecipeTemplateService], + }); + }); + + it('should be created', inject([RecipeTemplateService], (service: RecipeTemplateService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/overlord/src/app/recipe-template/recipe-template.service.ts b/overlord/src/app/recipe-template/recipe-template.service.ts new file mode 100644 index 00000000..6b5a0481 --- /dev/null +++ b/overlord/src/app/recipe-template/recipe-template.service.ts @@ -0,0 +1,58 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs/internal/Observable'; +import { catchError } from 'rxjs/operators'; + +import { ErrorLoggerService } from '../core/error-logger.service'; + +import { RecipeTemplate } from './recipe-template'; + +const url = '/api/recipe-templates'; +const serviceName = 'RecipeTemplateService'; + +@Injectable({ + providedIn: 'root', +}) +export class RecipeTemplateService { + constructor(private http: HttpClient, private log: ErrorLoggerService) {} + + get(id: string | null): Observable { + const getUrl: string = id === null ? url : `${url}/${id}`; + return this.http + .get(getUrl) + .pipe( + catchError(this.log.handleError(serviceName, `get id=${id}`)), + ) as Observable; + } + + list(): Observable { + return this.http + .get(`${url}/list`) + .pipe(catchError(this.log.handleError(serviceName, 'list'))) as Observable; + } + + save(recipeTemplate: RecipeTemplate): Observable { + return this.http + .post(`${url}`, recipeTemplate) + .pipe(catchError(this.log.handleError(serviceName, 'save'))) as Observable; + } + + update(recipeTemplate: RecipeTemplate): Observable { + return this.http + .put(`${url}/${recipeTemplate.id}`, recipeTemplate) + .pipe(catchError(this.log.handleError(serviceName, 'update'))) as Observable; + } + + saveOrUpdate(recipeTemplate: RecipeTemplate): Observable { + if (!recipeTemplate.id) { + return this.save(recipeTemplate); + } + return this.update(recipeTemplate); + } + + delete(id: string): Observable { + return this.http + .delete(`${url}/${id}`) + .pipe(catchError(this.log.handleError(serviceName, 'delete'))) as Observable; + } +} diff --git a/overlord/src/app/recipe-template/recipe-template.ts b/overlord/src/app/recipe-template/recipe-template.ts new file mode 100644 index 00000000..61598f25 --- /dev/null +++ b/overlord/src/app/recipe-template/recipe-template.ts @@ -0,0 +1,16 @@ +export class RecipeTemplate { + id: string; + name: string; + date: string; + text: string; + selected: boolean; + + public constructor(init?: Partial) { + this.id = ''; + this.name = ''; + this.date = ''; + this.text = ''; + this.selected = false; + Object.assign(this, init); + } +} diff --git a/overlord/src/app/recipe/recipe-detail/recipe-detail.component.html b/overlord/src/app/recipe/recipe-detail/recipe-detail.component.html index e37d4a07..deb606e2 100644 --- a/overlord/src/app/recipe/recipe-detail/recipe-detail.component.html +++ b/overlord/src/app/recipe/recipe-detail/recipe-detail.component.html @@ -5,12 +5,22 @@
+ + Date + + + + - - - {{ p.validFrom }} to {{ p.validTill }} - - + Source +
@@ -39,27 +49,12 @@ Yield - - Sale Price - - ₹  - - - Cost Price - - ₹  - - - Cost Percentage - - % -
- + Ingredient + Description + + + Rate ₹  @@ -98,7 +97,7 @@ Product - {{ row.product.name }} + {{ row.product.name }} {{ row.description }} @@ -109,22 +108,6 @@ > - - - Rate - {{ - row.price | currency : 'INR' - }} - - - - - Amount - {{ - row.quantity * row.price | currency : 'INR' - }} - - Action @@ -138,6 +121,18 @@ + + Instructions + + + + Garnishing + + + + Plating + + diff --git a/overlord/src/app/recipe/recipe-detail/recipe-detail.component.ts b/overlord/src/app/recipe/recipe-detail/recipe-detail.component.ts index fab1c693..e04fc3cf 100644 --- a/overlord/src/app/recipe/recipe-detail/recipe-detail.component.ts +++ b/overlord/src/app/recipe/recipe-detail/recipe-detail.component.ts @@ -3,7 +3,7 @@ import { FormControl, FormGroup } from '@angular/forms'; import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute, Router } from '@angular/router'; -import { round } from 'mathjs'; +import * as moment from 'moment'; import { BehaviorSubject, Observable, of as observableOf } from 'rxjs'; import { debounceTime, distinctUntilChanged, map, switchMap } from 'rxjs/operators'; import { Period } from 'src/app/period/period'; @@ -31,17 +31,19 @@ export class RecipeDetailComponent implements OnInit, AfterViewInit { public itemsObservable = new BehaviorSubject([]); dataSource: RecipeDetailDatasource = new RecipeDetailDatasource(this.itemsObservable); form: FormGroup<{ - period: FormControl; + date: FormControl; + source: FormControl; recipeYield: FormControl; - costPrice: FormControl; - salePrice: FormControl; - costPercentage: FormControl; product: FormControl; addRow: FormGroup<{ ingredient: FormControl; quantity: FormControl; + description: FormControl; rate: FormControl; }>; + instructions: FormControl; + garnishing: FormControl; + plating: FormControl; }>; periods: Period[] = []; @@ -51,7 +53,7 @@ export class RecipeDetailComponent implements OnInit, AfterViewInit { ingredients: Observable; item: Recipe = new Recipe(); - displayedColumns = ['product', 'quantity', 'rate', 'amount', 'action']; + displayedColumns = ['product', 'quantity', 'action']; constructor( private route: ActivatedRoute, @@ -65,17 +67,19 @@ export class RecipeDetailComponent implements OnInit, AfterViewInit { this.product = null; this.ingredient = null; this.form = new FormGroup({ - period: new FormControl(new Period(), { nonNullable: true }), + date: new FormControl(new Date(), { nonNullable: true }), + source: new FormControl('', { nonNullable: true }), recipeYield: new FormControl(null), - costPrice: new FormControl(null), - salePrice: new FormControl(null), - costPercentage: new FormControl(null), product: new FormControl(new ProductSku()), addRow: new FormGroup({ ingredient: new FormControl(''), quantity: new FormControl('', { nonNullable: true }), + description: new FormControl('', { nonNullable: true }), rate: new FormControl('', { nonNullable: true }), }), + instructions: new FormControl('', { nonNullable: true }), + garnishing: new FormControl('', { nonNullable: true }), + plating: new FormControl('', { nonNullable: true }), }); // Setup Product Autocomplete this.products = this.form.controls.product.valueChanges.pipe( @@ -107,19 +111,20 @@ export class RecipeDetailComponent implements OnInit, AfterViewInit { showItem(item: Recipe) { this.item = item; - item.period = this.periods.find((x) => x.id == item.period.id) as Period; this.form.setValue({ - period: item.period, + date: moment(item.date, 'DD-MMM-YYYY').toDate(), + source: item.source, recipeYield: `${item.recipeYield}`, - salePrice: `${item.salePrice}`, - costPrice: `${item.costPrice}`, - costPercentage: null, product: item.sku, addRow: { ingredient: null, quantity: '', + description: '', rate: '', }, + instructions: item.instructions, + garnishing: item.garnishing, + plating: item.plating, }); this.dataSource = new RecipeDetailDatasource(this.itemsObservable); } @@ -144,16 +149,11 @@ export class RecipeDetailComponent implements OnInit, AfterViewInit { const product: ProductSku = event.option.value; this.item.sku = product; this.form.controls.product.setValue(product); - this.form.controls.salePrice.setValue('' + (product.salePrice ?? 0)); } ingredientSelected(event: MatAutocompleteSelectedEvent): void { const ingredient: ProductSku = event.option.value; this.ingredient = ingredient; - const item = this.getItem(); - this.ser - .getIngredientDetails(ingredient.id, item.period.validFrom, item.period.validTill) - .subscribe((x) => this.form.controls.addRow.controls.rate.setValue('' + x.costPrice)); } addRow() { @@ -177,7 +177,7 @@ export class RecipeDetailComponent implements OnInit, AfterViewInit { new RecipeItem({ product: this.ingredient, quantity, - price: rate, + description: formValue.description ?? '', }), ); this.resetAddRow(); @@ -196,17 +196,6 @@ export class RecipeDetailComponent implements OnInit, AfterViewInit { updateView() { this.itemsObservable.next(this.item.items); - const costPrice = round( - this.item.items.map((x) => x.quantity * x.price).reduce((p, c) => p + c, 0), - 2, - ); - this.form.controls.costPrice.setValue(`${costPrice}`); - const salePrice = this.math.parseAmount(this.form.value.salePrice ?? '0', 2); - if (salePrice < 0) { - return; - } - const costPercentage = round((100 * costPrice) / salePrice, 2); - this.form.controls.costPercentage.setValue(costPercentage); } deleteRow(row: RecipeItem) { @@ -253,12 +242,12 @@ export class RecipeDetailComponent implements OnInit, AfterViewInit { getItem(): Recipe { const formModel = this.form.value; - if (formModel.period) { - this.item.period = formModel.period; - } + this.item.date = moment(formModel.date).format('DD-MMM-YYYY'); + this.item.source = formModel.source ?? ''; this.item.recipeYield = this.math.parseAmount(formModel.recipeYield ?? '1', 2); - this.item.salePrice = this.math.parseAmount(formModel.salePrice ?? '0', 2); - this.item.costPrice = this.math.parseAmount(formModel.costPrice ?? '0', 2); + this.item.instructions = formModel.instructions ?? ''; + this.item.garnishing = formModel.garnishing ?? ''; + this.item.plating = formModel.plating ?? ''; return this.item; } } diff --git a/overlord/src/app/recipe/recipe-item.ts b/overlord/src/app/recipe/recipe-item.ts index c62c3bf9..88fe6515 100644 --- a/overlord/src/app/recipe/recipe-item.ts +++ b/overlord/src/app/recipe/recipe-item.ts @@ -4,12 +4,12 @@ export class RecipeItem { id: string | undefined; product: ProductSku; quantity: number; - price: number; + description: string; public constructor(init?: Partial) { this.product = new ProductSku(); this.quantity = 0; - this.price = 0; + this.description = ''; Object.assign(this, init); } } diff --git a/overlord/src/app/recipe/recipe-list-resolver.service.ts b/overlord/src/app/recipe/recipe-list-resolver.service.ts index 9a47fdaa..0a275056 100644 --- a/overlord/src/app/recipe/recipe-list-resolver.service.ts +++ b/overlord/src/app/recipe/recipe-list-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Recipe } from './recipe'; @@ -8,7 +8,7 @@ import { RecipeService } from './recipe.service'; @Injectable({ providedIn: 'root', }) -export class RecipeListResolver implements Resolve { +export class RecipeListResolver { constructor(private ser: RecipeService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/recipe/recipe-list/recipe-list-datasource.ts b/overlord/src/app/recipe/recipe-list/recipe-list-datasource.ts index 57f0e9f4..ddc65944 100644 --- a/overlord/src/app/recipe/recipe-list/recipe-list-datasource.ts +++ b/overlord/src/app/recipe/recipe-list/recipe-list-datasource.ts @@ -2,7 +2,6 @@ import { DataSource } from '@angular/cdk/collections'; import { EventEmitter } from '@angular/core'; import { MatPaginator, PageEvent } from '@angular/material/paginator'; import { MatSort, Sort } from '@angular/material/sort'; -import * as moment from 'moment'; import { merge, Observable } from 'rxjs'; import { map, tap } from 'rxjs/operators'; @@ -27,23 +26,17 @@ export class RecipeListDatasource extends DataSource { } connect(): Observable { - const dataMutations: ( - | Observable - | Observable - | EventEmitter - | EventEmitter - )[] = [ - this.dataObs.pipe( - tap((x) => { - this.data = x; - }), - ), - this.productGroupFilter.pipe( - tap((x) => { - this.productGroup = x; - }), - ), - ]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; + const d = this.dataObs.pipe( + tap((x) => { + this.data = x; + }), + ); + const pg = this.productGroupFilter.pipe( + tap((x) => { + this.productGroup = x; + }), + ); if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -51,7 +44,7 @@ export class RecipeListDatasource extends DataSource { dataMutations.push((this.sort as MatSort).sortChange); } - return merge(...dataMutations).pipe( + return merge(d, pg, ...dataMutations).pipe( map(() => this.getFilteredData(this.data, this.productGroup)), tap((x: Recipe[]) => { if (this.paginator) { @@ -68,7 +61,7 @@ export class RecipeListDatasource extends DataSource { disconnect() {} private getFilteredData(data: Recipe[], productGroup: string): Recipe[] { - return data.filter((x: Recipe) => productGroup === '' || x.notes === productGroup); + return data.filter((x: Recipe) => productGroup === '' || x.productGroupId === productGroup); } private getPagedData(data: Recipe[]) { @@ -93,12 +86,6 @@ export class RecipeListDatasource extends DataSource { switch (sort.active) { case 'name': return compare(a.sku.name, b.sku.name, isAsc); - case 'salePrice': - return compare(a.salePrice, b.salePrice, isAsc); - case 'costPrice': - return compare(a.costPrice, b.costPrice, isAsc); - case 'costPercentage': - return compare(a.costPrice / a.salePrice, b.costPrice / b.salePrice, isAsc); default: return 0; } @@ -108,7 +95,6 @@ export class RecipeListDatasource extends DataSource { /** Simple sort comparator for example ID/Name columns (for client-side sorting). */ const compare = (a: string | number | Date, b: string | number | Date, isAsc: boolean) => (a < b ? -1 : 1) * (isAsc ? 1 : -1); -/** Simple sort comparator for example ID/Name columns (for client-side sorting). */ -const compareDate = (a: string, b: string, isAsc: boolean) => - (moment(a, 'DD-MMM-YYYY').toDate() < moment(b, 'DD-MMM-YYYY').toDate() ? -1 : 1) * - (isAsc ? 1 : -1); +// const compareDate = (a: string, b: string, isAsc: boolean) => +// (moment(a, 'DD-MMM-YYYY').toDate() < moment(b, 'DD-MMM-YYYY').toDate() ? -1 : 1) * +// (isAsc ? 1 : -1); diff --git a/overlord/src/app/recipe/recipe-list/recipe-list.component.html b/overlord/src/app/recipe/recipe-list/recipe-list.component.html index 544fe5a5..ae2173f9 100644 --- a/overlord/src/app/recipe/recipe-list/recipe-list.component.html +++ b/overlord/src/app/recipe/recipe-list/recipe-list.component.html @@ -2,6 +2,9 @@ Recipes + + save_alt + add_box Add @@ -40,25 +43,22 @@ >{{ row.sku.name }} - - - - Sale Price - {{ row.salePrice | currency : 'INR' }} + + + Yield + {{ row.recipeYield }} {{ row.units }} - - - Cost Price - {{ row.costPrice | currency : 'INR' }} + + + Date + {{ row.date }} - - - Cost %age - {{ - row.costPrice / row.salePrice | percent : '1.2-2' - }} + + + Source + {{ row.source }} diff --git a/overlord/src/app/recipe/recipe-list/recipe-list.component.ts b/overlord/src/app/recipe/recipe-list/recipe-list.component.ts index c40e25ac..065f9d22 100644 --- a/overlord/src/app/recipe/recipe-list/recipe-list.component.ts +++ b/overlord/src/app/recipe/recipe-list/recipe-list.component.ts @@ -33,7 +33,7 @@ export class RecipeListComponent implements OnInit { dataSource: RecipeListDatasource = new RecipeListDatasource(this.productGroupFilter, this.data); /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ - displayedColumns = ['name', 'salePrice', 'costPrice', 'costPercentage']; + displayedColumns = ['name', 'yield', 'date', 'source']; constructor(private route: ActivatedRoute, private router: Router) { this.form = new FormGroup({ @@ -77,4 +77,8 @@ export class RecipeListComponent implements OnInit { filterProductGroup(val: string) { this.productGroupFilter.next(val || ''); } + + excelLink() { + return `/api/recipes/xlsx`; + } } diff --git a/overlord/src/app/recipe/recipe-resolver.service.ts b/overlord/src/app/recipe/recipe-resolver.service.ts index 181b8fb1..983ae72d 100644 --- a/overlord/src/app/recipe/recipe-resolver.service.ts +++ b/overlord/src/app/recipe/recipe-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Recipe } from './recipe'; @@ -8,7 +8,7 @@ import { RecipeService } from './recipe.service'; @Injectable({ providedIn: 'root', }) -export class RecipeResolver implements Resolve { +export class RecipeResolver { constructor(private ser: RecipeService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/recipe/recipe-routing.module.ts b/overlord/src/app/recipe/recipe-routing.module.ts index d60ccf63..d8edc784 100644 --- a/overlord/src/app/recipe/recipe-routing.module.ts +++ b/overlord/src/app/recipe/recipe-routing.module.ts @@ -1,53 +1,62 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; import { PeriodListResolver } from '../period/period-list-resolver.service'; import { ProductGroupListResolver } from '../product-group/product-group-list-resolver.service'; import { RecipeDetailComponent } from './recipe-detail/recipe-detail.component'; -import { RecipeListResolver } from './recipe-list-resolver.service'; import { RecipeListComponent } from './recipe-list/recipe-list.component'; +import { RecipeListResolver } from './recipe-list-resolver.service'; import { RecipeResolver } from './recipe-resolver.service'; const recipeRoutes: Routes = [ { path: '', component: RecipeListComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Recipes', }, resolve: { - list: RecipeListResolver, - productGroups: ProductGroupListResolver, - periods: PeriodListResolver, + list: (route: ActivatedRouteSnapshot) => inject(RecipeListResolver).resolve(route), + productGroups: () => inject(ProductGroupListResolver).resolve(), + periods: () => inject(PeriodListResolver).resolve(), }, runGuardsAndResolvers: 'paramsOrQueryParamsChange', }, { path: 'new', component: RecipeDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Recipes', }, resolve: { - item: RecipeResolver, - periods: PeriodListResolver, + item: (route: ActivatedRouteSnapshot) => inject(RecipeResolver).resolve(route), + periods: () => inject(PeriodListResolver).resolve(), }, }, { path: ':id', component: RecipeDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Recipes', }, resolve: { - item: RecipeResolver, - periods: PeriodListResolver, + item: (route: ActivatedRouteSnapshot) => inject(RecipeResolver).resolve(route), + periods: () => inject(PeriodListResolver).resolve(), }, }, ]; diff --git a/overlord/src/app/recipe/recipe.module.ts b/overlord/src/app/recipe/recipe.module.ts index b407a44a..99b4aa9e 100644 --- a/overlord/src/app/recipe/recipe.module.ts +++ b/overlord/src/app/recipe/recipe.module.ts @@ -2,7 +2,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -17,6 +16,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSelectModule } from '@angular/material/select'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/recipe/recipe.service.ts b/overlord/src/app/recipe/recipe.service.ts index a572aa70..68b5dea3 100644 --- a/overlord/src/app/recipe/recipe.service.ts +++ b/overlord/src/app/recipe/recipe.service.ts @@ -4,7 +4,6 @@ import { Observable, of as observableOf } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { ErrorLoggerService } from '../core/error-logger.service'; -import { ProductSku } from '../core/product-sku'; import { Recipe } from './recipe'; @@ -55,26 +54,4 @@ export class RecipeService { .delete(`${url}/${id}`) .pipe(catchError(this.log.handleError(serviceName, 'delete'))) as Observable; } - - getIngredientDetails( - id: string, - startDate: string | null, - finishDate: string | null, - ): Observable { - const getUrl = `${url}/ingredient-details/${id}`; - const options = { - params: new HttpParams(), - }; - if (startDate !== null) { - options.params = options.params.set('s', startDate); - } - if (finishDate !== null) { - options.params = options.params.set('f', finishDate); - } - return this.http - .get(getUrl, options) - .pipe( - catchError(this.log.handleError(serviceName, `get id=${id}`)), - ) as Observable; - } } diff --git a/overlord/src/app/recipe/recipe.ts b/overlord/src/app/recipe/recipe.ts index 5003502e..c68c2b56 100644 --- a/overlord/src/app/recipe/recipe.ts +++ b/overlord/src/app/recipe/recipe.ts @@ -1,26 +1,31 @@ import { ProductSku } from '../core/product-sku'; -import { Period } from '../period/period'; import { RecipeItem } from './recipe-item'; export class Recipe { id: string | undefined; + date: string; + source: string; + instructions: string; + garnishing: string; + plating: string; sku: ProductSku; recipeYield: number; - costPrice: number; - salePrice: number; notes: string; items: RecipeItem[]; - period: Period; + units: string | undefined; + productGroupId: string | undefined; public constructor(init?: Partial) { + this.date = ''; + this.source = ''; + this.instructions = ''; + this.garnishing = ''; + this.plating = ''; this.sku = new ProductSku(); this.recipeYield = 0; - this.salePrice = 0; - this.costPrice = 0; this.notes = ''; this.items = []; - this.period = new Period(); Object.assign(this, init); } } diff --git a/overlord/src/app/role/role-list-resolver.service.ts b/overlord/src/app/role/role-list-resolver.service.ts index 1f5d7939..09121e99 100644 --- a/overlord/src/app/role/role-list-resolver.service.ts +++ b/overlord/src/app/role/role-list-resolver.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { Resolve } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Role } from './role'; @@ -8,7 +7,7 @@ import { RoleService } from './role.service'; @Injectable({ providedIn: 'root', }) -export class RoleListResolver implements Resolve { +export class RoleListResolver { constructor(private ser: RoleService) {} resolve(): Observable { diff --git a/overlord/src/app/role/role-list/role-list-datasource.ts b/overlord/src/app/role/role-list/role-list-datasource.ts index 2f0f1575..f9983df5 100644 --- a/overlord/src/app/role/role-list/role-list-datasource.ts +++ b/overlord/src/app/role/role-list/role-list-datasource.ts @@ -16,9 +16,7 @@ export class RoleListDatasource extends DataSource { } connect(): Observable { - const dataMutations: (Observable | EventEmitter | EventEmitter)[] = [ - observableOf(this.data), - ]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -31,7 +29,7 @@ export class RoleListDatasource extends DataSource { this.paginator.length = this.data.length; } - return merge(...dataMutations).pipe( + return merge(observableOf(this.data), ...dataMutations).pipe( map(() => this.getPagedData(this.getSortedData([...this.data]))), ); } diff --git a/overlord/src/app/role/role-resolver.service.ts b/overlord/src/app/role/role-resolver.service.ts index cac01dc9..5a122b9e 100644 --- a/overlord/src/app/role/role-resolver.service.ts +++ b/overlord/src/app/role/role-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { Role } from './role'; @@ -8,7 +8,7 @@ import { RoleService } from './role.service'; @Injectable({ providedIn: 'root', }) -export class RoleResolver implements Resolve { +export class RoleResolver { constructor(private ser: RoleService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/role/role-routing.module.ts b/overlord/src/app/role/role-routing.module.ts index 12ddf8eb..262e2a60 100644 --- a/overlord/src/app/role/role-routing.module.ts +++ b/overlord/src/app/role/role-routing.module.ts @@ -1,46 +1,55 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; import { RoleDetailComponent } from './role-detail/role-detail.component'; -import { RoleListResolver } from './role-list-resolver.service'; import { RoleListComponent } from './role-list/role-list.component'; +import { RoleListResolver } from './role-list-resolver.service'; import { RoleResolver } from './role-resolver.service'; const roleRoutes: Routes = [ { path: '', component: RoleListComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Users', }, resolve: { - list: RoleListResolver, + list: () => inject(RoleListResolver).resolve(), }, }, { path: 'new', component: RoleDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Users', }, resolve: { - item: RoleResolver, + item: (route: ActivatedRouteSnapshot) => inject(RoleResolver).resolve(route), }, }, { path: ':id', component: RoleDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Users', }, resolve: { - item: RoleResolver, + item: (route: ActivatedRouteSnapshot) => inject(RoleResolver).resolve(route), }, }, ]; diff --git a/overlord/src/app/settings/lock-information-resolver.service.ts b/overlord/src/app/settings/lock-information-resolver.service.ts index d4943359..add293d0 100644 --- a/overlord/src/app/settings/lock-information-resolver.service.ts +++ b/overlord/src/app/settings/lock-information-resolver.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { Resolve } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { LockInfo } from './lock-info'; @@ -8,7 +7,7 @@ import { SettingsService } from './settings.service'; @Injectable({ providedIn: 'root', }) -export class LockInformationResolver implements Resolve<[LockInfo]> { +export class LockInformationResolver { constructor(private ser: SettingsService) {} resolve(): Observable<[LockInfo]> { diff --git a/overlord/src/app/settings/maintenance-resolver.service.ts b/overlord/src/app/settings/maintenance-resolver.service.ts index fd448eda..a64231e7 100644 --- a/overlord/src/app/settings/maintenance-resolver.service.ts +++ b/overlord/src/app/settings/maintenance-resolver.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { Resolve } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { SettingsService } from './settings.service'; @@ -7,7 +6,7 @@ import { SettingsService } from './settings.service'; @Injectable({ providedIn: 'root', }) -export class MaintenanceResolver implements Resolve<{ enabled: boolean; user: string }> { +export class MaintenanceResolver { constructor(private ser: SettingsService) {} resolve(): Observable<{ enabled: boolean; user: string }> { diff --git a/overlord/src/app/settings/settings-routing.module.ts b/overlord/src/app/settings/settings-routing.module.ts index 92bb3436..5425a512 100644 --- a/overlord/src/app/settings/settings-routing.module.ts +++ b/overlord/src/app/settings/settings-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AccountTypeResolver } from '../account/account-type-resolver.service'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -14,15 +14,18 @@ const settingsRoutes: Routes = [ { path: '', component: SettingsComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Maintenance', // 'Lock Date' }, resolve: { - maintenance: MaintenanceResolver, - lockInformation: LockInformationResolver, - accountTypes: AccountTypeResolver, - voucherTypes: VoucherTypeResolver, + maintenance: () => inject(MaintenanceResolver).resolve(), + lockInformation: () => inject(LockInformationResolver).resolve(), + accountTypes: () => inject(AccountTypeResolver).resolve(), + voucherTypes: () => inject(VoucherTypeResolver).resolve(), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/settings/settings.component.ts b/overlord/src/app/settings/settings.component.ts index 1e6e45b2..4c29809a 100644 --- a/overlord/src/app/settings/settings.component.ts +++ b/overlord/src/app/settings/settings.component.ts @@ -112,7 +112,7 @@ export class SettingsComponent implements OnInit { showLockInformation(info: LockInfo[]) { this.lockInformation = info; this.lockInfoForm.controls.accountTypes.clear(); - this.accountTypes.forEach((x) => + this.accountTypes.forEach(() => this.lockInfoForm.controls.accountTypes.push( new FormGroup({ accountType: new FormControl(false, { nonNullable: true }), @@ -120,7 +120,7 @@ export class SettingsComponent implements OnInit { ), ); this.lockInfoForm.controls.voucherTypes.clear(); - this.voucherTypes.forEach((x) => + this.voucherTypes.forEach(() => this.lockInfoForm.controls.voucherTypes.push( new FormGroup({ voucherType: new FormControl(false, { nonNullable: true }), diff --git a/overlord/src/app/settings/settings.module.ts b/overlord/src/app/settings/settings.module.ts index 4046fad3..4c75d3c0 100644 --- a/overlord/src/app/settings/settings.module.ts +++ b/overlord/src/app/settings/settings.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -25,6 +24,7 @@ import { MatSelectModule } from '@angular/material/select'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; import { MatTabsModule } from '@angular/material/tabs'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/settings/voucher-type-resolver.service.ts b/overlord/src/app/settings/voucher-type-resolver.service.ts index a3769f1d..ca85f2af 100644 --- a/overlord/src/app/settings/voucher-type-resolver.service.ts +++ b/overlord/src/app/settings/voucher-type-resolver.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { Resolve } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { AccountType } from '../core/account-type'; @@ -9,7 +8,7 @@ import { VoucherTypeService } from './voucher-type.service'; @Injectable({ providedIn: 'root', }) -export class VoucherTypeResolver implements Resolve { +export class VoucherTypeResolver { constructor(private ser: VoucherTypeService) {} resolve(): Observable { diff --git a/overlord/src/app/shared/cookie.service.ts b/overlord/src/app/shared/cookie.service.ts index dab5946f..f007fe28 100644 --- a/overlord/src/app/shared/cookie.service.ts +++ b/overlord/src/app/shared/cookie.service.ts @@ -23,7 +23,7 @@ export class CookieService { this.setCookie(name, '', -1); } - public setCookie(name: string, value: string, expireDays: number, path: string = '') { + public setCookie(name: string, value: string, expireDays: number, path = '') { const d: Date = new Date(); d.setTime(d.getTime() + expireDays * 24 * 60 * 60 * 1000); const expires = `expires=${d.toUTCString()}`; diff --git a/overlord/src/app/shared/math.service.ts b/overlord/src/app/shared/math.service.ts index 4e80a3a0..e1006609 100644 --- a/overlord/src/app/shared/math.service.ts +++ b/overlord/src/app/shared/math.service.ts @@ -5,7 +5,7 @@ import { evaluate, round } from 'mathjs'; providedIn: 'root', }) export class MathService { - journalAmount(amount: string = '', debit: number): { debit: number; amount: number } { + journalAmount(amount = '', debit: number): { debit: number; amount: number } { const val = this.parseAmount(amount, 2); let newDebit = debit; if (val < 0) { @@ -14,7 +14,7 @@ export class MathService { return { debit: newDebit, amount: Math.abs(val) }; } - parseAmount(amount: string = '', rounding: number = 2): number { + parseAmount(amount = '', rounding = 2): number { const cleaned = `${amount}`.replace(new RegExp('(₹[s]*)|(,)|(s)', 'g'), ''); if (cleaned === '') { return 0; diff --git a/overlord/src/app/stock-movement/stock-movement-datasource.ts b/overlord/src/app/stock-movement/stock-movement-datasource.ts index 298ba11c..a8711a08 100644 --- a/overlord/src/app/stock-movement/stock-movement-datasource.ts +++ b/overlord/src/app/stock-movement/stock-movement-datasource.ts @@ -20,11 +20,7 @@ export class StockMovementDataSource extends DataSource { } connect(): Observable { - const dataMutations: ( - | Observable - | EventEmitter - | EventEmitter - )[] = [observableOf(this.data)]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -37,7 +33,7 @@ export class StockMovementDataSource extends DataSource { this.paginator.length = this.data.length; } - return merge(...dataMutations).pipe( + return merge(observableOf(this.data), ...dataMutations).pipe( map(() => this.getPagedData(this.getSortedData([...this.data]))), ); } diff --git a/overlord/src/app/stock-movement/stock-movement-resolver.service.ts b/overlord/src/app/stock-movement/stock-movement-resolver.service.ts index cb5d9499..2a676a04 100644 --- a/overlord/src/app/stock-movement/stock-movement-resolver.service.ts +++ b/overlord/src/app/stock-movement/stock-movement-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { StockMovement } from './stock-movement'; @@ -8,7 +8,7 @@ import { StockMovementService } from './stock-movement.service'; @Injectable({ providedIn: 'root', }) -export class StockMovementResolver implements Resolve { +export class StockMovementResolver { constructor(private ser: StockMovementService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/stock-movement/stock-movement-routing.module.ts b/overlord/src/app/stock-movement/stock-movement-routing.module.ts index 13fc6b75..667cc409 100644 --- a/overlord/src/app/stock-movement/stock-movement-routing.module.ts +++ b/overlord/src/app/stock-movement/stock-movement-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -11,12 +11,15 @@ const stockMovementRoutes: Routes = [ { path: '', component: StockMovementComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Stock Movement', }, resolve: { - info: StockMovementResolver, + info: (route: ActivatedRouteSnapshot) => inject(StockMovementResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/stock-movement/stock-movement.module.ts b/overlord/src/app/stock-movement/stock-movement.module.ts index b7a31c32..bdefef51 100644 --- a/overlord/src/app/stock-movement/stock-movement.module.ts +++ b/overlord/src/app/stock-movement/stock-movement.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -22,6 +21,7 @@ import { MatPaginatorModule } from '@angular/material/paginator'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/trial-balance/trial-balance-datasource.ts b/overlord/src/app/trial-balance/trial-balance-datasource.ts index 2d3a46e2..6ff46a20 100644 --- a/overlord/src/app/trial-balance/trial-balance-datasource.ts +++ b/overlord/src/app/trial-balance/trial-balance-datasource.ts @@ -20,11 +20,7 @@ export class TrialBalanceDataSource extends DataSource { } connect(): Observable { - const dataMutations: ( - | Observable - | EventEmitter - | EventEmitter - )[] = [observableOf(this.data)]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -37,7 +33,7 @@ export class TrialBalanceDataSource extends DataSource { this.paginator.length = this.data.length; } - return merge(...dataMutations).pipe( + return merge(observableOf(this.data), ...dataMutations).pipe( map(() => this.getPagedData(this.getSortedData([...this.data]))), ); } diff --git a/overlord/src/app/trial-balance/trial-balance-resolver.service.ts b/overlord/src/app/trial-balance/trial-balance-resolver.service.ts index d4ad12a4..cdf6ed12 100644 --- a/overlord/src/app/trial-balance/trial-balance-resolver.service.ts +++ b/overlord/src/app/trial-balance/trial-balance-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { TrialBalance } from './trial-balance'; @@ -8,7 +8,7 @@ import { TrialBalanceService } from './trial-balance.service'; @Injectable({ providedIn: 'root', }) -export class TrialBalanceResolver implements Resolve { +export class TrialBalanceResolver { constructor(private ser: TrialBalanceService) {} resolve(route: ActivatedRouteSnapshot): Observable { diff --git a/overlord/src/app/trial-balance/trial-balance-routing.module.ts b/overlord/src/app/trial-balance/trial-balance-routing.module.ts index 4e4a8b23..c4d698f3 100644 --- a/overlord/src/app/trial-balance/trial-balance-routing.module.ts +++ b/overlord/src/app/trial-balance/trial-balance-routing.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; @@ -11,24 +11,30 @@ const trialBalanceRoutes: Routes = [ { path: '', component: TrialBalanceComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Trial Balance', }, resolve: { - info: TrialBalanceResolver, + info: (route: ActivatedRouteSnapshot) => inject(TrialBalanceResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, { path: ':date', component: TrialBalanceComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Trial Balance', }, resolve: { - info: TrialBalanceResolver, + info: (route: ActivatedRouteSnapshot) => inject(TrialBalanceResolver).resolve(route), }, runGuardsAndResolvers: 'always', }, diff --git a/overlord/src/app/trial-balance/trial-balance.module.ts b/overlord/src/app/trial-balance/trial-balance.module.ts index 9c342f0b..890f57e5 100644 --- a/overlord/src/app/trial-balance/trial-balance.module.ts +++ b/overlord/src/app/trial-balance/trial-balance.module.ts @@ -3,7 +3,6 @@ import { CdkTableModule } from '@angular/cdk/table'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -20,6 +19,7 @@ import { MatPaginatorModule } from '@angular/material/paginator'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSortModule } from '@angular/material/sort'; import { MatTableModule } from '@angular/material/table'; +import { MomentDateAdapter } from '@angular/material-moment-adapter'; import { SharedModule } from '../shared/shared.module'; diff --git a/overlord/src/app/user/user-list-resolver.service.ts b/overlord/src/app/user/user-list-resolver.service.ts index 02c89622..34ea9a77 100644 --- a/overlord/src/app/user/user-list-resolver.service.ts +++ b/overlord/src/app/user/user-list-resolver.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { Resolve, Router } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { User } from '../core/user'; @@ -9,8 +8,8 @@ import { UserService } from './user.service'; @Injectable({ providedIn: 'root', }) -export class UserListResolver implements Resolve { - constructor(private ser: UserService, private router: Router) {} +export class UserListResolver { + constructor(private ser: UserService) {} resolve(): Observable { return this.ser.list(); diff --git a/overlord/src/app/user/user-list/user-list-datasource.ts b/overlord/src/app/user/user-list/user-list-datasource.ts index 72663b54..c20074c7 100644 --- a/overlord/src/app/user/user-list/user-list-datasource.ts +++ b/overlord/src/app/user/user-list/user-list-datasource.ts @@ -17,9 +17,7 @@ export class UserListDataSource extends DataSource { } connect(): Observable { - const dataMutations: (Observable | EventEmitter | EventEmitter)[] = [ - observableOf(this.data), - ]; + const dataMutations: (EventEmitter | EventEmitter)[] = []; if (this.paginator) { dataMutations.push((this.paginator as MatPaginator).page); } @@ -32,7 +30,7 @@ export class UserListDataSource extends DataSource { this.paginator.length = this.data.length; } - return merge(...dataMutations).pipe( + return merge(observableOf(this.data), ...dataMutations).pipe( map(() => this.getPagedData(this.getSortedData([...this.data]))), ); } diff --git a/overlord/src/app/user/user-resolver.service.ts b/overlord/src/app/user/user-resolver.service.ts index c55cd65f..61653321 100644 --- a/overlord/src/app/user/user-resolver.service.ts +++ b/overlord/src/app/user/user-resolver.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve, Router } from '@angular/router'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { Observable } from 'rxjs/internal/Observable'; import { User } from '../core/user'; @@ -9,8 +9,8 @@ import { UserService } from './user.service'; @Injectable({ providedIn: 'root', }) -export class UserResolver implements Resolve { - constructor(private ser: UserService, private router: Router) {} +export class UserResolver { + constructor(private ser: UserService) {} resolve(route: ActivatedRouteSnapshot): Observable { const id = route.paramMap.get('id'); diff --git a/overlord/src/app/user/user-routing.module.ts b/overlord/src/app/user/user-routing.module.ts index d3a9a246..5cd439f1 100644 --- a/overlord/src/app/user/user-routing.module.ts +++ b/overlord/src/app/user/user-routing.module.ts @@ -1,43 +1,52 @@ import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { NgModule, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { AuthGuard } from '../auth/auth-guard.service'; import { UserDetailComponent } from './user-detail/user-detail.component'; -import { UserListResolver } from './user-list-resolver.service'; import { UserListComponent } from './user-list/user-list.component'; +import { UserListResolver } from './user-list-resolver.service'; import { UserResolver } from './user-resolver.service'; const userRoutes: Routes = [ { path: '', component: UserListComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Users', }, resolve: { - list: UserListResolver, + list: () => inject(UserListResolver).resolve(), }, }, { path: 'new', component: UserDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], data: { permission: 'Users', }, resolve: { - item: UserResolver, + item: (route: ActivatedRouteSnapshot) => inject(UserResolver).resolve(route), }, }, { path: ':id', component: UserDetailComponent, - canActivate: [AuthGuard], + canActivate: [ + (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => + inject(AuthGuard).canActivate(route, state), + ], resolve: { - item: UserResolver, + item: (route: ActivatedRouteSnapshot) => inject(UserResolver).resolve(route), }, }, ]; diff --git a/overlord/src/polyfills.ts b/overlord/src/polyfills.ts deleted file mode 100644 index dcd18eac..00000000 --- a/overlord/src/polyfills.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * This file includes polyfills needed by Angular and is loaded before the app. - * You can add your own extra polyfills to this file. - * - * This file is divided into 2 sections: - * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. - * 2. Application imports. Files imported after ZoneJS that should be loaded before your main - * file. - * - * The current setup is for so-called "evergreen" browsers; the last versions of browsers that - * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), - * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. - * - * Learn more in https://angular.io/guide/browser-support - */ - -/*************************************************************************************************** - * BROWSER POLYFILLS - */ - -/** - * By default, zone.js will patch all possible macroTask and DomEvents - * user can disable parts of macroTask/DomEvents patch by setting following flags - * because those flags need to be set before `zone.js` being loaded, and webpack - * will put import in the top of bundle, so user need to create a separate file - * in this directory (for example: zone-flags.ts), and put the following flags - * into that file, and then add the following code before importing zone.js. - * import './zone-flags'; - * - * The flags allowed in zone-flags.ts are listed here. - * - * The following flags will work for all browsers. - * - * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame - * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick - * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames - * - * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js - * with the following flag, it will bypass `zone.js` patch for IE/Edge - * - * (window as any).__Zone_enable_cross_context_check = true; - * - */ - -/*************************************************************************************************** - * Zone JS is required by default for Angular itself. - */ -import 'zone.js'; // Included with Angular CLI. - - -/*************************************************************************************************** - * APPLICATION IMPORTS - */ diff --git a/overlord/tsconfig.app.json b/overlord/tsconfig.app.json index 3178957e..0e090b48 100644 --- a/overlord/tsconfig.app.json +++ b/overlord/tsconfig.app.json @@ -7,7 +7,6 @@ }, "files": [ "src/main.ts", - "src/polyfills.ts" ], "include": [ "src/**/*.d.ts" diff --git a/overlord/tsconfig.json b/overlord/tsconfig.json index 87d46a3b..cc01d431 100644 --- a/overlord/tsconfig.json +++ b/overlord/tsconfig.json @@ -6,6 +6,8 @@ "outDir": "../frontend", "forceConsistentCasingInFileNames": true, "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "sourceMap": true, @@ -15,12 +17,12 @@ "moduleResolution": "node", "importHelpers": true, "target": "ES2022", - "module": "es2020", + "module": "ES2022", + "useDefineForClassFields": false, "lib": [ - "es2018", + "ES2022", "dom" - ], - "useDefineForClassFields": false + ] }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/overlord/tsconfig.spec.json b/overlord/tsconfig.spec.json index 092345b0..be7e9da7 100644 --- a/overlord/tsconfig.spec.json +++ b/overlord/tsconfig.spec.json @@ -7,10 +7,6 @@ "jasmine" ] }, - "files": [ - "src/test.ts", - "src/polyfills.ts" - ], "include": [ "src/**/*.spec.ts", "src/**/*.d.ts"