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)
|
deleted = models.BooleanField(default=False)
|
||||||
|
|
||||||
def __str__(self):
|
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):
|
class Price(models.Model):
|
||||||
uuid = models.UUIDField(default=uuid.uuid4, editable=False)
|
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)
|
amount = serializers.DecimalField(max_digits=8, decimal_places=2)
|
||||||
work = serializers.CharField()
|
work = serializers.CharField()
|
||||||
|
|
||||||
|
management = serializers.CharField()
|
||||||
client = serializers.CharField()
|
client = serializers.CharField()
|
||||||
provider = serializers.CharField()
|
provider = serializers.CharField()
|
||||||
payday = serializers.DateField()
|
payday = serializers.DateField()
|
||||||
paystart = serializers.DateField()
|
paystart = serializers.DateField()
|
||||||
payend = serializers.DateField()
|
payend = serializers.DateField()
|
||||||
shifts = ShiftSerializer(many=True)
|
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.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework.renderers import StaticHTMLRenderer
|
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):
|
class UserViewSet(viewsets.ModelViewSet):
|
||||||
lookup_field = 'username'
|
lookup_field = 'username'
|
||||||
|
@ -158,9 +158,10 @@ def get_paystart(payday):
|
||||||
def get_payend(payday):
|
def get_payend(payday):
|
||||||
return payday - timedelta(days=4)
|
return payday - timedelta(days=4)
|
||||||
|
|
||||||
def get_timesheet(user, payday, manage=None):
|
def gen_timesheets(user, payday, manage=None):
|
||||||
paystart = get_paystart(payday)
|
paystart = get_paystart(payday)
|
||||||
payend = get_payend(payday)
|
payend = get_payend(payday)
|
||||||
|
timesheets = []
|
||||||
|
|
||||||
shifts = Shift.objects.filter(deleted=False) \
|
shifts = Shift.objects.filter(deleted=False) \
|
||||||
.exclude(actual_end__isnull=True) \
|
.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)
|
manage_shifts = groupby(shifts, lambda x: x.price.management)
|
||||||
|
|
||||||
timesheets = []
|
|
||||||
for management, shifts in manage_shifts:
|
for management, shifts in manage_shifts:
|
||||||
timesheet = {}
|
timesheet = {}
|
||||||
|
timesheet['management'] = management
|
||||||
timesheet['client'] = management.client
|
timesheet['client'] = management.client
|
||||||
timesheet['provider'] = management.provider
|
timesheet['provider'] = management.provider
|
||||||
timesheet['payday'] = payday
|
timesheet['payday'] = payday
|
||||||
|
@ -200,6 +201,220 @@ def get_timesheet(user, payday, manage=None):
|
||||||
|
|
||||||
return timesheets
|
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):
|
class ReportView(APIView):
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
response = []
|
response = []
|
||||||
|
@ -211,9 +426,17 @@ class ReportView(APIView):
|
||||||
payday = validate_param(qp.get('payday'), serializers.DateField())
|
payday = validate_param(qp.get('payday'), serializers.DateField())
|
||||||
manage = validate_param(qp.get('manage'), serializers.UUIDField(allow_null=True))
|
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)
|
return Response(response, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user