/home/techb158/ileadtechnology.com/SSM/app/Repositories/employee
Edit: /home/techb158/ileadtechnology.com/SSM/app/Repositories/employee/PayrollRepository.php (21383B)
payroll = $payroll;
$this->payroll_detail = $payroll_detail;
$this->employee = $employee;
$this->salary = $salary;
$this->attendance = $attendance;
$this->holiday = $holiday;
$this->leave_request = $leave_request;
$this->attendance_type = $attendance_type;
}
/**
* Find payroll with given id or throw an error.
*
* @param integer $id
* @return Payroll
*/
public function findOrFail($id)
{
$payroll = $this->payroll->info()->filterById($id)->first();
if (! $payroll) {
throw ValidationException::withMessages(['message' => trans('employee.could_not_find_payroll')]);
}
return $payroll;
}
/**
* Find payroll with given uuid or throw an error.
*
* @param string $uuid
* @return Payroll
*/
public function findByUuidOrFail($uuid)
{
$payroll = $this->payroll->info()->whereUuid($uuid)->first();
if (! $payroll) {
throw ValidationException::withMessages(['message' => trans('employee.could_not_find_payroll')]);
}
return $payroll;
}
/**
* Get all filtered data
*
* @param array $params
* @return Payroll
*/
public function getData($params)
{
$sort_by = gv($params, 'sort_by', 'created_at');
$order = gv($params, 'order', 'desc');
$name = gv($params, 'name');
$employee_id = gv($params, 'employee_id');
$employee_id = is_array($employee_id) ? $employee_id : ($employee_id ? explode(',', $employee_id) : []);
$accessible_employee_ids = $this->employee->getAccessibleEmployeeId();
array_push($accessible_employee_ids, \Auth::user()->Employee->id);
if (array_diff($employee_id, $accessible_employee_ids)) {
throw ValidationException::withMessages(['message' => trans('user.permission_denied')]);
}
$query = $this->payroll->info()->whereIn('employee_id', $accessible_employee_ids);
if (count($employee_id)) {
$query->whereIn('employee_id', $employee_id);
}
return $query->orderBy($sort_by, $order);
}
/**
* Paginate all payroll using given params.
*
* @param array $params
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/
public function paginate($params)
{
$page_length = gv($params, 'page_length', config('config.page_length'));
return $this->getData($params)->paginate($page_length);
}
/**
* Get all filtered data for printing
*
* @param array $params
* @return Payroll
*/
public function print($params)
{
return $this->getData($params)->get();
}
/**
* Get payroll filters.
*
* @return Array
*/
public function getFilters()
{
$employees = $this->employee->getAccessibleEmployeeList();
return compact('employees');
}
/**
* Get payroll pre requisite.
*
* @return Array
*/
public function getPreRequisite()
{
return $this->getFilters();
}
/**
* Fetch first unpaid payroll of given employee
*
* @param array $params array of data
* @return Payroll
*/
public function fetchUnpaid($params = array())
{
$employee_id = gv($params, 'employee_id');
$employee = $this->employee->findOrFail($employee_id);
$this->employee->isAccessible($employee);
$payroll = $this->payroll->filterByEmployeeId($employee->id)->where('payment_status','!=','paid')->orderBy('start_date')->first();
return compact('payroll');
}
/**
* Fetch payroll data.
*
* @param array $params
* @return void
*/
public function fetch($params = array())
{
$employee_id = gv($params, 'employee_id');
$start_date = toDate(gv($params, 'start_date'));
$end_date = toDate(gv($params, 'end_date'));
$payroll_id = gv($params, 'payroll_id');
$self = gbv($params, 'self');
$employee = $this->employee->findOrFail($employee_id);
$this->employee->isAccessible($employee, $self);
$salary = $this->salary->info()->filterByEmployeeId($employee->id)->where('date_effective', '<=', $start_date)->orderBy('date_effective', 'desc')->first();
if (! $salary) {
throw ValidationException::withMessages(['message' => trans('employee.could_not_find_salary_structure')]);
}
$overlapping_query = (! $payroll_id) ? $this->payroll : $this->payroll->where('id', '!=', $payroll_id);
$overlapping = $overlapping_query->filterByEmployeeId($employee_id)->where(function ($q) use ($start_date, $end_date) {
$q->where(function($q1) use($start_date, $end_date) {
$q1->where('start_date','<=',$start_date)->where('end_date','>=',$end_date);
})->orWhere(function($q2) use($start_date) {
$q2->where('start_date','<=',$start_date)->where('end_date','>',$start_date);
})->orWhere(function($q2) use($end_date) {
$q2->where('start_date','<',$end_date)->where('end_date','>=',$end_date);
})->orWhere(function($q3) use($start_date, $end_date) {
$q3->where('start_date','>',$start_date)->where('end_date','<',$end_date);
});
})->count();
if ($overlapping) {
throw ValidationException::withMessages(['message' => trans('employee.payroll_overlapping')]);
}
$attendance_types = $this->attendance_type->select('id','name','alias','type','unit')->get();
$attendance_types->push(array('id' => null, 'name' => 'Leave', 'alias' => 'L', 'type' => 'leave', 'unit' => null));
$attendance_types->transform(function ($attendance_type) {
$attendance_type['count'] = 0;
$attendance_type['value'] = 0;
return $attendance_type;
});
$attendances = $this->attendance->with([
'attendanceType:id,name,alias,type,unit',
'attendanceDetails:id,employee_attendance_id,employee_attendance_type_id,value',
'attendanceDetails.attendanceType:id,name,alias,type,unit'
])->filterByEmployeeId($employee_id)->where('date_of_attendance','>=',$start_date)->where('date_of_attendance','<=',$end_date)->get();
$holidays = $this->holiday->dateBetween([
'start_date' => $start_date,
'end_date' => $end_date
])->get();
$holiday_alias = optional($attendance_types->firstWhere('type', 'holiday'))->alias;
$leave_requests = $this->leave_request->select('id','start_date','end_date','options')->filterByEmployeeId($employee_id)->filterByStatus('approved')->where(function($q) use($start_date, $end_date) {
$q->where(function($q1) use($start_date, $end_date) {
$q1->where('start_date','>=',$start_date)->where('end_date','<=',$end_date);
})->orWhere(function($q2) use($start_date, $end_date) {
$q2->where('start_date','<',$start_date)->where('end_date','>=',$end_date);
})->orWhere(function($q3) use($start_date, $end_date) {
$q3->where('start_date','>=',$start_date)->where('end_date','<',$end_date);
});
})->get();
$date = $start_date;
$per_day_calculation_basis = config('config.per_day_salary_calculation_basis') ? : 'calendar_period';
$salary->per_day_calculation_basis = $per_day_calculation_basis;
if ($per_day_calculation_basis == 'calendar_period') {
$per_day_calculation_basis = Carbon::parse($start_date)->daysInMonth;
} else {
$user_defined_days = config('config.user_defined_per_day_salary_calculation_basis');
$per_day_calculation_basis = (is_numeric($user_defined_days) && $user_defined_days > 0) ? $user_defined_days : 30;
$salary->user_defined_days = $per_day_calculation_basis;
}
while($date <= $end_date) {
$emplyoee_attendance = '';
// $marked_attendance = $attendances->where('date_of_attendance', $date)->first();
$marked_attendance = $attendances->filter(function($attendance) use ($date) {
return toDate($attendance->date_of_attendance) === $date;
})->first();
if ($marked_attendance) {
$marked = $marked_attendance->attendanceType;
if ($marked) {
$emplyoee_attendance = $marked->alias;
}
$production_attendances = $marked_attendance->attendanceDetails;
foreach ($production_attendances as $production_attendance) {
$production_attendance_alias = $production_attendance->attendanceType->alias;
$production_attendance_value = $production_attendance->value;
$attendance_types->transform(function($attendance_type) use($production_attendance_alias, $production_attendance_value) {
$attendance_type['count'] = $attendance_type['count'] + (($attendance_type['alias'] == $production_attendance_alias) ? 1 : 0);
$attendance_type['value'] = $attendance_type['value'] + (($attendance_type['alias'] == $production_attendance_alias) ? $production_attendance_value : 0);
return $attendance_type;
});
}
}
$leave_request = $leave_requests->filter(function($item) use ($date) {
return (data_get($item, 'start_date') <= getDateTime($date)) && (data_get($item, 'end_date') >= getDateTime($date));
})->first();
if ($leave_request) {
$emplyoee_attendance = 'L';
}
if (! $emplyoee_attendance) {
$holiday = $holidays->firstWhere('date', getDateTime($date));
if ($holiday) {
$emplyoee_attendance = $holiday_alias ? : 'H';
}
}
if ($emplyoee_attendance) {
$attendance_types->transform(function($attendance_type) use($emplyoee_attendance) {
$attendance_type['count'] = $attendance_type['count'] + (($attendance_type['alias'] == $emplyoee_attendance) ? 1 : 0);
return $attendance_type;
});
}
$date = date('Y-m-d', strtotime($date.' + 1 day'));
}
$payroll_template_details = $salary->payrollTemplate->payrollTemplateDetails->sortBy('position');
$attendance = $attendance_types->whereIn('type',['present','holiday'])->sum('count');
$half_day_data = $attendance_types->firstWhere('type','half_day');
$half_day = $half_day_data ? $half_day_data->count : 0;
$leave = $attendance_types->firstWhere('type','leave')['count'];
$salary_heads = array();
$total_earning = 0;
$total_deduction = 0;
foreach ($payroll_template_details as $payroll_template_detail) {
if ($payroll_template_detail->category == 'not_applicable') {
$amount = 0 ;
} else if ($payroll_template_detail->category == 'attendance') {
$salary_detail = $salary->salaryDetails->firstWhere('payroll_template_detail_id', $payroll_template_detail->id);
$amount = ($salary_detail) ? $salary_detail->amount : 0;
$per_day_salary = $amount / $per_day_calculation_basis;
$total_attendance = ($attendance + $leave + ($half_day / 2));
$amount = round($total_attendance * $per_day_salary, getDefaultCurrency('decimal_place'));
} else if ($payroll_template_detail->category == 'flat_rate') {
$salary_detail = $salary->salaryDetails->firstWhere('payroll_template_detail_id', $payroll_template_detail->id);
$amount = ($salary_detail) ? $salary_detail->amount : 0;
} else if ($payroll_template_detail->category == 'user_defined') {
$amount = 0;
} else if ($payroll_template_detail->category == 'production') {
$attendance_type = $attendance_types->firstWhere('id', $payroll_template_detail->employee_attendance_type_id);
$value = ($attendance_type) ? $attendance_type->value : 0;
$salary_detail = $salary->salaryDetails->firstWhere('payroll_template_detail_id', $payroll_template_detail->id);
$amount = ($salary_detail) ? round($value * $salary_detail->amount, getDefaultCurrency('decimal_place')) : 0;
} else if ($payroll_template_detail->category == 'computation') {
$computation = $payroll_template_detail->computation;
foreach ($salary_heads as $alias => $salary_amount) {
$computation = str_replace($alias, $salary_amount, $computation);
}
$amount = $this->evaluate($computation);
if ($amount === 'invalid') {
throw ValidationException::withMessages(['message' => trans('employee.salatry_structure_contains_invalid_computation')]);
}
$amount = round($amount, getDefaultCurrency('decimal_place'));
}
$total_earning += (($payroll_template_detail->payHead->type == 'earning') ? $amount : 0);
$total_deduction += (($payroll_template_detail->payHead->type == 'deduction') ? $amount : 0);
$salary_heads[$payroll_template_detail->payHead->alias] = $amount;
$payroll_template_detail->amount = $amount;
}
$salary->total_earning = $total_earning;
$salary->total_deduction = $total_deduction;
$salary->net_salary = $total_earning - $total_deduction;
return compact('attendance_types','salary');
}
/**
* Validate input
* @param array $params array of input
* @return void
*/
private function validateInput($salary, $params = array())
{
$can_edit_payroll_amount = (\Auth::user()->can('edit-payroll-amount')) ? true : false;
$pay_heads = gv($params, 'pay_heads', []);
foreach ($pay_heads as $pay_head) {
$amount = gv($pay_head, 'amount', 0);
$pay_head_id = gv($pay_head, 'pay_head_id');
if (! is_numeric($amount)) {
throw ValidationException::withMessages(['message' => trans('validation.integer', ['attribute' => gv($pay_head, 'name')])]);
}
if ($amount < 0) {
throw ValidationException::withMessages(['message' => trans('validation.min.numeric', ['attribute' => gv($pay_head, 'name'), 'min' => 0])]);
}
$payroll_template_detail = $salary->payrollTemplate->payrollTemplateDetails->firstWhere('pay_head_id', $pay_head_id);
if (! $can_edit_payroll_amount && $payroll_template_detail->amount != $amount) {
throw ValidationException::withMessages(['message' => trans('general.invalid_input')]);
}
}
}
/**
* Generate a new payroll.
*
* @param array $params
* @return void
*/
public function generate($params = array())
{
$data = $this->fetch($params);
$salary = gv($data, 'salary');
$this->validateInput($salary, $params);
$params['employee_salary_id'] = $salary->id;
$params['per_day_calculation_basis'] = $salary->per_day_calculation_basis;
$params['user_defined_days'] = $salary->per_day_calculation_basis == 'user_defined' ? $salary->user_defined_days : null;
$payroll = $this->payroll->forceCreate($this->formatParams($params));
$this->updatePayrollDetail($payroll, $params);
return $payroll;
}
/**
* Update Payroll details
*
* @param Payroll $payroll
* @param array $params
* @return void
*/
private function updatePayrollDetail(Payroll $payroll, $params)
{
$pay_heads = gv($params, 'pay_heads', []);
$payroll_details = $payroll->PayrollDetails;
$total = 0;
foreach ($pay_heads as $index => $pay_head) {
$amount = gv($pay_head, 'amount', 0);
$type = gv($pay_head, 'type');
$payroll_detail = $this->payroll_detail->firstOrCreate([
'payroll_id' => $payroll->id,
'pay_head_id' => gv($pay_head, 'pay_head_id')
]);
$payroll_detail->amount = $amount;
$payroll_detail->save();
$total += ($type == 'earning') ? $amount : 0;
$total -= ($type == 'deduction') ? $amount : 0;
}
$payroll->total = $total;
$payroll->save();
}
/**
* Validate Payroll total with payroll transaction
*
* @param Payroll $payroll
* @param array $params
* @return void
*/
private function validatePayrollDetail(Payroll $payroll, $params)
{
$pay_heads = gv($params, 'pay_heads', []);
$payroll_details = $payroll->PayrollDetails;
$total = 0;
foreach ($pay_heads as $index => $pay_head) {
$amount = gv($pay_head, 'amount', 0);
$type = gv($pay_head, 'type');
$total += ($type == 'earning') ? $amount : 0;
$total -= ($type == 'deduction') ? $amount : 0;
}
if ($payroll->paid > $total) {
throw ValidationException::withMessages(['message' => trans('employee.payroll_total_cannot_less_than_payroll_transaction_amount', ['amount' => currency($payroll->paid, 1)])]);
}
}
/**
* Prepare given params for inserting into database.
*
* @param array $params
* @param string $type
* @return array
*/
private function formatParams($params, $payroll_id = null)
{
$formatted = [
'employee_id' => gv($params, 'employee_id'),
'employee_salary_id' => gv($params, 'employee_salary_id'),
'start_date' => toDate(gv($params, 'start_date')),
'end_date' => toDate(gv($params, 'end_date')),
'per_day_calculation_basis' => gv($params, 'per_day_calculation_basis'),
'user_defined_days' => gv($params, 'user_defined_days',0),
'payment_status' => 'unpaid',
'remarks' => gv($params, 'remarks'),
'options' => []
];
if (! $payroll_id) {
$formatted['uuid'] = Str::uuid();
}
return $formatted;
}
/**
* Update given payroll.
*
* @param Payroll $payroll
* @param array $params
*
* @return Payroll
*/
public function update(Payroll $payroll, $params)
{
$params['payroll_id'] = $payroll->id;
$data = $this->fetch($params);
$salary = gv($data, 'salary');
$this->validateInput($salary, $params);
$this->validatePayrollDetail($payroll, $params);
$this->updatePayrollDetail($payroll, $params);
return $payroll;
}
/**
* Find payroll is deletable or not.
*
* @param string $uuid
* @return bool|null
*/
public function deletable($uuid)
{
$payroll = $this->findByUuidOrFail($uuid);
if ($payroll->transactions()->where('is_cancelled',0)->count()) {
throw ValidationException::withMessages(['message' => trans('employee.payroll_associated_with_transaction')]);
}
return $payroll;
}
/**
* Delete payroll.
*
* @param integer $id
* @return bool|null
*/
public function delete(Payroll $payroll)
{
return $payroll->delete();
}
/**
* Delete multiple payroll.
*
* @param array $ids
* @return bool|null
*/
public function deleteMultiple($ids)
{
return $this->payroll->whereIn('id', $ids)->delete();
}
}