Spamworldpro Mini Shell
Spamworldpro


Server : Apache
System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64
User : corals ( 1002)
PHP Version : 7.4.33
Disable Function : exec,passthru,shell_exec,system
Directory :  /home/corals/ts.corals.io/corals-api/Corals/modules/Timesheet/Services/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/corals/ts.corals.io/corals-api/Corals/modules/Timesheet/Services/EntryService.php
<?php

namespace Corals\Modules\Timesheet\Services;


use Carbon\Carbon;
use Corals\Foundation\Services\BaseServiceClass;
use Corals\Modules\Timesheet\Classes\BagParameters;
use Corals\Modules\Timesheet\Classes\InvoiceGenerator;
use Corals\Modules\Timesheet\Facades\Timesheet;
use Corals\Modules\Timesheet\Models\Activity;
use Corals\Modules\Timesheet\Models\Client;
use Corals\Modules\Timesheet\Models\Entry;
use Corals\Modules\Timesheet\Models\Project;
use Corals\Settings\Facades\Settings;
use Corals\User\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;

class EntryService extends BaseServiceClass
{
    protected $excludedRequestParams = [
        'tags'
    ];

    /**
     * @var null
     */
    protected $client = null;

    /**
     * @return array
     */
    public function getFormData(Request $request)
    {
        if (!user()->canAny(['create', 'update'], new Entry())) {
            return [];
        }

        if (user()->hasRole('member')) {
            $latestEntry = Entry::query()
                ->join('timesheet_activities', 'timesheet_activities.id', 'timesheet_entries.activity_id')
                ->where('user_id', user()->id)
                ->where('timesheet_activities.is_limited', 0)
                ->where('timesheet_activities.is_special', 0)
                ->select('timesheet_entries.*')
                ->latest()
                ->first();

            if ($latestEntry) {
                $default = [
                    'activity_id' => $latestEntry->activity_id,
                    'user_id' => $latestEntry->user_id,
                    'project_id' => $latestEntry->project_id
                ];

                if (!$request->has('project_id')) {
                    $request->merge(['project_id' => $latestEntry->project_id]);
                }
            } else {
                $default = [
                    'user_id' => user()->user_id,
                ];
            }
        }

        //get default date from latest entry for user if not superuser.
        return [
            'activities' => $this->getActivitiesFormData($request),
            'users' => $this->getUsersFormData($request),
            'projects' => Timesheet::getProjectsFormData(['logged_in_user' => true]),
            'defaults' => $default ?? []
        ];
    }

    /**
     * @return array[]
     */
    protected function getActivitiesFormData(Request $request)
    {
        $billable = [];
        $nonBillable = [];

        Activity::query()
            ->select('timesheet_activities.*')
            ->join('timesheet_project_activity', 'timesheet_activities.id', '=', 'timesheet_project_activity.activity_id')
            ->where('timesheet_project_activity.project_id', '=', $request->get('project_id'))
            ->when(!Timesheet::isTimesheetAdministration(), function ($query) {
                $query->where('is_limited', 0)
                    ->where('is_special', 0);
            })
            ->each(function (Activity $activity) use (&$billable, &$nonBillable) {
                if ($activity->billable) {
                    $billable[] = [
                        'label' => $activity->title,
                        'value' => $activity->id,
                        'group' => 'billable',
                        'group_label' => 'Billable',
                        'is_active' => $activity->is_active
                    ];
                } else {
                    $nonBillable[] = [
                        'label' => $activity->title,
                        'value' => $activity->id,
                        'group' => 'non_billable',
                        'group_label' => 'Non Billable',
                        'is_active' => $activity->is_active
                    ];
                }
            });


        return array_merge([
            [
                'code' => 'billable',
                'group' => true,
                'status' => 'active',
                'label' => 'Billable',
                'is_group_empty' => !count($billable),
                'empty_group_message' => "No Options!",
                'notSelectable' => true
            ],
            ...$billable,
        ], $nonBillable ? [
            [
                'code' => 'non_billable',
                'group' => true,
                'status' => 'active',
                'label' => 'Non-Billable',
                'is_group_empty' => !count($nonBillable),
                'empty_group_message' => "No Options!",
                'notSelectable' => true
            ],
            ...$nonBillable
        ] : []);
    }

