Files
rkm-conectasus-ci-sandbox/api/app/Services/Farmacia/LivroService.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

1202 lines
61 KiB
PHP

<?php
namespace App\Services\Farmacia;
use App\Enums\Almoxarifado\EntradaTipo as EntradaTipoEnum;
use App\Enums\Almoxarifado\MotivoBaixaTipo as MotivoBaixaTipoEnum;
use App\Enums\Almoxarifado\SaidaTipo as SaidaTipoEnum;
use App\Enums\Cadastro\Material\LivroRegistro as LivroRegistroEnum;
use App\Enums\Cadastro\Material\LivroRegistroTipo as LivroRegistroTipoEnum;
use App\Enums\Cadastro\SujeitoAtencao\TipoRegistroUsuario;
use App\Enums\Farmacia\LivroStatus as LivroStatusEnum;
use App\Models\Cadastro\Material\LivroRegistro;
use App\Models\Cadastro\Material\LivroRegistroTipo;
use App\Models\Farmacia\Livro;
use App\Models\Farmacia\LivroPagina;
use App\Models\Farmacia\LivroPaginaLinha;
use App\Models\Farmacia\LivroStatus;
use App\Models\Intranet\Cliente;
use App\Services\Cadastro\Material\MaterialService;
use App\Services\Service;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class LivroService extends Service
{
private const QUANTIDADE_LINHAS_POR_PAGINA_PADRAO = 27;
private const QUANTIDADE_LINHAS_POR_PAGINA_TALIDOMIDA_A_BASE = 31;
private const QUANTIDADE_LINHAS_POR_PAGINA_TALIDOMIDA_ESPECIFICO = 29;
public static function datatable($livroRegistroIds, $livroRegistroTipoIds, $allowLivroRegistroTipoNull = false)
{
$livros = DB::table('farmacia_livro as livro')
->join(
'cadastro_livro_registro as livro_registro',
'livro_registro.id',
'livro.livro_registro_id'
)
->join(
'farmacia_livro_status as status',
'status.id',
'livro.status_id'
)
->leftJoin(
'cadastro_livro_registro_tipo as livro_registro_tipo',
'livro_registro_tipo.id',
'livro.livro_registro_tipo_id'
)
->where('livro.created_centro_custo_id', self::getCurrentCentroCusto()->id)
->whereIn('livro.livro_registro_id', $livroRegistroIds)
->where(fn ($q) => $q
->whereIn('livro.livro_registro_tipo_id', $livroRegistroTipoIds)
->when($allowLivroRegistroTipoNull, fn ($q) => $q->orWhereNull('livro.livro_registro_tipo_id'))
)
->when(request()->livro_registro_id, fn ($q, $id) => $q->where('livro.livro_registro_id', $id))
->when(request()->livro_registro_tipo_id, fn ($q, $id) => $q->where('livro.livro_registro_tipo_id', $id))
->when(request()->status_id, fn ($q, $id) => $q->where('livro.status_id', $id))
->select(DB::raw("
livro.id,
livro.numero,
livro.created_centro_custo_id,
livro.aberto_em,
livro.livro_registro_id,
livro.livro_registro_tipo_id,
UPPER(livro_registro.nome) as livro,
livro_registro_tipo.nome as tipo,
status.id as status_id,
status.nome as status,
(
SELECT IF(MIN(COALESCE((
SELECT count(linha.id)
FROM farmacia_livro_pagina_linha as linha
WHERE linha.pagina_id = pagina.id
), 0)) = (
CASE livro.livro_registro_tipo_id
WHEN " . LivroRegistroTipoEnum::A_BASE . "
THEN " . self::QUANTIDADE_LINHAS_POR_PAGINA_TALIDOMIDA_A_BASE . "
WHEN " . LivroRegistroTipoEnum::ESPECIFICO . "
THEN " . self::QUANTIDADE_LINHAS_POR_PAGINA_TALIDOMIDA_ESPECIFICO . "
ELSE " . self::QUANTIDADE_LINHAS_POR_PAGINA_PADRAO . "
END
), 1, 0)
FROM farmacia_livro_pagina as pagina
WHERE pagina.livro_id = livro.id
) as totalmente_preenchido
"));
return datatables()
->of($livros)
->filterColumn('livro.aberto_em', function ($query, $keyword) {
$query->whereRaw("DATE_FORMAT(livro.aberto_em, '%d/%m/%Y') like ?", ["%{$keyword}%"])
->orWhereRaw(
"CONCAT(
SUBSTR(livro.aberto_em,9,2),
SUBSTR(livro.aberto_em,6,2),
SUBSTR(livro.aberto_em,1,4)
) like ?",
["%{$keyword}%"]
);
})
->addColumn('has_available_update', function ($livro) {
return self::getMovimentacoesAptasParaVincular($livro)->isNotEmpty();
})
->skipTotalRecords()
->toJson();
}
public static function list($livroRegistroIds, $livroRegistroTipoIds, $allowLivroRegistroTipoNull = false)
{
return [
'form' => [
'materiais_com_movimentacoes_sem_livro' => self::getMateriaisComMovimentacoesSemLivro(
$livroRegistroIds,
$livroRegistroTipoIds,
$allowLivroRegistroTipoNull
),
'livros_registro' => LivroRegistro::whereIn('id', $livroRegistroIds)
->orderBy('nome')
->get(['id', 'nome'])
->each(fn ($l) => $l->nome = Str::upper($l->nome)),
'tipos_livro_registro' => LivroRegistroTipo::whereIn('id', $livroRegistroTipoIds)
->orderBy('nome')
->get(['id', 'nome']),
'status' => LivroStatus::orderBy('nome')->get(['id', 'nome']),
],
'model' => [
'livro_registro_id' => null,
'livro_registro_tipo_id' => null,
'status_id' => null,
],
];
}
public static function create($livroRegistroIds, $livroRegistroTipoIds)
{
return [
'form' => self::getFormData($livroRegistroIds, $livroRegistroTipoIds),
'model' => [
'numero' => null,
'status_id' => null,
'aberto_em' => null,
'livro_registro_id' => null,
'livro_registro_tipo_id' => null,
'quantidade_paginas' => null,
'registro' => null,
'usuario_farmaceutico' => null,
'inscricao_estadual' => null,
'autoridade_sanitaria' => null,
],
];
}
public static function edit($livro, $livroRegistroIds, $livroRegistroTipoIds)
{
return [
'form' => self::getFormData($livroRegistroIds, $livroRegistroTipoIds),
'model' => [
'numero' => $livro->numero,
'status_id' => $livro->status_id,
'aberto_em' => $livro->aberto_em->format('d/m/Y'),
'livro_registro_id' => $livro->livro_registro_id,
'livro_registro_tipo_id' => $livro->livro_registro_tipo_id,
'quantidade_paginas' => $livro->quantidade_paginas,
'registro' => $livro->registro,
'usuario_farmaceutico' => [
'id' => $livro->usuarioFarmaceutico->id,
'cidadao_nome' => $livro->usuarioFarmaceutico->cidadao->nome,
],
'inscricao_estadual' => $livro->inscricao_estadual,
'autoridade_sanitaria' => $livro->autoridade_sanitaria,
'paginas' => self::getPaginasEmUso($livro->id),
],
];
}
private static function getFormData($livroRegistroIds, $livroRegistroTipoIds)
{
$municipio = Cliente::where('link', app('config')->get('database.default'))->first()->municipio->nome;
$infraestruturaLogada = self::getCurrentCentroCusto()->infraestrutura;
return [
'livros_registro' => LivroRegistro::whereIn('id', $livroRegistroIds)
->orderBy('nome')
->get(['id', 'nome'])
->each(fn ($l) => $l->nome = Str::upper($l->nome)),
'tipos_livro_registro' => LivroRegistroTipo::whereIn('id', $livroRegistroTipoIds)
->orderBy('nome')
->get(['id', 'nome']),
'firma' => "PREFEITURA MUNICIPAL DE {$municipio}",
'farmacia' => $infraestruturaLogada->nome,
'endereco' => $infraestruturaLogada->endereco->completo,
];
}
public static function store($data)
{
$data['numero'] = self::getNextNumero(
$data['livro_registro_id'],
$data['livro_registro_tipo_id']
);
$data['status_id'] = LivroStatusEnum::ABERTO;
$livro = Livro::create($data);
for ($numeroPagina = 1; $numeroPagina <= $data['quantidade_paginas']; $numeroPagina++) {
$livro->paginas()->create(['numero' => $numeroPagina]);
}
self::updateMovimentacoes($livro);
return $livro->fresh();
}
private static function getNextNumero($livroRegistroId, $livroRegistroTipoId)
{
return Livro::where('livro_registro_id', $livroRegistroId)
->where('livro_registro_tipo_id', $livroRegistroTipoId)
->max('numero') + 1;
}
public static function updateMovimentacoes($livro)
{
self::getMovimentacoesAptasParaVincular($livro)
->each(function ($movimentacao) use ($livro) {
$pagina = self::getPaginaDisponivel($livro, $movimentacao->material_id);
if (!is_null($pagina)) {
if (is_null($pagina->material_id)) {
LivroPagina::where('id', $pagina->id)
->update(['material_id' => $movimentacao->material_id]);
}
LivroPaginaLinha::create([
'pagina_id' => $pagina->id,
'entrada_item_id' => $movimentacao->movimento == 'entrada' ? $movimentacao->id : null,
'saida_item_id' => $movimentacao->movimento == 'saida' ? $movimentacao->id : null,
]);
}
});
}
private static function getMovimentacoesAptasParaVincular($livro)
{
return self::getMovimentacoes((object) [
'centro_custo_id' => $livro->created_centro_custo_id,
'data' => $livro->aberto_em,
'livro_registro_id' => $livro->livro_registro_id,
'livro_registro_tipo_id' => $livro->livro_registro_tipo_id,
])
->reject(fn ($m) => self::hasEspacoDisponivelEmLivroAnterior($livro, $m->material_id))
->reject(fn ($m) => is_null(self::getPaginaDisponivel($livro, $m->material_id)));
}
private static function getMovimentacoes($params)
{
$isTalidomida = $params->livro_registro_id == LivroRegistroEnum::TALIDOMIDA;
return DB::table('almoxarifado_entrada_item as entrada_item')
->join('almoxarifado_entrada as entrada', 'entrada.id', 'entrada_item.entrada_id')
->join('cadastro_material as material', 'material.id', 'entrada_item.material_id')
->where('entrada.finalizada', true)
->where('entrada.tipo_id', '<>', EntradaTipoEnum::ESTORNO)
->where('entrada.centro_custo_id', $params->centro_custo_id)
->when(isset($params->data), fn ($q) => $q->whereDate('entrada.created_at', '>=', $params->data))
->where('material.livro_registro_id', $params->livro_registro_id)
->when(!$isTalidomida, function ($query) use ($params) {
$query->where('material.livro_registro_tipo_id', $params->livro_registro_tipo_id);
})
->whereNotExists(function ($query) use ($isTalidomida, $params) {
$query->select(DB::raw(1))
->from('farmacia_livro_pagina_linha as linha')
->when($isTalidomida, function ($query) use ($params) {
$query->join('farmacia_livro_pagina as pagina', 'pagina.id', 'linha.pagina_id')
->join('farmacia_livro as livro', 'livro.id', 'pagina.livro_id')
->where('livro.livro_registro_tipo_id', $params->livro_registro_tipo_id);
})
->whereColumn('linha.entrada_item_id', 'entrada_item.id');
})
->select(
DB::raw("'entrada' as movimento"),
'entrada_item.id',
'entrada_item.material_id',
DB::raw("entrada.created_at as data_movimentacao")
)
->union(DB::table('almoxarifado_saida_item as saida_item')
->join('almoxarifado_saida as saida', 'saida.id', 'saida_item.saida_id')
->join('cadastro_material as material', 'material.id', 'saida_item.material_id')
->leftJoin('almoxarifado_saida_baixa as baixa', 'baixa.saida_id', 'saida.id')
->leftJoin('almoxarifado_motivo_baixa as motivo', 'motivo.id', 'baixa.motivo_id')
->where('saida.finalizada', true)
->where('saida.tipo_id', '<>', SaidaTipoEnum::ESTORNO)
->where('saida.centro_custo_id', $params->centro_custo_id)
->when(isset($params->data), fn ($q) => $q->whereDate('saida.created_at', '>=', $params->data))
->where('material.livro_registro_id', $params->livro_registro_id)
->when(!$isTalidomida, function ($query) use ($params) {
$query->where('material.livro_registro_tipo_id', $params->livro_registro_tipo_id);
})
->whereNotExists(function ($query) use ($isTalidomida, $params) {
$query->select(DB::raw(1))
->from('farmacia_livro_pagina_linha as linha')
->when($isTalidomida, function ($query) use ($params) {
$query->join('farmacia_livro_pagina as pagina', 'pagina.id', 'linha.pagina_id')
->join('farmacia_livro as livro', 'livro.id', 'pagina.livro_id')
->where('livro.livro_registro_tipo_id', $params->livro_registro_tipo_id);
})
->whereColumn('linha.saida_item_id', 'saida_item.id');
})
->select(
DB::raw("'saida' as movimento"),
'saida_item.id',
'saida_item.material_id',
DB::raw("saida.created_at as data_movimentacao")
)
)
->orderBy('data_movimentacao')
->get();
}
private static function hasEspacoDisponivelEmLivroAnterior($livro, $materialId)
{
return DB::table('farmacia_livro_pagina as pagina')
->join('farmacia_livro as livro', 'livro.id', 'pagina.livro_id')
->where('livro.id', '<', $livro->id)
->where('livro.created_centro_custo_id', $livro->created_centro_custo_id)
->where('livro.status_id', LivroStatusEnum::ABERTO)
->where('livro.livro_registro_id', $livro->livro_registro_id)
->where('livro.livro_registro_tipo_id', $livro->livro_registro_tipo_id)
->whereDate('livro.aberto_em', '<=', $livro->aberto_em)
->where(function ($query) use ($materialId) {
$query->where(function ($query) use ($materialId) {
$query->where('pagina.material_id', $materialId)
->whereRaw("(
SELECT count(linha.id)
FROM farmacia_livro_pagina_linha as linha
WHERE linha.pagina_id = pagina.id
) < (
CASE livro.livro_registro_tipo_id
WHEN " . LivroRegistroTipoEnum::A_BASE . "
THEN " . self::QUANTIDADE_LINHAS_POR_PAGINA_TALIDOMIDA_A_BASE . "
WHEN " . LivroRegistroTipoEnum::ESPECIFICO . "
THEN " . self::QUANTIDADE_LINHAS_POR_PAGINA_TALIDOMIDA_ESPECIFICO . "
ELSE " . self::QUANTIDADE_LINHAS_POR_PAGINA_PADRAO . "
END
)");
})->orWhereNull('pagina.material_id');
})
->exists();
}
private static function getPaginaDisponivel($livro, $materialId)
{
$quantidadeLinhasPorPagina = self::QUANTIDADE_LINHAS_POR_PAGINA_PADRAO;
if ($livro->livro_registro_tipo_id == LivroRegistroTipoEnum::A_BASE) {
$quantidadeLinhasPorPagina = self::QUANTIDADE_LINHAS_POR_PAGINA_TALIDOMIDA_A_BASE;
}
else if ($livro->livro_registro_tipo_id == LivroRegistroTipoEnum::ESPECIFICO) {
$quantidadeLinhasPorPagina = self::QUANTIDADE_LINHAS_POR_PAGINA_TALIDOMIDA_ESPECIFICO;
}
return DB::table('farmacia_livro_pagina as pagina')
->where('pagina.livro_id', $livro->id)
->where(function ($query) use ($materialId, $quantidadeLinhasPorPagina) {
$query->where(function ($query) use ($materialId, $quantidadeLinhasPorPagina) {
$query->where('pagina.material_id', $materialId)
->whereRaw("(
SELECT count(linha.id)
FROM farmacia_livro_pagina_linha as linha
WHERE linha.pagina_id = pagina.id
) < {$quantidadeLinhasPorPagina}");
})->orWhereNull('pagina.material_id');
})
->orderBy('pagina.numero')
->limit(1)
->select(
'pagina.id',
'pagina.material_id'
)
->first();
}
public static function fecharLivro($livro)
{
if ($livro->status_id === LivroStatusEnum::ABERTO) {
$livro->update([
'status_id' => LivroStatusEnum::FECHADO,
'fechado_em' => Carbon::now(),
'lotacao_fechamento_id' => self::getCurrentLotacao()->id,
]);
}
}
public static function getDataAbertura($livroRegistroId, $livroRegistroTipoId = null)
{
$dataMinimaAbertura = null;
$dataMaximaAbertura = Carbon::now()->setTime(0, 0, 0);
$abertoEm = Livro::where('livro_registro_id', $livroRegistroId)
->where('livro_registro_tipo_id', $livroRegistroTipoId)
->where('created_centro_custo_id', self::getCurrentCentroCusto()->id)
->max('aberto_em');
if (!is_null($abertoEm)) {
$dataMinimaAbertura = (new Carbon($abertoEm))->setTime(0, 0, 0);
}
$dataPrimeiraMovimentacao = self::getMovimentacoes((object) [
'centro_custo_id' => self::getCurrentCentroCusto()->id,
'data' => $dataMinimaAbertura ? $dataMinimaAbertura->format('Y-m-d') : null,
'livro_registro_id' => $livroRegistroId,
'livro_registro_tipo_id' => $livroRegistroTipoId,
])->first()->data_movimentacao ?? null;
if (!is_null($dataPrimeiraMovimentacao)) {
$dataPrimeiraMovimentacao = (new Carbon($dataPrimeiraMovimentacao))->setTime(0, 0, 0);
if ($dataPrimeiraMovimentacao < $dataMaximaAbertura) {
$dataMaximaAbertura = $dataPrimeiraMovimentacao;
}
}
return [
'data_minima' => $dataMinimaAbertura ? $dataMinimaAbertura->format('d/m/Y') : null,
'data_maxima' => $dataMaximaAbertura ? $dataMaximaAbertura->format('d/m/Y') : null,
];
}
private static function getMateriaisComMovimentacoesSemLivro(
$livroRegistroIds = [],
$livroRegistroTipoIds = [],
$allowLivroRegistroTipoNull
) {
$centroCustoLogadoId = self::getCurrentCentroCusto()->id;
$materiais = collect();
$setMateriais = function ($livroRegistroId, $livroRegistroTipoId) use ($centroCustoLogadoId, $materiais) {
$isTalidomida = $livroRegistroId === LivroRegistroEnum::TALIDOMIDA;
DB::table('cadastro_material as material')
->join(
'cadastro_apresentacao as apresentacao',
'apresentacao.id',
'material.apresentacao_id'
)
->join(
'cadastro_livro_registro as livro_registro',
'livro_registro.id',
'material.livro_registro_id'
)
->leftJoin(
'cadastro_unidade_medida as apresentacao_unidade_medida',
'apresentacao_unidade_medida.id',
'material.apresentacao_unidade_medida_id'
)
->where('material.livro_registro_id', $livroRegistroId)
->when(!$isTalidomida, fn ($q) => $q->where('material.livro_registro_tipo_id', $livroRegistroTipoId))
->where(function ($query) use ($centroCustoLogadoId, $isTalidomida, $livroRegistroTipoId) {
$query->whereExists(function ($query) use ($centroCustoLogadoId, $isTalidomida, $livroRegistroTipoId) {
$query->select(DB::raw(1))
->from('almoxarifado_entrada_item as entrada_item')
->join('almoxarifado_entrada as entrada', 'entrada.id', 'entrada_item.entrada_id')
->whereColumn('entrada_item.material_id', 'material.id')
->where('entrada.finalizada', true)
->where('entrada.tipo_id', '<>', EntradaTipoEnum::ESTORNO)
->where('entrada.centro_custo_id', $centroCustoLogadoId)
->whereNotExists(function ($query) use ($isTalidomida, $livroRegistroTipoId) {
$query->select(DB::raw(1))
->from('farmacia_livro_pagina_linha as linha')
->when($isTalidomida, function ($query) use ($livroRegistroTipoId) {
$query->join('farmacia_livro_pagina as pagina', 'pagina.id', 'linha.pagina_id')
->join('farmacia_livro as livro', 'livro.id', 'pagina.livro_id')
->where('livro.livro_registro_tipo_id', $livroRegistroTipoId);
})
->whereColumn('linha.entrada_item_id', 'entrada_item.id');
})
->whereNotExists(function ($query) use ($centroCustoLogadoId, $livroRegistroTipoId) {
$query->select(DB::raw(1))
->from('farmacia_livro_pagina as pagina')
->join('farmacia_livro as livro', 'livro.id', 'pagina.livro_id')
->whereColumn('livro.livro_registro_id', 'material.livro_registro_id')
->whereColumn('livro.aberto_em', '<=', 'entrada.created_at')
->where('livro.livro_registro_tipo_id', $livroRegistroTipoId)
->where('livro.created_centro_custo_id', $centroCustoLogadoId)
->where('livro.status_id', LivroStatusEnum::ABERTO)
->where(function ($query) {
$query->where(function ($query) {
$query->whereColumn('pagina.material_id', 'material.id')
->whereRaw("(
SELECT count(linha.id)
FROM farmacia_livro_pagina_linha as linha
WHERE linha.pagina_id = pagina.id
) < (
CASE livro.livro_registro_tipo_id
WHEN " . LivroRegistroTipoEnum::A_BASE . "
THEN " . self::QUANTIDADE_LINHAS_POR_PAGINA_TALIDOMIDA_A_BASE . "
WHEN " . LivroRegistroTipoEnum::ESPECIFICO . "
THEN " . self::QUANTIDADE_LINHAS_POR_PAGINA_TALIDOMIDA_ESPECIFICO . "
ELSE " . self::QUANTIDADE_LINHAS_POR_PAGINA_PADRAO . "
END
)");
})->orWhereNull('pagina.material_id');
});
});
})->orWhereExists(function ($query) use ($centroCustoLogadoId, $isTalidomida, $livroRegistroTipoId) {
$query->select(DB::raw(1))
->from('almoxarifado_saida_item as saida_item')
->join('almoxarifado_saida as saida', 'saida.id', 'saida_item.saida_id')
->whereColumn('saida_item.material_id', 'material.id')
->where('saida.finalizada', true)
->where('saida.tipo_id', '<>', SaidaTipoEnum::ESTORNO)
->where('saida.centro_custo_id', $centroCustoLogadoId)
->whereNotExists(function ($query) use ($isTalidomida, $livroRegistroTipoId) {
$query->select(DB::raw(1))
->from('farmacia_livro_pagina_linha as linha')
->when($isTalidomida, function ($query) use ($livroRegistroTipoId) {
$query->join('farmacia_livro_pagina as pagina', 'pagina.id', 'linha.pagina_id')
->join('farmacia_livro as livro', 'livro.id', 'pagina.livro_id')
->where('livro.livro_registro_tipo_id', $livroRegistroTipoId);
})
->whereColumn('linha.saida_item_id', 'saida_item.id');
})
->whereNotExists(function ($query) use ($centroCustoLogadoId, $livroRegistroTipoId) {
$query->select(DB::raw(1))
->from('farmacia_livro_pagina as pagina')
->join('farmacia_livro as livro', 'livro.id', 'pagina.livro_id')
->whereColumn('livro.livro_registro_id', 'material.livro_registro_id')
->whereColumn('livro.aberto_em', '<=', 'saida.created_at')
->where('livro.livro_registro_tipo_id', $livroRegistroTipoId)
->where('livro.created_centro_custo_id', $centroCustoLogadoId)
->where('livro.status_id', LivroStatusEnum::ABERTO)
->where(function ($query) {
$query->where(function ($query) {
$query->whereColumn('pagina.material_id', 'material.id')
->whereRaw("(
SELECT count(linha.id)
FROM farmacia_livro_pagina_linha as linha
WHERE linha.pagina_id = pagina.id
) < (
CASE livro.livro_registro_tipo_id
WHEN " . LivroRegistroTipoEnum::A_BASE . "
THEN " . self::QUANTIDADE_LINHAS_POR_PAGINA_TALIDOMIDA_A_BASE . "
WHEN " . LivroRegistroTipoEnum::ESPECIFICO . "
THEN " . self::QUANTIDADE_LINHAS_POR_PAGINA_TALIDOMIDA_ESPECIFICO . "
ELSE " . self::QUANTIDADE_LINHAS_POR_PAGINA_PADRAO . "
END
)");
})->orWhereNull('pagina.material_id');
});
});
});
})
->select(DB::raw("
" . MaterialService::getDescricaoCompletaRaw(
'material',
'apresentacao',
'apresentacao_unidade_medida'
) . " AS descricao,
UPPER(livro_registro.nome) as livro,
" . (is_null($livroRegistroTipoId) ? 'NULL' : "(
SELECT tipo.nome
FROM cadastro_livro_registro_tipo as tipo
WHERE tipo.id = {$livroRegistroTipoId}
)" ) . " as tipo
"))
->get()
->each(function ($material) use ($materiais) {
$agrupado = $materiais
->where('livro', $material->livro)
->where('tipo', $material->tipo)
->first();
if (is_null($agrupado)) {
$agrupado = (object) [
'livro' => $material->livro,
'tipo' => $material->tipo,
'materiais' => collect(),
];
$materiais->push($agrupado);
}
$agrupado->materiais->push((object) [
'descricao' => $material->descricao
]);
});
};
if ($allowLivroRegistroTipoNull) {
$livroRegistroTipoIds[] = null;
}
collect($livroRegistroIds)->each(fn ($livroRegistroId) => collect($livroRegistroTipoIds)
->each(fn ($livroRegistroTipoId) => $setMateriais($livroRegistroId, $livroRegistroTipoId)));
return $materiais;
}
public static function getLivroCompletoImpressao($livroId)
{
$paginas = self::getPaginasEmUso($livroId);
$paginas->each(function ($paginaEmUso) {
$pagina = LivroPagina::findOrFail($paginaEmUso->id);
$paginaData = self::getLinhas($pagina);
$paginaEmUso->estorno_message = $paginaData['estorno_message'];
$paginaEmUso->transferido_de = $paginaData['transferido_de'];
$paginaEmUso->transferido_para = $paginaData['transferido_para'];
$paginaEmUso->linhas = $paginaData['linhas']->map(fn ($linha) => (object) [
'data_movimentacao' => (new Carbon($linha->data_movimentacao))->format('d/m/Y'),
'entrada' => $linha->entrada,
'saida' => $linha->saida,
'perda' => $linha->perda,
'estoque' => $linha->estoque,
'numero_registro' => $linha->numero_registro ?? null,
'numero_documento' => $linha->numero_documento ?? null,
'numero_notificacao' => $linha->numero_notificacao ?? null,
'paciente_data_nascimento' => isset($linha->paciente_data_nascimento)
? (new Carbon($linha->paciente_data_nascimento))->format('d/m/Y') : null,
'paciente_sexo' => $linha->paciente_sexo ?? null,
'medico_nome' => $linha->medico_nome ?? null,
'medico_crm' => $linha->medico_crm ?? null,
'cid_nome' => $linha->cid_nome ?? null,
'cid_codigo' => $linha->cid_codigo ?? null,
'historico' => $linha->historico ?? null,
'observacoes' => $linha->observacoes ?? null,
]);
});
return $paginas;
}
private static function getPaginasEmUso($livroId)
{
return DB::table('farmacia_livro_pagina as pagina')
->join('cadastro_material as material', 'material.id', 'pagina.material_id')
->join('cadastro_apresentacao as apresentacao', 'apresentacao.id', 'material.apresentacao_id')
->leftJoin(
'cadastro_unidade_medida as apresentacao_unidade_medida',
'apresentacao_unidade_medida.id',
'material.apresentacao_unidade_medida_id'
)
->where('pagina.livro_id', $livroId)
->select(DB::raw("
pagina.id,
pagina.numero,
" . MaterialService::getDescricaoCompletaRaw(
'material',
'apresentacao',
'apresentacao_unidade_medida'
) . " as material
"))
->orderBy('pagina.numero')
->get();
}
public static function getLinhas($pagina)
{
$linhas = self::getQueryLinhas($pagina)->get();
$linhas->each(function ($linha) {
$linha->entrada = null;
$linha->saida = null;
$linha->perda = null;
$linha->{$linha->movimento} = $linha->quantidade;
unset($linha->movimento, $linha->quantidade);
});
$hasEstorno = DB::table('farmacia_livro_pagina_linha as linha')
->leftJoin('almoxarifado_entrada_item as entrada_item', 'linha.entrada_item_id', 'entrada_item.id')
->leftJoin('almoxarifado_saida_item as saida_item', 'linha.saida_item_id', 'saida_item.id')
->where('linha.pagina_id', $pagina->id)
->where(function ($query) {
$query->whereNotNull('entrada_item.estornado_em')
->orWhereNotNull('saida_item.estornado_em');
})
->select('linha.id')
->limit(1)
->get()
->isNotEmpty();
$transferenciasData = self::getTransferenciasData($pagina);
return [
'estorno_message' => $hasEstorno
? "A página contém menos registros, pois uma ou mais movimentações foram estornadas."
: null,
'transferido_de' => $transferenciasData->de,
'transferido_para' => $transferenciasData->para,
'linhas' => $linhas,
];
}
private static function getQueryLinhas($pagina)
{
$isTalidomida = $pagina->livro->livro_registro_id === LivroRegistroEnum::TALIDOMIDA;
$isTalidomidaABase = $isTalidomida
&& $pagina->livro->livro_registro_tipo_id === LivroRegistroTipoEnum::A_BASE;
$isTalidomidaEspecifico = $isTalidomida
&& $pagina->livro->livro_registro_tipo_id === LivroRegistroTipoEnum::ESPECIFICO;
$historicoEntrada = "(
CASE entrada.tipo_id
WHEN " . EntradaTipoEnum::REQUISICAO . " THEN (
SELECT CONCAT(
'ENTRADA - ',
u.nome_fantasia,
' (Local de atendimento: ',
COALESCE(i.nome, u.nome_fantasia),
' - ',
'Setor: ',
s.nome,
')'
)
FROM almoxarifado_requisicao_resposta_item_entrada_item riei
JOIN almoxarifado_requisicao_resposta_item ri
ON ri.id = riei.requisicao_resposta_item_id
JOIN cadastro_centro_custo as cc
ON cc.id = ri.created_centro_custo_id
JOIN cadastro_setor s
ON s.id = cc.setor_id
JOIN cadastro_infraestrutura as i
ON i.id = cc.infraestrutura_id
JOIN cadastro_unidade as u
ON u.id = i.unidade_id
WHERE riei.entrada_item_id = entrada_item.id
)
WHEN " . EntradaTipoEnum::TRANSFERENCIA . " THEN (
SELECT CONCAT(
'ENTRADA - ',
u.nome_fantasia,
' (Local de atendimento: ',
COALESCE(i.nome, u.nome_fantasia),
' - ',
'Setor: ',
s.nome,
')'
)
FROM cadastro_centro_custo as cc
JOIN cadastro_setor s ON s.id = cc.setor_id
JOIN cadastro_infraestrutura as i ON i.id = cc.infraestrutura_id
JOIN cadastro_unidade as u ON u.id = i.unidade_id
WHERE cc.id = entrada.created_centro_custo_id
)
WHEN " . EntradaTipoEnum::INVENTARIO . " THEN
'ENTRADA - AJUSTE DE ESTOQUE'
ELSE
CONCAT(
'ENTRADA',
IF(fabricante.nome IS NULL, '', CONCAT(' - ', fabricante.nome)),
IF(documento.numero IS NULL, '', CONCAT(' - ', documento_tipo.nome, ': ', documento.numero)),
IF(entrada_item.lote IS NULL, '', CONCAT(' - Lote: ', entrada_item.lote)),
IF(entrada_item.validade_lote IS NULL, '', CONCAT(' - Validade: ',
DATE_FORMAT(entrada_item.validade_lote, '%d/%m/%Y')))
)
END
)";
$historicoSaida = "(
CASE saida.tipo_id
WHEN " . SaidaTipoEnum::BAIXA . " THEN
IF(motivo_baixa.tipo_id IN (
" . MotivoBaixaTipoEnum::QUEBRADO . ",
" . MotivoBaixaTipoEnum::VENCIDO . "
), 'BAIXA POR PERDA', 'BAIXA')
WHEN " . SaidaTipoEnum::DISPENSACAO_FARMACIA . " THEN (
SELECT CONCAT(c.nome, ' - ', LPAD(COALESCE(p.prontuario, 0), 6, '0'))
FROM farmacia_receita_item_entrega rie
JOIN farmacia_receita_item ri ON ri.id = rie.receita_item_id
JOIN farmacia_receita r ON r.id = ri.receita_id
JOIN cadastro_paciente p ON p.id = r.paciente_id
JOIN cadastro_cidadao c ON c.id = p.cidadao_id
WHERE rie.saida_item_id = saida_item.id
)
WHEN " . SaidaTipoEnum::INVENTARIO . " THEN
'SAÍDA - AJUSTE DE ESTOQUE'
ELSE
'SAÍDA'
END
)";
return DB::table('farmacia_livro_pagina_linha as linha')
->join('almoxarifado_entrada_item as entrada_item', 'linha.entrada_item_id', 'entrada_item.id')
->join('almoxarifado_entrada as entrada', 'entrada_item.entrada_id', 'entrada.id')
->leftJoin('almoxarifado_entrada_documento as documento', 'entrada.id', 'documento.entrada_id')
->leftJoin('almoxarifado_documento_tipo as documento_tipo', 'documento.tipo_id', 'documento_tipo.id')
->leftJoin('cadastro_fabricante as fabricante', 'entrada_item.fabricante_id', 'fabricante.id')
->where('linha.pagina_id', $pagina->id)
->whereNull('entrada_item.estornado_em')
->select(DB::raw("
'entrada' as movimento,
entrada.created_at as data_movimentacao,
" . ($isTalidomidaABase ? '' : "{$historicoEntrada} as historico,") . "
entrada_item.quantidade,
(
SELECT SUM(
almoxarifado_entrada_item.quantidade
-
COALESCE((
SELECT SUM(almoxarifado_saida_item_entrada_item.quantidade_saida)
FROM almoxarifado_saida_item_entrada_item
JOIN almoxarifado_saida_item
ON almoxarifado_saida_item.id = almoxarifado_saida_item_entrada_item.saida_item_id
JOIN almoxarifado_saida
ON almoxarifado_saida.id = almoxarifado_saida_item.saida_id
WHERE almoxarifado_saida_item_entrada_item.entrada_item_id = almoxarifado_entrada_item.id
AND almoxarifado_saida.finalizada = 1
AND almoxarifado_saida.centro_custo_id = {$pagina->livro->created_centro_custo_id}
AND almoxarifado_saida_item.estornado_em IS NULL
AND (
almoxarifado_saida.created_at <= entrada.created_at
OR
almoxarifado_saida_item.id = COALESCE(almoxarifado_entrada_item.saida_item_estornado_id, 0)
)
), 0)
)
FROM almoxarifado_entrada_item
JOIN almoxarifado_entrada
ON almoxarifado_entrada.id = almoxarifado_entrada_item.entrada_id
WHERE almoxarifado_entrada.finalizada = 1
AND almoxarifado_entrada.centro_custo_id = {$pagina->livro->created_centro_custo_id}
AND almoxarifado_entrada_item.material_id = {$pagina->material_id}
AND almoxarifado_entrada.created_at <= entrada.created_at
) as estoque
" . (
$isTalidomidaEspecifico
? ", NULL as numero_registro,
documento.numero as numero_documento,
NULL as observacoes"
: ""
) . "
" . (
$isTalidomidaABase
? ", documento.numero as numero_documento,
NULL as numero_notificacao,
NULL as paciente_data_nascimento,
NULL as paciente_sexo,
NULL as medico_nome,
NULL as medico_crm,
NULL as cid_nome,
NULL as cid_codigo,
NULL as numero_registro"
: ""
) . "
"))
->unionAll(DB::table('farmacia_livro_pagina_linha as linha')
->join('almoxarifado_saida_item as saida_item', 'linha.saida_item_id', 'saida_item.id')
->join('almoxarifado_saida as saida', 'saida_item.saida_id', 'saida.id')
->leftJoin('almoxarifado_saida_baixa as baixa', 'baixa.saida_id', 'saida.id')
->leftJoin('almoxarifado_motivo_baixa as motivo_baixa', 'motivo_baixa.id', 'baixa.motivo_id')
->where('linha.pagina_id', $pagina->id)
->whereNull('saida_item.estornado_em')
->select(DB::raw("
IF(saida.tipo_id = " . SaidaTipoEnum::BAIXA . " AND motivo_baixa.tipo_id IN (
" . MotivoBaixaTipoEnum::QUEBRADO . ",
" . MotivoBaixaTipoEnum::VENCIDO . "
), 'perda', 'saida') as movimento,
saida.created_at as data_movimentacao,
" . ($isTalidomidaABase ? '' : "{$historicoSaida} as historico,") . "
(
SELECT SUM(siei.quantidade_saida)
FROM almoxarifado_saida_item_entrada_item as siei
WHERE siei.saida_item_id = saida_item.id
) as quantidade,
(
SELECT SUM(
almoxarifado_entrada_item.quantidade
-
COALESCE((
SELECT SUM(almoxarifado_saida_item_entrada_item.quantidade_saida)
FROM almoxarifado_saida_item_entrada_item
JOIN almoxarifado_saida_item
ON almoxarifado_saida_item.id = almoxarifado_saida_item_entrada_item.saida_item_id
JOIN almoxarifado_saida
ON almoxarifado_saida.id = almoxarifado_saida_item.saida_id
WHERE almoxarifado_saida_item_entrada_item.entrada_item_id = almoxarifado_entrada_item.id
AND almoxarifado_saida.finalizada = 1
AND almoxarifado_saida.centro_custo_id = {$pagina->livro->created_centro_custo_id}
AND (
almoxarifado_saida.created_at <= saida.created_at
OR
almoxarifado_saida_item.id = COALESCE(almoxarifado_entrada_item.saida_item_estornado_id, 0)
)
), 0)
)
FROM almoxarifado_entrada_item
JOIN almoxarifado_entrada
ON almoxarifado_entrada.id = almoxarifado_entrada_item.entrada_id
WHERE almoxarifado_entrada.finalizada = 1
AND almoxarifado_entrada.centro_custo_id = {$pagina->livro->created_centro_custo_id}
AND almoxarifado_entrada_item.material_id = {$pagina->material_id}
AND almoxarifado_entrada.created_at <= saida.created_at
) as estoque
" . (
$isTalidomidaEspecifico
? ", (
SELECT u.registro_numero
FROM farmacia_receita_item_entrega rie
JOIN cadastro_lotacao l ON l.id = rie.updated_lotacao_id
JOIN cadastro_usuario u ON u.id = l.usuario_id
WHERE rie.saida_item_id = saida_item.id
AND u.registro_tipo_id = " . TipoRegistroUsuario::FARMACIA . "
) as numero_registro,
NULL as numero_documento,
(
SELECT c.nome
FROM farmacia_receita_item_entrega rie
JOIN farmacia_receita_item ri ON ri.id = rie.receita_item_id
JOIN farmacia_receita r ON r.id = ri.receita_id
JOIN cadastro_usuario u ON u.id = r.usuario_id
JOIN cadastro_cidadao c ON c.id = u.cidadao_id
WHERE rie.saida_item_id = saida_item.id
) as observacoes"
: ""
) . "
" . (
$isTalidomidaABase
? ", NULL as numero_documento,
(
SELECT r.numero_notificacao
FROM farmacia_receita_item_entrega rie
JOIN farmacia_receita_item ri ON ri.id = rie.receita_item_id
JOIN farmacia_receita r ON r.id = ri.receita_id
WHERE rie.saida_item_id = saida_item.id
) as numero_notificacao,
(
SELECT c.data_nascimento
FROM farmacia_receita_item_entrega rie
JOIN farmacia_receita_item ri ON ri.id = rie.receita_item_id
JOIN farmacia_receita r ON r.id = ri.receita_id
JOIN cadastro_paciente p ON p.id = r.paciente_id
JOIN cadastro_cidadao c ON c.id = p.cidadao_id
WHERE rie.saida_item_id = saida_item.id
) as paciente_data_nascimento,
(
SELECT s.descricao
FROM farmacia_receita_item_entrega rie
JOIN farmacia_receita_item ri ON ri.id = rie.receita_item_id
JOIN farmacia_receita r ON r.id = ri.receita_id
JOIN cadastro_paciente p ON p.id = r.paciente_id
JOIN cadastro_cidadao c ON c.id = p.cidadao_id
JOIN cadastro_sexo s ON s.id = c.sexo_id
WHERE rie.saida_item_id = saida_item.id
) as paciente_sexo,
(
SELECT c.nome
FROM farmacia_receita_item_entrega rie
JOIN farmacia_receita_item ri ON ri.id = rie.receita_item_id
JOIN farmacia_receita r ON r.id = ri.receita_id
JOIN cadastro_usuario u ON u.id = r.usuario_id
JOIN cadastro_cidadao c ON c.id = u.cidadao_id
WHERE rie.saida_item_id = saida_item.id
) as medico_nome,
(
SELECT u.registro_numero
FROM farmacia_receita_item_entrega rie
JOIN farmacia_receita_item ri ON ri.id = rie.receita_item_id
JOIN farmacia_receita r ON r.id = ri.receita_id
JOIN cadastro_usuario u ON u.id = r.usuario_id
WHERE rie.saida_item_id = saida_item.id
AND u.registro_tipo_id = " . TipoRegistroUsuario::MEDICINA . "
) as medico_crm,
(
SELECT c.nome
FROM farmacia_receita_item_entrega rie
JOIN farmacia_receita_item ri ON ri.id = rie.receita_item_id
JOIN farmacia_receita r ON r.id = ri.receita_id
JOIN sigtap.cid c ON c.id = r.cid_id
WHERE rie.saida_item_id = saida_item.id
) as cid_nome,
(
SELECT c.codigo
FROM farmacia_receita_item_entrega rie
JOIN farmacia_receita_item ri ON ri.id = rie.receita_item_id
JOIN farmacia_receita r ON r.id = ri.receita_id
JOIN sigtap.cid c ON c.id = r.cid_id
WHERE rie.saida_item_id = saida_item.id
) as cid_codigo,
(
SELECT u.registro_numero
FROM farmacia_receita_item_entrega rie
JOIN cadastro_lotacao l ON l.id = rie.updated_lotacao_id
JOIN cadastro_usuario u ON u.id = l.usuario_id
WHERE rie.saida_item_id = saida_item.id
AND u.registro_tipo_id = " . TipoRegistroUsuario::FARMACIA . "
) as numero_registro"
: ""
) . "
"))
)
->orderBy('data_movimentacao');
}
private static function getTransferenciasData($pagina)
{
$de = null;
$para = null;
if (self::isPrimeraPaginaMaterial($pagina)) {
$de = self::getMessageUltimaPaginaLivroAnterior($pagina);
}
if (self::isUltimaPaginaMaterial($pagina)) {
$para = self::getMessagePrimeiraPaginaLivroPosterior($pagina);
}
return (object) compact('de', 'para');
}
private static function isPrimeraPaginaMaterial($pagina)
{
$primeiraPaginaLivro = $pagina->livro()->first(['id'])->paginas()
->where('material_id', $pagina->material_id)
->orderBy('numero')
->first(['numero']);
return $pagina->numero === $primeiraPaginaLivro->numero;
}
private static function getMessageUltimaPaginaLivroAnterior($pagina)
{
$livro = $pagina->livro()->first(['livro_registro_id', 'livro_registro_tipo_id']);
$isTalidomida = $livro->livro_registro_id === LivroRegistroEnum::TALIDOMIDA;
$ultimaPaginaLivroAnterior = LivroPagina::where('created_centro_custo_id', $pagina->created_centro_custo_id)
->where('livro_id', '<', $pagina->livro_id)
->where('material_id', $pagina->material_id)
->when($isTalidomida, function ($query) use ($livro) {
$query->whereHas('livro', function ($query) use ($livro) {
$query->where('livro_registro_tipo_id', $livro->livro_registro_tipo_id);
});
})
->orderByDesc('livro_id')
->orderByDesc('numero')
->first(['id', 'livro_id', 'material_id', 'numero']);
if (!is_null($ultimaPaginaLivroAnterior)) {
$getEstoque = function () use ($ultimaPaginaLivroAnterior) {
$centroCustoId = $ultimaPaginaLivroAnterior->livro()
->first(['created_centro_custo_id'])
->created_centro_custo_id;
return DB::table('farmacia_livro_pagina_linha as linha')
->leftJoin('almoxarifado_entrada_item as entrada_item', 'linha.entrada_item_id', 'entrada_item.id')
->leftJoin('almoxarifado_entrada as entrada', 'entrada_item.entrada_id', 'entrada.id')
->leftJoin('almoxarifado_saida_item as saida_item', 'linha.saida_item_id', 'saida_item.id')
->leftJoin('almoxarifado_saida as saida', 'saida_item.saida_id', 'saida.id')
->where('linha.pagina_id', $ultimaPaginaLivroAnterior->id)
->select(DB::raw("(
SELECT SUM(
almoxarifado_entrada_item.quantidade
-
COALESCE((
SELECT SUM(almoxarifado_saida_item_entrada_item.quantidade_saida)
FROM almoxarifado_saida_item_entrada_item
JOIN almoxarifado_saida_item
ON almoxarifado_saida_item.id = almoxarifado_saida_item_entrada_item.saida_item_id
JOIN almoxarifado_saida
ON almoxarifado_saida.id = almoxarifado_saida_item.saida_id
WHERE almoxarifado_saida_item_entrada_item.entrada_item_id = almoxarifado_entrada_item.id
AND almoxarifado_saida.finalizada = 1
AND almoxarifado_saida.centro_custo_id = {$centroCustoId}
AND almoxarifado_saida_item.estornado_em IS NULL
AND (
almoxarifado_saida.created_at <= COALESCE(entrada.created_at, saida.created_at)
OR
almoxarifado_saida_item.id = COALESCE(almoxarifado_entrada_item.saida_item_estornado_id, 0)
)
), 0)
)
FROM almoxarifado_entrada_item
JOIN almoxarifado_entrada
ON almoxarifado_entrada.id = almoxarifado_entrada_item.entrada_id
WHERE almoxarifado_entrada.finalizada = 1
AND almoxarifado_entrada.centro_custo_id = {$centroCustoId}
AND almoxarifado_entrada_item.material_id = {$ultimaPaginaLivroAnterior->material_id}
AND almoxarifado_entrada.created_at <= COALESCE(entrada.created_at, saida.created_at)
) as estoque"))
->orderByDesc('linha.id')
->limit(1)
->first()
->estoque;
};
$primeiraLinhaLivroNovo = $pagina->linhas()->orderBy('id')->first(['created_at']);
$data = [
'data' => (new Carbon($primeiraLinhaLivroNovo->created_at))->format('d/m/Y'),
'livro' => $ultimaPaginaLivroAnterior->livro()->first(['numero'])->numero,
'pagina' => $ultimaPaginaLivroAnterior->numero,
'estoque' => $getEstoque(),
];
return "Transferido do livro nº {$data['livro']}, página {$data['pagina']}, "
. "na data {$data['data']}. Estoque {$data['estoque']}.";
}
return null;
}
private static function isUltimaPaginaMaterial($pagina)
{
$ultimaPaginaLivro = $pagina->livro()->first(['id'])->paginas()
->where('material_id', $pagina->material_id)
->orderByDesc('numero')
->first(['numero']);
return $pagina->numero === $ultimaPaginaLivro->numero;
}
private static function getMessagePrimeiraPaginaLivroPosterior($pagina)
{
$livro = $pagina->livro()->first(['livro_registro_id', 'livro_registro_tipo_id']);
$isTalidomida = $livro->livro_registro_id === LivroRegistroEnum::TALIDOMIDA;
$primeiraPaginaLivroPosterior = LivroPagina::where('created_centro_custo_id', $pagina->created_centro_custo_id)
->where('livro_id', '>', $pagina->livro_id)
->where('material_id', $pagina->material_id)
->when($isTalidomida, function ($query) use ($livro) {
$query->whereHas('livro', function ($query) use ($livro) {
$query->where('livro_registro_tipo_id', $livro->livro_registro_tipo_id);
});
})
->orderBy('livro_id')
->orderBy('numero')
->first(['id', 'livro_id', 'numero']);
if (!is_null($primeiraPaginaLivroPosterior)) {
$primeiraLinhaLivroNovo = $primeiraPaginaLivroPosterior->linhas()->orderBy('id')->first(['created_at']);
$data = [
'data' => (new Carbon($primeiraLinhaLivroNovo->created_at))->format('d/m/Y'),
'livro' => $primeiraPaginaLivroPosterior->livro()->first(['numero'])->numero,
'pagina' => $primeiraPaginaLivroPosterior->numero,
];
return "Transferido para o livro nº {$data['livro']}, página {$data['pagina']}, "
. "na data {$data['data']}.";
}
return null;
}
}