matchService = $matchService; $this->budgetService = $budgetService; } /** * Recebe fatura do SGC e processa no SGF */ public function receberFatura(array $payload, string $idempotencyKey): array { // Verificar idempotência $logExistente = DB::table('sgc_integration_log') ->where('idempotency_key', $idempotencyKey) ->first(); if ($logExistente) { return [ 'status' => 'DUPLICATE', 'original_receipt_id' => $logExistente->id, 'message' => 'Idempotency key já processada', ]; } try { $resultado = DB::transaction(function () use ($payload) { // Buscar ou criar fornecedor $fornecedor = $this->sincronizarFornecedor($payload['supplier']); // Buscar OC $oc = OrdemCompra::where('oc_id', $payload['oc_reference'])->first(); if (!$oc) { throw new \Exception('OC não encontrada: ' . $payload['oc_reference']); } // Verificar se fornecedor bate if ($oc->fornecedor_id !== $fornecedor->id) { throw new \Exception('Fornecedor não corresponde à OC'); } // Criar fatura $fatura = Fatura::create([ 'fatura_id' => $payload['sgf_invoice_id'], 'ordem_compra_id' => $oc->id, 'fornecedor_id' => $fornecedor->id, 'data_fatura' => $payload['dates']['invoice_date'], 'data_vencimento' => $payload['dates']['due_date'], 'valor_fatura' => $payload['amounts']['subtotal'], 'iva_valor' => $payload['amounts']['iva_amount'], 'moeda' => $payload['amounts']['currency'], 'taxa_cambio' => $payload['amounts']['exchange_rate'], 'status_3_way_match' => 'Pendente', 'status_pagamento' => 'Aguardando', 'sgf_doc_number' => $payload['sgf_doc_number'], ]); // Executar 3-Way Match $matchResult = $this->matchService->executarMatch($fatura); return [ 'status' => 'ACCEPTED', 'fatura_id' => $fatura->id, 'sgf_doc_number' => $fatura->sgf_doc_number, 'match_result' => $matchResult, ]; }); // Log de sucesso DB::table('sgc_integration_log')->insert([ 'idempotency_key' => $idempotencyKey, 'endpoint' => '/api/v1/integration/sgc/invoices', 'event_type' => 'INVOICE_CREATED', 'payload' => json_encode($payload), 'status' => 'ACCEPTED', 'response' => json_encode($resultado), 'created_at' => now(), 'updated_at' => now(), ]); return $resultado; } catch (\Exception $e) { // Log de erro DB::table('sgc_integration_log')->insert([ 'idempotency_key' => $idempotencyKey, 'endpoint' => '/api/v1/integration/sgc/invoices', 'event_type' => 'INVOICE_CREATED', 'payload' => json_encode($payload), 'status' => 'REJECTED', 'error_code' => 'PROCESSING_ERROR', 'error_message' => $e->getMessage(), 'created_at' => now(), 'updated_at' => now(), ]); Log::error('Erro ao processar fatura', [ 'idempotency_key' => $idempotencyKey, 'error' => $e->getMessage(), ]); return [ 'status' => 'REJECTED', 'error_code' => 'PROCESSING_ERROR', 'message' => $e->getMessage(), ]; } } /** * Recebe confirmação de pagamento do SGC */ public function receberPagamento(array $payload, string $idempotencyKey): array { // Verificar idempotência $logExistente = DB::table('sgc_integration_log') ->where('idempotency_key', $idempotencyKey) ->first(); if ($logExistente) { return [ 'status' => 'DUPLICATE', 'original_receipt_id' => $logExistente->id, ]; } try { $resultado = DB::transaction(function () use ($payload) { // Buscar fatura $fatura = Fatura::where('sgf_doc_number', $payload['sgf_doc_number'])->first(); if (!$fatura) { throw new \Exception('Fatura não encontrada: ' . $payload['sgf_doc_number']); } // Atualizar fatura com pagamento $fatura->update([ 'data_pagamento' => $payload['payment_date'], 'valor_pago' => $payload['amount_paid'], 'status_pagamento' => 'Pago', 'numero_comprovativo' => $payload['bank_reference'], ]); // Atualizar OC se totalmente paga $oc = $fatura->ordemCompra; $totalPago = $oc->faturas()->where('status_pagamento', 'Pago')->sum('valor_pago'); if ($totalPago >= $oc->valor_comprometido) { $oc->update(['status' => 'Totalmente Paga']); } return [ 'status' => 'ACCEPTED', 'fatura_id' => $fatura->id, 'payment_id' => $payload['payment_id'], ]; }); // Log de sucesso DB::table('sgc_integration_log')->insert([ 'idempotency_key' => $idempotencyKey, 'endpoint' => '/api/v1/integration/sgc/payments', 'event_type' => 'PAYMENT_EXECUTED', 'payload' => json_encode($payload), 'status' => 'ACCEPTED', 'response' => json_encode($resultado), 'created_at' => now(), 'updated_at' => now(), ]); return $resultado; } catch (\Exception $e) { DB::table('sgc_integration_log')->insert([ 'idempotency_key' => $idempotencyKey, 'endpoint' => '/api/v1/integration/sgc/payments', 'event_type' => 'PAYMENT_EXECUTED', 'payload' => json_encode($payload), 'status' => 'REJECTED', 'error_code' => 'PROCESSING_ERROR', 'error_message' => $e->getMessage(), 'created_at' => now(), 'updated_at' => now(), ]); return [ 'status' => 'REJECTED', 'error_code' => 'PROCESSING_ERROR', 'message' => $e->getMessage(), ]; } } /** * Sincroniza fornecedor do SGC para o SGF */ private function sincronizarFornecedor(array $supplierData): Fornecedor { return Fornecedor::updateOrCreate( ['sgf_fornecedor_id' => $supplierData['sgf_supplier_id']], [ 'nuit' => $supplierData['nuit'], 'nome_fornecedor' => $supplierData['name'], 'categoria_fornecedor' => $supplierData['category'] ?? 'Geral', ] ); } }