diff --git a/alembic/env.py b/alembic/env.py
index 7d4a5ce4..81608ff8 100644
--- a/alembic/env.py
+++ b/alembic/env.py
@@ -50,6 +50,7 @@ def run_migrations_offline():
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
+ compare_type=True
)
with context.begin_transaction():
@@ -73,7 +74,8 @@ def run_migrations_online():
with connectable.connect() as connection:
context.configure(
- connection=connection, target_metadata=target_metadata
+ connection=connection, target_metadata=target_metadata,
+ compare_type=True
)
with context.begin_transaction():
diff --git a/alembic/versions/03ea3e9cb1e5_rename_further.py b/alembic/versions/03ea3e9cb1e5_rename_further.py
new file mode 100644
index 00000000..6a972c1d
--- /dev/null
+++ b/alembic/versions/03ea3e9cb1e5_rename_further.py
@@ -0,0 +1,54 @@
+"""test
+
+Revision ID: 03ea3e9cb1e5
+Revises: 5498fc4bf58d
+Create Date: 2020-05-14 21:25:08.945280
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '03ea3e9cb1e5'
+down_revision = '5498fc4bf58d'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table("attendances") as batch_op:
+ batch_op.alter_column('date', type_=sa.Date(), nullable=False)
+ with op.batch_alter_table("employees") as batch_op:
+ batch_op.alter_column('Designation', new_column_name='designation', nullable=False)
+ batch_op.alter_column('Salary', new_column_name='salary', nullable=False)
+ batch_op.alter_column('ServicePoints', new_column_name='points', nullable=False)
+ batch_op.alter_column('JoiningDate', new_column_name='joining_date', type_=sa.Date(), nullable=False)
+ batch_op.alter_column('LeavingDate', new_column_name='leaving_date', type_=sa.Date(), nullable=True)
+ with op.batch_alter_table("settings") as batch_op:
+ batch_op.alter_column('SettingID', new_column_name='id')
+ batch_op.alter_column('Name', new_column_name='name')
+ batch_op.alter_column('Data', new_column_name='data')
+ op.create_unique_constraint(op.f('uq_settings_name'), 'settings', ['name'])
+ op.drop_constraint('uq_settings_Name', 'settings', type_='unique')
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ with op.batch_alter_table("attendances") as batch_op:
+ batch_op.alter_column('date', type_=sa.DateTime())
+ with op.batch_alter_table("employees") as batch_op:
+ batch_op.alter_column('designation', new_column_name='Designation', nullable=True)
+ batch_op.alter_column('salary', new_column_name='Salary', nullable=True)
+ batch_op.alter_column('points', new_column_name='ServicePoints', nullable=True)
+ batch_op.alter_column('joining_date', new_column_name='JoiningDate', type_=sa.DateTime(), nullable=True)
+ batch_op.alter_column('leaving_date', new_column_name='LeavingDate', type_=sa.DateTime(), nullable=True)
+ with op.batch_alter_table("settings") as batch_op:
+ batch_op.alter_column('id', new_column_name='SettingID')
+ batch_op.alter_column('name', new_column_name='Name')
+ batch_op.alter_column('data', new_column_name='Data')
+ op.create_unique_constraint('uq_settings_Name', 'settings', ['name'])
+ op.drop_constraint(op.f('uq_settings_name'), 'settings', type_='unique')
+ # ### end Alembic commands ###
diff --git a/brewman/core/session.py b/brewman/core/session.py
index f6c91dd9..201987f3 100644
--- a/brewman/core/session.py
+++ b/brewman/core/session.py
@@ -7,7 +7,7 @@ def get_date(session) -> str:
return session["date"]
-def set_date(session, date_):
+def set_date(date_, session):
session["date"] = date_
return session["date"]
diff --git a/brewman/models/master.py b/brewman/models/master.py
index 399f6716..d1fc1baa 100644
--- a/brewman/models/master.py
+++ b/brewman/models/master.py
@@ -392,11 +392,11 @@ class Employee(AccountBase):
__mapper_args__ = {"polymorphic_identity": "employees"}
id = Column("id", GUID(), ForeignKey(AccountBase.id), primary_key=True)
- designation = Column("Designation", Unicode(255))
- salary = Column("Salary", Integer)
- points = Column("ServicePoints", Numeric(precision=5, scale=2))
- joining_date = Column("JoiningDate", DateTime)
- leaving_date = Column("LeavingDate", DateTime)
+ designation = Column("designation", Unicode(255), nullable=False)
+ salary = Column("salary", Integer, nullable=False)
+ points = Column("points", Numeric(precision=5, scale=2), nullable=False)
+ joining_date = Column("joining_date", Date, nullable=False)
+ leaving_date = Column("leaving_date", Date, nullable=True)
attendances = relationship(
"Attendance", backref="employee", cascade=None, cascade_backrefs=False
@@ -562,9 +562,9 @@ class AccountType:
class DbSetting(Base):
__tablename__ = "settings"
- id = Column("SettingID", GUID(), primary_key=True, default=uuid.uuid4)
- name = Column("Name", Unicode(255), unique=True, nullable=False)
- data = Column("Data", PickleType)
+ id = Column("id", GUID(), primary_key=True, default=uuid.uuid4)
+ name = Column("name", Unicode(255), unique=True, nullable=False)
+ data = Column("data", PickleType)
def __init__(self, id=None, name=None, data=None):
self.id = id
diff --git a/brewman/models/voucher.py b/brewman/models/voucher.py
index 0666ed7f..562662c4 100644
--- a/brewman/models/voucher.py
+++ b/brewman/models/voucher.py
@@ -9,7 +9,7 @@ from sqlalchemy import (
DateTime,
Numeric,
ForeignKey,
- UniqueConstraint,
+ UniqueConstraint, Date,
)
from sqlalchemy.dialects.postgresql import BYTEA
from sqlalchemy.ext.hybrid import hybrid_property
@@ -378,7 +378,7 @@ class Attendance(Base):
id = Column("id", GUID(), primary_key=True, default=uuid.uuid4)
employee_id = Column("employee_id", GUID(), ForeignKey("employees.id"))
- date = Column("date", DateTime)
+ date = Column("date", Date, nullable=False)
attendance_type = Column("attendance_type", Integer)
amount = Column("amount", Numeric)
creation_date = Column("creation_date", DateTime(timezone=True))
diff --git a/brewman/routers/account_types.py b/brewman/routers/account_types.py
index 24d69a9f..a065e823 100644
--- a/brewman/routers/account_types.py
+++ b/brewman/routers/account_types.py
@@ -1,15 +1,17 @@
+from typing import List
from fastapi import APIRouter, Depends
from ..schemas.auth import UserToken
from ..core.security import get_current_active_user as get_user
+import brewman.schemas.master as schemas
from brewman.models.master import AccountType
router = APIRouter()
-@router.get("")
+@router.get("", response_model=List[schemas.AccountType])
def account_type_list(user: UserToken = Depends(get_user)):
return [
- {"id": item.id, "name": item.name}
+ schemas.AccountType(id=item.id, name=item.name)
for item in AccountType.list()
]
diff --git a/brewman/routers/attendance.py b/brewman/routers/attendance.py
index 7d4aa4cd..45276127 100644
--- a/brewman/routers/attendance.py
+++ b/brewman/routers/attendance.py
@@ -1,8 +1,9 @@
-import uuid
-from datetime import datetime, date, timedelta
+import traceback
+from datetime import datetime, date, timedelta, time
from fastapi import APIRouter, HTTPException, status, Depends, Security, Request
from sqlalchemy import or_
+from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from ..schemas.auth import UserToken
from ..core.security import get_current_active_user as get_user
@@ -10,7 +11,7 @@ from ..db.session import SessionLocal
from ..models.master import Employee
from ..models.voucher import Attendance
from ..routers.fingerprint import get_prints
-from ..core.session import get_date
+from ..core.session import get_date, set_date
import brewman.schemas.voucher as schemas
router = APIRouter()
@@ -26,26 +27,37 @@ def get_db() -> Session:
@router.get("")
-def attendance_blank(request: Request, user: UserToken = Security(get_user, scopes=["attendance"])):
+def attendance_blank(
+ request: Request, user: UserToken = Security(get_user, scopes=["attendance"])
+):
return {"date": get_date(request.session), "body": []}
@router.get("/{date_}")
def attendance_date(
date_: str,
+ request: Request,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["attendance"]),
):
- return attendance_date_report(date_, db)
+ set_date(date_, request.session)
+ return {
+ "date": date_,
+ "body": attendance_date_report(datetime.strptime(date_, "%d-%b-%Y"), db),
+ }
-def attendance_date_report(date_: str, db):
- report = {"date": date_, "body": []}
- date_ = datetime.strptime(date_, "%d-%b-%Y").date()
+def attendance_date_report(date_: date, db: Session):
+ body = []
employees = (
db.query(Employee)
.filter(Employee.joining_date <= date_)
- .filter(or_(Employee.is_active, Employee.leaving_date >= date_))
+ .filter(
+ or_(
+ Employee.is_active,
+ Employee.leaving_date >= date_,
+ )
+ )
.order_by(Employee.cost_centre_id)
.order_by(Employee.designation)
.order_by(Employee.name)
@@ -55,50 +67,60 @@ def attendance_date_report(date_: str, db):
att = (
db.query(Attendance)
.filter(Attendance.employee_id == item.id)
- .filter(Attendance.date == date)
+ .filter(Attendance.date == date_)
.filter(Attendance.is_valid == True)
.first()
)
att = 0 if att is None else att.attendance_type
- prints, hours, worked = get_prints(item.id, date, db)
- report["body"].append(
- {
- "id": item.id,
- "code": item.code,
- "name": item.name,
- "designation": item.designation,
- "department": item.cost_centre.name,
- "attendanceType": {"id": att},
- "prints": prints,
- "hours": hours,
- "worked": worked,
- }
+ prints, hours_worked, full_day = get_prints(item.id, date_, db)
+ body.append(
+ schemas.AttendanceItem(
+ id=item.id,
+ code=item.code,
+ name=item.name,
+ designation=item.designation,
+ department=item.cost_centre.name,
+ attendanceType=schemas.AttendanceType(id=att),
+ prints=prints,
+ hoursWorked=hours_worked,
+ fullDay=full_day,
+ )
)
- return report
+ return body
-@router.post("/{date_}") # "Attendance"
+@router.post("/{date_}")
def save(
date_: str,
data: schemas.AttendanceIn,
db: Session = Depends(get_db),
user: UserToken = Security(get_user, scopes=["attendance"]),
):
- date_object = datetime.strptime(date_, "%d-%b-%Y").date()
-
- for item in data.body:
- attendance_type = item.attendance_type.id_
- if attendance_type != 0:
- attendance = Attendance(
- employee_id=item.employee_id,
- date=date_object,
- attendance_type=attendance_type,
- user_id=user.id_,
- )
- attendance.create(db)
- db.commit()
- return attendance_date_report(date_, db)
+ try:
+ att_date = datetime.strptime(date_, "%d-%b-%Y").date()
+ for item in data.body:
+ if item.attendance_type.id_ != 0:
+ attendance = Attendance(
+ employee_id=item.id_,
+ date=att_date,
+ attendance_type=item.attendance_type.id_,
+ user_id=user.id_,
+ )
+ attendance.create(db)
+ db.commit()
+ return {"date": date_, "body": attendance_date_report(att_date, db)}
+ except SQLAlchemyError as e:
+ db.rollback()
+ raise HTTPException(
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e),
+ )
+ except Exception:
+ db.rollback()
+ raise HTTPException(
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
+ detail=traceback.format_exc(),
+ )
def date_range(start: date, stop: date, step=timedelta(days=1), inclusive=False):
diff --git a/brewman/routers/attendance_types.py b/brewman/routers/attendance_types.py
index 183d74bf..d55adfc8 100644
--- a/brewman/routers/attendance_types.py
+++ b/brewman/routers/attendance_types.py
@@ -1,12 +1,13 @@
-from brewman.models.master import AttendanceType, Employee
-
-from fastapi import APIRouter
+from fastapi import APIRouter, Depends
+from ..schemas.auth import UserToken
+from ..core.security import get_current_active_user as get_user
+from brewman.models.master import AttendanceType
router = APIRouter()
-@router.get("/") # "Authenticated"
-def show_list(request):
+@router.get("")
+async def show_list(user: UserToken = Depends(get_user)):
list_ = AttendanceType.list()
attendance_types = []
for item in list_:
diff --git a/brewman/routers/fingerprint.py b/brewman/routers/fingerprint.py
index e22a557d..f9c4496d 100644
--- a/brewman/routers/fingerprint.py
+++ b/brewman/routers/fingerprint.py
@@ -1,11 +1,12 @@
import csv
-import datetime
+from datetime import datetime, date, timedelta, time
import uuid
from io import StringIO
from sqlalchemy import bindparam, select, exists, and_
from sqlalchemy.dialects.postgresql import insert as pg_insert
# from zope.sqlalchemy import mark_changed
+from sqlalchemy.orm import Session
from brewman.models.master import Employee
from brewman.models.voucher import Fingerprint
@@ -117,14 +118,12 @@ def fp(file_data, employees):
# return Fingerprint.__table__.insert().from_select([Fingerprint.id, Fingerprint.employee_id, Fingerprint.date], sel)
-def get_prints(employee_id, date, dbsession):
- start_fp = date + datetime.timedelta(hours=7)
- finish_fp = date + datetime.timedelta(hours=7, days=1)
+def get_prints(employee_id: uuid.UUID, date_: date, db: Session):
prints = (
- dbsession.query(Fingerprint)
+ db.query(Fingerprint)
.filter(Fingerprint.employee_id == employee_id)
- .filter(Fingerprint.date >= start_fp)
- .filter(Fingerprint.date < finish_fp)
+ .filter(Fingerprint.date >= datetime.combine(date_, time(hour=7)))
+ .filter(Fingerprint.date < datetime.combine(date_ + timedelta(days=1), time(hour=7)))
.order_by(Fingerprint.date)
.all()
)
@@ -132,31 +131,31 @@ def get_prints(employee_id, date, dbsession):
last = None
for i in range(len(prints), 0, -1):
item = prints[i - 1].date
- if last is not None and last - item < datetime.timedelta(minutes=10):
+ if last is not None and last - item < timedelta(minutes=10):
prints.remove(prints[i - 1])
else:
last = item
if len(prints) == 0:
- hours = "", ""
+ hours_worked, full_day = "", None
elif len(prints) == 2:
- hours = prints[1].date - prints[0].date
- hours = working_hours(hours)
+ time_worked = prints[1].date - prints[0].date
+ hours_worked, full_day = working_hours(time_worked)
elif len(prints) == 4:
- hours = (prints[1].date - prints[0].date) + (prints[3].date - prints[2].date)
- hours = working_hours(hours)
+ time_worked = (prints[1].date - prints[0].date) + (prints[3].date - prints[2].date)
+ hours_worked, full_day = working_hours(time_worked)
else:
- hours = "Error", "Error"
+ hours_worked, full_day = "Error", False
return (
", ".join([x.date.strftime("%H:%M") for x in prints]) + " ",
- hours[0],
- hours[1],
+ hours_worked,
+ full_day,
)
-def working_hours(delta):
+def working_hours(delta: timedelta):
minutes = (delta.seconds // 60) % 60
minutes = int(5 * round(float(minutes) / 5))
hours = delta.seconds // 3600
- worked = str(hours).zfill(2) + ":" + str(minutes).zfill(2)
- return worked, delta.seconds >= 60 * 60 * 9 # 9hrs
+ hours_worked = str(hours).zfill(2) + ":" + str(minutes).zfill(2)
+ return hours_worked, delta.seconds >= 60 * 60 * 9 # 9hrs
diff --git a/brewman/schemas/__init__.py b/brewman/schemas/__init__.py
index e69de29b..8bb04775 100644
--- a/brewman/schemas/__init__.py
+++ b/brewman/schemas/__init__.py
@@ -0,0 +1,5 @@
+def to_camel(string: str) -> str:
+ first, *others = string.split("_")
+ return "".join([first] + [word.capitalize() for word in others])
+
+
diff --git a/brewman/schemas/auth.py b/brewman/schemas/auth.py
index f7b2030f..7f08c165 100644
--- a/brewman/schemas/auth.py
+++ b/brewman/schemas/auth.py
@@ -3,10 +3,7 @@ from typing import List, Optional
from datetime import datetime
from pydantic import BaseModel
-
-def to_camel(string: str) -> str:
- first, *others = string.split("_")
- return "".join([first] + [word.capitalize() for word in others])
+from brewman.schemas import to_camel
class ClientIn(BaseModel):
diff --git a/brewman/schemas/master.py b/brewman/schemas/master.py
index 2c72f9b2..31d72094 100644
--- a/brewman/schemas/master.py
+++ b/brewman/schemas/master.py
@@ -5,10 +5,7 @@ from decimal import Decimal
from pydantic import BaseModel, Field, validator
-
-def to_camel(string: str) -> str:
- first, *others = string.split('_')
- return ''.join([first] + [word.capitalize() for word in others])
+from brewman.schemas import to_camel
class AccountLink(BaseModel):
@@ -55,7 +52,6 @@ class ProductIn(BaseModel):
fraction_units: str
product_yield: Decimal = Field(ge=0, le=1, multiple_of=0.00001, default=1)
product_group: ProductGroupLink = Field(...)
- account_id: AccountLink = Field(...)
price: Decimal = Field(ge=0, multiple_of=0.01, default=0)
sale_price: Decimal = Field(ge=0, multiple_of=0.01, default=0)
is_active: bool
@@ -71,6 +67,7 @@ class ProductIn(BaseModel):
class Product(ProductIn):
id_: uuid.UUID
code: int
+ account: AccountLink = Field(...)
is_fixture: bool
@@ -185,3 +182,13 @@ class DbSetting(BaseModel):
id_: uuid.UUID
name: str
data: bytes
+
+
+class AccountType(BaseModel):
+ id_: int
+ name: str
+
+ class Config:
+ fields = {'id_': 'id'}
+
+
diff --git a/brewman/schemas/reports.py b/brewman/schemas/reports.py
index cfb06f1d..d94ca01f 100644
--- a/brewman/schemas/reports.py
+++ b/brewman/schemas/reports.py
@@ -4,14 +4,10 @@ from typing import List, Optional
from datetime import datetime, date
from pydantic import BaseModel, Field, validator
+from brewman.schemas import to_camel
from brewman.schemas.master import AccountLink, ProductLink
-def to_camel(string: str) -> str:
- first, *others = string.split("_")
- return "".join([first] + [word.capitalize() for word in others])
-
-
class LedgerItem(BaseModel):
id_: Optional[uuid.UUID]
date_: date
diff --git a/brewman/schemas/voucher.py b/brewman/schemas/voucher.py
index eaba7f98..77e63ebf 100644
--- a/brewman/schemas/voucher.py
+++ b/brewman/schemas/voucher.py
@@ -1,8 +1,10 @@
import uuid
from datetime import datetime, date
from decimal import Decimal
-from typing import List
-from pydantic import BaseModel
+from typing import List, Optional
+from pydantic import BaseModel, validator
+
+from brewman.schemas import to_camel
class Voucher(BaseModel):
@@ -72,20 +74,26 @@ class Batch(BaseModel):
class AttendanceType(BaseModel):
id_: int
- name: str
- value: Decimal
+ name: Optional[str]
+ value: Optional[Decimal]
+
+ class Config:
+ alias_generator = to_camel
class AttendanceItem(BaseModel):
- id: uuid.UUID
- employee_id: uuid.UUID
- date: date
+ id_: uuid.UUID
+ code: int
+ name: str
+ designation: str
+ department: str
attendance_type: AttendanceType
- amount: Decimal
- creation_date: datetime
- user_id: uuid.UUID
- is_valid: bool
+ prints: str
+ hours_worked: str
+ full_day: Optional[bool]
+ class Config:
+ alias_generator = to_camel
class AttendanceIn(BaseModel):
body: List[AttendanceItem]
diff --git a/overlord/src/app/attendance/attendance.component.html b/overlord/src/app/attendance/attendance.component.html
index e8c249d7..f040c503 100644
--- a/overlord/src/app/attendance/attendance.component.html
+++ b/overlord/src/app/attendance/attendance.component.html
@@ -58,9 +58,9 @@
{{row.prints}}
new_releases
-
- {{row.hours}}
+
+ {{row.hoursWorked}}
diff --git a/overlord/src/app/attendance/attendance.ts b/overlord/src/app/attendance/attendance.ts
index 9b71ac45..3fb57b7e 100644
--- a/overlord/src/app/attendance/attendance.ts
+++ b/overlord/src/app/attendance/attendance.ts
@@ -17,8 +17,8 @@ export class AttendanceItem {
department: string;
attendanceType: AttendanceType;
prints: string;
- hours: string;
- worked: string;
+ hoursWorked: string;
+ fullDay?: boolean;
public constructor(init?: Partial) {
Object.assign(this, init);
diff --git a/overlord/src/app/unposted/unposted-datasource.ts b/overlord/src/app/unposted/unposted-datasource.ts
index b89e105f..1afa893d 100644
--- a/overlord/src/app/unposted/unposted-datasource.ts
+++ b/overlord/src/app/unposted/unposted-datasource.ts
@@ -46,7 +46,7 @@ export class UnpostedDataSource extends DataSource {
case 'date':
return compare(a.date, b.date, isAsc);
case 'type':
- return compare(a.voucherType, b.voucherType, isAsc);
+ return compare(a.type, b.type, isAsc);
case 'debitAmount':
return compare(+a.debitAmount, +b.debitAmount, isAsc);
case 'creditAmount':