    protected function getUsersFormData(Request $request)
    {
        $project = Project::find($request->get('project_id'));

        $users = User::query()
            ->select('users.*')
            ->selectRaw("concat(users.name,' ',users.last_name,', ',users.email, (CASE WHEN users.status = 'inactive' THEN ' (Inactive)' ELSE '' END)) as label, users.id as value");

        if ($project && $project->assignable) {
            $users->join('timesheet_project_user', 'users.id', '=', 'timesheet_project_user.user_id')
                ->where('timesheet_project_user.project_id', '=', $request->get('project_id'));
        }

        $users->whereHas('roles', function (Builder $query) {
            $query->where('roles.name', '=', 'member');
        });

        $users->when(!Timesheet::isTimesheetAdministration(), function ($query) {
            $query->where('users.id', user()->id);
        });

        return $users->get();
    }


    public function setClient($client)
    {
        $this->client = $client;
        return $this;
    }

    protected function isClientExists()
    {
        if (!$this->client) {
            throw new \Exception('Client not set');
        }
    }

    /**
     * @return array
     * @throws \Exception
     */
    public function entriesPerBillingCycle()
    {
        $this->isClientExists();

        $nonBilledClientLevelEntries = $this->getEntriesPerBillingCycleBaseQuery()
            ->whereNull('timesheet_projects.bill_cycle')
            ->get()->groupBy('project_id', true);

        $result = [];

        foreach ($nonBilledClientLevelEntries as $projectId => $entries) {
            $result[] = $this->buildProjectEntries($projectId, $entries);
        }

        $nonBilledProjectLevelEntries = $this->getEntriesPerBillingCycleBaseQuery()
            ->whereNotNull('timesheet_projects.bill_cycle')
            ->get()->groupBy('project_id', true);

        foreach ($nonBilledProjectLevelEntries as $projectId => $entries) {
            $result[] = $this->buildProjectEntries($projectId, $entries, true);
        }

        return $result;
    }

    protected function buildProjectEntries($projectId, $entries, $perProject = false)
    {
        $project = Project::find($projectId);
        $projectEntries = Arr::only($project->toArray(), ['id', 'name', 'budget', 'hourly_rate', 'currency']);

        $cycles = [];

        foreach ($entries as $entry) {
            if (is_null($entry->evaluation_hours) && is_null($entry->evaluation_minutes)) {
                continue;
            }

            $cycle = $this->getBillingCycleEdges($entry->spent_at, $perProject ? $project : $this->client);

            if (!$cycle) {
                continue;
            }

            if (!isset($cycles[$cycle['code']])) {
                $cycle['entries'] = [];
                $cycle['total_evaluation_hours'] = 0;
                $cycle['total_evaluation_minutes'] = 0;
                $cycle['has_unreviewed_entries'] = false;
                $cycles[$cycle['code']] = $cycle;
            }
            /**
             * @var Entry $entry
             */

            $spentAt = Carbon::parse($entry->spent_at);

            $cycles[$cycle['code']]['entries'][] = [
                'id' => $entry->id,
                'spent_at' => $entry->spent_at,
                'spent_at_day' => [
                    'name' => $spentAt->englishDayOfWeek,
                    'short_name' => $spentAt->shortEnglishDayOfWeek
                ],
                'has_reviewed' => $entry->has_reviewed,
                'user' => $entry->user->full_name,
                'description' => $entry->description,
                'activity' => $entry->activity->title,
                'hourly_rate' => $entry->activity->hourly_rate,
                'evaluation_hours' => $entry['evaluation_hours'],
                'evaluation_minutes' => $entry['evaluation_minutes'],
                'evaluation_time' => Timesheet::formatHoursAndMinutes($entry['evaluation_hours'],
                    $entry['evaluation_minutes']),
                'labels' => $entry->present('labels')
            ];

            if (!$entry->has_reviewed) {
                $cycles[$cycle['code']]['has_unreviewed_entries'] = true;
            }

            $cycles[$cycle['code']]['total_evaluation_hours'] += $entry['evaluation_hours'];
            $cycles[$cycle['code']]['total_evaluation_minutes'] += $entry['evaluation_minutes'];

            $cycles[$cycle['code']]['total_evaluation_time'] = Timesheet::formatHoursAndMinutes(
                $cycles[$cycle['code']]['total_evaluation_hours'],
                $cycles[$cycle['code']]['total_evaluation_minutes']);
        }

        foreach ($cycles as $code => $cycle) {
            $cycle['entries'] = array_values(collect($cycle['entries'])
                ->sortByDesc('spent_at')
                ->groupBy('spent_at')
                ->toArray());
            foreach ($cycle['entries'] as $index => $entry) {
                $cycle['entries'][$index] = array_values(collect($entry)->sortBy('user')->toArray());
            }

            $cycles[$code] = $cycle;
        }

        $projectEntries['cycles'] = $cycles;

        return $projectEntries;
    }

