1368 lines
48 KiB
PHP
1368 lines
48 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Almoxarifado;
|
|
|
|
use App\Enums\Almoxarifado\EntradaTipo;
|
|
use App\Enums\Almoxarifado\ReservaMotivo;
|
|
use App\Enums\Almoxarifado\SaidaTipo;
|
|
use App\Exceptions\Almoxarifado\EstoqueIndisponivelException;
|
|
use App\Exceptions\Almoxarifado\EstornoNaoPermitidoException;
|
|
use App\Models\Almoxarifado\Entrada;
|
|
use App\Models\Almoxarifado\EntradaDocumento;
|
|
use App\Models\Almoxarifado\EntradaItem;
|
|
use App\Models\Almoxarifado\Reserva;
|
|
use App\Models\Almoxarifado\ReservaItem;
|
|
use App\Models\Almoxarifado\Saida;
|
|
use App\Models\Almoxarifado\SaidaBaixa;
|
|
use App\Models\Almoxarifado\SaidaItem;
|
|
use App\Models\Almoxarifado\SaidaRequisicao;
|
|
use App\Models\Almoxarifado\SaidaTransferencia;
|
|
use App\Models\Cadastro\Material\Material;
|
|
use App\Services\Service;
|
|
use Carbon\Carbon;
|
|
use Exception;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
|
|
class AlmoxarifadoService extends Service
|
|
{
|
|
private const MARIADB_QUANTIDADE_INSUFICIENTE_CODE = 12340;
|
|
|
|
public static function saveTransferencia($data, $id = null)
|
|
{
|
|
$saida = self::getSaidaInstance(SaidaTipo::TRANSFERENCIA, $id);
|
|
$estavaFinalizada = $saida->finalizada ?? false;
|
|
$finalizouAgora = !$estavaFinalizada && $data['finalizada'];
|
|
|
|
$itensData = collect($data['itens']);
|
|
$itensDataNovos = $itensData->filter(fn ($i) => is_null($i['id']));
|
|
$itensDataSalvos = $itensData->filter(fn ($i) => !is_null($i['id']));
|
|
$itensDataEstornados = $itensData->filter(fn ($i) => $i['estornado']);
|
|
|
|
self::deleteSaidaItensExcluidos($saida, $itensDataSalvos);
|
|
self::saveEstornoDeTransferencia($itensDataEstornados);
|
|
self::validarEstoqueSaidaItens($saida, $itensDataNovos, $finalizouAgora);
|
|
|
|
if ($finalizouAgora) {
|
|
|
|
$saida->finalizada = true;
|
|
$saida->finalizada_em = Carbon::now();
|
|
|
|
self::updateSaidaItens($saida);
|
|
}
|
|
|
|
$saida->save();
|
|
|
|
self::saveSaidaItensNovos($saida, $itensDataNovos);
|
|
|
|
if (!$estavaFinalizada) {
|
|
|
|
self::saveSaidaTransferencia($saida, $data['destino']);
|
|
}
|
|
|
|
$saida->refresh();
|
|
|
|
return $saida;
|
|
}
|
|
|
|
public static function saveEstornoDeTransferencia($saidaItensDataEstornados)
|
|
{
|
|
$entradaItensDataEstornados = collect([]);
|
|
|
|
$saidaItensDataEstornados->each(function ($item) use ($entradaItensDataEstornados) {
|
|
|
|
$saidaItem = SaidaItem::findOrFail($item['id']);
|
|
|
|
$saidaItem->entradaItensTransferencia->each(function ($entradaItem) use ($entradaItensDataEstornados) {
|
|
|
|
$entradaItensDataEstornados->push(['id' => $entradaItem->id]);
|
|
});
|
|
});
|
|
|
|
self::validarEstornoEntradaItens($entradaItensDataEstornados);
|
|
self::estornarEntradaItens($entradaItensDataEstornados);
|
|
self::estornarSaidaItens($saidaItensDataEstornados);
|
|
}
|
|
|
|
private static function saveSaidaTransferencia($saida, $data)
|
|
{
|
|
$transferencia = $saida->transferencia ?? new SaidaTransferencia();
|
|
|
|
$transferencia->centro_custo_id = $data['centro_custo_id'];
|
|
$transferencia->usuario_id = $data['usuario_id'];
|
|
|
|
$saida->transferencia()->save($transferencia);
|
|
}
|
|
|
|
public static function saveEntradasDeTransferencia($saida)
|
|
{
|
|
$saida->itens->filter(fn ($i) => !$i->estornado && $i->entradaItensTransferencia->isEmpty())->each(function ($saidaItem) {
|
|
|
|
$saidaItem->entradaItens->each(function ($entradaItemOrigem) use ($saidaItem) {
|
|
|
|
$centroCustoDestinoId = $saidaItem->saida->transferencia->centro_custo_id;
|
|
|
|
$entradaDestino = Entrada::create([
|
|
'centro_custo_id' => $centroCustoDestinoId,
|
|
'tipo_id' => EntradaTipo::TRANSFERENCIA,
|
|
'finalizada' => true,
|
|
'finalizada_em' => Carbon::now(),
|
|
]);
|
|
|
|
$quantidadeEntrada = $entradaItemOrigem->pivotSaidaItemEntradaItem->quantidade_saida;
|
|
|
|
$entradaItemDestinoSaved = self::saveEntradaItem($entradaDestino, [
|
|
'material_id' => $entradaItemOrigem->material_id,
|
|
'quantidade' => $quantidadeEntrada,
|
|
'lote' => $entradaItemOrigem->lote,
|
|
'validade_lote' => $entradaItemOrigem->validade_lote,
|
|
'valor_unitario' => $entradaItemOrigem->valor_unitario,
|
|
'valor_total' => $entradaItemOrigem->valor_unitario * $quantidadeEntrada,
|
|
'numero_empenho' => $entradaItemOrigem->numero_empenho,
|
|
'despesas' => $entradaItemOrigem->despesas,
|
|
'numero_autorizacao_fornecimento' => $entradaItemOrigem->numero_autorizacao_fornecimento,
|
|
'fabricante_id' => $entradaItemOrigem->fabricante_id,
|
|
'codigo_barras' => $entradaItemOrigem->codigo_barras,
|
|
]);
|
|
|
|
$saidaItem->entradaItensTransferencia()->attach($entradaItemDestinoSaved->id);
|
|
|
|
$entradaDestino->refresh();
|
|
});
|
|
});
|
|
}
|
|
|
|
public static function saveAjusteDeEstoqueDeInventario($itens)
|
|
{
|
|
$itens->each(function ($item) {
|
|
|
|
$material = Material::findOrFail($item['material_id']);
|
|
$estoque = self::getEstoque($material->id);
|
|
|
|
$entradas = collect($estoque['por_entrada'])
|
|
->where('lote', $item['lote'])
|
|
->when($item['validade_lote'], fn ($q, $v) => $q->where('validade_lote', $v->format('d/m/Y')));
|
|
|
|
$estoqueDoLote = $entradas->sum('estoque');
|
|
|
|
if ($item['contagem'] > $estoqueDoLote) {
|
|
|
|
self::saveEntradaDeInventario($item['contagem'] - $estoqueDoLote, [
|
|
'material_id' => $material->id,
|
|
'integracao_bnafar_medicamento_id' => $item['integracao_bnafar_medicamento_id'],
|
|
'lote' => $item['lote'],
|
|
'validade_lote' => $item['validade_lote']
|
|
]);
|
|
}
|
|
else if ($item['contagem'] < $estoqueDoLote) {
|
|
|
|
self::saveSaidaDeInventario($estoqueDoLote - $item['contagem'], [
|
|
'material_id' => $material->id,
|
|
'lote' => $item['lote'],
|
|
'validade_lote' => $item['validade_lote']
|
|
], true);
|
|
}
|
|
});
|
|
}
|
|
|
|
public static function saveEntradaInicial($entradasData)
|
|
{
|
|
$centroCustoIdLogado = self::getCurrentCentroCusto()->id;
|
|
|
|
$entradasData->each(function ($entradaData) use ($centroCustoIdLogado) {
|
|
|
|
$entrada = new Entrada([
|
|
'centro_custo_id' => $centroCustoIdLogado,
|
|
'tipo_id' => EntradaTipo::INICIAL,
|
|
'finalizada' => true,
|
|
'finalizada_em' => Carbon::now(),
|
|
]);
|
|
|
|
$entrada->save();
|
|
|
|
if (isset($entradaData['fornecedor_id'])) {
|
|
|
|
self::saveEntradaDocumento($entrada, [
|
|
'fornecedor_id' => $entradaData['fornecedor_id'],
|
|
'tipo_id' => null,
|
|
'numero_pedido' => null,
|
|
'numero' => null,
|
|
'data' => null,
|
|
]);
|
|
}
|
|
|
|
self::saveEntradaItem($entrada, [
|
|
'material_id' => $entradaData['material_id'],
|
|
'integracao_bnafar_medicamento_id' => $entradaData['integracao_bnafar_medicamento_id'] ?? null,
|
|
'quantidade' => $entradaData['quantidade'],
|
|
'lote' => $entradaData['lote'],
|
|
'validade_lote' => $entradaData['validade_lote'],
|
|
'valor_unitario' => $entradaData['valor_unitario'],
|
|
'valor_total' => $entradaData['valor_total'],
|
|
'numero_empenho' => null,
|
|
'despesas' => null,
|
|
'numero_autorizacao_fornecimento' => null,
|
|
'fabricante_id' => $entradaData['fabricante_id'],
|
|
'codigo_barras' => $entradaData['codigo_barras'],
|
|
]);
|
|
|
|
$entrada->refresh();
|
|
});
|
|
}
|
|
|
|
private static function saveEntradaDeInventario($quantidadeDivergente, $data)
|
|
{
|
|
$entrada = new Entrada([
|
|
'centro_custo_id' => self::getCurrentCentroCusto()->id,
|
|
'tipo_id' => EntradaTipo::INVENTARIO,
|
|
'finalizada' => true,
|
|
'finalizada_em' => Carbon::now(),
|
|
]);
|
|
|
|
$entrada->save();
|
|
|
|
$valorUnitario = self::getUltimoValorUnitarioDoMaterial(
|
|
$data['material_id'],
|
|
$data['lote'] ?? null,
|
|
$data['validade_lote'] ?? null
|
|
);
|
|
|
|
$valorTotal = $quantidadeDivergente * $valorUnitario;
|
|
|
|
self::saveEntradaItem($entrada, [
|
|
'material_id' => $data['material_id'],
|
|
'integracao_bnafar_medicamento_id' => $data['integracao_bnafar_medicamento_id'],
|
|
'quantidade' => $quantidadeDivergente,
|
|
'lote' => $data['lote'] ?? null,
|
|
'validade_lote' => $data['validade_lote'] ?? null,
|
|
'valor_unitario' => $valorUnitario,
|
|
'valor_total' => $valorTotal,
|
|
'numero_empenho' => null,
|
|
'despesas' => 0,
|
|
'numero_autorizacao_fornecimento' => null,
|
|
'fabricante_id' => null,
|
|
'codigo_barras' => null,
|
|
]);
|
|
|
|
$entrada->refresh();
|
|
}
|
|
|
|
private static function getUltimoValorUnitarioDoMaterial($materialId, $lote = null, $validadeLote = null)
|
|
{
|
|
return (float) (EntradaItem::where('material_id', $materialId)
|
|
->when($lote, fn ($q, $l) => $q->where('lote', $l))
|
|
->when($validadeLote, fn ($q, $v) => $q->whereDate('validade_lote', $v))
|
|
->whereHas('entrada', function ($query) {
|
|
|
|
$query->where('centro_custo_id', self::getCurrentCentroCusto()->id);
|
|
})
|
|
->where('valor_unitario', '>', 0)
|
|
->orderByDesc('id')
|
|
->first()->valor_unitario ?? 0);
|
|
}
|
|
|
|
public static function saveEntradasDeRequisicao($itensData)
|
|
{
|
|
$centroCustoIdLogado = self::getCurrentCentroCusto()->id;
|
|
$entradaItensSaved = collect();
|
|
|
|
foreach ($itensData as $data) {
|
|
|
|
$saidaItem = SaidaItem::findOrFail($data['saida_item_id']);
|
|
$itens = $saidaItem->entradaItens()->orderBy('id')->get();
|
|
|
|
if ($data['possui_entrada_com_lote']) {
|
|
|
|
$itens = $itens->filter(fn ($i) => !is_null($data['entrada_itens']
|
|
->firstWhere('entrada_item_origem_id', $i->id)));
|
|
}
|
|
else {
|
|
|
|
$data['entrada_itens'] = collect();
|
|
|
|
$quantidade = $data['quantidade'];
|
|
$i = 0;
|
|
|
|
while ($quantidade > 0) {
|
|
|
|
$entradaItem = $itens[$i];
|
|
$quantidadeSaida = $entradaItem->pivotSaidaItemEntradaItem->quantidade_saida;
|
|
|
|
if ($quantidade > $quantidadeSaida) {
|
|
|
|
$data['entrada_itens']->push([
|
|
'entrada_item_origem_id' => $entradaItem->id,
|
|
'quantidade' => $quantidadeSaida,
|
|
]);
|
|
|
|
$quantidade -= $quantidadeSaida;
|
|
}
|
|
else {
|
|
|
|
$data['entrada_itens']->push([
|
|
'entrada_item_origem_id' => $entradaItem->id,
|
|
'quantidade' => $quantidade,
|
|
]);
|
|
|
|
$quantidade = 0;
|
|
}
|
|
|
|
$i++;
|
|
}
|
|
}
|
|
|
|
$itens->each(function ($entradaItem) use ($data, $entradaItensSaved, $centroCustoIdLogado) {
|
|
|
|
$entrada = Entrada::create([
|
|
'centro_custo_id' => $centroCustoIdLogado,
|
|
'tipo_id' => EntradaTipo::REQUISICAO,
|
|
'finalizada' => true,
|
|
'finalizada_em' => Carbon::now(),
|
|
]);
|
|
|
|
$entradaItemData = $data['entrada_itens']->firstWhere('entrada_item_origem_id', $entradaItem->id);
|
|
|
|
$entradaItemSaved = self::saveEntradaItem($entrada, [
|
|
'material_id' => $entradaItem->material_id,
|
|
'quantidade' => $entradaItemData['quantidade'],
|
|
'lote' => $entradaItemData['lote'] ?? null,
|
|
'validade_lote' => $entradaItemData['validade_lote'] ?? null,
|
|
'valor_unitario' => $entradaItem->valor_unitario,
|
|
'valor_total' => $entradaItem->valor_unitario * $entradaItemData['quantidade'],
|
|
'numero_empenho' => $entradaItem->numero_empenho,
|
|
'despesas' => $entradaItem->despesas,
|
|
'numero_autorizacao_fornecimento' => $entradaItem->numero_autorizacao_fornecimento,
|
|
'fabricante_id' => $entradaItem->fabricante_id,
|
|
'codigo_barras' => $entradaItem->codigo_barras,
|
|
]);
|
|
|
|
$entradaItensSaved->push([
|
|
'saida_item_id' => $data['saida_item_id'],
|
|
'entrada_item_id' => $entradaItemSaved->id,
|
|
]);
|
|
|
|
$entrada->refresh();
|
|
});
|
|
}
|
|
|
|
return $entradaItensSaved;
|
|
}
|
|
|
|
public static function saveEntradaManual($data, $id = null)
|
|
{
|
|
$entrada = self::getEntradaInstance(EntradaTipo::MANUAL, $id);
|
|
$estavaFinalizada = $entrada->finalizada ?? false;
|
|
$finalizouAgora = !$estavaFinalizada && $data['finalizada'];
|
|
|
|
$itensData = collect($data['itens']);
|
|
$itensDataSalvos = $itensData->filter(fn ($i) => !is_null($i['id']));
|
|
$itensDataEstornados = $itensData->filter(fn ($i) => $i['estornado']);
|
|
$itensDataNaoEstornados = $itensData->filter(fn ($i) => !$i['estornado']);
|
|
|
|
self::validarEstornoEntradaItens($itensDataEstornados);
|
|
self::deleteEntradaItensExcluidos($entrada, $itensDataSalvos);
|
|
self::estornarEntradaItens($itensDataEstornados);
|
|
|
|
if ($finalizouAgora) {
|
|
|
|
$entrada->finalizada = true;
|
|
$entrada->finalizada_em = Carbon::now();
|
|
}
|
|
|
|
$entrada->save();
|
|
|
|
self::saveEntradaItens($entrada, $itensDataNaoEstornados);
|
|
self::saveEntradaDocumento($entrada, $data['documento']);
|
|
|
|
$entrada->refresh();
|
|
|
|
return $entrada;
|
|
}
|
|
|
|
public static function saveEntradaManualWebService($centroCustoId, $data)
|
|
{
|
|
$entrada = Entrada::create([
|
|
'tipo_id' => EntradaTipo::MANUAL,
|
|
'centro_custo_id' => $centroCustoId,
|
|
'finalizada' => true,
|
|
'finalizada_em' => Carbon::now(),
|
|
]);
|
|
|
|
self::saveEntradaItens($entrada, collect($data['itens']));
|
|
self::saveEntradaDocumento($entrada, $data['documento']);
|
|
|
|
return $entrada->fresh();
|
|
}
|
|
|
|
private static function getEntradaInstance($tipoId, $id = null)
|
|
{
|
|
return is_null($id)
|
|
? new Entrada(['tipo_id' => $tipoId, 'centro_custo_id' => self::getCurrentCentroCusto()->id])
|
|
: Entrada::where('centro_custo_id', self::getCurrentCentroCusto()->id)
|
|
->where('tipo_id', $tipoId)->findOrFail($id);
|
|
}
|
|
|
|
private static function validarEstornoEntradaItens($itensDataEstornados)
|
|
{
|
|
$materiaisComSaida = collect();
|
|
|
|
$itensDataEstornados->each(function ($itemData) use ($materiaisComSaida) {
|
|
|
|
$item = EntradaItem::findOrFail($itemData['id']);
|
|
|
|
if (!$item->estornado && $item->has_saidas_finalizadas) {
|
|
|
|
$materiaisComSaida->push($item->material->descricao);
|
|
}
|
|
});
|
|
|
|
if ($materiaisComSaida->isNotEmpty()) {
|
|
|
|
$materiais = $materiaisComSaida->map(fn ($m) => ['descricao' => $m]);
|
|
|
|
throw new EstornoNaoPermitidoException(json_encode($materiais));
|
|
}
|
|
}
|
|
|
|
private static function deleteEntradaItensExcluidos($entrada, $itensDataSalvos)
|
|
{
|
|
$entrada->itens->each(function ($item) use ($itensDataSalvos) {
|
|
|
|
if (!$itensDataSalvos->contains('id', $item->id)) {
|
|
|
|
$item->delete();
|
|
}
|
|
});
|
|
}
|
|
|
|
private static function estornarEntradaItens($itensDataEstornados)
|
|
{
|
|
$itensDataEstornados->each(function ($itemData) {
|
|
|
|
$item = EntradaItem::findOrFail($itemData['id']);
|
|
|
|
if (!$item->estornado) {
|
|
|
|
self::saveSaidaDeEstorno($item);
|
|
|
|
$item->estornado_em = Carbon::now();
|
|
$item->save();
|
|
}
|
|
});
|
|
}
|
|
|
|
private static function saveSaidaDeEstorno($entradaItem)
|
|
{
|
|
$saida = new Saida([
|
|
'centro_custo_id' => self::getCurrentCentroCusto()->id,
|
|
'tipo_id' => SaidaTipo::ESTORNO,
|
|
'finalizada' => true,
|
|
'finalizada_em' => Carbon::now(),
|
|
]);
|
|
|
|
$saida->save();
|
|
|
|
$saidaItem = self::saveSaidaItem($saida, [
|
|
'material_id' => $entradaItem->material_id,
|
|
'escolheu_entrada_item_manualmente' => true,
|
|
'entrada_itens' => [
|
|
[
|
|
'entrada_item_id' => $entradaItem->id,
|
|
'quantidade_saida' => $entradaItem->quantidade,
|
|
]
|
|
],
|
|
]);
|
|
|
|
$entradaItem->saida_item_estornado_id = $saidaItem->id;
|
|
|
|
$saida->refresh();
|
|
|
|
return $saida;
|
|
}
|
|
|
|
private static function saveEntradaItens($entrada, $itens)
|
|
{
|
|
$itens->each(fn ($itemData) => self::saveEntradaItem($entrada, $itemData));
|
|
}
|
|
|
|
private static function saveEntradaItem($entrada, $itemData)
|
|
{
|
|
$item = isset($itemData['id']) ? EntradaItem::findOrFail($itemData['id']) : new EntradaItem();
|
|
|
|
$item->entrada_id = $entrada->id;
|
|
$item->material_id = $itemData['material_id'];
|
|
$item->integracao_bnafar_medicamento_id = $itemData['integracao_bnafar_medicamento_id'] ?? null;
|
|
$item->quantidade = $itemData['quantidade'];
|
|
$item->lote = $itemData['lote'];
|
|
$item->validade_lote = $itemData['validade_lote'];
|
|
$item->valor_unitario = $itemData['valor_unitario'];
|
|
$item->valor_total = $itemData['valor_total'];
|
|
$item->numero_empenho = $itemData['numero_empenho'];
|
|
$item->despesas = $itemData['despesas'];
|
|
$item->numero_autorizacao_fornecimento = $itemData['numero_autorizacao_fornecimento'];
|
|
$item->fabricante_id = $itemData['fabricante_id'];
|
|
$item->codigo_barras = $itemData['codigo_barras'];
|
|
|
|
$item->save();
|
|
|
|
return $item;
|
|
}
|
|
|
|
private static function saveEntradaDocumento($entrada, $data)
|
|
{
|
|
$documento = $entrada->documento ?? new EntradaDocumento();
|
|
|
|
$documento->fornecedor_id = $data['fornecedor_id'];
|
|
$documento->origem_programatica_id = $data['origem_programatica_id'] ?? null;
|
|
$documento->tipo_id = $data['tipo_id'];
|
|
$documento->numero_pedido = $data['numero_pedido'];
|
|
$documento->numero = $data['numero'];
|
|
$documento->data = $data['data'];
|
|
|
|
$entrada->documento()->save($documento);
|
|
}
|
|
|
|
public static function saveReservaDeRequisicao($itensData, $id = null)
|
|
{
|
|
if (!is_null($id)) {
|
|
|
|
Reserva::findOrFail($id)->itens()->each(fn ($i) => $i->entradaItens()->detach());
|
|
Reserva::destroy($id);
|
|
}
|
|
|
|
if ($itensData->isEmpty()) {
|
|
|
|
return null;
|
|
}
|
|
|
|
self::validarEstoque($itensData);
|
|
|
|
$reserva = Reserva::create(['motivo_id' => ReservaMotivo::REQUISICAO]);
|
|
|
|
self::saveReservaItensNovos($reserva, $itensData);
|
|
|
|
return $reserva->fresh();
|
|
}
|
|
|
|
private static function saveReservaItensNovos($reserva, $itensDataNovos)
|
|
{
|
|
$itensDataNovos->each(fn ($itemData) => self::saveReservaItem($reserva, $itemData));
|
|
}
|
|
|
|
private static function saveReservaItem($reserva, $itemData)
|
|
{
|
|
try {
|
|
|
|
$item = new ReservaItem();
|
|
$item->material_id = $itemData['material_id'];
|
|
$item->escolheu_entrada_item_manualmente = $itemData['escolheu_entrada_item_manualmente'];
|
|
|
|
$reserva->itens()->save($item);
|
|
$item->entradaItens()->sync(self::getEntradaItensData($item->material_id, $itemData));
|
|
|
|
return $item;
|
|
}
|
|
catch (Exception $e) {
|
|
|
|
if ((int) $e->getCode() === 45000
|
|
&& Str::contains($e->getMessage(), self::MARIADB_QUANTIDADE_INSUFICIENTE_CODE)
|
|
) {
|
|
|
|
throw new EstoqueIndisponivelException(json_encode([
|
|
['descricao' => Material::findOrFail($itemData['material_id'])->descricao]
|
|
]));
|
|
}
|
|
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
public static function saveSaidaDeDispensacaoDaFarmacia($itensData)
|
|
{
|
|
$saidaItensSaved = collect();
|
|
|
|
self::validarEstoque($itensData);
|
|
|
|
$saida = Saida::create([
|
|
'centro_custo_id' => self::getCurrentCentroCusto()->id,
|
|
'tipo_id' => SaidaTipo::DISPENSACAO_FARMACIA,
|
|
'finalizada' => true,
|
|
'finalizada_em' => Carbon::now(),
|
|
]);
|
|
|
|
$itensData->each(function ($itemData) use ($saida, $saidaItensSaved) {
|
|
|
|
$saidaItemSaved = self::saveSaidaItem($saida, $itemData);
|
|
|
|
$saidaItensSaved->push([
|
|
'receita_item_entrega_id' => $itemData['receita_item_entrega_id'],
|
|
'saida_item_id' => $saidaItemSaved->id,
|
|
]);
|
|
});
|
|
|
|
return $saidaItensSaved;
|
|
}
|
|
|
|
public static function saveSaidaDeDispensacaoDoAlmoxarifado($itensData)
|
|
{
|
|
$saidaItensSaved = collect();
|
|
self::validarEstoque($itensData);
|
|
$saida = Saida::create([
|
|
'centro_custo_id' => self::getCurrentCentroCusto()->id,
|
|
'tipo_id' => SaidaTipo::DISPENSACAO_ALMOXARIFADO,
|
|
'finalizada' => true,
|
|
'finalizada_em' => Carbon::now(),
|
|
])->fresh();
|
|
|
|
$itensData->each(function ($itemData) use ($saida, $saidaItensSaved) {
|
|
$saidaItemSaved = self::saveSaidaItem($saida, $itemData);
|
|
$saidaItensSaved->push([
|
|
'dispensacao_item_id' => $itemData['dispensacao_item_id'],
|
|
'saida_item_id' => $saidaItemSaved->id,
|
|
]);
|
|
});
|
|
|
|
return $saidaItensSaved;
|
|
}
|
|
|
|
public static function saveSaidaDeRequisicao($itensData, $reservaId = null)
|
|
{
|
|
if (!is_null($reservaId)) {
|
|
|
|
Reserva::findOrFail($reservaId)->itens()->each(fn ($i) => $i->entradaItens()->detach());
|
|
Reserva::destroy($reservaId);
|
|
}
|
|
|
|
if ($itensData->isEmpty()) {
|
|
|
|
return null;
|
|
}
|
|
|
|
self::validarEstoque($itensData);
|
|
|
|
$saida = Saida::create([
|
|
'centro_custo_id' => self::getCurrentCentroCusto()->id,
|
|
'tipo_id' => SaidaTipo::REQUISICAO,
|
|
'finalizada' => true,
|
|
'finalizada_em' => Carbon::now(),
|
|
]);
|
|
|
|
self::saveSaidaItensNovos($saida, $itensData);
|
|
|
|
$saida->refresh();
|
|
|
|
return $saida;
|
|
}
|
|
|
|
private static function saveSaidaDeInventario($quantidadeDivergente, $data, $verificarLote = false)
|
|
{
|
|
$saida = new Saida([
|
|
'centro_custo_id' => self::getCurrentCentroCusto()->id,
|
|
'tipo_id' => SaidaTipo::INVENTARIO,
|
|
'finalizada' => true,
|
|
'finalizada_em' => Carbon::now(),
|
|
]);
|
|
|
|
$saida->save();
|
|
|
|
self::saveSaidaItem($saida, [
|
|
'material_id' => $data['material_id'],
|
|
'escolheu_entrada_item_manualmente' => false,
|
|
'lote' => $data['lote'] ?? null,
|
|
'validade_lote' => $data['validade_lote'] ?? null,
|
|
'quantidade' => $quantidadeDivergente,
|
|
'verificar_lote' => $verificarLote,
|
|
]);
|
|
|
|
$saida->refresh();
|
|
|
|
return $saida;
|
|
}
|
|
|
|
public static function saveSaidaDeBaixa($data)
|
|
{
|
|
$saida = new Saida([
|
|
'centro_custo_id' => self::getCurrentCentroCusto()->id,
|
|
'tipo_id' => SaidaTipo::BAIXA,
|
|
'finalizada' => true,
|
|
'finalizada_em' => Carbon::now(),
|
|
]);
|
|
|
|
$itensData = collect([
|
|
[
|
|
'material_id' => $data['material_id'],
|
|
'quantidade' => $data['quantidade'],
|
|
'entrada_itens' => $data['entrada_itens'],
|
|
'escolheu_entrada_item_manualmente' => $data['escolheu_entrada_item_manualmente'],
|
|
]
|
|
]);
|
|
|
|
self::validarEstoque($itensData);
|
|
|
|
$saida->save();
|
|
|
|
self::saveSaidaItensNovos($saida, $itensData);
|
|
|
|
$baixaData = [
|
|
'motivo_id' => $data['motivo_id'],
|
|
'justificativa' => $data['justificativa'],
|
|
'usuario_solicitante_id' => $data['usuario_solicitante_id'],
|
|
];
|
|
|
|
self::saveSaidaBaixa($saida, $baixaData);
|
|
|
|
$saida->refresh();
|
|
|
|
return $saida;
|
|
}
|
|
|
|
private static function saveSaidaBaixa($saida, $data)
|
|
{
|
|
$baixa = $saida->baixa ?? new SaidaBaixa();
|
|
|
|
$baixa->motivo_id = $data['motivo_id'];
|
|
$baixa->justificativa = $data['justificativa'];
|
|
$baixa->usuario_solicitante_id = $data['usuario_solicitante_id'];
|
|
|
|
$saida->baixa()->save($baixa);
|
|
}
|
|
|
|
public static function saveSaidaManual($data, $id = null)
|
|
{
|
|
$saida = self::getSaidaInstance(SaidaTipo::MANUAL, $id);
|
|
$estavaFinalizada = $saida->finalizada ?? false;
|
|
$finalizouAgora = !$estavaFinalizada && $data['finalizada'];
|
|
|
|
$itensData = collect($data['itens']);
|
|
$itensDataNovos = $itensData->filter(fn ($i) => is_null($i['id']));
|
|
$itensDataSalvos = $itensData->filter(fn ($i) => !is_null($i['id']));
|
|
$itensDataEstornados = $itensData->filter(fn ($i) => $i['estornado']);
|
|
|
|
self::deleteSaidaItensExcluidos($saida, $itensDataSalvos);
|
|
self::estornarSaidaItens($itensDataEstornados);
|
|
self::validarEstoqueSaidaItens($saida, $itensDataNovos, $finalizouAgora);
|
|
|
|
if ($finalizouAgora) {
|
|
|
|
$saida->finalizada = true;
|
|
$saida->finalizada_em = Carbon::now();
|
|
|
|
self::updateSaidaItens($saida);
|
|
}
|
|
|
|
$saida->save();
|
|
|
|
self::saveSaidaItensNovos($saida, $itensDataNovos);
|
|
|
|
if (!$estavaFinalizada) {
|
|
|
|
self::saveSaidaRequisicao($saida, $data['requisicao']);
|
|
}
|
|
|
|
$saida->refresh();
|
|
|
|
return $saida;
|
|
}
|
|
|
|
public static function saveSaidaDeDispensacaoDoAmbulatorio($itemData)
|
|
{
|
|
self::validarEstoque(collect([$itemData]));
|
|
|
|
$saida = Saida::create([
|
|
'centro_custo_id' => self::getCurrentCentroCusto()->id,
|
|
'tipo_id' => SaidaTipo::DISPENSACAO_AMBULATORIO,
|
|
'finalizada' => true,
|
|
'finalizada_em' => Carbon::now(),
|
|
]);
|
|
|
|
return self::saveSaidaItem($saida, $itemData);
|
|
}
|
|
|
|
private static function getSaidaInstance($tipoId, $id = null)
|
|
{
|
|
return is_null($id)
|
|
? new Saida(['tipo_id' => $tipoId, 'centro_custo_id' => self::getCurrentCentroCusto()->id])
|
|
: Saida::where('centro_custo_id', self::getCurrentCentroCusto()->id)
|
|
->where('tipo_id', $tipoId)->findOrFail($id);
|
|
}
|
|
|
|
private static function deleteSaidaItensExcluidos($saida, $itensDataSalvos)
|
|
{
|
|
$saida->itens->each(function ($item) use ($itensDataSalvos) {
|
|
|
|
if (!$itensDataSalvos->contains('id', $item->id)) {
|
|
|
|
$item->entradaItens()->detach();
|
|
$item->delete();
|
|
}
|
|
});
|
|
}
|
|
|
|
public static function estornarSaidaItens($itensDataEstornados)
|
|
{
|
|
$itensDataEstornados->each(function ($itemData) {
|
|
|
|
$item = SaidaItem::findOrFail($itemData['id']);
|
|
|
|
if (!$item->estornado) {
|
|
|
|
self::saveEntradasDeEstorno($item);
|
|
|
|
$item->estornado_em = Carbon::now();
|
|
$item->save();
|
|
}
|
|
});
|
|
}
|
|
|
|
private static function saveEntradasDeEstorno($saidaItem)
|
|
{
|
|
$centroCustoIdLogado = $saidaItem['created_centro_custo_id'];
|
|
|
|
$saidaItem->entradaItens->each(function ($entradaItemOld) use ($centroCustoIdLogado) {
|
|
|
|
$entrada = new Entrada([
|
|
'centro_custo_id' => $centroCustoIdLogado,
|
|
'tipo_id' => EntradaTipo::ESTORNO,
|
|
'finalizada' => true,
|
|
'finalizada_em' => Carbon::now(),
|
|
]);
|
|
|
|
$entrada->save();
|
|
|
|
$entradaItemNew = self::saveEntradaItem($entrada, [
|
|
'material_id' => $entradaItemOld->material_id,
|
|
'quantidade' => $entradaItemOld->pivotSaidaItemEntradaItem->quantidade_saida,
|
|
'lote' => $entradaItemOld->lote,
|
|
'validade_lote' => $entradaItemOld->validade_lote,
|
|
'valor_unitario' => $entradaItemOld->valor_unitario,
|
|
'valor_total' => $entradaItemOld->valor_total,
|
|
'numero_empenho' => $entradaItemOld->numero_empenho,
|
|
'despesas' => $entradaItemOld->despesas,
|
|
'numero_autorizacao_fornecimento' => $entradaItemOld->numero_autorizacao_fornecimento,
|
|
'fabricante_id' => $entradaItemOld->fabricante_id,
|
|
'codigo_barras' => $entradaItemOld->codigo_barras,
|
|
]);
|
|
|
|
$entradaItemOld->pivotSaidaItemEntradaItem->entrada_item_estornado_id = $entradaItemNew->id;
|
|
$entradaItemOld->pivotSaidaItemEntradaItem->save();
|
|
|
|
$entrada->refresh();
|
|
});
|
|
}
|
|
|
|
private static function validarEstoqueSaidaItens($saida, $itensDataNovos, $validarItensSalvos)
|
|
{
|
|
$materiaisSemEstoque = self::filterMateriaisSemEstoqueDispovivel($itensDataNovos);
|
|
|
|
if ($validarItensSalvos) {
|
|
|
|
$saida->itens->each(function ($saidaItem) use ($materiaisSemEstoque) {
|
|
|
|
$estoque = self::getEstoque($saidaItem->material->id);
|
|
$estoqueTotal = $estoque['total'];
|
|
$estoquePorEntrada = collect($estoque['por_entrada']);
|
|
|
|
if ($saidaItem->quantidade > $estoqueTotal) {
|
|
|
|
$materiaisSemEstoque->push($saidaItem->material->descricao);
|
|
} else if ($saidaItem->escolheu_entrada_item_manualmente) {
|
|
|
|
foreach ($saidaItem->entradaItens as $entradaItem) {
|
|
|
|
$estoqueEntradaItem = $estoquePorEntrada
|
|
->where('entrada_item_id', $entradaItem->id)
|
|
->first();
|
|
|
|
if (
|
|
is_null($estoqueEntradaItem)
|
|
|| $entradaItem->pivotSaidaItemEntradaItem->quantidade_saida > $estoqueEntradaItem['estoque']
|
|
) {
|
|
|
|
$materiaisSemEstoque->push($saidaItem->material->descricao);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
if ($materiaisSemEstoque->isNotEmpty()) {
|
|
|
|
$materiais = $materiaisSemEstoque->map(fn ($m) => ['descricao' => $m]);
|
|
|
|
throw new EstoqueIndisponivelException(json_encode($materiais));
|
|
}
|
|
}
|
|
|
|
private static function updateSaidaItens($saida)
|
|
{
|
|
foreach ($saida->itens as $saidaItem) {
|
|
|
|
if ($saidaItem->escolheu_entrada_item_manualmente) {
|
|
|
|
continue;
|
|
}
|
|
|
|
$refazerSaida = false;
|
|
$estoquePorEntrada = collect(self::getEstoquePorEntradas($saidaItem->material_id));
|
|
|
|
foreach ($saidaItem->entradaItens as $entradaItem) {
|
|
|
|
$estoqueEntradaItem = $estoquePorEntrada
|
|
->where('entrada_item_id', $entradaItem->id)
|
|
->first();
|
|
|
|
if (is_null($estoqueEntradaItem)
|
|
|| $entradaItem->pivotSaidaItemEntradaItem->quantidade_saida > $estoqueEntradaItem['estoque']
|
|
) {
|
|
|
|
$refazerSaida = true;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($refazerSaida) {
|
|
|
|
$entradaItens = self::getEntradaItensData(
|
|
$saidaItem->material_id,
|
|
[
|
|
'escolheu_entrada_item_manualmente' => $saidaItem->escolheu_entrada_item_manualmente,
|
|
'quantidade' => $saidaItem->quantidade
|
|
]
|
|
);
|
|
|
|
$saidaItem->entradaItens()->sync($entradaItens);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static function saveSaidaItensNovos($saida, $itensDataNovos)
|
|
{
|
|
$itensDataNovos->each(fn ($itemData) => self::saveSaidaItem($saida, $itemData));
|
|
}
|
|
|
|
private static function saveSaidaItem($saida, $itemData)
|
|
{
|
|
try {
|
|
|
|
$item = new SaidaItem();
|
|
$item->material_id = $itemData['material_id'];
|
|
$item->escolheu_entrada_item_manualmente = $itemData['escolheu_entrada_item_manualmente'];
|
|
|
|
$saida->itens()->save($item);
|
|
$item->entradaItens()->sync(self::getEntradaItensData($item->material_id, $itemData));
|
|
|
|
return $item;
|
|
}
|
|
catch (Exception $e) {
|
|
|
|
if ((int) $e->getCode() === 45000
|
|
&& Str::contains($e->getMessage(), self::MARIADB_QUANTIDADE_INSUFICIENTE_CODE)
|
|
) {
|
|
|
|
throw new EstoqueIndisponivelException(json_encode([
|
|
['descricao' => Material::findOrFail($itemData['material_id'])->descricao]
|
|
]));
|
|
}
|
|
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
private static function saveSaidaRequisicao($saida, $data)
|
|
{
|
|
$requisicao = $saida->requisicao ?? new SaidaRequisicao();
|
|
|
|
$requisicao->centro_custo_id = $data['centro_custo_id'];
|
|
$requisicao->usuario_id = $data['usuario_id'];
|
|
$requisicao->numero = $data['numero'];
|
|
$requisicao->data = $data['data'];
|
|
|
|
$saida->requisicao()->save($requisicao);
|
|
}
|
|
|
|
private static function validarEstoque($itensDataNovos)
|
|
{
|
|
$materiaisSemEstoque = self::filterMateriaisSemEstoqueDispovivel($itensDataNovos);
|
|
|
|
if ($materiaisSemEstoque->isNotEmpty()) {
|
|
|
|
$materiais = $materiaisSemEstoque->map(fn ($m) => ['descricao' => $m]);
|
|
|
|
throw new EstoqueIndisponivelException(json_encode($materiais));
|
|
}
|
|
}
|
|
|
|
private static function filterMateriaisSemEstoqueDispovivel($itensData)
|
|
{
|
|
$materiaisSemEstoque = collect();
|
|
|
|
$itensData->each(function ($itemData) use ($materiaisSemEstoque) {
|
|
|
|
$material = Material::findOrFail($itemData['material_id']);
|
|
$estoque = self::getEstoque($material->id);
|
|
$estoqueTotal = $estoque['total'];
|
|
$estoquePorEntrada = collect($estoque['por_entrada']);
|
|
|
|
if (((int) $itemData['quantidade']) > $estoqueTotal) {
|
|
|
|
$materiaisSemEstoque->push($material->descricao);
|
|
}
|
|
else if ($itemData['escolheu_entrada_item_manualmente']) {
|
|
|
|
foreach ($itemData['entrada_itens'] as $entradaItemData) {
|
|
|
|
$estoqueEntradaItem = $estoquePorEntrada
|
|
->where('entrada_item_id', $entradaItemData['entrada_item_id'])
|
|
->first();
|
|
|
|
if (is_null($estoqueEntradaItem)
|
|
|| ((int) $entradaItemData['quantidade_saida']) > $estoqueEntradaItem['estoque']
|
|
) {
|
|
|
|
$materiaisSemEstoque->push($material->descricao);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
return $materiaisSemEstoque;
|
|
}
|
|
|
|
public static function getEntradaItensData($materialId, $itemData)
|
|
{
|
|
$entradaItens = [];
|
|
|
|
if ($itemData['escolheu_entrada_item_manualmente']) {
|
|
|
|
foreach ($itemData['entrada_itens'] as $entradaItem) {
|
|
|
|
$entradaItens[$entradaItem['entrada_item_id']] = [
|
|
'quantidade_saida' => (int) $entradaItem['quantidade_saida']
|
|
];
|
|
}
|
|
}
|
|
else {
|
|
|
|
$estoquePorEntrada = collect(self::getEstoquePorEntradas($materialId));
|
|
|
|
if (isset($itemData['lote'])) {
|
|
|
|
$estoquePorEntrada = $estoquePorEntrada->where('lote', $itemData['lote']);
|
|
}
|
|
else if ($itemData['verificar_lote'] ?? false) {
|
|
|
|
$estoquePorEntrada = $estoquePorEntrada->whereNull('lote');
|
|
}
|
|
|
|
if (isset($itemData['validade_lote'])) {
|
|
|
|
$estoquePorEntrada = $estoquePorEntrada
|
|
->where('validade_lote', $itemData['validade_lote']->format('d/m/Y'));
|
|
}
|
|
|
|
$estoquePorEntrada = $estoquePorEntrada->values()->all();
|
|
$quantidade = (int) $itemData['quantidade'];
|
|
$i = 0;
|
|
|
|
while ($quantidade > 0) {
|
|
|
|
$entradaItem = $estoquePorEntrada[$i];
|
|
|
|
if ($quantidade > $entradaItem['estoque']) {
|
|
|
|
$entradaItens[$entradaItem['entrada_item_id']] = [
|
|
'quantidade_saida' => $entradaItem['estoque']
|
|
];
|
|
|
|
$quantidade -= $entradaItem['estoque'];
|
|
}
|
|
else {
|
|
|
|
$entradaItens[$entradaItem['entrada_item_id']] = [
|
|
'quantidade_saida' => $quantidade
|
|
];
|
|
|
|
$quantidade = 0;
|
|
}
|
|
|
|
$i++;
|
|
}
|
|
}
|
|
|
|
return $entradaItens;
|
|
}
|
|
|
|
public static function getReservas($materialId, $centroCustoId = null)
|
|
{
|
|
if (is_null($centroCustoId)) {
|
|
|
|
$centroCustoId = self::getCurrentCentroCusto()->id;
|
|
}
|
|
|
|
$reservaTotal = 0;
|
|
$reservas = collect();
|
|
|
|
Entrada::with([
|
|
'itens' => function ($query) use ($materialId) {
|
|
|
|
$query
|
|
->where('material_id', $materialId)
|
|
->whereHas('reservaItens');
|
|
}
|
|
])
|
|
->where('finalizada', true)
|
|
->where('centro_custo_id', $centroCustoId)
|
|
->get()
|
|
->each(function ($entrada) use (&$reservaTotal, $reservas) {
|
|
|
|
$entrada->itens->each(function ($entradaItem) use (&$reservaTotal, $reservas) {
|
|
|
|
$estoque = (int) $entradaItem->quantidade;
|
|
|
|
$entradaItem->saidaItens
|
|
->filter(fn ($s) => $s->saida->finalizada)
|
|
->each(function ($saidaItem) use (&$estoque) {
|
|
|
|
$estoque -= (int) $saidaItem->pivotSaidaItemEntradaItem->quantidade_saida;
|
|
});
|
|
|
|
$reservado = 0;
|
|
$reservasPorRequisicao = collect();
|
|
|
|
$entradaItem->reservaItens
|
|
->filter(fn ($reservaItem) =>
|
|
$reservaItem->requisicaoRespostaItem &&
|
|
$reservaItem->requisicaoRespostaItem->requisicaoItem &&
|
|
$reservaItem->requisicaoRespostaItem->requisicaoItem->requisicao
|
|
)
|
|
->each(function ($reservaItem) use (&$reservado, $reservasPorRequisicao) {
|
|
|
|
$requisicao = $reservaItem->requisicaoRespostaItem->requisicaoItem->requisicao;
|
|
$quantidade = (int) $reservaItem->pivotReservaItemEntradaItem->quantidade_saida;
|
|
|
|
$reservasPorRequisicao->push(collect([
|
|
'requisicao_id' => $requisicao->id,
|
|
'requisicao' => [
|
|
'id' => $requisicao->id,
|
|
'numero' => $requisicao->numero,
|
|
],
|
|
'reservado' => $quantidade,
|
|
]));
|
|
|
|
$reservado += $quantidade;
|
|
});
|
|
|
|
$validadeLoteFormatted = $entradaItem->validade_lote
|
|
? $entradaItem->validade_lote->format('d/m/Y') : null;
|
|
|
|
$reserva = $reservas
|
|
->where('fornecedor_id', $entradaItem->entrada->documento->fornecedor->id ?? null)
|
|
->where('lote', $entradaItem->lote)
|
|
->where('validade_lote_formatted', $validadeLoteFormatted)
|
|
->first();
|
|
|
|
if (is_null($reserva)) {
|
|
|
|
$reserva = collect([
|
|
'fornecedor_id' => $entradaItem->entrada->documento->fornecedor->id ?? null,
|
|
'fornecedor' => [
|
|
'id' => $entradaItem->entrada->documento->fornecedor->id ?? null,
|
|
'nome_fantasia' => $entradaItem->entrada->documento->fornecedor->nome_fantasia ?? null,
|
|
],
|
|
'lote' => $entradaItem->lote,
|
|
'validade_lote_formatted' => $validadeLoteFormatted,
|
|
'validade_lote' => $entradaItem->validade_lote,
|
|
'estoque' => 0,
|
|
'reservado' => 0,
|
|
'estoque_disponivel' => 0,
|
|
'reservas_por_requisicao' => collect(),
|
|
]);
|
|
|
|
$reservas->push($reserva);
|
|
}
|
|
|
|
$reserva['estoque'] += $estoque;
|
|
$reserva['reservado'] += $reservado;
|
|
$reserva['estoque_disponivel'] = $reserva['estoque'] - $reserva['reservado'];
|
|
|
|
$reservasPorRequisicao->each(function ($reservaPorRequisicaoData) use ($reserva) {
|
|
|
|
$reservaPorRequisicao = $reserva['reservas_por_requisicao']
|
|
->where('requisicao_id', $reservaPorRequisicaoData['requisicao_id'])
|
|
->first();
|
|
|
|
if (is_null($reservaPorRequisicao)) {
|
|
|
|
$reservaPorRequisicao = collect([
|
|
'requisicao_id' => $reservaPorRequisicaoData['requisicao_id'],
|
|
'requisicao' => $reservaPorRequisicaoData['requisicao'],
|
|
'reservado' => 0,
|
|
]);
|
|
|
|
$reserva['reservas_por_requisicao']->push($reservaPorRequisicao);
|
|
}
|
|
|
|
$reservaPorRequisicao['reservado'] += $reservaPorRequisicaoData['reservado'];
|
|
});
|
|
|
|
$reservaTotal += $reservado;
|
|
});
|
|
});
|
|
|
|
$reservas = $reservas
|
|
->sortBy('validade_lote')
|
|
->each(function ($reserva) {
|
|
|
|
if (!is_null($reserva['validade_lote'])) {
|
|
|
|
$reserva['validade_lote'] = $reserva['validade_lote']->format('d/m/Y');
|
|
}
|
|
|
|
$reserva['reservas_por_requisicao']->each(function ($reservaPorRequisicao) {
|
|
|
|
unset($reservaPorRequisicao['requisicao_id']);
|
|
});
|
|
|
|
unset(
|
|
$reserva['fornecedor_id'],
|
|
$reserva['validade_lote_formatted']
|
|
);
|
|
})
|
|
->values()
|
|
->all();
|
|
|
|
return [
|
|
'total' => $reservaTotal,
|
|
'agrupadas' => $reservas,
|
|
];
|
|
}
|
|
|
|
public static function isQuantidadeDisponivel(int $quantidade, $materialId, $centroCustoId = null)
|
|
{
|
|
return $quantidade <= self::getEstoqueTotal($materialId, $centroCustoId);
|
|
}
|
|
|
|
public static function getEstoqueTotal($materialId, $centroCustoId = null)
|
|
{
|
|
return self::getEstoque($materialId, $centroCustoId)['total'];
|
|
}
|
|
|
|
public static function getEstoquePorEntradas($materialId, $centroCustoId = null)
|
|
{
|
|
return self::getEstoque($materialId, $centroCustoId)['por_entrada'];
|
|
}
|
|
|
|
public static function getEstoque($materialId, $centroCustoId = null, $comReservaItemId = null)
|
|
{
|
|
if (is_null($centroCustoId)) {
|
|
|
|
$centroCustoId = self::getCurrentCentroCusto()->id;
|
|
}
|
|
|
|
$selectEstoque = "
|
|
COALESCE((
|
|
COALESCE(entrada_item.quantidade, 0)
|
|
-
|
|
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 = entrada_item.id
|
|
AND almoxarifado_saida.finalizada = 1
|
|
), 0)
|
|
-
|
|
COALESCE((
|
|
SELECT SUM(almoxarifado_reserva_item_entrada_item.quantidade_saida)
|
|
FROM almoxarifado_reserva_item_entrada_item
|
|
JOIN almoxarifado_reserva_item
|
|
ON almoxarifado_reserva_item.id = almoxarifado_reserva_item_entrada_item.reserva_item_id
|
|
WHERE almoxarifado_reserva_item_entrada_item.entrada_item_id = entrada_item.id
|
|
" . (is_null($comReservaItemId) ? '' : "AND almoxarifado_reserva_item.id <> {$comReservaItemId}") . "
|
|
), 0)
|
|
), 0)
|
|
";
|
|
|
|
$entradaItens = DB::table('almoxarifado_entrada_item as entrada_item')
|
|
->join(
|
|
'almoxarifado_entrada as entrada',
|
|
'entrada.id',
|
|
'entrada_item.entrada_id'
|
|
)
|
|
->leftJoin(
|
|
'almoxarifado_entrada_documento as entrada_documento',
|
|
'entrada_documento.entrada_id',
|
|
'entrada.id'
|
|
)
|
|
->leftJoin(
|
|
'cadastro_fornecedor as fornecedor',
|
|
'fornecedor.id',
|
|
'entrada_documento.fornecedor_id'
|
|
)
|
|
->where('entrada_item.material_id', $materialId)
|
|
->where('entrada.finalizada', true)
|
|
->where('entrada.centro_custo_id', $centroCustoId)
|
|
->whereRaw("{$selectEstoque} > 0")
|
|
->select(DB::raw("
|
|
entrada_item.id as entrada_item_id,
|
|
fornecedor.id as fornecedor_id,
|
|
fornecedor.nome_fantasia as fornecedor_nome_fantasia,
|
|
entrada_item.lote,
|
|
entrada_item.validade_lote,
|
|
{$selectEstoque} as estoque
|
|
"))
|
|
->orderBy('entrada_item.id')
|
|
->get();
|
|
|
|
$estoqueTotal = 0;
|
|
|
|
$entradaItens = $entradaItens->map(function ($entradaItem) use (&$estoqueTotal) {
|
|
|
|
$estoqueTotal += $entradaItem->estoque;
|
|
|
|
return collect([
|
|
'entrada_item_id' => $entradaItem->entrada_item_id,
|
|
'fornecedor' => [
|
|
'id' => $entradaItem->fornecedor_id,
|
|
'nome_fantasia' => $entradaItem->fornecedor_nome_fantasia,
|
|
],
|
|
'lote' => $entradaItem->lote,
|
|
'validade_lote' => $entradaItem->validade_lote,
|
|
'estoque' => $entradaItem->estoque,
|
|
]);
|
|
});
|
|
|
|
$semLote = $entradaItens->whereNull('lote')->values();
|
|
|
|
$comLote = $entradaItens->whereNotNull('lote')
|
|
->sortBy([
|
|
['validade_lote', 'asc'],
|
|
['entrada_item_id', 'asc']
|
|
])
|
|
->values();
|
|
|
|
$estoquePorEntrada = $comLote->concat($semLote);
|
|
|
|
$estoquePorEntrada
|
|
->each(function ($estoqueEntrada) {
|
|
|
|
if (!is_null($estoqueEntrada['validade_lote'])) {
|
|
|
|
$estoqueEntrada['validade_lote'] = (new Carbon($estoqueEntrada['validade_lote']))->format('d/m/Y');
|
|
}
|
|
});
|
|
|
|
return [
|
|
'total' => $estoqueTotal,
|
|
'por_entrada' => $estoquePorEntrada->all(),
|
|
];
|
|
}
|
|
}
|