join('web_service_almoxarifado_cliente_entrada AS entrada', 'entrada.id', 'entrada_item.entrada_id') ->whereBetween('entrada.data_documento', [ $request->query('data_documento_inicial'), $request->query('data_documento_final') ]) ->when($request->query('status_id'), fn ($q, $id) => $q->where('entrada.status_id', $id)) ->select([ 'entrada.id AS entrada_id', 'entrada.unidade_id', 'entrada.fornecedor_id', 'entrada.codigo_nota', 'entrada.documento_tipo_id', 'entrada.data_documento', 'entrada.numero_documento', 'entrada.numero_pedido', 'entrada.status_id', 'entrada_item.material_id', 'entrada_item.fabricante_id', 'entrada_item.lote', 'entrada_item.validade_lote', 'entrada_item.quantidade', 'entrada_item.valor_unitario', ]) ->orderBy('entrada.data_documento') ->each(function ($entradaItem) use ($entradas) { $entrada = $entradas->where('id', $entradaItem->entrada_id)->first(); if (is_null($entrada)) { $entrada = (object) [ 'id' => $entradaItem->entrada_id, 'unidade_id' => $entradaItem->unidade_id, 'fornecedor_id' => $entradaItem->fornecedor_id, 'codigo_nota' => $entradaItem->codigo_nota, 'documento_tipo_id' => $entradaItem->documento_tipo_id, 'data_documento' => $entradaItem->data_documento, 'numero_documento' => $entradaItem->numero_documento, 'numero_pedido' => $entradaItem->numero_pedido, 'status_id' => $entradaItem->status_id, 'itens' => collect(), ]; $entradas->push($entrada); } $entrada->itens->push((object) [ 'material_id' => $entradaItem->material_id, 'fabricante_id' => $entradaItem->fabricante_id, 'lote' => $entradaItem->lote, 'validade_lote' => $entradaItem->validade_lote, 'quantidade' => $entradaItem->quantidade, 'valor_unitario' => (float) $entradaItem->valor_unitario, ]); }); return $entradas; } private static function validateIndex($request) { $request->validate([ 'data_documento_inicial' => [ 'required', 'date_format:Y-m-d', 'before_or_equal:data_documento_final', ], 'data_documento_final' => [ 'required', 'date_format:Y-m-d', 'after_or_equal:data_documento_inicial', ], 'status_id' => [ 'integer', Rule::exists(EntradaStatus::class, 'id'), ] ]); $dataDocumentoInicial = Carbon::create($request->data_documento_inicial); $dataDocumentoFinal = Carbon::create($request->data_documento_final); if ($dataDocumentoInicial->diff($dataDocumentoFinal)->days > self::LIMITE_DIAS_PERIODO_BUSCA) { throw new InvalidSearchPeriodException(); } } public static function show($id) { $entrada = self::getEntradaModel($id)->load('itens'); $entrada->itens->each(function ($item) { $item->valor_unitario = (float) $item->valor_unitario; }); return $entrada; } public static function store($request) { self::validate($request); $data = self::getEntradaData($request); $entrada = new Entrada(); DB::transaction(function () use ($entrada, $data) { $entrada->fill($data); $entrada->save(); $entrada->itens()->createMany($data['itens']); }); return self::show($entrada->id); } private static function getEntradaData($request) { $data = $request->only([ 'unidade_id', 'fornecedor_id', 'codigo_nota', 'documento_tipo_id', 'data_documento', 'numero_documento', 'numero_pedido', ]); $data['status_id'] = EntradaStatusEnum::PENDENTE; $data['itens'] = collect($request->itens)->map(fn ($item) => [ 'material_id' => $item['material_id'], 'fabricante_id' => $item['fabricante_id'], 'lote' => $item['lote'], 'validade_lote' => $item['validade_lote'], 'quantidade' => $item['quantidade'], 'valor_unitario' => $item['valor_unitario'], ]); return $data; } public static function destroy($id) { $entrada = self::getEntradaModel($id); if ($entrada->status_id === EntradaStatusEnum::APROVADA) { throw new CannotDeleteEntradaAprovadaException(); } $entrada->delete(); } private static function validate($request) { $request->validate([ 'unidade_id' => [ 'bail', 'required', 'integer', Rule::exists(Unidade::class, 'id'), function ($attribute, $value, $fail) { if (!Unidade::find($value)->is_almoxarifado) { $fail("O campo {$attribute} não é um almoxarifado."); } }, ], 'fornecedor_id' => [ 'required', 'integer', Rule::exists(Fornecedor::class, 'id'), ], 'codigo_nota' => [ 'required', 'integer', ], 'documento_tipo_id' => [ 'required', 'integer', Rule::exists(EntradaDocumentoTipo::class, 'id'), ], 'data_documento' => [ 'bail', 'required', 'date_format:Y-m-d', 'before_or_equal:' . Carbon::today()->format('Y-m-d'), ], 'numero_documento' => [ 'nullable', 'present', 'digits_between:1,10', ], 'numero_pedido' => [ 'nullable', 'present', 'digits_between:1,20', ], 'itens' => [ 'required', 'array', ], 'itens.*.material_id' => [ 'required', 'integer', Rule::exists(Material::class, 'id'), ], 'itens.*.fabricante_id' => [ 'required', 'integer', Rule::exists(Fabricante::class, 'id'), ], 'itens.*.lote' => [ 'required', 'string', 'max:15', ], 'itens.*.validade_lote' => [ 'bail', 'required', 'date_format:Y-m-d', 'after_or_equal:data_documento', ], 'itens.*.quantidade' => [ 'required', 'integer', 'between:1,99999999' ], 'itens.*.valor_unitario' => [ 'required', 'numeric', 'min:' . ( (EntradaDocumentoTipo::find($request->documento_tipo_id)->id ?? null) === ClienteEntradaDocumentoTipo::DOACAO ? '0.000000' : '0.000001' ), 'max:99999999.999999' ], ]); $hasDuplicatedMaterialLote = collect($request->itens) ->groupBy(['material_id', 'lote']) ->filter(fn ($m) => $m->filter(fn ($l) => $l->count() > 1)->isNotEmpty()) ->isNotEmpty(); if ($hasDuplicatedMaterialLote) { throw new DuplicatedMaterialLoteException(); } $entradaConflitante = Entrada::where('unidade_id', $request->unidade_id) ->where('fornecedor_id', $request->fornecedor_id) ->where('codigo_nota', $request->codigo_nota) ->where('documento_tipo_id', $request->documento_tipo_id) ->where('data_documento', $request->data_documento) ->where('numero_documento', $request->numero_documento) ->where('numero_pedido', $request->numero_pedido) ->whereIn('status_id', [EntradaStatusEnum::PENDENTE, EntradaStatusEnum::APROVADA]) ->exists(); if ($entradaConflitante) { throw new ConflictEntradaException(); } if (self::isDataDocumentoEmPeriodoContabilFechado($request->data_documento)) { throw new PeriodoContabilClosedException(); } if (self::isDataDocumentoEmBalanceteDoAlmoxarifadoFechado($request->data_documento)) { throw new BalanceteConectaSusClosedException(); } } private static function isDataDocumentoEmPeriodoContabilFechado($dataDocumento) { return PeriodoContabil::where('inicio', '<=', $dataDocumento) ->where('fim', '>=', $dataDocumento) ->exists(); } private static function isDataDocumentoEmBalanceteDoAlmoxarifadoFechado($dataDocumento) { return BalanceteService::checkDate($dataDocumento)['mes_fechado']; } private static function getEntradaModel($id) { $entrada = Entrada::find($id); if (is_null($entrada)) { throw new EntradaNotFoundException(); } return $entrada; } }