Files
rkm-conectasus-ci-sandbox/api/app/Services/WebService/Almoxarifado/Cliente/EntradaService.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

342 lines
12 KiB
PHP

<?php
namespace App\Services\WebService\Almoxarifado\Cliente;
use App\Enums\WebService\Almoxarifado\Cliente\EntradaDocumentoTipo as ClienteEntradaDocumentoTipo;
use App\Enums\WebService\Almoxarifado\Cliente\EntradaStatus as EntradaStatusEnum;
use App\Exceptions\WebService\Almoxarifado\Cliente\Entrada\BalanceteConectaSusClosedException;
use App\Exceptions\WebService\Almoxarifado\Cliente\Entrada\CannotDeleteEntradaAprovadaException;
use App\Exceptions\WebService\Almoxarifado\Cliente\Entrada\ConflictEntradaException;
use App\Exceptions\WebService\Almoxarifado\Cliente\Entrada\DuplicatedMaterialLoteException;
use App\Exceptions\WebService\Almoxarifado\Cliente\Entrada\EntradaNotFoundException;
use App\Exceptions\WebService\Almoxarifado\Cliente\Entrada\InvalidSearchPeriodException;
use App\Exceptions\WebService\Almoxarifado\Cliente\Entrada\PeriodoContabilClosedException;
use App\Models\WebService\Almoxarifado\Cliente\Entrada;
use App\Models\WebService\Almoxarifado\Cliente\EntradaDocumentoTipo;
use App\Models\WebService\Almoxarifado\Cliente\EntradaStatus;
use App\Models\WebService\Almoxarifado\Cliente\Fabricante;
use App\Models\WebService\Almoxarifado\Cliente\Fornecedor;
use App\Models\WebService\Almoxarifado\Cliente\Material;
use App\Models\WebService\Almoxarifado\Cliente\PeriodoContabil;
use App\Models\WebService\Almoxarifado\Cliente\Unidade;
use App\Services\Almoxarifado\BalanceteService;
use App\Services\Service;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
class EntradaService extends Service
{
public const LIMITE_DIAS_PERIODO_BUSCA = 31;
public static function index($request)
{
self::validateIndex($request);
$entradas = collect();
DB::table('web_service_almoxarifado_cliente_entrada_item AS entrada_item')
->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;
}
}