Initial commit: Horux Backend API

- API REST para gestion de facturas electronicas mexicanas (CFDI)
- Laravel 9 con autenticacion OAuth 2.0 (Passport)
- Integracion con Syntage, Clerk y Facturama
- 30 modelos Eloquent, 39 controladores
- Documentacion completa en /docs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-18 07:44:29 +00:00
commit 61320b44d8
191 changed files with 22895 additions and 0 deletions

View File

@@ -0,0 +1,312 @@
<?php
namespace App\Http\Controllers;
use App\Models\User;
use App\Models\Rfc;
use App\Models\Taxpayer;
use App\Models\EconomicActivity;
use App\Models\TaxRegime;
use App\Models\Risk;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Http\Client\RequestException;
class TaxpayerController extends Controller
{
public function obtenerContribuyente(Request $request)
{
$start = microtime(true);
$user = $request->user();
//$user = User::where('email', strip_tags($request->email))->first();
$rfc = Rfc::where('rfc', strip_tags($request->rfc))->first();
$taxpayer = Taxpayer::where('rfc_id', $rfc->id)->first();
if ($taxpayer) {
$last_request = new Carbon($taxpayer->updated_at);
$now = Carbon::now();
$min_days = ($last_request->diff($now)->days > 7) ? true : null;
} else {
$min_days = true;
}
if (in_array($rfc->id, $user->rfcs->pluck('id')->toArray()) && $min_days == true) {
try {
// Realizar una petición GET con encabezados
$response = Http::withHeaders([
'accept' => 'application/ld+json',
'X-API-Key' => 'b16ec9be960cdf3a8302e92a7aec84d2'
])->get('https://api.syntage.com/insights/'. $request->rfc .'/summary');
// Verificar si la petición fue exitosa
if ($response->successful()) {
// Manejar la respuesta exitosa
$data = $response->json();
$taxpayer_data = $data;
// code...
$taxpayer = TaxPayer::firstOrNew(['rfc_id' => $rfc->id]);
$taxpayer->name = $taxpayer_data['name'];
$taxpayer->fiscal_address = $taxpayer_data['fiscalAddress'];
$taxpayer->fiscal_address_status = $taxpayer_data['fiscalAddressStatusRaw'];
$taxpayer->save();
$economic_activities = $taxpayer_data['economicActivities'];
foreach ($economic_activities as $economic_activity_data) {
$economic_activity = EconomicActivity::where('description', $economic_activity_data['name'])->first();
if (!$economic_activity) {
$economic_activity = new EconomicActivity();
$economic_activity->description = $economic_activity_data['name'];
$economic_activity->save();
}
$taxpayer->economicActivities()->attach($economic_activity, [
'order' => $economic_activity_data['order'],
'percentage' => $economic_activity_data['percentage'],
'started_at' => $economic_activity_data['startDate'],
'ended_at' => $economic_activity_data['endDate']
]);
$taxpayer->save();
}
$tax_regimes = $taxpayer_data['taxRegimes'];
foreach ($tax_regimes as $tax_regime_data) {
$tax_regime = TaxRegime::where('id', $tax_regime_data['code'])->first();
if (!$tax_regime) {
$tax_regime = new TaxRegime();
$tax_regime->id = $tax_regime_data['code'];
$tax_regime->description = $tax_regime_data['name'];
$tax_regime->save();
}
$taxpayer->taxRegimes()->attach($tax_regime, [
'started_at' => $tax_regime_data['startDate']
]);
$taxpayer->save();
}
$taxpayer->total_employees = $taxpayer_data['totalEmployees'];
$taxpayer->last_year_net_income = $taxpayer_data['lastYearNetIncome'];
$taxpayer->last_year_total_income = $taxpayer_data['lastYearTotalIncome'];
$taxpayer->total_revenue_last_tax_return = $taxpayer_data['totalRevenueLastTaxReturn'];
$taxpayer->total_net_profit_last_tax_return = $taxpayer_data['totalNetProfitLastTaxReturn'];
$taxpayer->last_tax_return_year = $taxpayer_data['lastTaxReturnYear'];
$taxpayer->total_sales_revenue_current_year = $taxpayer_data['totalSalesRevenueCurrentYear'];
$taxpayer->registration_date = Carbon::parse(substr($taxpayer_data['registrationDate'], 0, 21));
$taxpayer->save();
$time = microtime(true) - $start;
return response()->json(Taxpayer::where('rfc_id', $rfc->id)->get());
} else {
// Manejar la respuesta fallida
return response()->json(['error' => 'Error al obtener los datos'], $response->status());
}
} catch (RequestException $e) {
// Manejar excepciones específicas de solicitudes HTTP
return response()->json(['error' => 'Excepción al obtener los datos: ' . $e->getMessage()], 500);
} catch (\Exception $e) {
// Manejar otras excepciones
return response()->json(['error' => 'Error inesperado: ' . $e->getMessage()], 500);
}
} else if (in_array($rfc->id, $user->rfcs->pluck('id')->toArray())) {
return response()->json(Taxpayer::where('rfc_id', $rfc->id)->get());
}
}
public function generarCSD(Request $request)
{
$user = $request->user();
//$user = User::where('email', strip_tags($request->email))->first();
$rfc = Rfc::where('rfc', strip_tags($request->rfc))->first();
if (in_array($rfc->id, $user->rfcs->pluck('id')->toArray())) {
$facturama = new Facturama\Client('pruebas', 'pruebas2011');
$params = [
'Rfc' => $rfc->rfc,
'Certificate' => $request->certificate,
'PrivateKey' => $request->privateKey,
'PrivateKeyPassword' => $request->privateKeyPassword
];
$result = $facturama->post('api-lite/csds', $params );
}
}
//Obtener SalesRevenues/Ingresos y copiarlos a la base de datos
public function ingresos(Request $request){
try {
$postdate = Carbon::parse($request->date)->addMonth()->format('Y-m');
// Realizar una petición GET con encabezados
$response = Http::withHeaders([
'accept' => 'application/ld+json',
'X-API-Key' => 'b16ec9be960cdf3a8302e92a7aec84d2'
])->get('https://api.syntage.com/insights/'. $request->rfc .'/sales-revenue?options[from]='.$request->date.'-01T06%3A00%3A00&options[to]='.$postdate.'-01T05%3A59%3A59&options[periodicity]=monthly&options[type]=total');
// Verificar si la petición fue exitosa
if ($response->successful()) {
// Manejar la respuesta exitosa
$egresos = $this->egresos($request);
$data = $response->json();
// Filtrar los datos y eliminar claves numéricas
$filteredData = array_values(collect($data['data'])
->where('date', $request->date)
->all());
$filteredEgresos = array_values(collect($egresos['data'])
->where('date', $request->date)
->all());
return response()->json([
'ingresos' => $filteredData,
'egresos' => $filteredEgresos,
'datos' => $data
]);
} else {
// Manejar la respuesta fallida
return response()->json(['error' => 'Error al obtener los datos'], $response->status());
}
} catch (RequestException $e) {
// Manejar excepciones específicas de solicitudes HTTP
return response()->json(['error' => 'Excepción al obtener los datos: ' . $e->getMessage()], 500);
} catch (\Exception $e) {
// Manejar otras excepciones
return response()->json(['error' => 'Error inesperado: ' . $e->getMessage()], 500);
}
}
public function egresos(Request $request){
try {
$postdate = Carbon::parse($request->date)->addMonth()->format('Y-m');
// Realizar una petición GET con encabezados
$response = Http::withHeaders([
'accept' => 'application/ld+json',
'X-API-Key' => 'b16ec9be960cdf3a8302e92a7aec84d2'
])->get('https://api.syntage.com/insights/'. $request->rfc .'/expenditures?options[from]='.$request->date.'-01T06%3A00%3A00&options[to]='.$postdate.'-01T05%3A59%3A59&options[periodicity]=monthly&options[type]=total');
// Verificar si la petición fue exitosa
if ($response->successful()) {
// Manejar la respuesta exitosa
$data = $response->json();
return $data;
} else {
// Manejar la respuesta fallida
return response()->json(['error' => 'Error al obtener los datos'], $response->status());
}
} catch (RequestException $e) {
// Manejar excepciones específicas de solicitudes HTTP
return response()->json(['error' => 'Excepción al obtener los datos: ' . $e->getMessage()], 500);
} catch (\Exception $e) {
// Manejar otras excepciones
return response()->json(['error' => 'Error inesperado: ' . $e->getMessage()], 500);
}
}
public function utilidadBrutaVsIndustria(Request $request){
try {
// Realizar una petición GET con encabezados
$response = Http::withHeaders([
'accept' => 'application/ld+json',
'X-API-Key' => 'b16ec9be960cdf3a8302e92a7aec84d2'
])->get('https://api.syntage.com/taxpayers/'.$request->rfc.'/invoices?itemsPerPage=20');
// Verificar si la petición fue exitosa
if ($response->successful()) {
// Manejar la respuesta exitosa
$data = $response->json();
return $data;
} else {
// Manejar la respuesta fallida
return response()->json(['error' => 'Error al obtener los datos'], $response->status());
}
} catch (RequestException $e) {
// Manejar excepciones específicas de solicitudes HTTP
return response()->json(['error' => 'Excepción al obtener los datos: ' . $e->getMessage()], 500);
} catch (\Exception $e) {
// Manejar otras excepciones
return response()->json(['error' => 'Error inesperado: ' . $e->getMessage()], 500);
}
return $request;
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* @param \App\Models\Taxpayer $taxpayer
* @return \Illuminate\Http\Response
*/
public function show(Taxpayer $taxpayer)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Taxpayer $taxpayer
* @return \Illuminate\Http\Response
*/
public function edit(Taxpayer $taxpayer)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\Models\Taxpayer $taxpayer
* @return \Illuminate\Http\Response
*/
public function update(Request $request, Taxpayer $taxpayer)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Taxpayer $taxpayer
* @return \Illuminate\Http\Response
*/
public function destroy(Taxpayer $taxpayer)
{
//
}
}