2020-05-30 06:36:37 +00:00
import uuid
2020-10-07 15:18:43 +00:00
from datetime import date , datetime
2016-12-24 11:41:01 +00:00
from decimal import Decimal
2020-10-07 15:18:43 +00:00
from typing import Optional
2020-05-30 06:36:37 +00:00
2020-10-07 15:18:43 +00:00
import brewman . schemas . voucher as output
from fastapi import APIRouter , Depends , HTTPException , Security , status
2015-02-12 12:12:46 +00:00
from sqlalchemy import func , or_
2020-05-30 06:36:37 +00:00
from sqlalchemy . exc import SQLAlchemyError
2020-05-17 10:08:13 +00:00
from sqlalchemy . orm import Session
2014-06-03 11:42:52 +00:00
2020-10-07 15:18:43 +00:00
from . . core . security import get_current_active_user as get_user
from . . core . session import get_first_day
from . . db . session import SessionLocal
from . . models . master import Account , AccountBase , AttendanceType , CostCentre , Employee
from . . models . voucher import (
2019-04-06 04:13:12 +00:00
Attendance ,
2020-10-07 15:18:43 +00:00
DbImage ,
Inventory ,
2019-04-06 04:13:12 +00:00
Journal ,
2020-10-07 15:18:43 +00:00
Voucher ,
VoucherType ,
2019-04-06 04:13:12 +00:00
)
2020-10-07 15:18:43 +00:00
from . . routers import get_lock_info
2020-05-30 06:36:37 +00:00
from . . schemas . auth import UserToken
2020-10-07 15:18:43 +00:00
2020-05-17 10:08:13 +00:00
2020-05-12 04:25:33 +00:00
router = APIRouter ( )
2020-05-30 06:36:37 +00:00
# Dependency
def get_db ( ) - > Session :
try :
db = SessionLocal ( )
yield db
finally :
db . close ( )
@router.post ( " /post-voucher/ {id_} " , response_model = output . Voucher )
def post_voucher (
2020-10-07 15:18:43 +00:00
id_ : uuid . UUID ,
db : Session = Depends ( get_db ) ,
user : UserToken = Security ( get_user , scopes = [ " post-vouchers " ] ) ,
2020-05-30 06:36:37 +00:00
) :
try :
voucher : Voucher = db . query ( Voucher ) . filter ( Voucher . id == id_ ) . first ( )
check_voucher_lock_info ( voucher . date , voucher . date , db )
voucher . posted = True
voucher . poster_id = user . id_
db . commit ( )
return voucher_info ( voucher , db )
except SQLAlchemyError as e :
db . rollback ( )
raise HTTPException (
2020-10-07 15:18:43 +00:00
status_code = status . HTTP_500_INTERNAL_SERVER_ERROR ,
detail = str ( e ) ,
2020-05-23 04:15:02 +00:00
)
2020-05-30 06:36:37 +00:00
except Exception :
db . rollback ( )
2020-06-01 03:31:31 +00:00
raise
2012-10-22 13:53:58 +00:00
2012-10-29 16:47:22 +00:00
2020-05-30 06:36:37 +00:00
def check_delete_permissions ( voucher : Voucher , user : UserToken ) :
voucher_type = VoucherType . by_id ( voucher . type ) . name . replace ( " " , " - " ) . lower ( )
2020-09-13 01:54:24 +00:00
if voucher_type in [ " payment " , " receipt " ] :
2020-05-30 06:36:37 +00:00
voucher_type = " journal "
if voucher . posted and " edit-posted-vouchers " not in user . permissions :
raise HTTPException (
2020-10-07 15:18:43 +00:00
status_code = status . HTTP_403_FORBIDDEN ,
detail = " You are not allowed to edit posted vouchers " ,
2020-05-23 04:15:02 +00:00
)
2020-10-07 16:59:24 +00:00
elif (
voucher . user_id != user . id_
and " edit-other-user ' s-vouchers " not in user . permissions
) :
2020-05-30 06:36:37 +00:00
raise HTTPException (
2020-10-07 15:18:43 +00:00
status_code = status . HTTP_403_FORBIDDEN ,
detail = " You are not allowed to edit other user ' s vouchers " ,
2020-05-23 04:15:02 +00:00
)
2020-05-30 06:36:37 +00:00
elif voucher_type not in user . permissions :
raise HTTPException (
2020-05-31 08:56:50 +00:00
status_code = status . HTTP_403_FORBIDDEN ,
detail = f " You are not allowed ( { VoucherType . by_id ( voucher . type ) . name } ) vouchers " ,
2020-05-23 04:15:02 +00:00
)
2013-07-07 20:03:02 +00:00
2012-10-28 19:32:22 +00:00
2020-05-30 06:36:37 +00:00
@router.delete ( " /delete/ {id_} " )
def delete_voucher (
2020-10-07 15:18:43 +00:00
id_ : uuid . UUID ,
db : Session = Depends ( get_db ) ,
user : UserToken = Security ( get_user ) ,
2020-05-30 06:36:37 +00:00
) :
voucher : Voucher = db . query ( Voucher ) . filter ( Voucher . id == id_ ) . first ( )
images = db . query ( DbImage ) . filter ( DbImage . resource_id == voucher . id ) . all ( )
check_delete_permissions ( voucher , user )
check_voucher_lock_info ( None , voucher . date , db )
json_voucher = voucher_info ( voucher , db )
2013-04-14 11:09:55 +00:00
batches_to_delete = [ ]
2019-04-06 04:13:12 +00:00
if voucher . type == VoucherType . by_name ( " Issue " ) . id :
2012-10-22 13:53:58 +00:00
for item in voucher . journals :
if item . debit == 1 :
2015-02-12 12:12:46 +00:00
destination = item . cost_centre_id
2012-09-12 08:22:28 +00:00
else :
2015-02-12 12:12:46 +00:00
source = item . cost_centre_id
2016-12-24 11:41:01 +00:00
if source == CostCentre . cost_centre_purchase ( ) :
batch_consumed = True
elif destination == CostCentre . cost_centre_purchase ( ) :
batch_consumed = False
else :
batch_consumed = None
2012-10-22 13:53:58 +00:00
if batch_consumed is None :
pass
elif batch_consumed :
for item in voucher . inventories :
item . batch . quantity_remaining + = item . quantity
else :
2012-09-12 08:22:28 +00:00
for item in voucher . inventories :
2020-10-07 14:07:28 +00:00
if item . batch . quantity_remaining < item . quantity :
raise HTTPException (
status_code = status . HTTP_423_LOCKED ,
detail = f " Only { item . batch . quantity_remaining } of { item . product . name } remaining. \n So it cannot be deleted " ,
2019-04-06 04:13:12 +00:00
)
2012-09-12 08:22:28 +00:00
item . batch . quantity_remaining - = item . quantity
2019-04-06 04:13:12 +00:00
elif voucher . type == VoucherType . by_name ( " Purchase " ) . id :
2012-10-22 13:53:58 +00:00
for item in voucher . inventories :
2019-04-06 04:13:12 +00:00
uses = (
2020-05-30 06:36:37 +00:00
db . query ( func . count ( Inventory . id ) )
2020-05-23 04:15:02 +00:00
. filter ( Inventory . batch_id == item . batch . id )
. filter ( Inventory . id != item . id )
. scalar ( )
2019-04-06 04:13:12 +00:00
)
2013-02-07 13:22:58 +00:00
if uses > 0 :
2020-10-07 14:07:28 +00:00
raise HTTPException (
status_code = status . HTTP_423_LOCKED ,
detail = f " { item . product . name } has been issued and cannot be deleted " ,
)
2013-04-14 11:09:55 +00:00
batches_to_delete . append ( item . batch )
2019-04-06 04:13:12 +00:00
elif voucher . type == VoucherType . by_name ( " Purchase Return " ) . id :
2012-11-04 09:57:46 +00:00
for item in voucher . inventories :
item . batch . quantity_remaining + = item . quantity
2013-04-14 11:09:55 +00:00
for b in batches_to_delete :
2020-05-30 06:36:37 +00:00
db . delete ( b )
db . delete ( voucher )
2014-06-03 11:42:52 +00:00
for image in images :
2020-05-30 06:36:37 +00:00
db . delete ( image )
db . commit ( )
return blank_voucher ( info = json_voucher , db = db )
2012-10-22 13:53:58 +00:00
2020-05-17 10:08:13 +00:00
def voucher_info ( voucher , db ) :
2019-04-06 04:13:12 +00:00
json_voucher = {
" id " : voucher . id ,
" date " : voucher . date . strftime ( " %d - % b- % Y " ) ,
" isStarred " : voucher . is_starred ,
" type " : VoucherType . by_id ( voucher . type ) . name ,
" posted " : voucher . posted ,
" narration " : voucher . narration ,
" journals " : [ ] ,
" inventories " : [ ] ,
" employeeBenefits " : [ ] ,
" incentives " : [ ] ,
2020-05-23 04:15:02 +00:00
" incentive " : 0 ,
2019-04-06 04:13:12 +00:00
" files " : [ ] ,
" creationDate " : voucher . creation_date . strftime ( " %d - % b- % Y % H: % M " ) ,
" lastEditDate " : voucher . last_edit_date . strftime ( " %d - % b- % Y % H: % M " ) ,
" user " : { " id " : voucher . user . id , " name " : voucher . user . name } ,
" poster " : voucher . poster . name if voucher . posted else " " ,
}
2013-10-03 10:12:43 +00:00
if voucher . reconcile_date is not None :
2020-05-23 04:15:02 +00:00
json_voucher [ " reconcileDate " ] = voucher . reconcile_date . strftime ( " %d - % b- % Y " )
2020-05-21 07:41:47 +00:00
if voucher . type == 2 : # "Purchase"
item = [ j for j in voucher . journals if j . debit == - 1 ] [ 0 ]
json_voucher [ " vendor " ] = { " id " : item . account . id , " name " : item . account . name }
2020-05-21 19:45:25 +00:00
elif voucher . type == 3 : # "Issue"
item = [ j for j in voucher . journals if j . debit == - 1 ] [ 0 ]
json_voucher [ " source " ] = { " id " : item . cost_centre_id , " name " : " " }
item = [ j for j in voucher . journals if j . debit == 1 ] [ 0 ]
json_voucher [ " destination " ] = { " id " : item . cost_centre_id , " name " : " " }
2020-05-21 07:41:47 +00:00
else :
for item in voucher . journals :
json_voucher [ " journals " ] . append (
{
" id " : item . id ,
" debit " : item . debit ,
" amount " : item . amount ,
" account " : { " id " : item . account . id , " name " : item . account . name } ,
" costCentre " : { " id " : item . cost_centre_id } ,
}
)
for item in voucher . employee_benefits :
2019-04-06 04:13:12 +00:00
json_voucher [ " employeeBenefits " ] . append (
{
" grossSalary " : item . gross_salary ,
" daysWorked " : item . days_worked ,
" esiEmployee " : item . esi_ee ,
" pfEmployee " : item . pf_ee ,
" esiEmployer " : item . esi_er ,
" pfEmployer " : item . pf_er ,
2020-05-23 04:15:02 +00:00
" employee " : {
" id " : item . journal . account . id ,
" name " : item . journal . account . name ,
" designation " : item . journal . account . designation ,
" costCentre " : {
" id " : item . journal . account . cost_centre . id ,
" name " : item . journal . account . cost_centre . name ,
2019-04-06 04:13:12 +00:00
} ,
} ,
}
)
2020-05-12 15:22:07 +00:00
for item in voucher . incentives :
2020-10-07 16:59:24 +00:00
employee = (
db . query ( Employee ) . filter ( Employee . id == item . journal . account_id ) . first ( )
)
2019-04-06 04:13:12 +00:00
json_voucher [ " incentives " ] . append (
2018-07-07 11:01:44 +00:00
{
2020-05-23 04:15:02 +00:00
" employeeId " : item . journal . account_id ,
2019-04-06 04:13:12 +00:00
" name " : item . journal . account . name ,
" designation " : employee . designation ,
" department " : item . journal . account . cost_centre . name ,
" daysWorked " : item . days_worked ,
" points " : item . points ,
2018-07-07 11:01:44 +00:00
}
)
2019-04-06 04:13:12 +00:00
if len ( json_voucher [ " incentives " ] ) > 0 :
2020-10-07 16:59:24 +00:00
json_voucher [ " incentive " ] = next (
x . amount for x in voucher . journals if x . account_id == Account . incentive_id ( )
)
2012-09-12 08:22:28 +00:00
for item in voucher . inventories :
2020-05-21 07:41:47 +00:00
text = f " { item . product . name } ( { item . product . units } ) { item . batch . quantity_remaining : .2f } @ { item . batch . rate : .2f } from { item . batch . name . strftime ( ' %d - % b- % Y ' ) } "
2019-04-06 04:13:12 +00:00
json_voucher [ " inventories " ] . append (
{
" id " : item . id ,
" quantity " : item . quantity ,
" rate " : item . rate ,
" tax " : item . tax ,
" discount " : item . discount ,
" amount " : item . amount ,
" product " : {
" id " : item . product . id ,
" name " : item . product . full_name ,
" units " : item . product . units ,
" price " : item . rate ,
} ,
" batch " : {
" id " : item . batch . id ,
" name " : text ,
" quantityRemaining " : item . batch . quantity_remaining ,
" tax " : item . batch . tax ,
" discount " : item . batch . discount ,
" rate " : item . batch . rate ,
2020-10-07 15:18:43 +00:00
" product " : {
" id " : item . batch . product . id ,
" name " : item . batch . product . full_name ,
} ,
2019-04-06 04:13:12 +00:00
} ,
2018-05-25 13:49:00 +00:00
}
2019-04-06 04:13:12 +00:00
)
2020-05-23 04:15:02 +00:00
images = db . query ( DbImage ) . filter ( DbImage . resource_id == voucher . id ) . all ( )
2014-06-03 11:42:52 +00:00
for image in images :
2020-05-31 08:56:50 +00:00
json_voucher [ " files " ] . append (
{
" id " : image . id ,
" resized " : f " /db-image/ { image . id } /resized " ,
" thumbnail " : f " /db-image/ { image . id } /thumbnail " ,
}
)
2012-09-12 08:22:28 +00:00
return json_voucher
2020-05-21 07:41:47 +00:00
def blank_voucher ( info , db ) :
2019-04-06 04:13:12 +00:00
if " type " not in info :
2020-10-07 14:07:28 +00:00
raise HTTPException (
2020-10-07 15:18:43 +00:00
status_code = status . HTTP_404_NOT_FOUND ,
detail = " Voucher Type is null " ,
2020-10-07 14:07:28 +00:00
)
2019-05-10 03:49:53 +00:00
type_ = info [ " type " ]
2019-04-06 04:13:12 +00:00
if " date " not in info :
2020-10-07 14:07:28 +00:00
raise HTTPException (
2020-10-07 15:18:43 +00:00
status_code = status . HTTP_404_NOT_FOUND ,
detail = f " Date cannot be null " ,
2020-10-07 14:07:28 +00:00
)
2018-07-07 11:01:44 +00:00
json_voucher = {
2019-05-10 03:49:53 +00:00
" type " : type_ ,
2019-04-06 04:13:12 +00:00
" date " : info [ " date " ] ,
" isStarred " : False ,
" posted " : False ,
" narration " : " " ,
" journals " : [ ] ,
" inventories " : [ ] ,
2020-05-29 20:28:17 +00:00
" employeeBenefits " : [ ] ,
2018-07-07 11:01:44 +00:00
}
2019-05-10 03:49:53 +00:00
if type_ == " Journal " :
2012-09-12 08:22:28 +00:00
pass
2019-05-10 03:49:53 +00:00
elif type_ == " Payment " :
2018-07-07 11:01:44 +00:00
account = None
2020-05-21 07:41:47 +00:00
if info and " account " in info and info [ " account " ] :
2020-10-07 16:59:24 +00:00
account = (
db . query ( AccountBase )
. filter ( AccountBase . id == uuid . UUID ( info [ " account " ] ) )
. first ( )
)
2018-07-07 11:01:44 +00:00
if account is not None :
2019-04-06 04:13:12 +00:00
account = { " id " : account . id , " name " : account . name }
2013-10-05 11:45:36 +00:00
else :
2018-07-07 11:01:44 +00:00
account = AccountBase . cash_in_hand ( )
2019-04-06 04:13:12 +00:00
json_voucher [ " journals " ] . append ( { " account " : account , " amount " : 0 , " debit " : - 1 } )
2019-05-10 03:49:53 +00:00
elif type_ == " Receipt " :
2018-07-07 11:01:44 +00:00
account = None
2020-05-21 07:41:47 +00:00
if info and " account " in info and info [ " account " ] :
2020-10-07 16:59:24 +00:00
account = (
db . query ( AccountBase )
. filter ( AccountBase . id == uuid . UUID ( info [ " account " ] ) )
. first ( )
)
2018-07-07 11:01:44 +00:00
if account is not None :
2019-04-06 04:13:12 +00:00
account = { " id " : account . id , " name " : account . name }
2013-10-05 11:45:36 +00:00
else :
2018-07-07 11:01:44 +00:00
account = AccountBase . cash_in_hand ( )
2019-04-06 04:13:12 +00:00
json_voucher [ " journals " ] . append ( { " account " : account , " amount " : 0 , " debit " : 1 } )
2019-05-10 03:49:53 +00:00
elif type_ == " Purchase " :
2020-05-21 07:41:47 +00:00
json_voucher [ " vendor " ] = AccountBase . local_purchase ( )
2019-05-10 03:49:53 +00:00
elif type_ == " Purchase Return " :
2020-10-07 16:59:24 +00:00
json_voucher [ " journals " ] . append (
{ " account " : AccountBase . local_purchase ( ) , " amount " : 0 , " debit " : 1 }
)
2019-05-10 03:49:53 +00:00
elif type_ == " Issue " :
2020-05-21 19:45:25 +00:00
if " source " in info :
json_voucher [ " source " ] = { " id " : info [ " source " ] }
2012-09-12 08:22:28 +00:00
else :
2020-05-21 19:45:25 +00:00
json_voucher [ " source " ] = { " id " : CostCentre . cost_centre_purchase ( ) }
if " destination " in info :
json_voucher [ " destination " ] = { " id " : info [ " destination " ] }
else :
json_voucher [ " destination " ] = { " id " : CostCentre . cost_centre_kitchen ( ) }
2020-05-21 07:41:47 +00:00
elif type_ == " Employee Benefit " :
2019-04-06 04:13:12 +00:00
json_voucher [ " employeeBenefits " ] = [ ]
2020-05-12 15:22:07 +00:00
elif type_ == " Incentive " :
2020-10-07 16:59:24 +00:00
json_voucher [ " incentives " ] , json_voucher [ " incentive " ] = incentive_employees (
info [ " date " ] , db
)
2012-09-12 08:22:28 +00:00
else :
2020-10-07 14:07:28 +00:00
raise HTTPException (
2020-10-07 15:18:43 +00:00
status_code = status . HTTP_404_NOT_FOUND ,
detail = f ' Voucher of type " { type_ } " does not exist. ' ,
2020-10-07 14:07:28 +00:00
)
2019-04-06 04:13:12 +00:00
json_voucher [ " files " ] = [ ]
2012-09-12 08:22:28 +00:00
return json_voucher
2012-11-28 07:58:16 +00:00
2020-05-23 04:15:02 +00:00
def incentive_employees ( date_ , db : Session ) :
date_ = datetime . strptime ( date_ , " %d - % b- % Y " )
start_date = get_first_day ( date_ )
finish_date = date_
2015-02-12 12:12:46 +00:00
details = [ ]
2019-04-06 04:13:12 +00:00
employees = (
2020-05-23 04:15:02 +00:00
db . query ( Employee )
. filter ( Employee . joining_date < = finish_date )
. filter ( or_ ( Employee . is_active , Employee . leaving_date > = start_date ) )
. order_by ( Employee . cost_centre_id )
. order_by ( Employee . designation )
. order_by ( Employee . name )
. all ( )
2019-04-06 04:13:12 +00:00
)
2015-02-12 12:12:46 +00:00
for employee in employees :
2019-04-06 04:13:12 +00:00
att = (
2020-05-23 04:15:02 +00:00
db . query ( Attendance )
. filter ( Attendance . employee_id == employee . id )
. filter ( Attendance . date > = start_date )
. filter ( Attendance . date < = finish_date )
. filter ( Attendance . is_valid == True )
. all ( )
2019-04-06 04:13:12 +00:00
)
2015-02-12 12:12:46 +00:00
att = sum ( map ( lambda x : AttendanceType . by_id ( x . attendance_type ) . value , att ) )
2019-04-06 04:13:12 +00:00
details . append (
{
2020-05-23 04:15:02 +00:00
" employeeId " : employee . id ,
2019-04-06 04:13:12 +00:00
" name " : employee . name ,
" designation " : employee . designation ,
" department " : employee . cost_centre . name ,
" daysWorked " : att ,
2020-05-10 08:02:08 +00:00
" points " : employee . points ,
2019-04-06 04:13:12 +00:00
}
)
2015-02-12 12:12:46 +00:00
2019-04-06 04:13:12 +00:00
amount = (
2020-05-23 04:15:02 +00:00
db . query ( func . sum ( Journal . amount * Journal . debit ) )
. join ( Journal . voucher )
. filter ( Voucher . date < finish_date )
. filter ( Voucher . type != VoucherType . by_name ( " Issue " ) . id )
. filter ( Journal . account_id == Account . incentive_id ( ) )
. scalar ( )
2019-04-06 04:13:12 +00:00
)
amount = 0 if amount is None else amount * Decimal ( 0.9 ) * - 1
2015-02-12 12:12:46 +00:00
return details , amount
2020-05-17 10:08:13 +00:00
def check_voucher_lock_info ( voucher_date : Optional [ date ] , data_date : date , db : Session ) :
start , finish = get_lock_info ( db )
2020-10-07 16:59:24 +00:00
if (
start is not None
and start > data_date
or voucher_date is not None
and start > voucher_date
) :
2020-05-17 10:08:13 +00:00
raise HTTPException (
status_code = status . HTTP_423_LOCKED ,
detail = f " Vouchers before { start . strftime ( ' %d - % b- % Y ' ) } have been locked. " ,
)
2020-10-07 16:59:24 +00:00
elif (
finish is not None
and finish < data_date
or voucher_date is not None
and finish < voucher_date
) :
2020-05-17 10:08:13 +00:00
raise HTTPException (
status_code = status . HTTP_423_LOCKED ,
detail = f " Vouchers after { finish . strftime ( ' %d - % b- % Y ' ) } have been locked. " ,
)
# elif start is not None and (start > data.date_ or start > item.date):
# raise HTTPException(
# status_code=status.HTTP_403_FORBIDDEN,
# detail=f"Vouchers before {start.strftime('%d-%b-%Y')} have been locked.",
# )
# elif finish is not None and (finish < data.date_ or finish < item.date):
# raise HTTPException(
# status_code=status.HTTP_403_FORBIDDEN,
# detail=f"Vouchers after {finish.strftime('%d-%b-%Y')} have been locked.",
# )
def check_voucher_edit_allowed ( voucher : Voucher , user : UserToken ) :
if voucher . posted and " edit-posted-vouchers " not in user . permissions :
raise HTTPException (
2020-10-07 15:18:43 +00:00
status_code = status . HTTP_403_FORBIDDEN ,
detail = " You are not allowed to edit posted vouchers " ,
2020-05-17 10:08:13 +00:00
)
2020-10-07 16:59:24 +00:00
elif (
voucher . user_id != user . id_
and " edit-other-user ' s-vouchers " not in user . permissions
) :
2020-05-17 10:08:13 +00:00
raise HTTPException (
2020-10-07 15:18:43 +00:00
status_code = status . HTTP_403_FORBIDDEN ,
detail = " You are not allowed to edit other user ' s vouchers " ,
2020-05-17 10:08:13 +00:00
)