Files
rkm-conectasus-ci-sandbox/api/app/Http/Controllers/ProntoAtendimento/Plantao/PlantaoController.php
Antonio Lopes dos Santos afec302c3e
All checks were successful
build-api-image / build (push) Successful in 51s
init commit
2026-08-04 16:52:02 -03:00

1041 lines
35 KiB
PHP

<?php
namespace App\Http\Controllers\ProntoAtendimento\Plantao;
use App\Enums\ProntoAtendimento\Plantao\Status as PlantaoStatus;
use App\Enums\ProntoAtendimento\Plantao\TipoEscala;
use App\Exceptions\ProntoAtendimento\Plantao\ConflictingPlantaoException;
use App\Exceptions\ProntoAtendimento\Plantao\PlantaoComTempoAdicionalConflitanteException;
use App\Http\Controllers\Controller;
use App\Http\Requests\ProntoAtendimento\Plantao\PlantaoRequest;
use App\Models\Cadastro\Estabelecimento\Servico;
use App\Models\Cadastro\Estabelecimento\Unidade;
use App\Models\Cadastro\SujeitoAtencao\Lotacao;
use App\Models\Geral\FaixaEtaria;
use App\Models\ProntoAtendimento\Parametrizacao\Parametrizacao;
use App\Models\ProntoAtendimento\Plantao\Plantao;
use App\Models\ProntoAtendimento\Plantao\Status;
use App\Utils\Calculations\Week as WeekCalc;
use App\Utils\DataUtil;
use App\Utils\DateTimeUtil;
use Carbon\Carbon;
use Exception;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;
class PlantaoController extends Controller
{
protected $modulo = 'pronto-atendimento';
protected $funcionalidade = 'plantao';
private $businessRulesErrors = [];
public function datatable()
{
$plantoes = DB::table('pronto_atendimento_plantao')
->join(
'pronto_atendimento_plantao_status',
'pronto_atendimento_plantao.status_id',
'=',
'pronto_atendimento_plantao_status.id'
)
->join(
"cadastro_lotacao",
"pronto_atendimento_plantao.lotacao_id",
'=',
"cadastro_lotacao.id"
)
->join(
"cadastro_servico",
"pronto_atendimento_plantao.servico_id",
'=',
"cadastro_servico.id"
)
->join(
"cadastro_infraestrutura",
"cadastro_lotacao.infraestrutura_id",
'=',
"cadastro_infraestrutura.id"
)
->join(
'cadastro_unidade',
"cadastro_infraestrutura.unidade_id",
'=',
'cadastro_unidade.id'
)
->join(
"sigtap.ocupacao",
"cadastro_lotacao.ocupacao_id",
'=',
"sigtap.ocupacao.id"
)
->join(
"cadastro_usuario",
"cadastro_lotacao.usuario_id",
'=',
"cadastro_usuario.id"
)
->join(
"cadastro_cidadao",
"cadastro_usuario.cidadao_id",
'=',
"cadastro_cidadao.id"
)
->leftJoin(
"geral_faixa_etaria",
"pronto_atendimento_plantao.faixa_etaria_id",
'=',
"geral_faixa_etaria.id"
)
->select(
'pronto_atendimento_plantao.id',
DB::raw("DATE_FORMAT(pronto_atendimento_plantao.inicio, '%d/%m/%Y %H:%i') as inicio"),
DB::raw("DATE_FORMAT(pronto_atendimento_plantao.fim, '%d/%m/%Y %H:%i') as fim"),
'pronto_atendimento_plantao.quantidade_atendimentos',
'pronto_atendimento_plantao.status_id',
'cadastro_unidade.nome_fantasia as unidade_nome_fantasia',
'cadastro_servico.nome as nome_servico',
DB::raw('IF(cadastro_infraestrutura.nome IS NULL, '
. 'cadastro_unidade.nome_fantasia, cadastro_infraestrutura.nome) as infraestrutura_nome'),
'cadastro_cidadao.nome as cidadao_nome',
DB::raw("IF(geral_faixa_etaria.nome IS NULL, "
. "'TODOS', geral_faixa_etaria.nome) as faixa_etaria_nome"),
DB::raw("CONCAT(sigtap.ocupacao.codigo, ' - ', sigtap.ocupacao.nome) as ocupacao_codigo_nome")
)
->whereNull('pronto_atendimento_plantao.deleted_at');
if (DataUtil::isStringTrue(request()->query('current_date'))) {
$currentTimestamp = Carbon::now();
if (DataUtil::isStringTrue(request()->query('one_hour_before_start'))) {
$plantoes->whereRaw("DATE_SUB(pronto_atendimento_plantao.inicio, INTERVAL 60 MINUTE) "
. "<= '{$currentTimestamp->format('Y-m-d H:i:s')}'");
}
else {
$plantoes->where('pronto_atendimento_plantao.inicio', '<=', $currentTimestamp->format('Y-m-d H:i:s'));
}
$plantoes->where('pronto_atendimento_plantao.fim', '>=', $currentTimestamp->format('Y-m-d H:i:s'));
}
else {
if (DateTimeUtil::isFormattedCorrectly(request()->query('inicio'), 'Y-m-d H:i')) {
$plantoes->where('pronto_atendimento_plantao.inicio', '>=', request()->query('inicio'));
}
if (DateTimeUtil::isFormattedCorrectly(request()->query('fim'), 'Y-m-d H:i')) {
$plantoes->where('pronto_atendimento_plantao.fim', '<=', request()->query('fim'));
}
}
if (is_numeric(request()->query('unidade_id'))) {
$plantoes->where('cadastro_unidade.id', request()->query('unidade_id'));
}
if (is_numeric(request()->query('infraestrutura_id'))) {
$plantoes->where("cadastro_lotacao.infraestrutura_id", request()->query('infraestrutura_id'));
}
if (is_numeric(request()->query('usuario_id'))) {
$plantoes->where('cadastro_lotacao.usuario_id', request()->query('usuario_id'));
}
if (is_numeric(request()->query('servico_id'))) {
$plantoes->where('pronto_atendimento_plantao.servico_id', request()->query('servico_id'));
}
if (is_numeric(request()->query('ocupacao_id'))) {
$plantoes->where('cadastro_lotacao.ocupacao_id', request()->query('ocupacao_id'));
}
if (is_numeric(request()->query('status_id'))) {
$plantoes->where('pronto_atendimento_plantao.status_id', request()->query('status_id'));
}
else if (is_array(request()->query('status_id'))) {
$plantoes->whereIn('pronto_atendimento_plantao.status_id', request()->query('status_id'));
}
if (is_string(request()->query('order_by'))) {
switch (request()->query('order_by')) {
case 'inicio': {
$plantoes->orderBy('pronto_atendimento_plantao.inicio');
break;
}
case 'fim': {
$plantoes->orderBy('pronto_atendimento_plantao.fim');
break;
}
case 'unidade': {
$plantoes->orderBy("cadastro_unidade.nome_fantasia");
break;
}
case 'infraestrutura': {
$plantoes->orderByRaw("IF (cadastro_infraestrutura.nome IS NULL,
cadastro_unidade.nome_fantasia,
cadastro_infraestrutura.nome
)");
break;
}
case 'servico': {
$plantoes->orderBy("cadastro_servico.nome");
break;
}
case 'usuario': {
$plantoes->orderBy("cadastro_cidadao.nome");
break;
}
case 'ocupacao': {
$plantoes->orderBy("sigtap.ocupacao.codigo");
break;
}
case 'status': {
$plantoes->orderBy('pronto_atendimento_plantao_status.nome');
break;
}
}
}
return response()->json($plantoes->get(), Response::HTTP_OK);
}
public function list()
{
$this->auth(__FUNCTION__);
return response()->json(
[
'data_atual' => Carbon::now()->format('d/m/Y'),
'form' => [
'unidades' => $this->getUnidadesComLotacaoCentroCusto(),
'status' => Status::orderBy('nome')->get()->map(fn ($status) => [
'id' => $status->id,
'nome' => $status->nome,
'cor' => $status->cor,
'cor_hover' => $status->cor_hover,
]),
'servicos' => Servico::orderBy('nome')->get()->map(fn ($servicos) => [
'id' => $servicos->id,
'nome' => $servicos->nome,
]),
'ordenacao' => [
['value' => 'ocupacao', 'text' => 'CBO'],
['value' => 'inicio', 'text' => 'DATA DE ENTRADA'],
['value' => 'fim', 'text' => 'DATA DE SAÍDA'],
['value' => 'infraestrutura', 'text' => 'LOCAL DE ATENDIMENTO'],
['value' => 'usuario', 'text' => 'PROFISSIONAL'],
['value' => 'servico', 'text' => 'SERVIÇO'],
['value' => 'status', 'text' => 'STATUS'],
],
]
],
Response::HTTP_OK
);
}
public function index()
{
$this->auth(__FUNCTION__);
$plantoes = $this->get(Plantao::class);
if (is_numeric(request()->query('not_id'))) {
$plantoes->where('id', '<>', request()->query('not_id'));
}
if (is_numeric(request()->query('usuario_id'))) {
$plantoes->whereHas('lotacao', function ($lotacao) {
$lotacao->where('usuario_id', request()->query('usuario_id'));
});
}
$plantoes = $plantoes->get();
$restParams = $this->getRestParams();
if (!empty($restParams)) {
$inicioDescanso = $restParams['inicio'];
$fimDescanso = $restParams['fim'];
$between = fn ($value, $start, $end) => !is_null($value) && $value >= $start && $value <= $end;
$plantoes = $plantoes
->filter(function ($plantao) use ($between, $inicioDescanso, $fimDescanso) {
$periodoDescanso = $plantao->periodo_descanso;
if (empty($periodoDescanso)) {
return false;
}
return $between($inicioDescanso, $periodoDescanso['inicio'], $periodoDescanso['fim'])
|| $between($fimDescanso, $periodoDescanso['inicio'], $periodoDescanso['fim']);
})
->values();
}
return response()->json($plantoes, Response::HTTP_OK);
}
private function getRestParams()
{
if (is_array(request()->query('resting'))) {
$restParams = request()->query('resting');
$inicio = null;
$fim = null;
if (DateTimeUtil::isFormattedCorrectly($restParams['inicio'] ?? '', 'Y-m-d H:i:s')) {
$inicio = new Carbon(request()->query('resting')['inicio']);
}
if (DateTimeUtil::isFormattedCorrectly($restParams['fim'] ?? '', 'Y-m-d H:i:s')) {
$fim = new Carbon(request()->query('resting')['fim']);
}
return compact('inicio', 'fim');
}
return [];
}
public function create()
{
$this->auth(__FUNCTION__);
return response()->json(
[
'min_date' => Carbon::now()->subYear()->format('d/m/Y'),
'max_date' => Carbon::now()->addYear()->format('d/m/Y'),
'form' => $this->getFormData(),
'additionals' => [
'status_id' => null
]
],
Response::HTTP_OK
);
}
public function store(PlantaoRequest $request)
{
$this->auth(__FUNCTION__);
$this->validateBusinessRules();
if ($this->hasBusinessRulesErrors()) {
return response()->json($this->businessRulesErrors, Response::HTTP_UNPROCESSABLE_ENTITY);
}
$plantoes = null;
DB::transaction(function () use (&$plantoes, $request) {
$data = [
'lotacao_id' => $request->lotacao_id,
'servico_id' => $request->servico_id,
'faixa_etaria_id' => $request->faixa_etaria_id,
'escala_tipo_id' => $request->escala['tipo_id'],
'status_id' => $request->status_id,
];
if ($request->escala['tipo_id'] === TipoEscala::FIXA) {
$plantoes = $this->createPlantoesWithEscalasFixasTo($data);
}
else if ($request->escala['tipo_id'] === TipoEscala::DINAMICA) {
$plantoes = $this->createPlantoesWithEscalasDinamicasTo($data);
}
else if ($request->escala['tipo_id'] === TipoEscala::UNICA) {
$plantoes = $this->createPlantaoWithEscalaUnicaTo($data);
}
});
return response()->json($plantoes, Response::HTTP_CREATED);
}
private function createPlantoesWithEscalasFixasTo($data)
{
$plantoes = collect();
$inicioOfInterval = new Carbon(request()->escala['inicio']);
$fimOfInterval = new Carbon(request()->escala['fim'] . " {$inicioOfInterval->format('H:i:s')}");
$jornadaEmHoras = request()->escala['jornada_em_horas'];
$diasSemana = request()->escala['dias_semana'];
$currentDate = $inicioOfInterval->copy();
while ($currentDate <= $fimOfInterval) {
if (in_array($currentDate->format('w') + 1, $diasSemana)) {
$fim = $currentDate->copy()->modify("+{$jornadaEmHoras} hours");
$this->validateTimeEscala($data['lotacao_id'], $currentDate, $fim);
$this->verificaConflitoTempoAdicionalPlantao($data['lotacao_id'], $currentDate);
$plantao = Plantao::create([
'lotacao_id' => $data['lotacao_id'],
'servico_id' => $data['servico_id'],
'faixa_etaria_id' => $data['faixa_etaria_id'],
'escala_tipo_id' => $data['escala_tipo_id'],
'inicio' => $currentDate->format('Y-m-d H:i'),
'fim' => $fim->format('Y-m-d H:i'),
'quantidade_atendimentos' => request()->escala['quantidade_atendimentos'],
'status_id' => $data['status_id'] ?? PlantaoStatus::CADASTRADO,
]);
$plantoes->push($plantao);
}
$currentDate->addDay();
}
}
private function createPlantoesWithEscalasDinamicasTo($data)
{
$plantoes = collect();
$inicioOfInterval = new Carbon(request()->escala['inicio']);
$fimOfInterval = new Carbon(request()->escala['fim'] . " {$inicioOfInterval->format('H:i:s')}");
$jornadaEmHoras = request()->escala['jornada_em_horas'];
$restHours = $this->getRestHours();
$inicio = $inicioOfInterval->copy();
while ($inicio <= $fimOfInterval) {
$fim = $inicio->copy()->modify("+{$jornadaEmHoras} hours");
$this->validateTimeEscala($data['lotacao_id'], $inicio, $fim);
$this->verificaConflitoTempoAdicionalPlantao($data['lotacao_id'], $inicio);
$plantao = Plantao::create([
'lotacao_id' => $data['lotacao_id'],
'servico_id' => $data['servico_id'],
'faixa_etaria_id' => $data['faixa_etaria_id'],
'escala_tipo_id' => $data['escala_tipo_id'],
'inicio' => $inicio->format('Y-m-d H:i'),
'fim' => $fim->format('Y-m-d H:i'),
'quantidade_atendimentos' => request()->escala['quantidade_atendimentos'],
'status_id' => $data['status_id'] ?? PlantaoStatus::CADASTRADO,
]);
$plantoes->push($plantao);
$inicio = $fim->modify("+{$restHours} hours");
}
}
private function getRestHours()
{
switch (request()->escala['jornada_em_horas']) {
case 6: {
return 18;
}
case 8: {
return 16;
}
case 12: {
return 36;
}
case 24: {
return 48;
}
}
}
private function createPlantaoWithEscalaUnicaTo($data)
{
$inicio = new Carbon(request()->escala['inicio']);
$fim = new Carbon(request()->escala['fim']);
$this->validateTimeEscala($data['lotacao_id'], $inicio, $fim);
$this->verificaConflitoTempoAdicionalPlantao($data['lotacao_id'], $inicio);
return Plantao::create([
'lotacao_id' => $data['lotacao_id'],
'servico_id' => $data['servico_id'],
'faixa_etaria_id' => $data['faixa_etaria_id'],
'escala_tipo_id' => $data['escala_tipo_id'],
'inicio' => $inicio->format('Y-m-d H:i'),
'fim' => $fim->format('Y-m-d H:i'),
'quantidade_atendimentos' => request()->escala['quantidade_atendimentos'],
'status_id' => $data['status_id'] ?? PlantaoStatus::CADASTRADO,
]);
}
public function show($id)
{
$this->auth(__FUNCTION__);
$plantao = $this->get(Plantao::class, $id);
return response()->json($plantao, Response::HTTP_OK);
}
public function edit($id)
{
$this->auth(__FUNCTION__);
$plantao = Plantao::findOrFail($id);
return response()->json(
[
'min_date' => Carbon::now()->subYear()->format('d/m/Y'),
'max_date' => Carbon::now()->addYear()->format('d/m/Y'),
'form' => array_merge($this->getFormData(), [
'unidade_id' => $plantao->lotacao->unidade_id,
'infraestrutura_id' => $plantao->lotacao->infraestrutura_id,
'servico_id' => $plantao->servico_id,
'usuario_id' => $plantao->lotacao->usuario_id,
'ocupacao_id' => $plantao->lotacao->ocupacao_id,
]),
'model' => [
'faixa_etaria_id' => $plantao->faixa_etaria_id,
'escala' => [
'periodo' => [
'start' => $plantao->inicio->format('d/m/Y'),
'end' => $plantao->fim->format('d/m/Y'),
],
'hora_entrada' => $plantao->inicio->format('H:i:s'),
'hora_saida' => $plantao->fim->format('H:i:s'),
'quantidade_atendimentos' => $plantao->quantidade_atendimentos,
]
],
'additionals' => [
'status_id' => $plantao->status_id
]
],
Response::HTTP_OK
);
}
private function getFormData()
{
return [
'servicos' => Servico::orderBy('nome')->get()->map(fn ($servicos) => [
'id' => $servicos->id,
'nome' => $servicos->nome,
]),
'unidades' => $this->getUnidadesComLotacaoCentroCusto(),
'faixas_etarias' => FaixaEtaria::orderBy('nome')->get(),
'dias_semana' => [
['value' => 1, 'text' => 'Domingo', 'disabled' => true],
['value' => 2, 'text' => 'Segunda', 'disabled' => true],
['value' => 3, 'text' => 'Terça', 'disabled' => true],
['value' => 4, 'text' => 'Quarta', 'disabled' => true],
['value' => 5, 'text' => 'Quinta', 'disabled' => true],
['value' => 6, 'text' => 'Sexta', 'disabled' => true],
['value' => 7, 'text' => 'Sábado', 'disabled' => true],
],
'jornadas' => [
['value' => 6, 'text' => '6 H'],
['value' => 8, 'text' => '8 H'],
['value' => 12, 'text' => '12 H'],
['value' => 24, 'text' => '24 H'],
],
'descansos' => [
['text' => '16 H', 'value' => 16, 'disabled' => true],
['text' => '18 H', 'value' => 18, 'disabled' => true],
['text' => '36 H', 'value' => 36, 'disabled' => true],
['text' => '48 H', 'value' => 48, 'disabled' => true],
],
];
}
private function getUnidadesComLotacaoCentroCusto()
{
$unidades = DB::table('cadastro_unidade')
->whereExists(function ($query) {
$query
->select(DB::raw(1))
->from('cadastro_infraestrutura')
->whereColumn('cadastro_infraestrutura.unidade_id', 'cadastro_unidade.id')
->whereExists(function ($query) {
$query
->select(DB::raw(1))
->from('cadastro_lotacao')
->whereColumn('cadastro_lotacao.infraestrutura_id', 'cadastro_infraestrutura.id');
})
->whereExists(function ($query) {
$query
->select(DB::raw(1))
->from('cadastro_centro_custo')
->whereColumn('cadastro_centro_custo.infraestrutura_id', 'cadastro_infraestrutura.id');
});
})
->select(
'cadastro_unidade.id',
'cadastro_unidade.nome_fantasia'
)
->orderBy('cadastro_unidade.nome_fantasia')
->get();
$unidades->each(function ($unidade) {
$unidade->infraestruturas = DB::table('cadastro_infraestrutura')
->where('cadastro_infraestrutura.unidade_id', $unidade->id)
->whereExists(function ($query) {
$query
->select(DB::raw(1))
->from('cadastro_lotacao')
->whereColumn('cadastro_lotacao.infraestrutura_id', 'cadastro_infraestrutura.id');
})
->whereExists(function ($query) {
$query
->select(DB::raw(1))
->from('cadastro_centro_custo')
->whereColumn('cadastro_centro_custo.infraestrutura_id', 'cadastro_infraestrutura.id');
})
->select(
'cadastro_infraestrutura.id',
DB::raw("COALESCE(cadastro_infraestrutura.nome, '{$unidade->nome_fantasia}') AS nome")
)
->orderByRaw("COALESCE(cadastro_infraestrutura.nome, '{$unidade->nome_fantasia}')")
->get();
$unidade->infraestruturas->each(function ($infraestrutura) {
$infraestrutura->usuarios = DB::table('cadastro_usuario')
->join(
'cadastro_cidadao',
'cadastro_cidadao.id',
'cadastro_usuario.cidadao_id'
)
->whereExists(function ($query) use ($infraestrutura) {
$query
->select(DB::raw(1))
->from('cadastro_lotacao')
->whereColumn('cadastro_lotacao.usuario_id', 'cadastro_usuario.id')
->where('cadastro_lotacao.infraestrutura_id', $infraestrutura->id)
->whereNull('cadastro_lotacao.deleted_at')
->where('realiza_atendimento', true);
})
->select(
'cadastro_usuario.id',
'cadastro_cidadao.nome'
)
->orderBy('cadastro_cidadao.nome')
->get();
$infraestrutura->usuarios->each(function ($usuario) use ($infraestrutura) {
$usuario->ocupacoes = DB::table('sigtap.ocupacao')
->join(
'cadastro_lotacao',
'cadastro_lotacao.ocupacao_id',
'sigtap.ocupacao.id'
)
->where('cadastro_lotacao.usuario_id', $usuario->id)
->where('cadastro_lotacao.infraestrutura_id', $infraestrutura->id)
->whereNull('cadastro_lotacao.deleted_at')
->where('realiza_atendimento', true)
->select(
'sigtap.ocupacao.id',
DB::raw("CONCAT(sigtap.ocupacao.codigo, ' - ', sigtap.ocupacao.nome) as codigo_nome"),
'cadastro_lotacao.id AS lotacao_id'
)
->orderBy('sigtap.ocupacao.nome')
->get();
});
});
});
return $unidades;
}
public function update(PlantaoRequest $request, $id)
{
$this->auth(__FUNCTION__);
$plantao = Plantao::findOrFail($id);
$params = $request->all();
if (!empty($params)) {
if (isset($params['status_id']) && is_numeric($params['status_id'])) {
$plantao->status_id = $params['status_id'];
}
if (isset($params['escala'])) {
$escala = $params['escala'];
if (isset($escala['inicio'])) {
$this->validateBusinessRulesToUpdateSinglePlantao($plantao);
if ($this->hasBusinessRulesErrors()) {
return response()->json($this->businessRulesErrors, Response::HTTP_UNPROCESSABLE_ENTITY);
}
$inicio = new Carbon($escala['inicio']);
$fim = new Carbon($escala['fim']);
$this->validateTimeEscala($plantao->lotacao_id, $inicio, $fim, $plantao->id);
$plantao->inicio = $escala['inicio'];
$plantao->fim = $escala['fim'];
}
if (isset($escala['quantidade_atendimentos']) && is_numeric($escala['quantidade_atendimentos'])) {
$plantao->quantidade_atendimentos = $escala['quantidade_atendimentos'];
}
}
$plantao->save();
}
return response()->json($plantao->fresh(), Response::HTTP_OK);
}
public function updateAllStatus()
{
$this->auth(__FUNCTION__);
$plantoes = collect();
collect(request()->all())->each(function ($plantao) use ($plantoes) {
$plantao = tap(Plantao::findOrFail($plantao['id']))->update($plantao);
$plantoes->push($plantao->fresh());
});
return response()->json($plantoes, Response::HTTP_OK);
}
public function destroy($id)
{
$this->auth(__FUNCTION__);
Plantao::findOrFail($id)->delete();
return response()->json(null, Response::HTTP_NO_CONTENT);
}
public function destroyAll()
{
$this->auth(__FUNCTION__);
Plantao::destroy(request()->all());
return response()->json(null, Response::HTTP_NO_CONTENT);
}
private function validateBusinessRules()
{
$this->validateInicio();
$this->validateFim();
if ($this->isEscalaTipoFixa()) {
$this->validateDiasSemana();
}
}
private function validateBusinessRulesToUpdateSinglePlantao($plantao)
{
$this->validateInicio($plantao);
$this->validateFim($plantao);
}
private function validateInicio($plantao = null)
{
$errors = [];
if ($this->isInicioLessThanMinDate()) {
$errors[] = 'A data/hora inicial não pode ser uma data/hora anterior a um ano anterior da data atual.';
}
$inicio = new Carbon(request()->escala['inicio']);
if ($plantao && $plantao->date_time_of_first_atendimento && $inicio > $plantao->date_time_of_first_atendimento) {
$errors[] = "Início do plantão deve ser inferior ou igual ao primeiro "
. "atendimento {$plantao->date_time_of_first_atendimento->format('d/m/Y H:i')}.";
}
if (!empty($errors)) {
$this->businessRulesErrors['escala.inicio'] = $errors;
}
}
private function isInicioLessThanMinDate()
{
$minDate = Carbon::now()->subYear();
return new Carbon(request()->escala['inicio']) < $minDate;
}
private function validateFim($plantao = null)
{
$errors = [];
if ($this->isFimGreaterThanMaxDate()) {
$errors[] = 'A data/hora final não pode ser uma data/hora posterior a um ano anterior da data atual.';
}
if ($this->isEscalaTipoUnica() && $this->isFimGreaterThan24HoursFromInicio()) {
$errors[] = 'A data/hora final não pode ultrapassar 24 horas da data/hora inicial.';
}
$fim = new Carbon(request()->escala['fim']);
if ($plantao && $plantao->date_time_of_last_atendimento && $fim < $plantao->date_time_of_last_atendimento) {
$errors[] = "Fim do plantão deve ser superior ou igual ao último "
. "atendimento {$plantao->date_time_of_last_atendimento->format('d/m/Y H:i')}.";
}
if (!empty($errors)) {
$this->businessRulesErrors['escala.fim'] = $errors;
}
}
private function isFimGreaterThanMaxDate()
{
$maxDate = Carbon::now()->addYear();
return new Carbon(request()->escala['fim']) > $maxDate;
}
private function isEscalaTipoUnica()
{
return request()->escala['tipo_id'] === TipoEscala::UNICA;
}
private function isFimGreaterThan24HoursFromInicio()
{
$dateAfterInicio = (new Carbon(request()->escala['inicio']))->addDay();
return new Carbon(request()->escala['fim']) > $dateAfterInicio;
}
private function isEscalaTipoFixa()
{
return request()->escala['tipo_id'] === TipoEscala::FIXA;
}
private function validateDiasSemana()
{
if ($this->isJornada12Hours()) {
$this->validateDiasSemanaToJornada12Hours();
}
else if ($this->isJornada24Hours()) {
$this->validateDiasSemanaToJornada24Hours();
}
}
private function isJornada12Hours()
{
return request()->escala['jornada_em_horas'] === 12;
}
private function validateDiasSemanaToJornada12Hours()
{
$errors = [];
if ($this->hasNotAllowedDiasSemanaInDiasSemana($this->getNotAllowedDiasSemanaToJornada12Hours())) {
$errors[] = 'Se a jornada for de 12 horas, então deverá ter um intervalo de 1 dia '
. 'anterior e 1 dia posterior entre os dias da semana.';
}
if (!empty($errors)) {
$this->businessRulesErrors['escala.dias_semana.*'] = $errors;
}
}
private function getNotAllowedDiasSemanaToJornada12Hours()
{
$notAllowedDiasSemana = [];
foreach (request()->escala['dias_semana'] as $dia) {
array_push($notAllowedDiasSemana, WeekCalc::sub($dia, 1), WeekCalc::sum($dia, 1));
}
return $notAllowedDiasSemana;
}
private function isJornada24Hours()
{
return request()->escala['jornada_em_horas'] === 24;
}
private function validateDiasSemanaToJornada24Hours()
{
$errors = [];
if ($this->hasNotAllowedDiasSemanaInDiasSemana($this->getNotAllowedDiasSemanaToJornada24Hours())) {
$errors[] = 'Se a jornada for de 24 horas, então deverá ter um intervalo de 2 dias '
. 'anteriores e 2 dias posteriores entre os dias da semana.';
}
if (!empty($errors)) {
$this->businessRulesErrors['escala.dias_semana.*'] = $errors;
}
}
private function hasNotAllowedDiasSemanaInDiasSemana($notAllowedDiasSemana)
{
foreach (request()->escala['dias_semana'] as $dia) {
if (in_array($dia, $notAllowedDiasSemana)) {
return true;
}
}
}
private function getNotAllowedDiasSemanaToJornada24Hours()
{
$notAllowedDiasSemana = [];
foreach (request()->escala['dias_semana'] as $dia) {
array_push(
$notAllowedDiasSemana,
WeekCalc::sub($dia, 1),
WeekCalc::sub($dia, 2),
WeekCalc::sum($dia, 1),
WeekCalc::sum($dia, 2)
);
}
return $notAllowedDiasSemana;
}
private function hasBusinessRulesErrors()
{
return !empty($this->businessRulesErrors);
}
private function validateTimeEscala($lotacaoId, $inicio, $fim, $currentPlantaoId = null)
{
$usuarioId = Lotacao::findOrFail($lotacaoId)->usuario_id;
if ($this->isConflictingEscala($usuarioId, $inicio, $fim, $currentPlantaoId)) {
throw new ConflictingPlantaoException;
}
}
private function isConflictingEscala($usuarioId, $inicio, $fim, $currentPlantaoId = null)
{
return Plantao::whereHas('lotacao', function ($lotacao) use ($usuarioId) {
$lotacao->where('usuario_id', $usuarioId);
})->when($currentPlantaoId, function ($query, $id) {
$query->where('id', '<>', $id);
})->where(function ($plantao) use ($inicio, $fim) {
$plantao
->where(function ($plantao) use ($inicio) {
$plantao
->where('inicio', '<=', $inicio)
->where('fim', '>=', $inicio);
})
->orWhere(function ($plantao) use ($fim) {
$plantao
->where('inicio', '<=', $fim)
->where('fim', '>=', $fim);
})
->orWhere(function ($plantao) use ($inicio, $fim) {
$plantao
->where('inicio', '>=', $inicio)
->where('inicio', '<=', $fim);
})
->orWhere(function ($plantao) use ($inicio, $fim) {
$plantao
->where('fim', '>=', $inicio)
->where('fim', '<=', $fim);
});
})->exists();
}
private function verificaConflitoTempoAdicionalPlantao($lotacaoId, $inicio)
{
$parametrizacao = Parametrizacao::first();
if (!is_null($parametrizacao) && $parametrizacao->tempo_adicional_plantao > 0) {
$plantaoComTempoAdicionalConflitante = Plantao::where('lotacao_id', $lotacaoId)
->whereDate('fim', $inicio->format('Y-m-d'))
->where('fim', '>=', $inicio->subMinutes($parametrizacao->tempo_adicional_plantao ?? 0)->format('Y-m-d H:i'))
->exists();
if ($plantaoComTempoAdicionalConflitante) {
throw new PlantaoComTempoAdicionalConflitanteException();
}
}
}
}