Moving to FastAPI

Config is not properly working with .env file as of now
Cost Centre is mostly working, going to work on Accounts now
This commit is contained in:
tanshu committed 2020-05-08 10:22:25 +05:30
1 parent 72044476a8
commit fe74ef44bf
89 files changed
+1516 -2421

No files matched your search

+9
View File
@@ -0,0 +1,9 @@
HOST=0.0.0.0
PORT=9998
LOG_LEVEL=info
DEBUG=true
DB_URL=postgresql://postgres:123456@localhost:5432/hops
MODULE_NAME=brewman.main
SERVER_NAME=localhost
SERVER_HOST=localhost
PROJECT_NAME=bifrost
+1
View File
@@ -1,5 +1,6 @@
*.pyc
env
venv
.project
.pydevproject
*/__pycache__/
+1 -1
View File
@@ -1,2 +1,2 @@
include *.txt *.ini *.cfg *.rst
include *.txt *.ini *.cfg *.rst .env
recursive-include brewman *.ico *.png *.css *.gif *.jpg *.txt *.js *.html *.map *.eot *.svg *.ttf *.woff
-35
View File
@@ -1,38 +1,3 @@
import os
from pyramid.config import Configurator
from pyramid.session import SignedCookieSessionFactory
from brewman.renderers import json_renderer, CSVRenderer
from brewman.transactional_view_deriver import transactional_view
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
if 'sqlalchemy.url' not in settings:
DB_NAME = os.environ['DB_NAME']
DB_USER = os.environ['DB_USER']
DB_PASS = os.environ['DB_PASS']
DB_URI = 'postgresql://{0}:{1}@postgres:5432/{2}'.format(
DB_USER, DB_PASS, DB_NAME
)
settings['sqlalchemy.url']= DB_URI
SECRET_KEY = os.environ.get('SECRET_KEY', settings.get('secret_key', ''))
session_factory = SignedCookieSessionFactory(SECRET_KEY)
config = Configurator(
settings=settings,
session_factory=session_factory
)
config.include('.security')
config.include('.models')
config.include('.routes')
config.add_view_deriver(transactional_view)
config.add_renderer(name='json', factory=json_renderer)
config.add_renderer(name='csv', factory=CSVRenderer)
config.scan()
return config.make_wsgi_app()
+3
View File
@@ -0,0 +1,3 @@
from brewman.main import init
init()
+12
View File
@@ -0,0 +1,12 @@
from environs import Env
env = Env()
env.read_env() # read .env file, if it exists
class Settings:
debug: bool = env("DEBUG")
host: str = env("HOST")
port: int = env.int("PORT")
db_url: str = env("DB_URL")
log_level: str = env("LOG_LEVEL")
File renamed without changes.
+52
View File
@@ -0,0 +1,52 @@
import secrets
from typing import Any, Dict, List, Optional, Union
from pydantic import AnyHttpUrl, BaseSettings, PostgresDsn, validator
class Settings(BaseSettings):
API_V1_STR: str = "/api/v1"
SECRET_KEY: str = secrets.token_urlsafe(32)
# 60 minutes * 24 hours * 8 days = 8 days
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8
SERVER_NAME: str = "localhost"
SERVER_HOST: AnyHttpUrl = "http://localhost:9998"
# BACKEND_CORS_ORIGINS is a JSON-formatted list of origins
# e.g: '["http://localhost", "http://localhost:4200", "http://localhost:3000", \
# "http://localhost:8080", "http://local.dockertoolbox.tiangolo.com"]'
BACKEND_CORS_ORIGINS: List[AnyHttpUrl] = []
@validator("BACKEND_CORS_ORIGINS", pre=True)
def assemble_cors_origins(cls, v: Union[str, List[str]]) -> Union[List[str], str]:
if isinstance(v, str) and not v.startswith("["):
return [i.strip() for i in v.split(",")]
elif isinstance(v, (list, str)):
return v
raise ValueError(v)
PROJECT_NAME: str = "bifrost"
POSTGRES_SERVER: str = "localhost"
POSTGRES_USER: str = "postgres"
POSTGRES_PASSWORD: str = "123456"
POSTGRES_DB: str = "hops"
SQLALCHEMY_DATABASE_URI: Optional[PostgresDsn] = None
@validator("SQLALCHEMY_DATABASE_URI", pre=True)
def assemble_db_connection(cls, v: Optional[str], values: Dict[str, Any]) -> Any:
if isinstance(v, str):
return v
return PostgresDsn.build(
scheme="postgresql",
user=values.get("POSTGRES_USER"),
password=values.get("POSTGRES_PASSWORD"),
host=values.get("POSTGRES_SERVER"),
path=f"/{values.get('POSTGRES_DB') or ''}",
)
class Config:
case_sensitive = True
env_file = '.env'
settings = Settings()
+122
View File
@@ -0,0 +1,122 @@
import uuid
from datetime import datetime, timedelta
from typing import List
from jwt import PyJWTError
from fastapi import Depends, HTTPException, status, Security
from fastapi.security import OAuth2PasswordBearer, SecurityScopes
from pydantic import BaseModel, ValidationError
from sqlalchemy.orm import Session
from jose import jwt
from brewman.models.auth import User as UserModel
from ..db.session import SessionLocal
# to get a string like this run:
# openssl rand -hex 32
SECRET_KEY = "8546a61262dab7c05ccf2e26abe30bc10966904df6dfd29259ea85dd0844a8e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
oauth2_scheme = OAuth2PasswordBearer(
tokenUrl="/token",
scopes={}
)
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
username: str = None
scopes: List[str] = []
class User(BaseModel):
id_: uuid.UUID
name: str
locked_out: bool = None
password: str
def f7(seq):
seen = set()
seen_add = seen.add
return [x for x in seq if not (x in seen or seen_add(x))]
# Dependency
def get_db():
try:
db = SessionLocal()
yield db
finally:
db.close()
def create_access_token(*, data: dict, expires_delta: timedelta = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
def get_user(username: str, db: Session):
user = db.query(UserModel).filter(UserModel.name.ilike(username)).first()
if user:
return User(id_=user.id, name=user.name, locked_out=user.locked_out, password=user.password)
def authenticate_user(username: str, password: str, db: Session):
found, user = UserModel.auth(username, password, db)
if not found:
return False
return user
async def get_current_user(security_scopes: SecurityScopes, token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)):
if security_scopes.scopes:
authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
else:
authenticate_value = f"Bearer"
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": authenticate_value},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
token_scopes = payload.get("scopes", [])
token_data = TokenData(scopes=token_scopes, username=username)
except (PyJWTError, ValidationError):
raise credentials_exception
user = get_user(username=token_data.username, db=db)
if user is None:
raise credentials_exception
for scope in security_scopes.scopes:
if scope not in token_data.scopes:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Not enough permissions",
headers={"WWW-Authenticate": authenticate_value},
)
return user
async def get_current_active_user(current_user: User = Security(get_current_user, scopes=["authenticated"])):
if current_user.locked_out:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
File renamed without changes.
+31
View File
@@ -0,0 +1,31 @@
# Import all the models, so that Base has them before being
# imported by Alembic
from brewman.db.base_class import Base # noqa
from brewman.models import (
Client,
user_group,
role_group,
User,
LoginHistory,
Group,
Role,
Product,
AttendanceType,
CostCentre,
Employee,
Account,
AccountBase,
AccountType,
ProductGroup,
Recipe,
RecipeItem,
Attendance,
Batch,
Fingerprint,
Inventory,
Journal,
Product,
SalaryDeduction,
Voucher,
VoucherType,
) # noqa
+16
View File
@@ -0,0 +1,16 @@
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.schema import MetaData
# Recommended naming convention used by Alembic, as various different database
# providers will autogenerate vastly different names making migrations more
# difficult. See: http://alembic.zzzcomputing.com/en/latest/naming.html
NAMING_CONVENTION = {
"ix": "ix_%(column_0_label)s",
"uq": "uq_%(table_name)s_%(column_0_name)s",
"ck": "ck_%(table_name)s_%(constraint_name)s",
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
"pk": "pk_%(table_name)s",
}
metadata = MetaData(naming_convention=NAMING_CONVENTION)
Base = declarative_base(metadata=metadata)
+25
View File
@@ -0,0 +1,25 @@
from sqlalchemy.orm import Session
from app import crud, schemas
from brewman.core.config import settings
from brewman.db import base # noqa: F401
# make sure all SQL Alchemy models are imported (app.db.base) before initializing DB
# otherwise, SQL Alchemy might fail to initialize relationships properly
# for more details: https://github.com/tiangolo/full-stack-fastapi-postgresql/issues/28
def init_db(db: Session) -> None:
# Tables should be created with Alembic migrations
# But if you don't want to use migrations, create
# the tables un-commenting the next line
# Base.metadata.create_all(bind=engine)
user = crud.user.get_by_email(db, email=settings.FIRST_SUPERUSER)
if not user:
user_in = schemas.UserCreate(
email=settings.FIRST_SUPERUSER,
password=settings.FIRST_SUPERUSER_PASSWORD,
is_superuser=True,
)
user = crud.user.create(db, obj_in=user_in) # noqa: F841
+7
View File
@@ -0,0 +1,7 @@
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from brewman.core.config import settings
engine = create_engine(settings.SQLALCHEMY_DATABASE_URI, pool_pre_ping=True)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
+50
View File
@@ -0,0 +1,50 @@
import uvicorn
from fastapi import FastAPI
from .routers import (
account,
account_types,
attendance_types,
attendance,
employee_attendance,
attendance_report,
cost_centre,
employee,
fingerprint,
product,
product_group,
recipe,
login
)
from .routers.auth import client, user, group
from .db.base_class import Base
from .config import Settings as settings
from .db.session import SessionLocal, engine
Base.metadata.create_all(bind=engine)
app = FastAPI()
# app.include_router(brewman.routers, prefix="/api/db-image", tags=["db-image"])
app.include_router(login.router, tags=["login"])
app.include_router(account.router, prefix="/api/accounts", tags=["accounts"])
app.include_router(account_types.router, prefix="/api/account-types", tags=["accounts"])
app.include_router(attendance.router, prefix="/api/attendance", tags=["attendance"])
app.include_router(attendance_types.router, prefix="/api/attendance-types", tags=["attendance"])
app.include_router(employee_attendance.router, prefix="/api/employee-attendance", tags=["attendance"])
app.include_router(employee_attendance.router, prefix="/api/employee-attendance", tags=["attendance"])
app.include_router(attendance_report.router, prefix="/api/attendance-report", tags=["attendance"])
app.include_router(cost_centre.router, prefix="/api/cost-centres", tags=["cost-centres"])
app.include_router(employee.router, prefix="/api/employees", tags=["employees"])
app.include_router(fingerprint.router, prefix="/api/fingerprint", tags=["employees"])
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(client.router, prefix="/api/clients", tags=["users"])
app.include_router(group.router, prefix="/api/groups", tags=["users"])
app.include_router(user.router, prefix="/api/users", tags=["users"])
def init():
uvicorn.run(app, host=settings.host, port=settings.port)
+19 -12
View File
@@ -1,20 +1,17 @@
from sqlalchemy import engine_from_config
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import configure_mappers
import zope.sqlalchemy
# import or define all models here to ensure they are attached to the
# Base.metadata prior to any initialization routines
from .voucher import (
Attendance,
Batch,
Fingerprint,
Inventory,
Journal,
Product,
SalaryDeduction,
Voucher,
VoucherType,
from .auth import (
Client,
user_group,
role_group,
User,
LoginHistory,
Group,
Role
)
from .master import (
Product,
@@ -28,7 +25,17 @@ from .master import (
Recipe,
RecipeItem,
)
from .auth import Client, Group, Role, User, role_group, user_group, LoginHistory
from .voucher import (
Attendance,
Batch,
Fingerprint,
Inventory,
Journal,
Product,
SalaryDeduction,
Voucher,
VoucherType,
)
# run configure_mappers after defining all of the models to ensure
# all relationships can be setup
+1 -1
View File
@@ -108,7 +108,7 @@ class User(Base):
self.id = id
@classmethod
def auth(cls, name, password, dbsession):
def auth(cls, name, password, dbsession) -> (bool, any):
if password is None:
return False, None
user = dbsession.query(User).filter(User.name.ilike(name)).first()
-55
View File
@@ -1,55 +0,0 @@
import csv
from decimal import Decimal
from io import StringIO
import uuid
from pyramid.renderers import JSON
class CSVRenderer(object):
def __init__(self, info):
pass
def __call__(self, value, system):
csv_data = StringIO()
writer = csv.writer(csv_data, delimiter=",", quoting=csv.QUOTE_MINIMAL)
if "header" in value:
writer.writerow(value["header"])
if "rows" in value:
writer.writerows(value["rows"])
if "footer" in value:
writer.writerows(value["footer"])
request = system.get("request")
if request is not None:
response = request.response
ct = response.content_type
if ct == response.default_content_type:
response.content_type = "text/csv"
filename = value["filename"] if "filename" in value else "report.csv"
response.content_disposition = 'attachment;filename="{0}"'.format(filename)
return csv_data.getvalue()
json_renderer = JSON()
class DecimalAsFloatHack(float):
def __init__(self, d):
self.d = d
def __repr__(self):
return str(self.d)
def decimal_adaptor(obj, request):
return DecimalAsFloatHack(obj)
def uuid_adaptor(obj, request):
return str(obj)
json_renderer.add_adapter(Decimal, decimal_adaptor)
json_renderer.add_adapter(uuid.UUID, uuid_adaptor)
File renamed without changes.
@@ -1,16 +1,8 @@
from pyramid.view import view_config
from sqlalchemy import func, distinct, over, desc
import transaction
from brewman.models.voucher import Attendance
@view_config(
request_method="POST",
route_name="api_db_integrity",
renderer="json",
permission="Authenticated",
)
@router.post("/api/db-integrity") # "Authenticated"
def post_check_db(request):
info = {}
@@ -1,9 +1,6 @@
import datetime
import uuid
from decimal import Decimal
import transaction
from pyramid.view import view_config
from sqlalchemy import func, and_, distinct
from sqlalchemy.orm import aliased, joinedload_all
@@ -22,13 +19,7 @@ from brewman.models.voucher import (
)
@view_config(
request_method="POST",
route_name="api_rebase",
renderer="json",
permission="Rebase",
trans=True,
)
@router.post("/api/rebase/{date}") # "Rebase"
def rebase(request):
# request.dbsession.execute('SET statement_timeout TO 300000;') # 5 minutes
date = request.matchdict.get("date", None)
@@ -1,28 +1,9 @@
import datetime
import pkg_resources
import transaction
from pyramid.response import FileResponse
from pyramid.view import view_config
from brewman.models.auth import User
from brewman.models.master import DbSetting
@view_config(route_name="settings", permission="Authenticated")
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
@view_config(
request_method="POST",
route_name="api_lock_information",
renderer="json",
permission="Lock Date",
trans=True,
)
@router.post("/api/lock-information") # "Lock Date"
def set_lock_info(request):
start_locked = request.json_body["lockOlder"]
finish_locked = request.json_body["lockNewer"]
@@ -59,13 +40,7 @@ def set_lock_info(request):
return get_lock_info(request)
@view_config(
request_method="DELETE",
route_name="api_lock_information",
renderer="json",
permission="Lock Date",
trans=True,
)
@router.delete("/api/lock-information") # "Lock Date"
def clear_lock_info(request):
lock_date = (
request.dbsession.query(DbSetting).filter(DbSetting.name == "Lock Info").first()
@@ -76,12 +51,7 @@ def clear_lock_info(request):
return {}
@view_config(
request_method="GET",
route_name="api_lock_information",
renderer="json",
permission="Authenticated",
)
@router.get("/api/lock-information") # "Authenticated"
def get_lock_info(request):
data = (
request.dbsession.query(DbSetting).filter(DbSetting.name == "Lock Info").first()
@@ -106,12 +76,7 @@ def get_lock_info(request):
return info
@view_config(
request_method="GET",
route_name="api_maintenance",
renderer="json",
permission="Authenticated",
)
@router.get("/api/maintenance") # "Authenticated"
def get_maintenance(request):
data = (
request.dbsession.query(DbSetting)
@@ -124,13 +89,7 @@ def get_maintenance(request):
return {"enabled": True, "user": user.name}
@view_config(
request_method="POST",
route_name="api_maintenance",
renderer="json",
permission="Maintenance",
trans=True,
)
@router.post("/api/maintenance") # "Maintenance"
def set_maintenance(request):
status = request.json_body["enabled"]
maintenance = (
@@ -2,22 +2,13 @@ import datetime
from decimal import Decimal
import uuid
from pyramid.view import view_config
from sqlalchemy import func
import transaction
from brewman.models.master import AccountBase, CostCentre, Product
from brewman.models.validation_exception import ValidationError
from brewman.models.voucher import Journal, Voucher, VoucherType, Batch, Inventory
@view_config(
request_method="POST",
route_name="api_reset_stock",
renderer="json",
permission="Reset Stock",
trans=True,
)
@router.post("/api/reset-stock/{id}") # "Reset Stock"
def rebase(request):
user_id = uuid.UUID(request.authenticated_userid)
product = (
@@ -4,37 +4,15 @@ from datetime import date, datetime, timedelta, time
from decimal import Decimal
from io import BytesIO
import pkg_resources
from pyramid.httpexceptions import HTTPForbidden, HTTPFound
from pyramid.response import FileResponse, Response
from pyramid.view import view_config
from brewman.models.master import DbSetting
from brewman.models.voucher import DbImage
from fastapi import APIRouter
@view_config(route_name="home")
@view_config(request_method="GET", route_name="login")
def home(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
router = APIRouter()
@view_config(context=HTTPForbidden)
def forbidden_json(request):
if request.accept.quality("application/json") == 1:
response = Response("Forbidden")
response.status_int = 401
return response
else:
ret = HTTPFound(
location=request.route_url("login", _query={"returnUrl": request.path_qs})
)
return ret
@view_config(route_name="db_image")
@router.get("/{id}/{type}")
def db_image(request):
item = (
request.dbsession.query(DbImage)
+243
View File
@@ -0,0 +1,243 @@
import uuid
from datetime import datetime
from fastapi import APIRouter, Depends, Security
from pydantic import ValidationError
from sqlalchemy import func
from sqlalchemy.orm import joinedload_all, Session
from ..core.security import User, get_current_active_user as get_user
from ..db.session import SessionLocal
from ..models.master import CostCentre, Account, AccountType, AccountBase
from ..models.validation_exception import ValidationError
from ..models.voucher import Voucher, Journal, VoucherType
from ..schemas.master import Account as AccountSchema
router = APIRouter()
# Dependency
def get_db():
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("/")
def save(data: AccountSchema, db: Session = Depends(get_db), user: User = Security(get_user, scopes=["accounts"])):
try:
item = Account(
code=0,
name=data.name,
type=data.type,
is_starred=data.is_starred,
is_active=data.is_active,
is_reconcilable=data.is_reconcilable,
cost_centre_id=data.cost_centre.id_,
).create(db)
db.commit()
return account_info(item.id, db)
except:
db.rollback()
finally:
db.close()
@router.put("/{id_}")
def update(id_: uuid.UUID, db: Session = Depends(get_db), user: User = Security(get_user, scopes=["accounts"])):
item = (
db.query(Account)
.filter(Account.id == id_)
.first()
)
if item.is_fixture:
raise ValidationError(
"{0} is a fixture and cannot be edited or deleted.".format(item.name)
)
new_type = int(request.json_body["type"])
if not item.type == new_type:
item.code = Account.get_code(new_type, request.dbsession)
item.type = new_type
item.name = request.json_body["name"]
item.is_active = request.json_body["isActive"]
item.is_reconcilable = request.json_body["isReconcilable"]
item.is_starred = request.json_body["isStarred"]
item.cost_centre_id = uuid.UUID(request.json_body["costCentre"]["id"])
transaction.commit()
return account_info(item.id, request.dbsession)
@router.delete("/{id}") # "Accounts"
def delete(id_: uuid.UUID, db: Session = Depends(get_db), user: User = Security(get_user, scopes=["accounts"])):
account = (
db.query(Account)
.filter(Account.id == id_)
.first()
)
can_delete, reason = account.can_delete(request.has_permission("Advanced Delete"))
if can_delete:
delete_with_data(account, db)
transaction.commit()
return account_info(None, db)
else:
transaction.abort()
response = Response("Cannot delete account because {0}".format(reason))
response.status_int = 500
return response
@router.get("/list") # "Authenticated"
async def show_list(db: Session = Depends(get_db), user: User = Depends(get_user)):
list_ = (
db.query(Account)
.order_by(Account.type)
.order_by(Account.name)
.order_by(Account.code)
.all()
)
accounts = []
for item in list_:
accounts.append(
{
"id": item.id,
"name": item.name,
"type": item.type_object.name,
"isActive": item.is_active,
"isReconcilable": item.is_reconcilable,
"isStarred": item.is_starred,
"costCentre": item.cost_centre.name,
"isFixture": item.is_fixture,
}
)
return accounts
@router.get("/query") # "Authenticated"
async def show_term(q: str, t: int = None, r: bool = None, a: bool = None, c: int = None,
db: Session = Depends(get_db), current_user: User = Depends(get_user)):
count = c
list_ = []
for index, item in enumerate(AccountBase.list(t, q, r, a, db)):
list_.append({"id": item.id, "name": item.name})
if count is not None and index == count - 1:
break
return {"user": current_user.name, "list": list_}
@router.get("/{id_}/balance") # "Authenticated"
async def show_balance(id_: uuid.UUID, d: str = None, db: Session = Depends(get_db), user: User = Depends(get_user)):
date = (
None
if d is None or d == ""
else datetime.datetime.strptime(d, "%d-%b-%Y")
)
return {"date": balance(id_, date, db), "total": balance(id_, None, db)}
@router.get("/{id_}") # "Accounts"
def show_id(id_: uuid.UUID, db: Session = Depends(get_db), user: User = Security(get_user, scopes=["accounts"])):
return account_info(id_, db)
def balance(id_: uuid.UUID, date, dbsession: Session):
account = dbsession.query(AccountBase).filter(AccountBase.id == id_).first()
if not account.type_object.balance_sheet:
return 0
bal = dbsession.query(func.sum(Journal.amount * Journal.debit)).join(
Journal.voucher
)
if date is not None:
bal = bal.filter(Voucher.date <= date)
bal = (
bal.filter(Voucher.type != VoucherType.by_name("Issue").id)
.filter(Journal.account_id == id_)
.scalar()
)
return 0 if bal is None else bal
@router.get("/") # "Accounts"
def show_blank(db: Session = Depends(get_db), user: User = Security(get_user, scopes=["accounts"])):
return account_info(None, db)
def account_info(id_, db):
if id_ is None:
account = {
"code": "(Auto)",
"type": AccountType.by_name("Creditors").id,
"isActive": True,
"isReconcilable": False,
"isStarred": False,
"costCentre": CostCentre.overall(),
}
else:
account = db.query(Account).filter(Account.id == id_).first()
account = {
"id": account.id,
"code": account.code,
"name": account.name,
"type": account.type,
"isActive": account.is_active,
"isReconcilable": account.is_reconcilable,
"isStarred": account.is_starred,
"isFixture": account.is_fixture,
"costCentre": {
"id": account.cost_centre_id,
"name": account.cost_centre.name,
},
}
return account
def delete_with_data(account, db):
suspense_account = (
db.query(Account).filter(Account.id == Account.suspense()).first()
)
query = (
db.query(Voucher)
.options(joinedload_all(Voucher.journals, Journal.account, innerjoin=True))
.filter(Voucher.journals.any(Journal.account_id == account.id))
.all()
)
for voucher in query:
others, sus_jnl, acc_jnl = False, None, None
for journal in voucher.journals:
if journal.account_id == account.id:
acc_jnl = journal
elif journal.account_id == Account.suspense():
sus_jnl = journal
else:
others = True
if not others:
db.delete(voucher)
else:
if sus_jnl is None:
acc_jnl.account = suspense_account
voucher.narration += "\nSuspense \u20B9{0:,.2f} is {1}".format(
acc_jnl.amount, account.name
)
else:
amount = (sus_jnl.debit * sus_jnl.amount) + (
acc_jnl.debit * acc_jnl.amount
)
db.delete(acc_jnl)
if amount == 0:
db.delete(sus_jnl)
else:
sus_jnl.amount = abs(amount)
sus_jnl.debit = -1 if amount < 0 else 1
voucher.narration += "\nDeleted \u20B9{0:,.2f} of {1}".format(
acc_jnl.amount * acc_jnl.debit, account.name
)
if voucher.type in (
VoucherType.by_name("Payment").id,
VoucherType.by_name("Receipt").id,
):
voucher.type = VoucherType.by_name("Journal")
db.delete(account)
+12
View File
@@ -0,0 +1,12 @@
from brewman.models.master import AccountType
from fastapi import APIRouter
router = APIRouter()
@router.get("/") # "Authenticated"
def account_type_list(request):
account_types = []
for item in AccountType.list():
account_types.append({"id": item.id, "name": item.name})
return account_types
+101
View File
@@ -0,0 +1,101 @@
import datetime
import uuid
from sqlalchemy import or_
from brewman.models.master import Employee
from brewman.models.voucher import Attendance
from brewman.routers.fingerprint import get_prints
from brewman.routers.services.session import session_current_date
from fastapi import APIRouter
router = APIRouter()
@router.get("/") # "Attendance"
def attendance_blank(request):
return {"date": session_current_date(request), "body": []}
@router.get("/{date}") # "Attendance"
def attendance_date(request):
date = request.matchdict.get("date", None)
return attendance_date_report(date, request.dbsession)
def attendance_date_report(date, dbsession):
report = {"date": date, "body": []}
date = datetime.datetime.strptime(date, "%d-%b-%Y")
employees = (
dbsession.query(Employee)
.filter(Employee.joining_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)
.all()
)
for item in employees:
att = (
dbsession.query(Attendance)
.filter(Attendance.employee_id == item.id)
.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, dbsession)
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,
}
)
return report
@router.post("/{date}") # "Attendance"
def save(request):
user_id = uuid.UUID(request.authenticated_userid)
date = request.matchdict.get("date", None)
date_object = datetime.datetime.strptime(date, "%d-%b-%Y")
for item in request.json_body["body"]:
employee_id = uuid.UUID(item["id"])
attendance_type = item["attendanceType"]["id"]
if attendance_type != 0:
attendance = Attendance(
employee_id=employee_id,
date=date_object,
attendance_type=attendance_type,
user_id=user_id,
)
attendance.create(request.dbsession)
transaction.commit()
return attendance_date_report(date, request.dbsession)
def daterange(start, stop, step=datetime.timedelta(days=1), inclusive=False):
# inclusive=False to behave like range by default
if step.days > 0:
while start < stop:
yield start
start = start + step
# not +=! don't modify object passed in if it's mutable
# since this function is not restricted to
# only types from datetime module
elif step.days < 0:
while start > stop:
yield start
start = start + step
if inclusive and start == stop:
yield start
@@ -1,13 +1,15 @@
import datetime
from pyramid.view import view_config
from sqlalchemy import or_
from ..models.master import AttendanceType, Employee
from ..models.voucher import Attendance
from .attendance import daterange
from .services.session import session_period_start, session_period_finish
from fastapi import APIRouter
@view_config(route_name="attendance_report", renderer="csv", permission="Attendance")
router = APIRouter()
@router.get("/") # "Attendance", renderer="csv"
def get_report(request):
start_date = request.GET.get("StartDate", session_period_start(request))
finish_date = request.GET.get("FinishDate", session_period_finish(request))
+14
View File
@@ -0,0 +1,14 @@
from brewman.models.master import AttendanceType, Employee
from fastapi import APIRouter
router = APIRouter()
@router.get("/") # "Authenticated"
def show_list(request):
list_ = AttendanceType.list()
attendance_types = []
for item in list_:
attendance_types.append({"id": item.id, "name": item.name, "value": item.value})
return attendance_types
File renamed without changes.
@@ -1,29 +1,13 @@
import uuid
import pkg_resources
from pyramid.response import Response, FileResponse
from pyramid.view import view_config
from sqlalchemy import desc
import transaction
from brewman.models.auth import Client, LoginHistory
from fastapi import APIRouter
@view_config(request_method="GET", route_name="clients_list", permission="Clients")
@view_config(request_method="GET", route_name="clients_id", permission="Clients")
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
router = APIRouter()
@view_config(
request_method="PUT",
route_name="api_clients_id",
renderer="json",
permission="Clients",
trans=True,
)
@router.put("/{id}") # "Clients"
def update(request):
item = (
request.dbsession.query(Client)
@@ -38,13 +22,7 @@ def update(request):
return {}
@view_config(
request_method="DELETE",
route_name="api_clients_id",
renderer="json",
permission="Clients",
trans=True,
)
@router.delete("/{id}") # "Clients"
def delete(request):
id_ = request.matchdict.get("id", None)
if id_ is None:
@@ -61,14 +39,8 @@ def delete(request):
return {}
@view_config(
request_method="GET",
route_name="api_clients_list",
request_param="l",
renderer="json",
permission="Clients",
)
def show_list(request):
@router.get("/") # "Clients"
async def show_list(l: bool):
list_ = request.dbsession.query(Client).order_by(Client.name).all()
clients = []
for item in list_:
@@ -97,12 +69,7 @@ def show_list(request):
return clients
@view_config(
request_method="GET",
route_name="api_clients_id",
renderer="json",
permission="Clients",
)
@router.get("/{id}") # "Clients"
def show_id(request):
item = (
request.dbsession.query(Client)
@@ -1,29 +1,12 @@
import uuid
import pkg_resources
import transaction
from pyramid.response import Response, FileResponse
from pyramid.view import view_config
from brewman.models.auth import Group, Role
from fastapi import APIRouter
@view_config(request_method="GET", route_name="groups_new", permission="Users")
@view_config(request_method="GET", route_name="groups_id", permission="Users")
@view_config(route_name="groups_list", permission="Users")
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
router = APIRouter()
@view_config(
request_method="POST",
route_name="api_groups_new",
renderer="json",
permission="Users",
trans=True,
)
@router.post("/new") # "Users"
def save(request):
group = Group(request.json_body["name"])
request.dbsession.add(group)
@@ -32,13 +15,7 @@ def save(request):
return group_info(group.id, request.dbsession)
@view_config(
request_method="PUT",
route_name="api_groups_id",
renderer="json",
permission="Users",
trans=True,
)
@router.put("/{id}") # "Users"
def update(request):
id_ = request.matchdict.get("id", None)
group = request.dbsession.query(Group).filter(Group.id == uuid.UUID(id_)).one()
@@ -59,12 +36,7 @@ def add_permissions(group, permissions, dbsession):
group.roles.remove(gp)
@view_config(
request_method="DELETE",
route_name="api_groups_id",
renderer="json",
permission="Users",
)
@router.delete("/{id}") # "Users"
def delete(request):
id_ = request.matchdict.get("id", None)
if id_ is None:
@@ -77,28 +49,18 @@ def delete(request):
return response
@view_config(
request_method="GET", route_name="api_groups_id", renderer="json", permission="Users"
)
@router.get("/{id}") # "Users"
def show_id(request):
return group_info(uuid.UUID(request.matchdict.get("id", None)), request.dbsession)
@view_config(
request_method="GET", route_name="api_groups_new", renderer="json", permission="Users"
)
@router.get("/new") # "Users"
def show_blank(request):
return group_info(None, request.dbsession)
@view_config(
request_method="GET",
route_name="api_groups_list",
renderer="json",
request_param="l",
permission="Users",
)
def show_list(request):
@router.get("/") # "Users"
async def show_list(l: bool):
list_ = request.dbsession.query(Group).order_by(Group.name).all()
groups = []
@@ -1,14 +1,12 @@
import re
import uuid
import pkg_resources
import transaction
from pyramid.response import Response, FileResponse
from pyramid.view import view_config
from brewman.models.auth import User, Group
from brewman.models.validation_exception import ValidationError
from fastapi import APIRouter
router = APIRouter()
class UserView(object):
def __init__(self, request):
@@ -22,21 +20,7 @@ class UserView(object):
.one()
)
@view_config(request_method="GET", route_name="users_new", permission="Users")
@view_config(request_method="GET", route_name="users_id", permission="Authenticated")
@view_config(route_name="users_list", permission="Users")
def html(self):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=self.request)
@view_config(
request_method="POST",
route_name="api_users_new",
renderer="json",
permission="Users",
trans=True,
)
@router.post("/new") # "Users"
def save(self):
return self.save_user()
@@ -51,13 +35,7 @@ class UserView(object):
transaction.commit()
return self.user_info(user.id)
@view_config(
request_method="PUT",
route_name="api_users_id",
renderer="json",
permission="Authenticated",
trans=True,
)
@router.put("/{id}") # "Authenticated"
def update(self):
id_ = self.request.matchdict["id"]
p = re.compile(
@@ -91,12 +69,7 @@ class UserView(object):
transaction.commit()
return self.user_info(user.id)
@view_config(
request_method="DELETE",
route_name="api_users_id",
renderer="json",
permission="Users",
)
@router.delete("/{id}") # "Users"
def delete(self):
id_ = self.request.matchdict.get("id", None)
if id_ is None:
@@ -108,12 +81,7 @@ class UserView(object):
response.status_int = 500
return response
@view_config(
request_method="GET",
route_name="api_users_id",
renderer="json",
permission="Authenticated",
)
@router.get("/{id}") # "Authenticated"
def show_id(self):
id_ = self.request.matchdict["id"]
p = re.compile(
@@ -123,20 +91,12 @@ class UserView(object):
id_ = uuid.UUID(id_)
return self.user_info(id_)
@view_config(
request_method="GET", route_name="api_users_new", renderer="json", permission="Users"
)
@router.get("/new") # "Users"
def show_blank(self):
return self.user_info(None)
@view_config(
request_method="GET",
route_name="api_users_list",
renderer="json",
request_param="l",
permission="Users",
)
def show_list(self):
@router.get("/") # "Users"
async def show_list(l: bool):
list_ = self.request.dbsession.query(User).order_by(User.name).all()
users = []
for item in list_:
@@ -148,14 +108,8 @@ class UserView(object):
users.append(user)
return users
@view_config(
request_method="GET",
route_name="api_users_list",
renderer="json",
request_param="n",
permission="Authenticated",
)
def show_name(self):
@router.get("/") # "Authenticated"
async def show_name(n: bool):
list_ = (
self.request.dbsession.query(User)
.filter(User.locked_out == False)
+148
View File
@@ -0,0 +1,148 @@
import traceback
import uuid
from typing import List, Optional
from sqlalchemy.exc import SQLAlchemyError
import brewman.schemas.master as schemas
from fastapi import APIRouter, HTTPException, status, Depends, Security
from sqlalchemy.orm import Session
from ..core.security import User, get_current_active_user as get_user
from ..db.session import SessionLocal
from ..models.master import CostCentre
router = APIRouter()
# Dependency
def get_db():
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("/", response_model=schemas.CostCentre)
def save(
data: schemas.CostCentreSaveUpdate,
db: Session = Depends(get_db),
user: User = Security(get_user, scopes=["cost-centres"]),
):
try:
item = CostCentre(data.name)
db.add(item)
db.commit()
return cost_centre_info(item, db)
except Exception:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=traceback.format_exc(),
)
@router.put("/{id_}", response_model=schemas.CostCentre)
def update(
id_: uuid.UUID,
data: schemas.CostCentreSaveUpdate,
db: Session = Depends(get_db),
user: User = Security(get_user, scopes=["cost-centres"]),
):
try:
item = db.query(CostCentre).filter(CostCentre.id == id_).first()
if item.is_fixture:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"{item.name} is a fixture and cannot be edited or deleted.",
)
item.name = data.name
db.commit()
return cost_centre_info(item, 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(),
)
@router.delete("/{id_}")
def delete(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: User = Security(get_user, scopes=["cost-centres"]),
):
try:
item = db.query(CostCentre).filter(CostCentre.id == id_).first()
if item is None:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Cost Centre not found",
)
elif item.is_fixture:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"{item.name} is a fixture and cannot be edited or deleted.",
)
else:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Cost Centre deletion not implemented",
)
except Exception:
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=traceback.format_exc(),
)
@router.get("/")
def show_blank(
db: Session = Depends(get_db),
user: User = Security(get_user, scopes=["cost-centres"]),
):
return cost_centre_info(None, db)
@router.get("/list", response_model=List[schemas.CostCentre])
async def show_list(db: Session = Depends(get_db), user: User = Depends(get_user)):
return [
{"id": item.id, "name": item.name, "isFixture": item.is_fixture}
for item in db.query(CostCentre).order_by(CostCentre.name).all()
]
@router.get("/{id_}", response_model=schemas.CostCentre)
def show_id(
id_: uuid.UUID,
db: Session = Depends(get_db),
user: User = Security(get_user, scopes=["cost-centres"]),
):
item = db.query(CostCentre).filter(CostCentre.id == id_).first()
return cost_centre_info(item, db)
def cost_centre_info(item: Optional[CostCentre], db: Session):
if item is None:
return {
"name": "",
"isFixture": False,
}
else:
return {
"id": item.id,
"name": item.name,
"isFixture": item.is_fixture,
}
@@ -2,38 +2,20 @@ import datetime
import uuid
from decimal import Decimal
import pkg_resources
import transaction
from pyramid.response import Response, FileResponse
from pyramid.view import view_config
from sqlalchemy import desc
from sqlalchemy.orm import joinedload_all
from brewman.models.master import CostCentre, Employee, AccountBase, Account
from brewman.models.validation_exception import ValidationError
from brewman.models.voucher import Voucher, Journal, VoucherType
from brewman.views import to_uuid
from brewman.routers import to_uuid
from fastapi import APIRouter
router = APIRouter()
@view_config(request_method="GET", route_name="employees_new", permission="Employees")
@view_config(request_method="GET", route_name="employees_id", permission="Employees")
@view_config(route_name="employees_list", permission="Authenticated")
@view_config(
request_method="GET", route_name="employee_functions", permission="Employees"
)
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
@view_config(
request_method="POST",
route_name="api_employees_new",
renderer="json",
permission="Employees",
trans=True,
)
@router.post("/new") # "Employees"
def save(request):
name = request.json_body.get("name", "").strip()
if name == "":
@@ -93,13 +75,7 @@ def save(request):
return employee_info(item.id, request.dbsession)
@view_config(
request_method="PUT",
route_name="api_employees_id",
renderer="json",
permission="Employees",
trans=True,
)
@router.put("/{id}") # "Employees"
def update(request):
item = (
request.dbsession.query(Employee)
@@ -157,13 +133,7 @@ def update(request):
return employee_info(item.id, request.dbsession)
@view_config(
request_method="DELETE",
route_name="api_employees_id",
renderer="json",
permission="Employees",
trans=True,
)
@router.delete("/{id}") # "Employees"
def delete(request):
employee = (
request.dbsession.query(Employee)
@@ -182,13 +152,7 @@ def delete(request):
return response
@view_config(
request_method="GET",
route_name="api_employees_id",
renderer="json",
permission="Employees",
trans=True,
)
@router.get("/{id}") # "Employees"
def show_id(request):
id_ = to_uuid(request.matchdict["id"])
if id_ is None:
@@ -196,24 +160,13 @@ def show_id(request):
return employee_info(id_, request.dbsession)
@view_config(
request_method="GET",
route_name="api_employees_new",
renderer="json",
permission="Employees",
)
@router.get("/new") # "Employees"
def show_blank(request):
return employee_info(None, request.dbsession)
@view_config(
request_method="GET",
route_name="api_employees_list",
request_param="l",
renderer="json",
permission="Authenticated",
)
def show_list(request):
@router.get("/") # "Authenticated"
async def show_list(l: bool):
list_ = (
request.dbsession.query(Employee)
.order_by(desc(Employee.is_active))
@@ -244,14 +197,8 @@ def show_list(request):
return accounts
@view_config(
request_method="GET",
route_name="api_employees_list",
renderer="json",
request_param="q",
permission="Authenticated",
)
def show_term(request):
@router.get("/", ) # "Authenticated"
async def show_term(q: str):
filter_ = request.GET.get("q", None)
filter_ = None if filter_ == "" else filter_
count = request.GET.get("c", None)
+124
View File
@@ -0,0 +1,124 @@
import datetime
import uuid
from sqlalchemy import or_
from brewman.models.master import Employee
from brewman.models.validation_exception import ValidationError
from brewman.models.voucher import Attendance
from brewman.routers.fingerprint import get_prints
from brewman.routers.services.session import (
session_period_start,
session_period_finish,
session_current_date,
)
from fastapi import APIRouter
router = APIRouter()
@router.get("/") # "Attendance"
def employee_attendance_blank(request):
return {
"startDate": session_period_start(request),
"finishDate": session_period_finish(request),
"employee": None,
"body": [],
}
@router.get("/{id}") # "Attendance"
def employee_attendance_report(request):
employee = (
request.dbsession.query(Employee)
.filter(Employee.id == uuid.UUID(request.matchdict["id"]))
.first()
)
if employee is None:
raise ValidationError("Employee id is wrong")
start_date = request.GET.get("s", session_period_start(request))
finish_date = request.GET.get("f", session_period_finish(request))
info = {
"startDate": start_date,
"finishDate": finish_date,
"employee": {"id": employee.id, "name": employee.name},
}
start_date = datetime.datetime.strptime(start_date, "%d-%b-%Y")
finish_date = datetime.datetime.strptime(finish_date, "%d-%b-%Y")
start_date = (
employee.joining_date if employee.joining_date > start_date else start_date
)
finish_date = (
employee.leaving_date
if not employee.is_active and employee.leaving_date < finish_date
else finish_date
)
info["body"] = employee_attendance(
employee, start_date, finish_date, request.dbsession
)
return info
def employee_attendance(employee, start_date, finish_date, dbsession):
if not isinstance(start_date, datetime.datetime):
start_date = datetime.datetime.strptime(start_date, "%d-%b-%Y")
if not isinstance(finish_date, datetime.datetime):
finish_date = datetime.datetime.strptime(finish_date, "%d-%b-%Y")
list_ = []
for item in daterange(start_date, finish_date, inclusive=True):
att = (
dbsession.query(Attendance)
.filter(Attendance.employee_id == employee.id)
.filter(Attendance.date == item)
.filter(Attendance.is_valid == True)
.first()
)
att = 0 if att is None else att.attendance_type
prints, hours, worked = get_prints(employee.id, item, dbsession)
list_.append(
{
"date": item.strftime("%d-%b-%Y"),
"attendanceType": {"id": att},
"prints": prints,
"hours": hours,
"worked": worked,
}
)
return list_
@router.post("/{id}") # "Attendance"
def save_employee_attendance(request):
start_date = None
finish_date = None
user_id = uuid.UUID(request.authenticated_userid)
employee = (
request.dbsession.query(Employee)
.filter(Employee.id == uuid.UUID(request.matchdict["id"]))
.first()
)
for item in request.json_body["body"]:
if start_date is None:
start_date = item["date"]
finish_date = item["date"]
attendance_type = item["attendanceType"]["id"]
if attendance_type != 0:
date = datetime.datetime.strptime(item["date"], "%d-%b-%Y")
attendance = Attendance(
employee_id=employee.id,
date=date,
attendance_type=attendance_type,
user_id=user_id,
)
attendance.create(request.dbsession)
transaction.commit()
return {
"startDate": start_date,
"finishDate": finish_date,
"employee": {"id": employee.id, "name": employee.name},
"body": employee_attendance(
employee, start_date, finish_date, request.dbsession
),
}
@@ -3,24 +3,20 @@ import datetime
import uuid
from io import StringIO
import transaction
from pyramid.view import view_config
from sqlalchemy import bindparam, select, exists, and_
from sqlalchemy.dialects.postgresql import insert as pg_insert
from zope.sqlalchemy import mark_changed
# from zope.sqlalchemy import mark_changed
from brewman.models.master import Employee
from brewman.models.voucher import Fingerprint
from brewman.views import get_lock_info
from brewman.routers import get_lock_info
from fastapi import APIRouter
router = APIRouter()
@view_config(
request_method="POST",
route_name="api_fingerprint",
renderer="json",
permission="Authenticated",
trans=True,
)
@router.post("/") # "Authenticated"
def upload_prints(request):
fingerprints_file = request.POST["uploadedFile"].file
start, finish = get_lock_info(request.dbsession)
+54
View File
@@ -0,0 +1,54 @@
from datetime import timedelta
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
from ..core.security import (
Token,
authenticate_user,
ACCESS_TOKEN_EXPIRE_MINUTES,
create_access_token,
)
from ..db.session import SessionLocal
router = APIRouter()
# Dependency
def get_db():
try:
db = SessionLocal()
yield db
finally:
db.close()
@router.post("/token", response_model=Token)
async def login_for_access_token(
form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)
):
user = authenticate_user(form_data.username, form_data.password, db)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={
"sub": user.name,
"scopes": ["authenticated"]
+ list(
set(
[
p.name.replace(" ", "-").lower()
for g in user.groups
for p in g.roles
]
)
),
},
expires_delta=access_token_expires,
)
return {"access_token": access_token, "token_type": "bearer"}
@@ -1,10 +1,6 @@
import uuid
from decimal import Decimal, InvalidOperation
import pkg_resources
import transaction
from pyramid.response import Response, FileResponse
from pyramid.view import view_config
from sqlalchemy import desc
from sqlalchemy.orm import joinedload_all
@@ -12,23 +8,12 @@ from brewman.models.master import Product, Account
from brewman.models.validation_exception import ValidationError
from brewman.models.voucher import Voucher, Batch, Inventory, VoucherType
from fastapi import APIRouter
@view_config(request_method="GET", route_name="products_new", permission="Products")
@view_config(request_method="GET", route_name="products_id", permission="Products")
@view_config(route_name="products_list", permission="Authenticated")
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
router = APIRouter()
@view_config(
request_method="POST",
route_name="api_products_new",
renderer="json",
permission="Products",
trans=True,
)
@router.post("/new") # "Products"
def save(request):
json = request.json_body
@@ -92,13 +77,7 @@ def save(request):
return product_info(item.id, request.dbsession)
@view_config(
request_method="PUT",
route_name="api_products_id",
renderer="json",
permission="Products",
trans=True,
)
@router.put("/{id}") # "Products"
def update(request):
item = (
request.dbsession.query(Product)
@@ -148,13 +127,7 @@ def update(request):
return product_info(item.id, request.dbsession)
@view_config(
request_method="DELETE",
route_name="api_products_id",
renderer="json",
permission="Products",
trans=True,
)
@router.delete("/{id}") # "Products"
def delete(request):
product = (
request.dbsession.query(Product)
@@ -174,34 +147,18 @@ def delete(request):
return response
@view_config(
request_method="GET",
route_name="api_products_id",
renderer="json",
permission="Products",
)
@router.get("/{id}") # "Products"
def show_id(request):
return product_info(uuid.UUID(request.matchdict.get("id", None)), request.dbsession)
@view_config(
request_method="GET",
route_name="api_products_new",
renderer="json",
permission="Products",
)
@router.get("/new") # "Products"
def show_blank(request):
return product_info(None, request.dbsession)
@view_config(
request_method="GET",
route_name="api_products_list",
request_param="l",
renderer="json",
permission="Authenticated",
)
def show_list(request):
@router.get("/", ) # "Authenticated"
def show_list(l: bool):
list_ = (
request.dbsession.query(Product)
.order_by(desc(Product.is_active))
@@ -232,16 +189,10 @@ def show_list(request):
return products
@view_config(
request_method="GET",
route_name="api_products_list",
renderer="json",
request_param="t",
permission="Authenticated",
)
def show_term(request):
@router.get("/") # "Authenticated"
async def show_term(t: str):
term = request.GET.get("t", None)
term = term.strip() if term is not None and term.strip() is not "" else None
term = term.strip() if term is not None and term.strip() != "" else None
active = request.GET.get("a", None)
active = active if active is not None else None
count = request.GET.get("c", None)
@@ -1,30 +1,13 @@
import uuid
import pkg_resources
from pyramid.response import Response, FileResponse
from pyramid.view import view_config
import transaction
from brewman.models.master import ProductGroup
from brewman.models.validation_exception import ValidationError
from fastapi import APIRouter
router = APIRouter()
@view_config(request_method="GET", route_name="product_groups_new", permission="Product Groups")
@view_config(request_method="GET", route_name="product_groups_id", permission="Product Groups")
@view_config(route_name="product_groups_list", permission="Authenticated")
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
@view_config(
request_method="POST",
route_name="api_product_groups_new",
renderer="json",
permission="Product Groups",
trans=True,
)
@router.post("/new") # "Product Groups"
def save(request):
item = ProductGroup(request.json_body["name"])
request.dbsession.add(item)
@@ -32,13 +15,7 @@ def save(request):
return product_group_info(item.id, request.dbsession)
@view_config(
request_method="PUT",
route_name="api_product_groups_id",
renderer="json",
permission="Product Groups",
trans=True,
)
@router.put("/{id}") # "Product Groups"
def update(request):
item = (
request.dbsession.query(ProductGroup)
@@ -54,12 +31,7 @@ def update(request):
return product_group_info(item.id, request.dbsession)
@view_config(
request_method="DELETE",
route_name="api_product_groups_id",
renderer="json",
permission="Product Groups",
)
@router.delete("/{id}") # "Product Groups"
def delete(request):
item = (
request.dbsession.query(ProductGroup)
@@ -81,36 +53,20 @@ def delete(request):
return response
@view_config(
request_method="GET",
route_name="api_product_groups_id",
renderer="json",
permission="Product Groups",
)
@router.get("/{id}") # "Product Groups"
def show_id(request):
return product_group_info(
uuid.UUID(request.matchdict.get("id", None)), request.dbsession
)
@view_config(
request_method="GET",
route_name="api_product_groups_new",
renderer="json",
permission="Product Groups",
)
@router.get("/new") # "Product Groups"
def show_blank(request):
return product_group_info(None, request.dbsession)
@view_config(
request_method="GET",
route_name="api_product_groups_list",
request_param="l",
renderer="json",
permission="Authenticated",
)
def show_list(request):
@router.get("/") # "Authenticated"
async def show_list(l: bool):
list_ = request.dbsession.query(ProductGroup).order_by(ProductGroup.name).all()
product_groups = []
for item in list_:
@@ -3,45 +3,22 @@ import datetime
import time
from decimal import Decimal, InvalidOperation
import pkg_resources
from pyramid.response import FileResponse
from pyramid.view import view_config
from sqlalchemy import desc, or_, func
import transaction
from brewman.models.master import Recipe, Product, RecipeItem, CostCentre
from brewman.models.validation_exception import ValidationError
from brewman.models.voucher import Voucher, Inventory, VoucherType, Journal
from brewman.views.services.session import (
from brewman.routers.services.session import (
session_period_start,
session_period_finish,
session_period_set,
)
from fastapi import APIRouter
router = APIRouter()
@view_config(request_method="GET", route_name="recipes_new", permission="Recipes")
@view_config(request_method="GET", route_name="recipes_id", permission="Recipes")
@view_config(route_name="recipes_list", permission="Recipes")
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
@view_config(
request_method="POST",
route_name="api_recipes_new",
renderer="json",
permission="Recipes",
)
@view_config(
request_method="POST",
route_name="api_recipes_id",
renderer="json",
permission="Recipes",
trans=True,
)
# @router.post("/new") # "Recipes"
@router.post("/{id}") # "Recipes"
def save(request):
json = request.json_body
recipe_product = (
@@ -283,12 +260,7 @@ def update_old_rows(product_id, valid_from, valid_to, effective_date, dbsession)
dbsession.add(item)
@view_config(
request_method="DELETE",
route_name="api_recipes_id",
renderer="json",
permission="Recipes",
)
@router.delete("/{id}") # "Recipes"
def delete(request):
recipe = (
request.dbsession.query(Recipe)
@@ -304,31 +276,18 @@ def delete(request):
return recipe_info(None, request)
@view_config(
request_method="GET",
route_name="api_recipes_id",
renderer="json",
permission="Recipes",
)
@router.get("/{id}") # "Recipes"
def show_id(request):
return recipe_info(uuid.UUID(request.matchdict["id"]), request)
@view_config(
request_method="GET", route_name="api_recipes_new", renderer="json", permission="Recipes"
)
@router.get("/new") # "Recipes"
def show_blank(request):
return recipe_info(None, request)
@view_config(
request_method="GET",
route_name="api_recipes_list",
renderer="json",
request_param="list",
permission="Authenticated",
)
def show_list(request):
@router.get("/") # "Authenticated"
async def show_list(l: bool):
list_ = (
request.dbsession.query(Recipe)
.join(Recipe.product)
View File
Whitespace-only changes.
@@ -1,50 +1,28 @@
import datetime
import pkg_resources
from pyramid.response import FileResponse
from sqlalchemy.sql.expression import func, desc
from pyramid.view import view_config
from brewman.models.master import AccountType, AccountBase
from brewman.models.voucher import Voucher, Journal, VoucherType
from brewman.views.reports.closing_stock import get_closing_stock
from brewman.views.reports.profit_loss import get_accumulated_profit
from brewman.views.services.session import (
from brewman.routers.reports.closing_stock import get_closing_stock
from brewman.routers.reports.profit_loss import get_accumulated_profit
from brewman.routers.services.session import (
session_period_set,
session_period_start,
session_period_finish,
)
from fastapi import APIRouter
@view_config(
request_method="GET", route_name="balance_sheet", permission="Balance Sheet"
)
@view_config(
request_method="GET", route_name="balance_sheet_date", permission="Balance Sheet"
)
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
router = APIRouter()
@view_config(
request_method="GET",
route_name="api_balance_sheet",
renderer="json",
permission="Balance Sheet",
)
@router.get("/api/balance-sheet") # "Balance Sheet"
def report_blank(request):
return {"date": session_period_finish(request), "body": [], "footer": []}
@view_config(
request_method="GET",
route_name="api_balance_sheet_date",
renderer="json",
permission="Balance Sheet",
)
@router.get("/api/balance-sheet/{date}") # "Balance Sheet"
def report_data(request):
date = request.matchdict.get("date", None)
body, footer = build_balance_sheet(date, request.dbsession)
@@ -1,34 +1,22 @@
import datetime
import pkg_resources
from pyramid.response import FileResponse
from sqlalchemy.orm.util import aliased
from sqlalchemy.sql.expression import func, desc
from pyramid.view import view_config
from brewman.models.master import AccountBase, AccountType
from brewman.models.voucher import Voucher, Journal, VoucherType
from brewman.views.services.session import (
from brewman.routers.services.session import (
session_period_set,
session_period_start,
session_period_finish,
)
from fastapi import APIRouter
@view_config(request_method="GET", route_name="cash_flow", permission="Cash Flow")
@view_config(request_method="GET", route_name="cash_flow_id", permission="Cash Flow")
def get_html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
router = APIRouter()
@view_config(
request_method="GET",
route_name="api_cash_flow",
renderer="json",
permission="Cash Flow",
)
@router.get("/api/cash-flow") # "Cash Flow"
def report_blank(request):
return {
"startDate": session_period_start(request),
@@ -38,13 +26,7 @@ def report_blank(request):
}
@view_config(
request_method="GET",
route_name="api_cash_flow",
request_param=["s", "f"],
renderer="json",
permission="Cash Flow",
)
@router.get("/api/cash-flow", request_param=["s", "f"], permission="Cash Flow")
def report_data(request):
start_date = request.GET["s"]
finish_date = request.GET["f"]
@@ -58,13 +40,7 @@ def report_data(request):
}
@view_config(
request_method="GET",
route_name="api_cash_flow_id",
request_param=["s", "f"],
renderer="json",
permission="Cash Flow",
)
@router.get("/api/cash-flow/{id}", request_param=["s", "f"], permission="Cash Flow")
def get_cash_flow_id(request):
id_ = request.matchdict["id"]
start_date = request.GET["s"]
@@ -1,47 +1,26 @@
import datetime
import pkg_resources
from pyramid.response import FileResponse
from pyramid.view import view_config
from sqlalchemy.sql.expression import func
from brewman.models.master import Product, CostCentre
from brewman.models.voucher import Voucher, Journal, Inventory
from brewman.views.services.session import (
from brewman.routers.services.session import (
session_period_set,
session_period_start,
session_period_finish,
)
from fastapi import APIRouter
@view_config(
request_method="GET", route_name="closing_stock", permission="Closing Stock"
)
@view_config(
request_method="GET", route_name="closing_stock_date", permission="Closing Stock"
)
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
router = APIRouter()
@view_config(
request_method="GET",
route_name="api_closing_stock",
renderer="json",
permission="Closing Stock",
)
@router.get("/api/closing-stock") # "Closing Stock"
def report_blank(request):
return {"date": session_period_finish(request), "body": []}
@view_config(
request_method="GET",
route_name="api_closing_stock_date",
renderer="json",
permission="Closing Stock",
)
@router.get("/api/closing-stock/{date}") # "Closing Stock"
def report_data(request):
date = request.matchdict.get("date", None)
session_period_set(session_period_start(request), date, request)
@@ -1,32 +1,21 @@
import datetime
import pkg_resources
from pyramid.response import FileResponse
from pyramid.view import view_config
from sqlalchemy.orm import joinedload_all
from brewman.models.voucher import Voucher, Journal, VoucherType
from brewman.views.services.session import (
from brewman.routers.services.session import (
session_period_set,
session_period_start,
session_period_finish,
)
from brewman.views.services.voucher import get_edit_url
from brewman.routers.services.voucher import get_edit_url
from fastapi import APIRouter
router = APIRouter()
@view_config(request_method="GET", route_name="daybook", permission="Daybook")
def daybook_get(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
@view_config(
request_method="GET",
route_name="api_daybook",
renderer="json",
permission="Daybook",
)
@router.get("/api/daybook") # "Daybook"
def report_blank(request):
return {
"startDate": session_period_start(request),
@@ -35,13 +24,7 @@ def report_blank(request):
}
@view_config(
request_method="GET",
route_name="api_daybook",
request_param=["s", "f"],
renderer="json",
permission="Daybook",
)
@router.get("/api/daybook", request_param=["s", "f"], permission="Daybook")
def report_data(request):
start_date = request.GET["s"]
finish_date = request.GET["f"]
@@ -1,33 +1,24 @@
import datetime
import uuid
import pkg_resources
from pyramid.response import FileResponse
from sqlalchemy.orm import joinedload_all
from sqlalchemy.sql.expression import func
from pyramid.view import view_config
from brewman.models.master import AccountBase
from brewman.models.voucher import Voucher, Journal, VoucherType
from brewman.views.services.session import (
from brewman.routers.services.session import (
session_period_set,
session_period_start,
session_period_finish,
)
from brewman.views.services.voucher import get_edit_url
from brewman.routers.services.voucher import get_edit_url
from fastapi import APIRouter
router = APIRouter()
@view_config(request_method="GET", route_name="ledger_id", permission="Ledger")
@view_config(request_method="GET", route_name="ledger", permission="Ledger")
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
@view_config(
request_method="GET", route_name="api_ledger", renderer="json", permission="Ledger"
)
@router.get("/api/ledger") # "Ledger"
def show_blank(request):
return {
"startDate": session_period_start(request),
@@ -37,12 +28,7 @@ def show_blank(request):
}
@view_config(
request_method="GET",
route_name="api_ledger_id",
renderer="json",
permission="Ledger",
)
@router.get("/api/ledger/{id}") # "Ledger"
def show_data(request):
account = (
request.dbsession.query(AccountBase)
@@ -1,35 +1,21 @@
import datetime
import pkg_resources
from pyramid.response import FileResponse
from sqlalchemy.sql.expression import func, desc
from pyramid.view import view_config
from brewman.models.master import AccountBase
from brewman.models.voucher import Voucher, Journal, VoucherType
from brewman.views.services.session import (
from brewman.routers.services.session import (
session_period_set,
session_period_start,
session_period_finish,
)
from fastapi import APIRouter
@view_config(
request_method="GET", route_name="net_transactions", permission="Net Transactions"
)
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
router = APIRouter()
@view_config(
request_method="GET",
route_name="api_net_transactions",
renderer="json",
permission="Net Transactions",
)
@router.get("/api/net-transactions") # "Net Transactions"
def show_blank(request):
return {
"startDate": session_period_start(request),
@@ -38,13 +24,7 @@ def show_blank(request):
}
@view_config(
request_method="GET",
route_name="api_net_transactions",
request_param=["s", "f"],
renderer="json",
permission="Net Transactions",
)
@router.get("/api/net-transactions", request_param=["s", "f"], permission="Net Transactions")
def show_data(request):
start_date = request.GET["s"]
finish_date = request.GET["f"]
@@ -1,41 +1,24 @@
import datetime
import pkg_resources
from pyramid.response import FileResponse
from pyramid.view import view_config
from sqlalchemy.orm import joinedload
from sqlalchemy.sql.expression import func
from brewman.models.master import Product, CostCentre
from brewman.models.validation_exception import ValidationError
from brewman.models.voucher import Voucher, Journal, VoucherType, Inventory
from brewman.views import to_uuid
from brewman.views.services.session import (
from brewman.routers import to_uuid
from brewman.routers.services.session import (
session_period_set,
session_period_start,
session_period_finish,
)
from brewman.views.services.voucher import get_edit_url
from brewman.routers.services.voucher import get_edit_url
from fastapi import APIRouter
router = APIRouter()
@view_config(
request_method="GET", route_name="product_ledger_id", permission="Product Ledger"
)
@view_config(
request_method="GET", route_name="product_ledger", permission="Product Ledger"
)
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
@view_config(
request_method="GET",
route_name="api_product_ledger",
renderer="json",
permission="Product Ledger",
)
@router.get("/api/product-ledger") # "Product Ledger"
def show_blank(request):
return {
"startDate": session_period_start(request),
@@ -45,12 +28,7 @@ def show_blank(request):
}
@view_config(
request_method="GET",
route_name="api_product_ledger_id",
renderer="json",
permission="Product Ledger",
)
@router.get("/api/product-ledger/{id}") # "Product Ledger"
def show_data(request):
id_ = to_uuid(request.matchdict["id"])
if id_ is None:
@@ -1,33 +1,22 @@
import datetime
import pkg_resources
from pyramid.response import FileResponse
from pyramid.view import view_config
from sqlalchemy.sql.expression import func, desc
from brewman.models.master import AccountType, AccountBase
from brewman.models.voucher import Voucher, Journal, VoucherType
from brewman.views.reports.closing_stock import get_opening_stock, get_closing_stock
from brewman.views.services.session import (
from brewman.routers.reports.closing_stock import get_opening_stock, get_closing_stock
from brewman.routers.services.session import (
session_period_set,
session_period_start,
session_period_finish,
)
from fastapi import APIRouter
@view_config(request_method="GET", route_name="profit_loss", permission="Profit & Loss")
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
router = APIRouter()
@view_config(
request_method="GET",
route_name="api_profit_loss",
renderer="json",
permission="Profit & Loss",
)
@router.get("/api/profit-loss", permission="Profit & Loss")
def report_blank(request):
return {
"startDate": session_period_start(request),
@@ -37,13 +26,7 @@ def report_blank(request):
}
@view_config(
request_method="GET",
route_name="api_profit_loss",
request_param=["s", "f"],
renderer="json",
permission="Profit & Loss",
)
@router.get("/api/profit-loss", request_param=["s", "f"], permission="Profit & Loss")
def report_data(request):
start_date = request.GET["s"]
finish_date = request.GET["f"]
@@ -1,33 +1,19 @@
import datetime
import pkg_resources
from pyramid.response import FileResponse
from pyramid.view import view_config
from brewman.models.voucher import Voucher, VoucherType
from brewman.views.services.session import (
from brewman.routers.services.session import (
session_period_set,
session_period_start,
session_period_finish,
)
from brewman.views.services.voucher import get_edit_url
from brewman.routers.services.voucher import get_edit_url
from fastapi import APIRouter
router = APIRouter()
@view_config(
request_method="GET", route_name="purchase_entries", permission="Purchase Entries"
)
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
@view_config(
request_method="GET",
route_name="api_purchase_entries",
renderer="json",
permission="Purchase Entries",
)
@router.get("/api/purchase-entries") # "Purchase Entries"
def report_blank(request):
return {
"startDate": session_period_start(request),
@@ -36,13 +22,7 @@ def report_blank(request):
}
@view_config(
request_method="GET",
route_name="api_purchase_entries",
request_param=["s", "f"],
renderer="json",
permission="Purchase Entries",
)
@router.get("/api/purchase-entries", request_param=["s", "f"], permission="Purchase Entries")
def report_data(request):
start_date = request.GET["s"]
finish_date = request.GET["f"]
@@ -1,32 +1,21 @@
import datetime
import pkg_resources
from pyramid.response import FileResponse
from pyramid.view import view_config
from sqlalchemy.sql.expression import func, desc
from brewman.models.master import CostCentre, Product
from brewman.models.voucher import Voucher, Journal, Inventory, VoucherType
from brewman.views.services.session import (
from brewman.routers.services.session import (
session_period_set,
session_period_start,
session_period_finish,
)
from fastapi import APIRouter
@view_config(request_method="GET", route_name="purchases", permission="Purchases")
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
router = APIRouter()
@view_config(
request_method="GET",
route_name="api_purchases",
renderer="json",
permission="Purchases",
)
@router.get("/api/purchases") # "Purchases"
def report_blank(request):
return {
"startDate": session_period_start(request),
@@ -36,13 +25,7 @@ def report_blank(request):
}
@view_config(
request_method="GET",
route_name="api_purchases",
request_param=["s", "f"],
renderer="json",
permission="Purchases",
)
@router.get("/api/purchases", request_param=["s", "f"], permission="Purchases")
def report_data(request):
start_date = request.GET["s"]
finish_date = request.GET["f"]
@@ -1,40 +1,22 @@
import datetime
import uuid
import pkg_resources
from pyramid.response import FileResponse
from pyramid.view import view_config
from sqlalchemy.sql.expression import func, case
from brewman.models.master import AccountBase, CostCentre, Product, ProductGroup
from brewman.models.voucher import Voucher, Journal, Inventory
from brewman.views.services.session import (
from brewman.routers.services.session import (
session_period_set,
session_period_start,
session_period_finish,
)
from fastapi import APIRouter
@view_config(
request_method="GET", route_name="raw_material_cost", permission="Raw Material Cost"
)
@view_config(
request_method="GET",
route_name="raw_material_cost_id",
permission="Raw Material Cost",
)
def get_html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
router = APIRouter()
@view_config(
request_method="GET",
route_name="api_raw_material_cost",
renderer="json",
permission="Raw Material Cost",
)
@router.get("/api/raw-material-cost") # "Raw Material Cost"
def report_blank(request):
return {
"startDate": session_period_start(request),
@@ -44,13 +26,7 @@ def report_blank(request):
}
@view_config(
request_method="GET",
route_name="api_raw_material_cost",
request_param=["s", "f"],
renderer="json",
permission="Raw Material Cost",
)
@router.get("/api/raw-material-cost", request_param=["s", "f"], permission="Raw Material Cost")
def report_data(request):
start_date = request.GET["s"]
finish_date = request.GET["f"]
@@ -64,13 +40,7 @@ def report_data(request):
}
@view_config(
request_method="GET",
route_name="api_raw_material_cost_id",
request_param=["s", "f"],
renderer="json",
permission="Raw Material Cost",
)
@router.get("/api/raw-material-cost/{id}", request_param=["s", "f"], permission="Raw Material Cost")
def report_id(request):
id_ = request.matchdict["id"]
start_date = request.GET["s"]
@@ -1,37 +1,23 @@
import datetime
import uuid
import pkg_resources
import transaction
from pyramid.response import FileResponse
from pyramid.view import view_config
from sqlalchemy.orm import joinedload_all
from sqlalchemy.sql.expression import func, or_, and_
from brewman.models.master import AccountBase
from brewman.models.voucher import Voucher, Journal, VoucherType
from brewman.views.services.session import (
from brewman.routers.services.session import (
session_period_set,
session_period_start,
session_period_finish,
)
from brewman.views.services.voucher import get_edit_url
from brewman.routers.services.voucher import get_edit_url
from fastapi import APIRouter
router = APIRouter()
@view_config(request_method="GET", route_name="reconcile_id", permission="Reconcile")
@view_config(request_method="GET", route_name="reconcile", permission="Reconcile")
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
@view_config(
request_method="GET",
route_name="api_reconcile",
renderer="json",
permission="Reconcile",
)
@router.get("/api/reconcile") # "Reconcile"
def show_blank(request):
return {
"startDate": session_period_start(request),
@@ -41,13 +27,7 @@ def show_blank(request):
}
@view_config(
request_method="GET",
route_name="api_reconcile_id",
renderer="json",
permission="Reconcile",
trans=True,
)
@router.get("/api/reconcile/{id}") # "Reconcile"
def show_data(request):
account = (
request.dbsession.query(AccountBase)
@@ -160,12 +140,7 @@ def opening_balance(account_id, start_date, dbsession):
}
@view_config(
request_method="POST",
route_name="api_reconcile_id",
renderer="json",
permission="Reconcile",
)
@router.post("/api/reconcile/{id}") # "Reconcile"
def save(request):
account = (
request.dbsession.query(AccountBase)
@@ -1,34 +1,21 @@
import datetime
import pkg_resources
from pyramid.response import FileResponse
from pyramid.view import view_config
from sqlalchemy.sql.expression import func
from brewman.models.master import Product, CostCentre
from brewman.models.voucher import Voucher, Journal, VoucherType, Inventory
from brewman.views.services.session import (
from brewman.routers.services.session import (
session_period_set,
session_period_start,
session_period_finish,
)
from fastapi import APIRouter
@view_config(
request_method="GET", route_name="stock_movement", permission="Stock Movement"
)
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
router = APIRouter()
@view_config(
request_method="GET",
route_name="api_stock_movement",
renderer="json",
permission="Stock Movement",
)
@router.get("/api/stock-movement") # "Stock Movement"
def report_blank(request):
return {
"startDate": session_period_start(request),
@@ -37,13 +24,7 @@ def report_blank(request):
}
@view_config(
request_method="GET",
route_name="api_stock_movement",
request_param=["s", "f"],
renderer="json",
permission="Stock Movement",
)
@router.get("/api/stock-movement", request_param=["s", "f"], permission="Stock Movement")
def report_data(request):
start_date = request.GET["s"]
finish_date = request.GET["f"]
@@ -1,47 +1,26 @@
import datetime
import pkg_resources
from pyramid.response import FileResponse
from pyramid.view import view_config
from sqlalchemy.sql.expression import func
from brewman.models.master import AccountBase
from brewman.models.voucher import Voucher, Journal, VoucherType
from brewman.views.services.session import (
from brewman.routers.services.session import (
session_period_set,
session_period_start,
session_period_finish,
)
from fastapi import APIRouter
@view_config(
request_method="GET", route_name="trial_balance", permission="Trial Balance"
)
@view_config(
request_method="GET", route_name="trial_balance_date", permission="Trial Balance"
)
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
router = APIRouter()
@view_config(
request_method="GET",
route_name="api_trial_balance",
renderer="json",
permission="Trial Balance",
)
@router.get("/api/trial-balance") # "Trial Balance"
def report_blank(request):
return {"date": session_period_finish(request), "body": []}
@view_config(
request_method="GET",
route_name="api_trial_balance_date",
renderer="json",
permission="Trial Balance",
)
@router.get("/api/trial-balance/{date}") # "Trial Balance"
def report_data(request):
date = request.matchdict.get("date", None)
session_period_set(session_period_start(request), date, request)
@@ -1,25 +1,14 @@
import pkg_resources
from pyramid.response import FileResponse
from pyramid.view import view_config
from sqlalchemy.orm import joinedload_all
from brewman.models.voucher import Voucher, Journal, VoucherType
from brewman.views.services.voucher import get_edit_url
from brewman.routers.services.voucher import get_edit_url
from fastapi import APIRouter
router = APIRouter()
@view_config(request_method="GET", route_name="unposted", permission="Post Vouchers")
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
@view_config(
request_method="GET",
route_name="api_unposted",
renderer="json",
permission="Post Vouchers",
)
@router.get("/api/unposted") # "Post Vouchers"
def report_data(request):
return build_report(request)
Whitespace-only changes.
@@ -1,16 +1,9 @@
import datetime
from pyramid.view import view_config
from brewman.models.voucher import Batch
@view_config(
request_method="GET",
route_name="api_batch",
renderer="json",
permission="Authenticated",
)
@router.get("/api/batch") # "Authenticated"
def batch_term(request):
filter_ = request.GET.get("t", None)
filter_ = filter_ if filter_ is not None and filter_.strip() is not "" else None
@@ -1,14 +1,11 @@
import datetime
from pyramid.view import view_config
from sqlalchemy.orm.util import aliased
from brewman.models.voucher import Voucher, Journal, VoucherType
@view_config(
route_name="api_issue_grid", renderer="json", permission="Issue", trans=True
)
@router.get("/api/issue-grid/{date}") # "Issue"
def grid_date(request):
date = datetime.datetime.strptime(request.matchdict["date"], "%d-%b-%Y")
return get_grid(date, request.dbsession)
@@ -1,11 +1,5 @@
import uuid
from datetime import date, timedelta
from pyramid.view import view_config
from brewman.models.auth import User
from brewman.security import f7
def session_current_date(request):
session = request.session
@@ -44,25 +38,6 @@ def session_period_set(start, finish, request):
)
@view_config(route_name="api_auth", renderer="json")
def user_permission(request):
user_id = request.authenticated_userid
if user_id is None:
auth = {"isAuthenticated": False, "perms": {}}
elif "auth" in request.session:
auth = request.session["auth"]
else:
user = request.dbsession.query(User).filter(User.id == uuid.UUID(user_id)).one()
auth = {
"id": user.id,
"name": user.name,
"isAuthenticated": True,
"perms": sorted(f7([p.name for g in user.groups for p in g.roles])),
}
request.session["auth"] = auth
return auth
def get_first_day(dt, d_years=0, d_months=0):
# d_years, d_months are "deltas" to apply to dt
y, m = dt.year + d_years, dt.month + d_months
@@ -1,11 +1,6 @@
import datetime
import uuid
from decimal import Decimal
import pkg_resources
import transaction
from pyramid.response import Response, FileResponse
from pyramid.view import view_config
from sqlalchemy import func, or_
from brewman.models.auth import User
@@ -25,7 +20,7 @@ from brewman.models.voucher import (
Attendance,
Journal,
)
from brewman.views import get_lock_info
from brewman.routers import get_lock_info
from .issue import issue_create_voucher, issue_update_voucher
from .journal import journal_update_voucher, journal_create_voucher
from .purchase import purchase_create_voucher, purchase_update_voucher
@@ -33,44 +28,7 @@ from .service_charge import service_charge_create_voucher, service_charge_update
from ..session import get_first_day
@view_config(request_method="GET", route_name="journal_id", permission="Journal")
@view_config(request_method="GET", route_name="journal", permission="Journal")
@view_config(request_method="GET", route_name="payment_id", permission="Payment")
@view_config(request_method="GET", route_name="payment", permission="Payment")
@view_config(request_method="GET", route_name="receipt_id", permission="Receipt")
@view_config(request_method="GET", route_name="receipt", permission="Receipt")
@view_config(request_method="GET", route_name="purchase_id", permission="Purchase")
@view_config(request_method="GET", route_name="purchase", permission="Purchase")
@view_config(
request_method="GET", route_name="purchase_return_id", permission="Purchase Return"
)
@view_config(
request_method="GET", route_name="purchase_return", permission="Purchase Return"
)
@view_config(request_method="GET", route_name="issue_id", permission="Issue")
@view_config(request_method="GET", route_name="issue", permission="Issue")
@view_config(
request_method="GET", route_name="employee_benefits_id", permission="Issue"
)
@view_config(request_method="GET", route_name="employee_benefits", permission="Issue")
@view_config(
request_method="GET", route_name="incentive_id", permission="Service Charge"
)
@view_config(request_method="GET", route_name="incentive", permission="Service Charge")
def journal_get(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
@view_config(
request_method="POST",
route_name="api_voucher_id",
request_param="p",
renderer="json",
permission="Post Vouchers",
trans=True,
)
@router.post("/api/voucher/{id}", request_param="p") # "Post Vouchers"
def voucher_post(request):
user = (
request.dbsession.query(User)
@@ -140,9 +98,7 @@ def check_delete_permissions(request, voucher):
return response
@view_config(
request_method="DELETE", route_name="api_voucher_id", renderer="json", trans=True
)
@router.delete("/api/voucher/{id}")
def delete(request):
voucher = (
request.dbsession.query(Voucher)
@@ -211,9 +167,7 @@ def delete(request):
return blank_voucher(info=json_voucher, dbsession=request.dbsession)
@view_config(
request_method="GET", route_name="api_voucher_id", renderer="json", trans=True
)
@router.get("/api/voucher/{id}")
def get_old(request):
id_ = request.matchdict.get("id", None)
voucher = (
@@ -2,23 +2,14 @@ import calendar
import datetime
import uuid
from pyramid.view import view_config
from sqlalchemy import or_
import transaction
from ....models.auth import User
from ....models.master import Employee, AttendanceType, Account
from ....models.voucher import Voucher, VoucherType, Attendance, Journal
from ..session import get_first_day, get_last_day
@view_config(
request_method="POST",
route_name="api_credit_salary",
renderer="json",
permission="Attendance",
trans=True,
)
@router.post("/api/credit-salary") # "Attendance"
def credit_salary(request):
user = (
request.dbsession.query(User)
@@ -1,40 +1,38 @@
from pyramid.view import view_config, view_defaults
from brewman.views.services.session import session_current_date
from brewman.views.services.voucher import blank_voucher
from brewman.routers.services.session import session_current_date
from brewman.routers.services.voucher import blank_voucher
@view_defaults(request_method="GET", route_name="api_voucher", renderer="json")
class EmptyVoucher(object):
def __init__(self, request):
self.request = request
@view_config(request_param="t=Journal", permission="Journal")
@router.get("/api/voucher", request_param="t=Journal") # "Journal"
def journal(self):
return self.get_blank()
@view_config(request_param="t=Payment", permission="Payment")
@router.get("/api/voucher", request_param="t=Payment") # "Payment"
def payment(self):
account = self.request.GET.get("a", None)
return self.get_blank({"account": account})
@view_config(request_param="t=Receipt", permission="Receipt")
@router.get("/api/voucher", request_param="t=Receipt") # "Receipt"
def receipt(self):
account = self.request.GET.get("a", None)
return self.get_blank({"account": account})
@view_config(request_param="t=Purchase", permission="Purchase")
@router.get("/api/voucher", request_param="t=Purchase") # "Purchase"
def purchase(self):
return self.get_blank()
@view_config(request_param="t=Purchase Return", permission="Purchase Return")
@router.get("/api/voucher", request_param="t=Purchase Return") # "Purchase Return"
def purchase_return(self):
return self.get_blank()
@view_config(request_param="t=Salary Deduction", permission="Purchase Return")
@router.get("/api/voucher", request_param="t=Salary Deduction") # "Purchase Return"
def purchase_return(self):
return self.get_blank()
@view_config(request_param="t=Issue", permission="Issue")
@router.get("/api/voucher", request_param="t=Issue") # "Issue"
def issue(self):
voucher_type = self.request.GET.get("t", None)
date = self.request.GET.get("date", None)
@@ -53,7 +51,7 @@ class EmptyVoucher(object):
else:
return self.get_blank()
@view_config(request_param="t=Service Charge", permission="Service Charge")
@router.get("/api/voucher", request_param="t=Service Charge") # "Service Charge"
def service_charge(self):
voucher_type = self.request.GET.get("t", None)
date = self.request.GET.get("d", None)
@@ -5,7 +5,7 @@ from brewman.models.master import AccountBase, Employee
from brewman.models.operations import journals_valid
from brewman.models.validation_exception import ValidationError
from brewman.models.voucher import Journal, Voucher, VoucherType, SalaryDeduction
from brewman.views.services.session import get_last_day
from brewman.routers.services.session import get_last_day
def salary_deduction_create_voucher(json, user, dbsession):
@@ -1,15 +1,11 @@
import uuid
from json import loads
import datetime
from pyramid.view import view_defaults, view_config
import transaction
from brewman.models.auth import User
from brewman.models.validation_exception import ValidationError
from brewman.models.voucher import Voucher
from brewman.views import get_lock_info
from brewman.views.services.session import session_current_date_set
from brewman.routers import get_lock_info
from brewman.routers.services.session import session_current_date_set
from . import (
voucher_info,
journal_create_voucher,
@@ -21,9 +17,7 @@ from .purchase_return import purchase_return_create_voucher
from .salary_deduction import salary_deduction_create_voucher
@view_defaults(
request_method="POST", route_name="api_voucher", renderer="json", trans=True
)
@router.post("/api/voucher")
class SaveVoucher(object):
def __init__(self, request):
def parse_post(req):
@@ -51,35 +45,35 @@ class SaveVoucher(object):
self.start, self.finish = get_lock_info(request.dbsession)
self.voucher_date = datetime.datetime.strptime(self.json["date"], "%d-%b-%Y")
@view_config(request_param="t=Journal", permission="Journal")
@router.get("/api/voucher", request_param="t=Journal") # "Journal"
def journal(self):
return self.save()
@view_config(request_param="t=Payment", permission="Payment")
@router.get("/api/voucher", request_param="t=Payment") # "Payment"
def payment(self):
return self.save()
@view_config(request_param="t=Receipt", permission="Receipt")
@router.get("/api/voucher", request_param="t=Receipt") # "Receipt"
def receipt(self):
return self.save()
@view_config(request_param="t=Purchase", permission="Purchase")
@router.get("/api/voucher", request_param="t=Purchase") # "Purchase"
def purchase(self):
return self.save()
@view_config(request_param="t=Purchase Return", permission="Purchase Return")
@router.get("/api/voucher", request_param="t=Purchase Return") # "Purchase Return"
def purchase_return(self):
return self.save()
@view_config(request_param="t=Issue", permission="Issue")
@router.get("/api/voucher", request_param="t=Issue") # "Issue"
def issue(self):
return self.save()
@view_config(request_param="t=Salary Deduction", permission="Salary Deduction")
@router.get("/api/voucher", request_param="t=Salary Deduction") # "Salary Deduction"
def salary_deduction(self):
return self.save()
@view_config(request_param="t=Service Charge", permission="Service Charge")
@router.get("/api/voucher", request_param="t=Service Charge") # "Service Charge"
def salary_deduction(self):
return self.save()
@@ -13,7 +13,7 @@ from brewman.models.voucher import (
Attendance,
ServiceCharge,
)
from brewman.views.services.session import get_first_day
from brewman.routers.services.session import get_first_day
def service_charge_create_voucher(json, user, dbsession):
@@ -1,15 +1,13 @@
import datetime
import transaction
import uuid
from json import loads
from pyramid.response import Response
from pyramid.view import view_defaults, view_config
from brewman.models.auth import User
from brewman.models.voucher import Voucher
from brewman.views import get_lock_info
from brewman.views.services.session import session_current_date_set
from brewman.routers import get_lock_info
from brewman.routers.services.session import session_current_date_set
from . import (
voucher_info,
issue_update_voucher,
@@ -17,17 +15,14 @@ from . import (
journal_update_voucher,
)
from . import service_charge_update_voucher
from brewman.views.services.voucher.purchase_return import (
from brewman.routers.services.voucher.purchase_return import (
purchase_return_update_voucher,
)
from brewman.views.services.voucher.salary_deduction import (
from brewman.routers.services.voucher.salary_deduction import (
salary_deduction_update_voucher,
)
@view_defaults(
request_method="POST", route_name="api_voucher_id", renderer="json", trans=True
)
class UpdateVoucher(object):
def __init__(self, request):
def parse_post(request):
@@ -95,35 +90,35 @@ class UpdateVoucher(object):
else:
self.error = None
@view_config(request_param="t=Journal", permission="Journal")
@router.get("/api/voucher/{id}", request_param="t=Journal") # "Journal"
def journal(self):
return self.update()
@view_config(request_param="t=Payment", permission="Payment")
@router.get("/api/voucher/{id}", request_param="t=Payment") # "Payment"
def payment(self):
return self.update()
@view_config(request_param="t=Receipt", permission="Receipt")
@router.get("/api/voucher/{id}", request_param="t=Receipt") # "Receipt"
def receipt(self):
return self.update()
@view_config(request_param="t=Purchase", permission="Purchase")
@router.get("/api/voucher/{id}", request_param="t=Purchase") # "Purchase"
def purchase(self):
return self.update()
@view_config(request_param="t=Purchase Return", permission="Purchase Return")
@router.get("/api/voucher/{id}", request_param="t=Purchase Return") # "Purchase Return"
def purchase_return(self):
return self.update()
@view_config(request_param="t=Issue", permission="Issue")
@router.get("/api/voucher/{id}", request_param="t=Issue") # "Issue"
def issue(self):
return self.update()
@view_config(request_param="t=Salary Deduction", permission="Salary Deduction")
@router.get("/api/voucher/{id}", request_param="t=Salary Deduction") # "Salary Deduction"
def salary_deduction(self):
return self.update()
@view_config(request_param="t=Service Charge", permission="Service Charge")
@router.get("/api/voucher/{id}", request_param="t=Service Charge") # "Service Charge"
def service_charge(self):
return self.update()
-183
View File
@@ -1,183 +0,0 @@
def includeme(config):
config.add_route("api_db_integrity", "/api/db-integrity")
config.add_route("api_login", "/api/login")
config.add_route("login", "/login")
config.add_route("logout", "/logout")
config.add_route("api_logout", "/api/logout")
config.add_route("home", "/")
config.add_route("api_account_type_list", "/api/account-types")
config.add_route("cost_centres_new", "/cost-centres/new")
config.add_route("cost_centres_id", "/cost-centres/{id}")
config.add_route("cost_centres_list", "/cost-centres")
config.add_route("api_cost_centres_new", "/api/cost-centres/new")
config.add_route("api_cost_centres_id", "/api/cost-centres/{id}")
config.add_route("api_cost_centres_list", "/api/cost-centres")
config.add_route("accounts_new", "/accounts/new")
config.add_route("accounts_id", "/accounts/{id}")
config.add_route("accounts_list", "/accounts")
config.add_route("api_accounts_new", "/api/accounts/new")
config.add_route("api_accounts_id", "/api/accounts/{id}")
config.add_route("api_accounts_list", "/api/accounts")
config.add_route("employees_new", "/employees/new")
config.add_route("employees_id", "/employees/{id}")
config.add_route("employees_list", "/employees")
config.add_route("api_employees_new", "/api/employees/new")
config.add_route("api_employees_id", "/api/employees/{id}")
config.add_route("api_employees_list", "/api/employees")
config.add_route("users_new", "/users/new")
config.add_route("users_id", "/users/{id}")
config.add_route("users_list", "/users")
config.add_route("api_users_new", "/api/users/new")
config.add_route("api_users_id", "/api/users/{id}")
config.add_route("api_users_list", "/api/users")
config.add_route("groups_new", "/groups/new")
config.add_route("groups_id", "/groups/{id}")
config.add_route("groups_list", "/groups")
config.add_route("api_groups_new", "/api/groups/new")
config.add_route("api_groups_id", "/api/groups/{id}")
config.add_route("api_groups_list", "/api/groups")
config.add_route("clients_id", "/clients/{id}")
config.add_route("clients_list", "/clients")
config.add_route("api_clients_id", "/api/clients/{id}")
config.add_route("api_clients_list", "/api/clients")
config.add_route("products_new", "/products/new")
config.add_route("products_id", "/products/{id}")
config.add_route("products_list", "/products")
config.add_route("api_products_new", "/api/products/new")
config.add_route("api_products_id", "/api/products/{id}")
config.add_route("api_products_list", "/api/products")
config.add_route("recipes_new", "/recipes/new")
config.add_route("recipes_id", "/recipes/{id}")
config.add_route("recipes_list", "/recipes")
config.add_route("api_recipes_new", "/api/recipes/new")
config.add_route("api_recipes_id", "/api/recipes/{id}")
config.add_route("api_recipes_list", "/api/recipes")
config.add_route("product_groups_new", "/product-groups/new")
config.add_route("product_groups_id", "/product-groups/{id}")
config.add_route("product_groups_list", "/product-groups")
config.add_route("api_product_groups_new", "/api/product-groups/new")
config.add_route("api_product_groups_id", "/api/product-groups/{id}")
config.add_route("api_product_groups_list", "/api/product-groups")
config.add_route("journal_id", "/journal/{id}")
config.add_route("journal", "/journal")
config.add_route("purchase_id", "/purchase/{id}")
config.add_route("purchase", "/purchase")
config.add_route("purchase_return_id", "/return/{id}")
config.add_route("purchase_return", "/return")
config.add_route("payment_id", "/payment/{id}")
config.add_route("payment", "/payment")
config.add_route("receipt_id", "/receipt/{id}")
config.add_route("receipt", "/receipt")
config.add_route("issue_id", "/issue/{id}")
config.add_route("issue", "/issue")
config.add_route("api_issue_grid", "/api/issue-grid/{date}")
config.add_route("employee_benefits_id", "/employee-benefits/{id}")
config.add_route("employee_benefits", "/employee-benefits")
config.add_route("incentive_id", "/incentive/{id}")
config.add_route("incentive", "/incentive")
config.add_route("db_image", "/api/db-image/{id}/{type}")
config.add_route("api_voucher_id", "/api/voucher/{id}")
config.add_route("api_voucher", "/api/voucher")
config.add_route("settings", "/settings")
config.add_route("api_lock_information", "/api/lock-information")
config.add_route("api_maintenance", "/api/maintenance")
config.add_route("attendance_date", "/attendance/{date}")
config.add_route("attendance", "/attendance")
config.add_route("api_attendance_date", "/api/attendance/{date}")
config.add_route("api_attendance", "/api/attendance")
config.add_route("api_attendance_types", "/api/attendance-types")
config.add_route("employee_attendance_id", "/employee-attendance/{id}")
config.add_route("employee_attendance", "/employee-attendance")
config.add_route("api_employee_attendance_id", "/api/employee-attendance/{id}")
config.add_route("api_employee_attendance", "/api/employee-attendance")
config.add_route("attendance_report", "/attendance-report")
config.add_route("api_credit_salary", "/api/credit-salary")
config.add_route("employee_functions", "/employee-functions")
config.add_route("api_fingerprint", "/api/fingerprint")
config.add_route("ledger_id", "/ledger/{id}")
config.add_route("ledger", "/ledger")
config.add_route("api_ledger_id", "/api/ledger/{id}")
config.add_route("api_ledger", "/api/ledger")
config.add_route("reconcile_id", "/reconcile/{id}")
config.add_route("reconcile", "/reconcile")
config.add_route("api_reconcile_id", "/api/reconcile/{id}")
config.add_route("api_reconcile", "/api/reconcile")
config.add_route("product_ledger_id", "/product-ledger/{id}")
config.add_route("product_ledger", "/product-ledger")
config.add_route("api_product_ledger_id", "/api/product-ledger/{id}")
config.add_route("api_product_ledger", "/api/product-ledger")
config.add_route("trial_balance_date", "/trial-balance/{date}")
config.add_route("trial_balance", "/trial-balance")
config.add_route("api_trial_balance_date", "/api/trial-balance/{date}")
config.add_route("api_trial_balance", "/api/trial-balance")
config.add_route("net_transactions", "/net-transactions")
config.add_route("api_net_transactions", "/api/net-transactions")
config.add_route("purchases", "/purchases")
config.add_route("api_purchases", "/api/purchases")
config.add_route("closing_stock_date", "/closing-stock/{date}")
config.add_route("closing_stock", "/closing-stock")
config.add_route("api_closing_stock_date", "/api/closing-stock/{date}")
config.add_route("api_closing_stock", "/api/closing-stock")
config.add_route("cash_flow_id", "/cash-flow/{id}")
config.add_route("cash_flow", "/cash-flow")
config.add_route("api_cash_flow_id", "/api/cash-flow/{id}")
config.add_route("api_cash_flow", "/api/cash-flow")
config.add_route("raw_material_cost_id", "/raw-material-cost/{id}")
config.add_route("raw_material_cost", "/raw-material-cost")
config.add_route("api_raw_material_cost_id", "/api/raw-material-cost/{id}")
config.add_route("api_raw_material_cost", "/api/raw-material-cost")
config.add_route("api_daybook", "/api/daybook")
config.add_route("daybook", "/daybook")
config.add_route("unposted", "/unposted")
config.add_route("api_unposted", "/api/unposted")
config.add_route("profit_loss", "/profit-loss")
config.add_route("api_profit_loss", "/api/profit-loss")
config.add_route("stock_movement", "/stock-movement")
config.add_route("api_stock_movement", "/api/stock-movement")
config.add_route("balance_sheet_date", "/balance-sheet/{date}")
config.add_route("balance_sheet", "/balance-sheet")
config.add_route("api_balance_sheet_date", "/api/balance-sheet/{date}")
config.add_route("api_balance_sheet", "/api/balance-sheet")
config.add_route("purchase_entries", "/purchase-entries")
config.add_route("api_purchase_entries", "/api/purchase-entries")
config.add_route("api_auth", "/api/auth")
config.add_route("api_rebase", "/api/rebase/{date}")
config.add_route("api_reset_stock", "/api/reset-stock/{id}")
config.add_route("api_batch", "/api/batch")
config.add_static_view("", "brewman:static")
View File
Whitespace-only changes.
+36
View File
@@ -0,0 +1,36 @@
import uuid
from datetime import datetime
from pydantic import BaseModel
class Client(BaseModel):
id_: uuid.UUID
code: int
name: str
enabled: bool
otp: int
creation_date: datetime
class User(BaseModel):
id_: uuid.UUID
name: str
password: str
locked_out: bool
class LoginHistory(BaseModel):
id_: uuid.UUID
user_id: uuid.UUID
client_id: uuid.UUID
date: datetime
class Group(BaseModel):
id_: uuid.UUID
name: str
class Role(BaseModel):
id_: uuid.UUID
name: str
+88
View File
@@ -0,0 +1,88 @@
import uuid
from datetime import date
from decimal import Decimal
from pydantic import BaseModel, Field
class Product(BaseModel):
id_: uuid.UUID
code: int
name: str
units: str
fraction: Decimal
fraction_units: str
product_yield: Decimal
product_group_id: uuid.UUID
account_id: uuid.UUID
price: Decimal
sale_price: Decimal
is_active: bool
is_fixture: bool
is_purchased: bool
is_sold: bool
class Recipe(BaseModel):
id_: uuid.UUID
product_id: uuid.UUID
quantity: Decimal
cost_price: Decimal
sale_price: Decimal
notes: str
valid_from: date
valid_to: date
effective_from: date
effective_to: date
class RecipeItem(BaseModel):
id_: uuid.UUID
recipe_id: uuid.UUID
product_id: uuid.UUID
quantity: int
price: int
class ProductGroup(BaseModel):
id_: uuid.UUID
name: str
is_fixture: bool
class CostCentreSaveUpdate(BaseModel):
name: str
class CostCentre(CostCentreSaveUpdate):
id_: uuid.UUID = Field(alias="id")
is_fixture: bool = Field(alias="isFixture")
class Account(BaseModel):
id_: uuid.UUID
code: int
name: str
type: int
is_starred: bool
is_active: bool
is_reconcilable: bool
cost_centre: CostCentre
is_fixture: bool
class Employee(Account):
designation: str
salary: int
service_points: Decimal
joining_date: date
leaving_date: date
class DbSetting(BaseModel):
id_: uuid.UUID
name: str
data: bytes
+96
View File
@@ -0,0 +1,96 @@
import uuid
from datetime import datetime, date
from decimal import Decimal
from pydantic import BaseModel
class Voucher(BaseModel):
id: uuid.UUID
date: date
narration: str
is_reconciled: bool
reconcile_date: datetime
is_starred: bool
creation_date: datetime
last_edit_date: datetime
_type: int
user_id: uuid.UUID
posted: bool
poster_id: uuid.UUID
class Journal(BaseModel):
id: uuid.UUID
debit: int
amount: Decimal
voucher_id: uuid.UUID
account_id: uuid.UUID
cost_centre_id: uuid.UUID
class SalaryDeduction(BaseModel):
id: uuid.UUID
voucher_id: uuid.UUID
journal_id: uuid.UUID
gross_salary: int
days_worked: int
esi_ee: int
pf_ee: int
esi_er: int
pf_er: int
class ServiceCharge(BaseModel):
id: uuid.UUID
voucher_id: uuid.UUID
journal_id: uuid.UUID
days_worked: int
points: Decimal
class Inventory(BaseModel):
id: uuid.UUID
voucher_id: uuid.UUID
product_id: uuid.UUID
batch_id: uuid.UUID
quantity: Decimal
rate: Decimal
tax: Decimal
discount: Decimal
class Batch(BaseModel):
id: uuid.UUID
name: datetime
product_id: uuid.UUID
quantity_remaining: Decimal
rate: Decimal
tax: Decimal
discount: Decimal
class Attendance(BaseModel):
id: uuid.UUID
employee_id: uuid.UUID
date: date
attendance_type: int
amount: Decimal
creation_date: datetime
user_id: uuid.UUID
is_valid: bool
class Fingerprint(BaseModel):
id: uuid.UUID
employee_id: uuid.UUID
date: date
class DbImage(BaseModel):
id: uuid.UUID
resource_id: uuid.UUID
resource_type: str
image: bytearray
thumbnail: bytearray
creation_date: datetime
-42
View File
@@ -1,42 +0,0 @@
import uuid
from pyramid.authentication import AuthTktAuthenticationPolicy
from brewman.security.permission_authorization_policy import (
PermissionAuthorizationPolicy,
)
from ..models.auth import User
def get_user(request):
user_id = request.unauthenticated_userid
if user_id is not None:
user = request.dbsession.query(User).get(user_id)
return user
def groupfinder(user_id, request):
if "perms" in request.session:
perms = request.session["perms"]
else:
perms = []
user = request.dbsession.query(User).filter(User.id == uuid.UUID(user_id)).one()
for item in user.groups:
for perm in item.roles:
perms.append(perm.name)
perms = sorted(f7(perms))
request.session["perms"] = perms
return perms
def f7(seq):
seen = set()
seen_add = seen.add
return [x for x in seq if x not in seen and not seen_add(x)]
def includeme(config):
authn_policy = AuthTktAuthenticationPolicy(
"brewman", timeout=900, reissue_time=90, callback=groupfinder
)
config.set_authentication_policy(authn_policy)
config.set_authorization_policy(PermissionAuthorizationPolicy())
config.add_request_method(get_user, "user", reify=True)
@@ -1,19 +0,0 @@
from zope.interface import implementer
from pyramid.interfaces import IAuthorizationPolicy
@implementer(IAuthorizationPolicy)
class PermissionAuthorizationPolicy(object):
def permits(self, context, principals, permission):
if permission == "Authenticated":
permission = "system.Authenticated"
if permission == "Everyone":
permission = "system.Everyone"
allowed = permission in principals
return allowed
def principals_allowed_by_permission(self, context, permission):
allowed = set()
allowed.add(permission)
return allowed
-18
View File
@@ -1,18 +0,0 @@
import unittest
from pyramid import testing
class ViewTests(unittest.TestCase):
def setUp(self):
self.config = testing.setUp()
def tearDown(self):
testing.tearDown()
def test_my_view(self):
from .views import my_view
request = testing.DummyRequest()
info = my_view(request)
self.assertEqual(info["project"], "brewman")
-33
View File
@@ -1,33 +0,0 @@
from pyramid.response import Response
from sqlalchemy.exc import OperationalError, IntegrityError, DBAPIError
import transaction
from brewman.models.validation_exception import ValidationError
def transactional_view(view, info):
if info.options.get("trans"):
def wrapper_view(context, request):
try:
response = view(context, request)
except (
ValidationError,
ValueError,
KeyError,
AttributeError,
TypeError,
OperationalError,
IntegrityError,
DBAPIError,
) as ex:
transaction.abort()
response = Response("Failed validation: {0}".format(str(ex)))
response.status_int = 500
finally:
return response
return wrapper_view
return view
transactional_view.options = ("trans",)
-310
View File
@@ -1,310 +0,0 @@
import datetime
import uuid
import pkg_resources
import transaction
from pyramid.response import Response, FileResponse
from pyramid.view import view_config
from sqlalchemy import func
from sqlalchemy.orm import joinedload_all
from brewman.models.master import CostCentre, Account, AccountType, AccountBase
from brewman.models.validation_exception import ValidationError
from brewman.models.voucher import Voucher, Journal, VoucherType
@view_config(request_method="GET", route_name="accounts_new", permission="Accounts")
@view_config(request_method="GET", route_name="accounts_id", permission="Accounts")
@view_config(route_name="accounts_list", permission="Accounts")
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
@view_config(
request_method="POST",
route_name="api_accounts_new",
renderer="json",
permission="Accounts",
trans=True,
)
def save(request):
json = request.json_body
item = Account(
code=0,
name=json["name"],
type=int(json["type"]),
is_starred=json["isStarred"],
is_active=json["isActive"],
is_reconcilable=json["isReconcilable"],
cost_centre_id=uuid.UUID(json["costCentre"]["id"]),
).create(request.dbsession)
transaction.commit()
return account_info(item.id, request.dbsession)
@view_config(
request_method="PUT",
route_name="api_accounts_id",
renderer="json",
permission="Accounts",
trans=True,
)
def update(request):
item = (
request.dbsession.query(Account)
.filter(Account.id == uuid.UUID(request.matchdict["id"]))
.first()
)
if item.is_fixture:
raise ValidationError(
"{0} is a fixture and cannot be edited or deleted.".format(item.name)
)
new_type = int(request.json_body["type"])
if not item.type == new_type:
item.code = Account.get_code(new_type, request.dbsession)
item.type = new_type
item.name = request.json_body["name"]
item.is_active = request.json_body["isActive"]
item.is_reconcilable = request.json_body["isReconcilable"]
item.is_starred = request.json_body["isStarred"]
item.cost_centre_id = uuid.UUID(request.json_body["costCentre"]["id"])
transaction.commit()
return account_info(item.id, request.dbsession)
@view_config(
request_method="DELETE",
route_name="api_accounts_id",
renderer="json",
permission="Accounts",
)
def delete(request):
account = (
request.dbsession.query(Account)
.filter(Account.id == uuid.UUID(request.matchdict["id"]))
.first()
)
can_delete, reason = account.can_delete(request.has_permission("Advanced Delete"))
if can_delete:
delete_with_data(account, request.dbsession)
transaction.commit()
return account_info(None, request.dbsession)
else:
transaction.abort()
response = Response("Cannot delete account because {0}".format(reason))
response.status_int = 500
return response
@view_config(
request_method="GET",
route_name="api_accounts_id",
renderer="json",
request_param="b",
permission="Authenticated",
)
def show_balance(request):
date = request.GET.get("d", None)
date = (
None
if date is None or date == ""
else datetime.datetime.strptime(date, "%d-%b-%Y")
)
id_ = uuid.UUID(request.matchdict["id"])
return {
"date": balance(id_, date, request.dbsession),
"total": balance(id_, None, request.dbsession),
}
def balance(id_, date, dbsession):
account = dbsession.query(AccountBase).filter(AccountBase.id == id_).first()
if not account.type_object.balance_sheet:
return 0
bal = dbsession.query(func.sum(Journal.amount * Journal.debit)).join(
Journal.voucher
)
if date is not None:
bal = bal.filter(Voucher.date <= date)
bal = (
bal.filter(Voucher.type != VoucherType.by_name("Issue").id)
.filter(Journal.account_id == id_)
.scalar()
)
return 0 if bal is None else bal
@view_config(
request_method="GET",
route_name="api_accounts_id",
renderer="json",
permission="Accounts",
)
def show_id(request):
return account_info(uuid.UUID(request.matchdict["id"]), request.dbsession)
@view_config(
request_method="GET",
route_name="api_accounts_new",
renderer="json",
permission="Accounts",
)
def show_blank(request):
return account_info(None, request.dbsession)
@view_config(
request_method="GET",
route_name="api_accounts_list",
renderer="json",
request_param="l",
permission="Authenticated",
)
def show_list(request):
list_ = (
request.dbsession.query(Account)
.order_by(Account.type)
.order_by(Account.name)
.order_by(Account.code)
.all()
)
accounts = []
for item in list_:
accounts.append(
{
"id": item.id,
"name": item.name,
"type": item.type_object.name,
"isActive": item.is_active,
"isReconcilable": item.is_reconcilable,
"isStarred": item.is_starred,
"costCentre": item.cost_centre.name,
"isFixture": item.is_fixture,
}
)
return accounts
@view_config(
request_method="GET",
route_name="api_accounts_list",
renderer="json",
request_param="q",
permission="Authenticated",
)
def show_term(request):
type_ = request.GET.get("t", None)
type_ = int(type_) if type_ is not None else None
filter_ = request.GET.get("q", None)
filter_ = filter_ if filter_ is not None and filter_ is not "" else None
reconcilable = request.GET.get("r", None)
reconcilable = reconcilable if reconcilable is not None else None
active = request.GET.get("a", None)
active = active if active is not None else None
count = request.GET.get("c", None)
count = None if count is None or count == "" else int(count)
list_ = []
for index, item in enumerate(
AccountBase.list(type_, filter_, reconcilable, active, request.dbsession)
):
list_.append({"id": item.id, "name": item.name})
if count is not None and index == count - 1:
break
return list_
@view_config(
request_method="GET",
route_name="api_account_type_list",
renderer="json",
permission="Authenticated",
)
def account_type_list(request):
account_types = []
for item in AccountType.list():
account_types.append({"id": item.id, "name": item.name})
return account_types
def account_info(id_, dbsession):
if id_ is None:
account = {
"code": "(Auto)",
"type": AccountType.by_name("Creditors").id,
"isActive": True,
"isReconcilable": False,
"isStarred": False,
"costCentre": CostCentre.overall(),
}
else:
account = dbsession.query(Account).filter(Account.id == id_).first()
account = {
"id": account.id,
"code": account.code,
"name": account.name,
"type": account.type,
"isActive": account.is_active,
"isReconcilable": account.is_reconcilable,
"isStarred": account.is_starred,
"isFixture": account.is_fixture,
"costCentre": {
"id": account.cost_centre_id,
"name": account.cost_centre.name,
},
}
return account
def delete_with_data(account, dbsession):
suspense_account = (
dbsession.query(Account).filter(Account.id == Account.suspense()).first()
)
query = (
dbsession.query(Voucher)
.options(joinedload_all(Voucher.journals, Journal.account, innerjoin=True))
.filter(Voucher.journals.any(Journal.account_id == account.id))
.all()
)
for voucher in query:
others, sus_jnl, acc_jnl = False, None, None
for journal in voucher.journals:
if journal.account_id == account.id:
acc_jnl = journal
elif journal.account_id == Account.suspense():
sus_jnl = journal
else:
others = True
if not others:
dbsession.delete(voucher)
else:
if sus_jnl is None:
acc_jnl.account = suspense_account
voucher.narration += "\nSuspense \u20B9{0:,.2f} is {1}".format(
acc_jnl.amount, account.name
)
else:
amount = (sus_jnl.debit * sus_jnl.amount) + (
acc_jnl.debit * acc_jnl.amount
)
dbsession.delete(acc_jnl)
if amount == 0:
dbsession.delete(sus_jnl)
else:
sus_jnl.amount = abs(amount)
sus_jnl.debit = -1 if amount < 0 else 1
voucher.narration += "\nDeleted \u20B9{0:,.2f} of {1}".format(
acc_jnl.amount * acc_jnl.debit, account.name
)
if voucher.type in (
VoucherType.by_name("Payment").id,
VoucherType.by_name("Receipt").id,
):
voucher.type = VoucherType.by_name("Journal")
dbsession.delete(account)
-274
View File
@@ -1,274 +0,0 @@
import datetime
import uuid
import pkg_resources
import transaction
from pyramid.response import FileResponse
from pyramid.view import view_config
from sqlalchemy import or_
from brewman.models.master import AttendanceType, Employee
from brewman.models.validation_exception import ValidationError
from brewman.models.voucher import Attendance
from brewman.views.fingerprint import get_prints
from brewman.views.services.session import (
session_period_start,
session_period_finish,
session_current_date,
)
@view_config(
request_method="GET", route_name="attendance_date", permission="Attendance"
)
@view_config(request_method="GET", route_name="attendance", permission="Attendance")
@view_config(
request_method="GET", route_name="employee_attendance_id", permission="Attendance"
)
@view_config(
request_method="GET", route_name="employee_attendance", permission="Attendance"
)
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
@view_config(
request_method="GET",
route_name="api_attendance_types",
renderer="json",
permission="Authenticated",
)
def show_list(request):
list_ = AttendanceType.list()
attendance_types = []
for item in list_:
attendance_types.append({"id": item.id, "name": item.name, "value": item.value})
return attendance_types
@view_config(
request_method="GET",
route_name="api_attendance",
renderer="json",
permission="Attendance",
)
def attendance_blank(request):
return {"date": session_current_date(request), "body": []}
@view_config(
request_method="GET",
route_name="api_attendance_date",
renderer="json",
permission="Attendance",
)
def attendance_date(request):
date = request.matchdict.get("date", None)
return attendance_date_report(date, request.dbsession)
def attendance_date_report(date, dbsession):
report = {"date": date, "body": []}
date = datetime.datetime.strptime(date, "%d-%b-%Y")
employees = (
dbsession.query(Employee)
.filter(Employee.joining_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)
.all()
)
for item in employees:
att = (
dbsession.query(Attendance)
.filter(Attendance.employee_id == item.id)
.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, dbsession)
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,
}
)
return report
@view_config(
request_method="POST",
route_name="api_attendance_date",
renderer="json",
permission="Attendance",
trans=True,
)
def save(request):
user_id = uuid.UUID(request.authenticated_userid)
date = request.matchdict.get("date", None)
date_object = datetime.datetime.strptime(date, "%d-%b-%Y")
for item in request.json_body["body"]:
employee_id = uuid.UUID(item["id"])
attendance_type = item["attendanceType"]["id"]
if attendance_type != 0:
attendance = Attendance(
employee_id=employee_id,
date=date_object,
attendance_type=attendance_type,
user_id=user_id,
)
attendance.create(request.dbsession)
transaction.commit()
return attendance_date_report(date, request.dbsession)
@view_config(
request_method="GET",
route_name="api_employee_attendance",
renderer="json",
permission="Attendance",
)
def employee_attendance_blank(request):
return {
"startDate": session_period_start(request),
"finishDate": session_period_finish(request),
"employee": None,
"body": [],
}
@view_config(
request_method="GET",
route_name="api_employee_attendance_id",
renderer="json",
permission="Attendance",
)
def employee_attendance_report(request):
employee = (
request.dbsession.query(Employee)
.filter(Employee.id == uuid.UUID(request.matchdict["id"]))
.first()
)
if employee is None:
raise ValidationError("Employee id is wrong")
start_date = request.GET.get("s", session_period_start(request))
finish_date = request.GET.get("f", session_period_finish(request))
info = {
"startDate": start_date,
"finishDate": finish_date,
"employee": {"id": employee.id, "name": employee.name},
}
start_date = datetime.datetime.strptime(start_date, "%d-%b-%Y")
finish_date = datetime.datetime.strptime(finish_date, "%d-%b-%Y")
start_date = (
employee.joining_date if employee.joining_date > start_date else start_date
)
finish_date = (
employee.leaving_date
if not employee.is_active and employee.leaving_date < finish_date
else finish_date
)
info["body"] = employee_attendance(
employee, start_date, finish_date, request.dbsession
)
return info
def employee_attendance(employee, start_date, finish_date, dbsession):
if not isinstance(start_date, datetime.datetime):
start_date = datetime.datetime.strptime(start_date, "%d-%b-%Y")
if not isinstance(finish_date, datetime.datetime):
finish_date = datetime.datetime.strptime(finish_date, "%d-%b-%Y")
list_ = []
for item in daterange(start_date, finish_date, inclusive=True):
att = (
dbsession.query(Attendance)
.filter(Attendance.employee_id == employee.id)
.filter(Attendance.date == item)
.filter(Attendance.is_valid == True)
.first()
)
att = 0 if att is None else att.attendance_type
prints, hours, worked = get_prints(employee.id, item, dbsession)
list_.append(
{
"date": item.strftime("%d-%b-%Y"),
"attendanceType": {"id": att},
"prints": prints,
"hours": hours,
"worked": worked,
}
)
return list_
@view_config(
request_method="POST",
route_name="api_employee_attendance_id",
renderer="json",
permission="Attendance",
trans=True,
)
def save_employee_attendance(request):
start_date = None
finish_date = None
user_id = uuid.UUID(request.authenticated_userid)
employee = (
request.dbsession.query(Employee)
.filter(Employee.id == uuid.UUID(request.matchdict["id"]))
.first()
)
for item in request.json_body["body"]:
if start_date is None:
start_date = item["date"]
finish_date = item["date"]
attendance_type = item["attendanceType"]["id"]
if attendance_type != 0:
date = datetime.datetime.strptime(item["date"], "%d-%b-%Y")
attendance = Attendance(
employee_id=employee.id,
date=date,
attendance_type=attendance_type,
user_id=user_id,
)
attendance.create(request.dbsession)
transaction.commit()
return {
"startDate": start_date,
"finishDate": finish_date,
"employee": {"id": employee.id, "name": employee.name},
"body": employee_attendance(
employee, start_date, finish_date, request.dbsession
),
}
def daterange(start, stop, step=datetime.timedelta(days=1), inclusive=False):
# inclusive=False to behave like range by default
if step.days > 0:
while start < stop:
yield start
start = start + step
# not +=! don't modify object passed in if it's mutable
# since this function is not restricted to
# only types from datetime module
elif step.days < 0:
while start > stop:
yield start
start = start + step
if inclusive and start == stop:
yield start
-132
View File
@@ -1,132 +0,0 @@
import uuid
import pkg_resources
from pyramid.response import Response, FileResponse
from pyramid.view import view_config
import transaction
from brewman.models.master import CostCentre
from brewman.models.validation_exception import ValidationError
@view_config(request_method="GET", route_name="cost_centres_new", permission="Cost Centres")
@view_config(request_method="GET", route_name="cost_centres_id", permission="Cost Centres")
@view_config(route_name="cost_centres_list", permission="Authenticated")
def html(request):
package, resource = "brewman:static/index.html".split(":", 1)
file = pkg_resources.resource_filename(package, resource)
return FileResponse(file, request=request)
@view_config(
request_method="POST",
route_name="api_cost_centres_new",
renderer="json",
permission="Cost Centres",
trans=True,
)
def save(request):
item = CostCentre(request.json_body["name"])
request.dbsession.add(item)
transaction.commit()
return cost_centre_info(item.id, request.dbsession)
@view_config(
request_method="PUT",
route_name="api_cost_centres_id",
renderer="json",
permission="Cost Centres",
trans=True,
)
def update(request):
item = (
request.dbsession.query(CostCentre)
.filter(CostCentre.id == uuid.UUID(request.matchdict["id"]))
.first()
)
if item.is_fixture:
raise ValidationError(
"{0} is a fixture and cannot be edited or deleted.".format(item.name)
)
item.name = request.json_body["name"]
transaction.commit()
return cost_centre_info(item.id, request.dbsession)
@view_config(
request_method="DELETE",
route_name="api_cost_centres_id",
renderer="json",
permission="Cost Centres",
)
def delete(request):
item = (
request.dbsession.query(CostCentre)
.filter(CostCentre.id == uuid.UUID(request.matchdict["id"]))
.first()
)
if item is None:
response = Response("Cost Centre not found")
response.status_int = 500
return response
elif item.is_fixture:
raise ValidationError(
"{0} is a fixture and cannot be edited or deleted.".format(item.name)
)
else:
response = Response("Cost Centre deletion not implemented")
response.status_int = 500
return response
@view_config(
request_method="GET",
route_name="api_cost_centres_id",
renderer="json",
permission="Cost Centres",
)
def show_id(request):
return cost_centre_info(
uuid.UUID(request.matchdict.get("id", None)), request.dbsession
)
@view_config(
request_method="GET",
route_name="api_cost_centres_new",
renderer="json",
permission="Cost Centres",
)
def show_blank(request):
return cost_centre_info(None, request.dbsession)
@view_config(
request_method="GET",
route_name="api_cost_centres_list",
request_param="l",
renderer="json",
permission="Authenticated",
)
def show_list(request):
list_ = request.dbsession.query(CostCentre).order_by(CostCentre.name).all()
cost_centres = []
for item in list_:
cost_centres.append(
{"id": item.id, "name": item.name, "isFixture": item.is_fixture}
)
return cost_centres
def cost_centre_info(id_, dbsession):
if id_ is None:
return {}
else:
cost_centre = dbsession.query(CostCentre).filter(CostCentre.id == id_).first()
return {
"id": cost_centre.id,
"name": cost_centre.name,
"isFixture": cost_centre.is_fixture,
}
-155
View File
@@ -1,155 +0,0 @@
from datetime import datetime, timedelta
import transaction
from pyramid.httpexceptions import HTTPFound
from pyramid.response import Response
from pyramid.security import remember, forget
from pyramid.view import view_config
from sqlalchemy import and_
from brewman.models.auth import User, Client, LoginHistory
from brewman.security import f7
@view_config(route_name="logout")
def logout(request):
request.session.invalidate()
headers = forget(request)
return HTTPFound(location=request.route_url("home"), headers=headers)
@view_config(request_method="POST", route_name="api_logout", renderer="json")
def api_logout(request):
request.session.invalidate()
request.response.headers = forget(request)
return {"isAuthenticated": False, "perms": {}}
@view_config(request_method="POST", route_name="api_login", renderer="json", trans=True)
def login(request):
username = request.json_body.get("name", None)
password = request.json_body.get("password", None)
found, user = User.auth(username, password, request.dbsession)
client = Client.by_code(request.cookies.get("ClientID", None), request.dbsession)
otp = request.json_body.get("otp", None)
client_name = request.json_body.get("clientName", None)
if user is not None:
allowed, client, response = check_client(
client, otp, client_name, user, request.dbsession
)
if found and allowed:
headers = remember(request, str(user.id))
request.response.headers = headers
request.response.set_cookie(
"ClientID", value=str(client.code), max_age=365 * 24 * 60 * 60
)
record_login(user.id, client, request.dbsession)
transaction.commit()
return {
"id": user.id,
"name": user.name,
"isAuthenticated": True,
"perms": sorted(f7([p.name for g in user.groups for p in g.roles])),
}
elif not found:
response = Response("Login failed")
response.status_int = 403
transaction.commit()
return response
else:
transaction.commit()
return response
def check_client(client, otp, client_name, user, dbsession):
outside_login_allowed = False
for group in user.groups:
for perm in group.roles:
if perm.name == "Clients":
outside_login_allowed = True
if (
dbsession.query(Client).filter(Client.enabled == True).count() == 0
and outside_login_allowed
):
client = Client.create(dbsession)
client.otp = None
client.name = "Created on login by " + user.name
client.enabled = True
return True, client, None
if client is None:
if outside_login_allowed:
client = Client.create(dbsession)
return True, client, None
else:
client = Client.create(dbsession)
response = Response("Unknown Client")
response.status_int = 403
response.set_cookie(
"ClientID", value=str(client.code), max_age=10 * 365 * 24 * 60 * 60
)
return False, None, response
if client.enabled or outside_login_allowed:
return True, client, None
if client.otp is None:
response = Response("Client is Forbidden")
response.status_int = 403
return False, None, response
if otp is None:
response = Response("OTP not supplied")
response.status_int = 403
return False, None, response
elif client.otp != int(otp):
response = Response("OTP is wrong")
response.status_int = 403
return False, None, response
else:
client.otp = None
client.enabled = True
client.name = client_name
return True, client, None
def record_login(user_id, client, dbsession):
history = LoginHistory(user_id)
history.client = client
dbsession.add(history)
recent_logins = (
dbsession.query(LoginHistory.client_id.distinct())
.filter(LoginHistory.date > datetime.utcnow() - timedelta(days=30))
.subquery()
)
deletable_clients = (
dbsession.query(Client.id)
.filter(Client.creation_date < datetime.utcnow() - timedelta(days=3))
.filter(Client.enabled == False)
.subquery()
)
dbsession.execute(
LoginHistory.__table__.delete(
and_(
~LoginHistory.client_id.in_(recent_logins),
LoginHistory.client_id.in_(deletable_clients),
)
)
)
dbsession.execute(
Client.__table__.delete(
and_(
Client.creation_date < datetime.utcnow() - timedelta(days=3),
Client.enabled == False,
~Client.id.in_(recent_logins),
)
)
)
-51
View File
@@ -1,51 +0,0 @@
[app:main]
use = egg:brewman
pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.default_locale_name = en
# sqlalchemy.url = postgresql://hops:123456@localhost:7654/hops
sqlalchemy.url = postgresql://postgres:123456@localhost:5432/hops
[server:main]
use = egg:waitress#main
host = 0.0.0.0
port = 6543
# Begin logging configuration
[loggers]
keys = root, brewman, sqlalchemy.engine.base
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARNING
handlers = console
[logger_brewman]
level = WARNING
handlers =
qualname = brewman
[logger_sqlalchemy.engine.base]
level = DEBUG
handlers =
qualname = sqlalchemy.engine.base
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
# End logging configuration
-44
View File
@@ -1,44 +0,0 @@
[app:main]
use = egg:brewman
pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.default_locale_name = en
[server:main]
use = egg:waitress#main
host = 0.0.0.0
port = 6543
# Begin logging configuration
[loggers]
keys = root, brewman
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
[logger_brewman]
level = WARN
handlers =
qualname = brewman
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
# End logging configuration
+8 -7
View File
@@ -1,7 +1,8 @@
pyramid
waitress
transaction
zope.sqlalchemy
SQLAlchemy
psycopg2-binary
uvicorn
fastapi
environs
python-jose[cryptography]
passlib[bcrypt]
psycopg2
python-multipart
pyjwt
+6 -14
View File
@@ -3,20 +3,12 @@ import os
from setuptools import setup, find_packages
here = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(here, 'README.txt')) as f:
README = f.read()
with open(os.path.join(here, 'CHANGES.txt')) as f:
CHANGES = f.read()
requires = [
'pyramid',
'waitress',
'pyramid_tm',
'transaction',
'zope.sqlalchemy',
'SQLAlchemy',
'psycopg2-binary',
]
with open(os.path.join(here, 'README.txt'), "r") as r:
README = r.read()
with open(os.path.join(here, 'CHANGES.txt'), "r") as c:
CHANGES = c.read()
with open(os.path.join(here, 'requirements.txt'), "r") as r:
requires = r.read().splitlines()
setup(name='brewman',
version='6.0',