Add employee earnings record generation API route
This commit is contained in:
parent
4469db2602
commit
80cc811049
|
@ -45,7 +45,7 @@ class Manage(models.Model):
|
|||
deleted = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
return self.client.user.username + ' manages ' + self.provider.user.username
|
||||
return self.client.user.first_name + ' manages ' + self.provider.user.first_name
|
||||
|
||||
class Price(models.Model):
|
||||
uuid = models.UUIDField(default=uuid.uuid4, editable=False)
|
||||
|
|
|
@ -276,9 +276,36 @@ class TimeSheetSerializer(serializers.Serializer):
|
|||
amount = serializers.DecimalField(max_digits=8, decimal_places=2)
|
||||
work = serializers.CharField()
|
||||
|
||||
management = serializers.CharField()
|
||||
client = serializers.CharField()
|
||||
provider = serializers.CharField()
|
||||
payday = serializers.DateField()
|
||||
paystart = serializers.DateField()
|
||||
payend = serializers.DateField()
|
||||
shifts = ShiftSerializer(many=True)
|
||||
|
||||
class RecordSheetSerializer(serializers.Serializer):
|
||||
class RecordSerializer(serializers.Serializer):
|
||||
payday = serializers.DateField()
|
||||
reg_pay = serializers.DecimalField(max_digits=10, decimal_places=2)
|
||||
vac_pay = serializers.DecimalField(max_digits=10, decimal_places=2)
|
||||
earnings = serializers.DecimalField(max_digits=10, decimal_places=2)
|
||||
cpp_ytd = serializers.DecimalField(max_digits=10, decimal_places=2)
|
||||
cpp = serializers.DecimalField(max_digits=10, decimal_places=2)
|
||||
ei_ytd = serializers.DecimalField(max_digits=10, decimal_places=2)
|
||||
ei = serializers.DecimalField(max_digits=10, decimal_places=2)
|
||||
taxable_income = serializers.DecimalField(max_digits=10, decimal_places=2)
|
||||
fed_tax = serializers.DecimalField(max_digits=10, decimal_places=2)
|
||||
fed_tax_payable = serializers.DecimalField(max_digits=10, decimal_places=2)
|
||||
prov_tax = serializers.DecimalField(max_digits=10, decimal_places=2)
|
||||
prov_tax_deduction = serializers.DecimalField(max_digits=10, decimal_places=2)
|
||||
income_tax = serializers.DecimalField(max_digits=10, decimal_places=2)
|
||||
total_deductions = serializers.DecimalField(max_digits=10, decimal_places=2)
|
||||
net_pay = serializers.DecimalField(max_digits=10, decimal_places=2)
|
||||
|
||||
management = serializers.CharField()
|
||||
client = serializers.CharField()
|
||||
provider = serializers.CharField()
|
||||
phone_number = serializers.CharField()
|
||||
sin = serializers.CharField()
|
||||
record = RecordSerializer(many=True)
|
||||
|
|
|
@ -12,7 +12,7 @@ from rest_framework.decorators import api_view
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.renderers import StaticHTMLRenderer
|
||||
from caremyway.api.serializers import UserSerializer, UserInfoSerializer, ClientSerializer, ProviderSerializer, WorkTypeSerializer, EmployeeSerializer, EmployerSerializer, PriceSerializer, CShiftSerializer, PShiftSerializer, TimeSheetSerializer
|
||||
from caremyway.api.serializers import UserSerializer, UserInfoSerializer, ClientSerializer, ProviderSerializer, WorkTypeSerializer, EmployeeSerializer, EmployerSerializer, PriceSerializer, CShiftSerializer, PShiftSerializer, TimeSheetSerializer, RecordSheetSerializer
|
||||
|
||||
class UserViewSet(viewsets.ModelViewSet):
|
||||
lookup_field = 'username'
|
||||
|
@ -158,9 +158,10 @@ def get_paystart(payday):
|
|||
def get_payend(payday):
|
||||
return payday - timedelta(days=4)
|
||||
|
||||
def get_timesheet(user, payday, manage=None):
|
||||
def gen_timesheets(user, payday, manage=None):
|
||||
paystart = get_paystart(payday)
|
||||
payend = get_payend(payday)
|
||||
timesheets = []
|
||||
|
||||
shifts = Shift.objects.filter(deleted=False) \
|
||||
.exclude(actual_end__isnull=True) \
|
||||
|
@ -175,9 +176,9 @@ def get_timesheet(user, payday, manage=None):
|
|||
|
||||
manage_shifts = groupby(shifts, lambda x: x.price.management)
|
||||
|
||||
timesheets = []
|
||||
for management, shifts in manage_shifts:
|
||||
timesheet = {}
|
||||
timesheet['management'] = management
|
||||
timesheet['client'] = management.client
|
||||
timesheet['provider'] = management.provider
|
||||
timesheet['payday'] = payday
|
||||
|
@ -200,6 +201,220 @@ def get_timesheet(user, payday, manage=None):
|
|||
|
||||
return timesheets
|
||||
|
||||
def get_paydays_todate(payday):
|
||||
# Assumes payday is a valided date obj
|
||||
year = payday.year
|
||||
paydays = []
|
||||
|
||||
if payday.day == 15:
|
||||
paydays.append(payday)
|
||||
elif payday.day == calendar.monthrange(payday.year, payday.month)[1]:
|
||||
paydays.append(payday)
|
||||
paydays.append(datetime.date(year, payday.month, 15))
|
||||
else:
|
||||
raise serializers.ValidationError("Date is not a valid payday.")
|
||||
|
||||
for month in reversed(range(1, payday.month)):
|
||||
end_of_month = calendar.monthrange(year, month)[1]
|
||||
paydays.append(datetime.date(year, month, end_of_month))
|
||||
paydays.append(datetime.date(year, month, 15))
|
||||
|
||||
return list(reversed(paydays))
|
||||
|
||||
def get_cpp(cpp_ytd, earnings):
|
||||
D = cpp_ytd
|
||||
PI = earnings
|
||||
P = 24
|
||||
|
||||
C_i = Decimal(2564.10) - D
|
||||
C_ii = Decimal(0.0495) * (PI - Decimal(3500 / P))
|
||||
C = min(C_i, C_ii)
|
||||
|
||||
if C < 0:
|
||||
C = 0
|
||||
|
||||
return C
|
||||
|
||||
def get_ei(ei_ytd, earnings):
|
||||
D1 = ei_ytd
|
||||
IE = earnings
|
||||
|
||||
EI_i = Decimal(836.19) - D1
|
||||
EI_ii = Decimal(0.0163) * IE
|
||||
EI = min(EI_i, EI_ii)
|
||||
|
||||
if EI < 0:
|
||||
EI = 0
|
||||
|
||||
return EI
|
||||
|
||||
def get_taxable_income(earnings):
|
||||
P = 24
|
||||
I = earnings
|
||||
F = 0
|
||||
F2 = 0
|
||||
U1 = 0
|
||||
HD = 0
|
||||
F1 = 0
|
||||
|
||||
A = (P * (I - F - F2 - U1)) - HD - F1
|
||||
|
||||
return A
|
||||
|
||||
def get_fed_tax(taxable_income, cpp, ei, cpp_ytd, ei_ytd):
|
||||
table = [(0, 0.150, 0),
|
||||
(45916, 0.205, 2525),
|
||||
(91831, 0.260, 7576),
|
||||
(142352, 0.290, 11847),
|
||||
(202800, 0.330, 19959)]
|
||||
|
||||
A = taxable_income
|
||||
R = Decimal([row for row in table if row[0] < A][-1][1])
|
||||
K = [row for row in table if row[0] < A][-1][2]
|
||||
|
||||
TC = 11635
|
||||
K1 = Decimal(0.15 * TC)
|
||||
|
||||
P = 24
|
||||
if cpp_ytd + cpp >= 2564.10:
|
||||
PxC = Decimal(2564.10)
|
||||
else:
|
||||
C = cpp
|
||||
PxC = P * C
|
||||
if ei_ytd + ei >= 836.19:
|
||||
PxEI = Decimal(836.19)
|
||||
else:
|
||||
EI = ei
|
||||
PxEI = P * EI
|
||||
K2 = (Decimal(0.15) * PxC) + (Decimal(0.15) * PxEI)
|
||||
|
||||
K3 = 0
|
||||
|
||||
K4_i = Decimal(0.15) * A
|
||||
K4_ii = Decimal(0.15 * 1178)
|
||||
K4 = min(K4_i, K4_ii)
|
||||
|
||||
T3 = (R * A) - K - K1 - K2 - K3 - K4
|
||||
|
||||
if T3 < 0:
|
||||
T3 = 0
|
||||
|
||||
return T3
|
||||
|
||||
def get_fed_tax_payable(fed_tax):
|
||||
T3 = fed_tax
|
||||
LCF = 0
|
||||
|
||||
T1 = T3 - LCF
|
||||
|
||||
if T1 < 0:
|
||||
T1 = 0
|
||||
|
||||
return T1
|
||||
|
||||
def get_prov_tax(taxable_income, cpp, ei, cpp_ytd, ei_ytd):
|
||||
table = [(0, 0.10, 0),
|
||||
(126625, 0.12, 2533),
|
||||
(151950, 0.13, 4052),
|
||||
(202600, 0.14, 6078),
|
||||
(303900, 0.15, 9117)]
|
||||
|
||||
A = taxable_income
|
||||
V = Decimal([row for row in table if row[0] < A][-1][1])
|
||||
KP = [row for row in table if row[0] < A][-1][2]
|
||||
|
||||
TCP = 18690
|
||||
K1P = Decimal(0.1 * TCP)
|
||||
|
||||
P = 24
|
||||
if cpp_ytd + cpp >= 2564.10:
|
||||
PxC = Decimal(2564.10)
|
||||
else:
|
||||
C = cpp
|
||||
PxC = P * C
|
||||
if ei_ytd + ei >= 836.19:
|
||||
PxEI = Decimal(836.19)
|
||||
else:
|
||||
EI = ei
|
||||
PxEI = P * EI
|
||||
K2P = (Decimal(0.10) * PxC) + (Decimal(0.10) * PxEI)
|
||||
|
||||
K3P = 0
|
||||
|
||||
T4 = (V * A) - KP - K1P - K2P - K3P
|
||||
|
||||
if T4 < 0:
|
||||
T4 = 0
|
||||
|
||||
return T4
|
||||
|
||||
def get_prov_tax_deduction(prov_tax):
|
||||
T4 = prov_tax
|
||||
V1 = 0
|
||||
S = 0
|
||||
LCP = 0
|
||||
|
||||
T2 = T4 - V1 - S - LCP
|
||||
|
||||
if T2 < 0:
|
||||
T2 = 0
|
||||
|
||||
return T2
|
||||
|
||||
def get_income_tax(fed_tax_payable, prov_tax_deduction):
|
||||
T1 = fed_tax_payable
|
||||
T2 = prov_tax_deduction
|
||||
P = 24
|
||||
L = 0
|
||||
|
||||
T = Decimal((T1 + T2) / P) + L
|
||||
|
||||
return T
|
||||
|
||||
def gen_recordsheets(user, payday, manage):
|
||||
paydays = get_paydays_todate(payday)
|
||||
recordsheets = []
|
||||
flat_records = []
|
||||
|
||||
for payday in paydays:
|
||||
timesheets = gen_timesheets(user, payday, manage)
|
||||
|
||||
for timesheet in timesheets:
|
||||
record = {}
|
||||
record['payday'] = payday
|
||||
record['management'] = timesheet['management']
|
||||
record['reg_pay'] = sum(shift['amount'] for shift in timesheet['shifts'])
|
||||
record['vac_pay'] = record['reg_pay'] * Decimal(0.04)
|
||||
record['earnings'] = record['reg_pay'] + record['vac_pay']
|
||||
record['cpp_ytd'] = sum(r['cpp'] for r in flat_records if r['management'] == timesheet['management'])
|
||||
record['cpp'] = get_cpp(record['cpp_ytd'], record['earnings'])
|
||||
record['ei_ytd'] = sum(r['ei'] for r in flat_records if r['management'] == timesheet['management'])
|
||||
record['ei'] = get_ei(record['ei_ytd'], record['earnings'])
|
||||
record['taxable_income'] = get_taxable_income(record['earnings'])
|
||||
record['fed_tax'] = get_fed_tax(record['taxable_income'], record['cpp'], record['ei'], record['cpp_ytd'], record['ei_ytd'])
|
||||
record['fed_tax_payable'] = get_fed_tax_payable(record['fed_tax'])
|
||||
record['prov_tax'] = get_prov_tax(record['taxable_income'], record['cpp'], record['ei'], record['cpp_ytd'], record['ei_ytd'])
|
||||
record['prov_tax_deduction'] = get_prov_tax_deduction(record['prov_tax'])
|
||||
record['income_tax'] = get_income_tax(record['fed_tax_payable'], record['prov_tax_deduction'])
|
||||
record['total_deductions'] = record['income_tax'] + record['cpp'] + record['ei']
|
||||
record['net_pay'] = record['earnings'] - record['total_deductions']
|
||||
flat_records.append(record)
|
||||
|
||||
flat_records = sorted(flat_records, key=lambda x: x['management'].uuid)
|
||||
grouped_records = groupby(flat_records, lambda x: x['management'])
|
||||
|
||||
for management, record in grouped_records:
|
||||
recordsheet = {}
|
||||
recordsheet['management'] = management
|
||||
recordsheet['client'] = management.client
|
||||
recordsheet['provider'] = management.provider
|
||||
recordsheet['phone_number'] = management.provider.user.userinfo.phone_number
|
||||
recordsheet['sin'] = management.provider.sin
|
||||
recordsheet['record'] = list(record)
|
||||
recordsheets.append(recordsheet)
|
||||
|
||||
return list(recordsheets)
|
||||
|
||||
class ReportView(APIView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
response = []
|
||||
|
@ -211,9 +426,17 @@ class ReportView(APIView):
|
|||
payday = validate_param(qp.get('payday'), serializers.DateField())
|
||||
manage = validate_param(qp.get('manage'), serializers.UUIDField(allow_null=True))
|
||||
|
||||
timesheet = get_timesheet(request.user, payday, manage)
|
||||
timesheets = gen_timesheets(request.user, payday, manage)
|
||||
|
||||
response = TimeSheetSerializer(timesheet, many=True).data
|
||||
response = TimeSheetSerializer(timesheets, many=True).data
|
||||
|
||||
elif report_type == 'record':
|
||||
payday = validate_param(qp.get('payday'), serializers.DateField())
|
||||
manage = validate_param(qp.get('manage'), serializers.UUIDField(allow_null=True))
|
||||
|
||||
recordsheets = gen_recordsheets(request.user, payday, manage)
|
||||
|
||||
response = RecordSheetSerializer(recordsheets, many=True).data
|
||||
|
||||
return Response(response, status=status.HTTP_200_OK)
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user