    protected function getEntriesPerBillingCycleBaseQuery(): Builder
    {
        $this->isClientExists();

        return Entry::query()
            ->join('timesheet_projects', 'timesheet_projects.id', 'timesheet_entries.project_id')
            ->where('timesheet_projects.type', '=', 'time_and_materials')
            ->join('timesheet_activities', 'timesheet_activities.id', 'timesheet_entries.activity_id')
            ->join('timesheet_clients', 'timesheet_clients.id', 'timesheet_projects.client_id')
            ->whereNull('timesheet_entries.invoice_id')
            ->whereNull('timesheet_projects.budget')
            ->when(!Timesheet::isTimesheetAdministration(), function ($query) {
                $query->where('timesheet_entries.has_reviewed', 1);
            })
            ->where('timesheet_clients.id', $this->client->id)
            ->where('timesheet_activities.billable', 1)
            ->orderBy('timesheet_entries.spent_at')
            ->select('timesheet_entries.*');
    }

    public function getBillingCycleEdges($spentAt, $object)
    {
        $this->isClientExists();

        $billingCycle = $object->bill_cycle ?? 'monthly';
        $billingCycleStartsAt = $object->bill_cycle_starts_at ?? 1;

        $spentAt = Carbon::parse($spentAt);
        $date = Carbon::parse($spentAt);

        switch ($billingCycle) {
            case 'daily':
                return [
                    'code' => sprintf('%s-%s', $date->format('Ymd'), $date->format('Ymd')),
                    'start' => $date->toDateString(),
                    'end' => $date->toDateString(),
                    'is_current' => now()->isSameDay($date)
                ];
            case 'weekly':
                $billingCycleEdge = $date->startOfWeek(Settings::get('timesheet_start_of_week', Carbon::MONDAY));

                if ($spentAt->lt($billingCycleEdge)) {
                    $startOfWeek = $billingCycleEdge->subWeek();
                } else {
                    $startOfWeek = $billingCycleEdge;
                }

                $endOfWeek = $startOfWeek->copy()->addWeek();

                return [
                    'code' => sprintf('%s-%s', $startOfWeek->format('Ymd'), $endOfWeek->format('Ymd')),
                    'start' => $startOfWeek->toDateString(),
                    'end' => $endOfWeek->toDateString(),
                    'is_current' => now()->between($startOfWeek, $endOfWeek)
                ];
            case 'biweekly':
                $billingCycleEdge = $date->copy()->startOfMonth()->addDays($billingCycleStartsAt - 1);

                if ($spentAt->lt($billingCycleEdge)) {
                    $startOfBiweekly = $billingCycleEdge->subWeeks(2);
                } elseif ($spentAt->gte($billingCycleEdge->copy()->addWeeks(2))) {
                    $startOfBiweekly = $billingCycleEdge->addWeeks(2);
                } else {
                    $startOfBiweekly = $billingCycleEdge;
                }

                $endOfBiweekly = $startOfBiweekly->copy()->addWeeks(2);

                return [
                    'code' => sprintf('%s-%s', $startOfBiweekly->format('Ymd'), $endOfBiweekly->format('Ymd')),
                    'start' => $startOfBiweekly->toDateString(),
                    'end' => $endOfBiweekly->toDateString(),
                    'is_current' => now()->between($startOfBiweekly, $endOfBiweekly)
                ];
            case 'monthly':
                $billingCycleEdge = $date->copy()->startOfMonth()->addDays($billingCycleStartsAt - 1);

                if ($spentAt->lt($billingCycleEdge)) {
                    $startOfMonth = $billingCycleEdge->subMonth();
                } else {
                    $startOfMonth = $billingCycleEdge;
                }

                $endOfMonth = $startOfMonth->copy()->addMonth();

                return [
                    'code' => sprintf('%s-%s', $startOfMonth->format('Ymd'), $endOfMonth->format('Ymd')),
                    'start' => $startOfMonth->toDateString(),
                    'end' => $endOfMonth->toDateString(),
                    'is_current' => now()->between($startOfMonth, $endOfMonth)
                ];
        }
    }

    /**
     * @param Request $request
     * @param Project $project
     * @param Client $client
     * @return array
     */
    public function generateInvoicePerCycle(Request $request, Project $project, Client $client)
    {
        $bagParameters = new BagParameters([
            'project' => $project,
            'client' => $client,
            'status' => $request->get('status', 'pending'),
            'include_previous_entries' => $request->get('include_previous_entries', 0),
            'date_range' => [
                $request->get('cycle_start'),
                $request->get('cycle_end')
            ]
        ]);

        return (new InvoiceGenerator($bagParameters))->generate();
    }
}

Spamworldpro Mini