179 lines
5.4 KiB
PHP
179 lines
5.4 KiB
PHP
|
<?php
|
||
|
|
||
|
namespace Koneko\SatCatalogs\Services;
|
||
|
|
||
|
use Illuminate\Support\Facades\DB;
|
||
|
use Illuminate\Support\Facades\Log;
|
||
|
use Carbon\Carbon;
|
||
|
|
||
|
class CsvDatabaseService
|
||
|
{
|
||
|
/**
|
||
|
* Carga datos desde CSV a una tabla de la base de datos.
|
||
|
*/
|
||
|
public static function importCsvToTable(string $csvFilePath, string $tableName, array $columns, bool $updateExisting = false)
|
||
|
{
|
||
|
if (!file_exists($csvFilePath)) {
|
||
|
return ["error" => "Archivo CSV no encontrado: {$csvFilePath}"];
|
||
|
}
|
||
|
|
||
|
$handle = fopen($csvFilePath, 'r');
|
||
|
$batchSize = 5000;
|
||
|
$data = [];
|
||
|
$rowCount = 0;
|
||
|
$insertedCount = 0;
|
||
|
|
||
|
while (($row = fgetcsv($handle, 1000, ";")) !== false) {
|
||
|
$record = [];
|
||
|
|
||
|
foreach ($columns as $index => $column) {
|
||
|
$value = $row[$index] ?? null;
|
||
|
|
||
|
// Aplicar CAST según el tipo de dato
|
||
|
if ($value === '') {
|
||
|
$record[$column] = null;
|
||
|
|
||
|
} elseif (self::isDateColumn($column)) {
|
||
|
$record[$column] = self::formatDate($value);
|
||
|
|
||
|
} elseif (is_numeric($value)) {
|
||
|
$record[$column] = strpos($value, '.') !== false ? (float) $value : (int) $value;
|
||
|
|
||
|
} else {
|
||
|
$record[$column] = trim($value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$data[] = $record;
|
||
|
$rowCount++;
|
||
|
|
||
|
if (count($data) >= $batchSize) {
|
||
|
self::insertOrUpdate($tableName, $data, $updateExisting);
|
||
|
|
||
|
$insertedCount += count($data);
|
||
|
$data = [];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fclose($handle);
|
||
|
|
||
|
if (!empty($data)) {
|
||
|
self::insertOrUpdate($tableName, $data, $updateExisting);
|
||
|
|
||
|
$insertedCount += count($data);
|
||
|
}
|
||
|
|
||
|
return [
|
||
|
"inserted" => $insertedCount,
|
||
|
"total_rows" => $rowCount
|
||
|
];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Inserta o actualiza datos en la tabla.
|
||
|
*/
|
||
|
private static function insertOrUpdate(string $tableName, array $data, bool $updateExisting)
|
||
|
{
|
||
|
if (empty($data)) {
|
||
|
echo "⚠️ Datos vacíos para {$tableName}\n";
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
foreach ($data as &$record) {
|
||
|
foreach ($record as $key => $value) {
|
||
|
if ($value === '?') { // Si hay un '?', lo convertimos en NULL
|
||
|
$record[$key] = null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
unset($record);
|
||
|
|
||
|
try {
|
||
|
$primaryKeys = self::getPrimaryKeys($tableName);
|
||
|
$updateFields = array_diff(array_keys($data[0]), $primaryKeys);
|
||
|
|
||
|
if ($updateExisting && !empty($updateFields)) {
|
||
|
DB::table($tableName)->upsert($data, $primaryKeys, $updateFields);
|
||
|
|
||
|
echo "✅ Upsert ejecutado en {$tableName} (" . count($data) . " filas)\n";
|
||
|
|
||
|
} else {
|
||
|
DB::table($tableName)->insert($data);
|
||
|
|
||
|
echo "✅ Insert ejecutado en {$tableName} (" . count($data) . " filas)\n";
|
||
|
}
|
||
|
|
||
|
} catch (\Exception $e) {
|
||
|
Log::error("🚨 Error en {$tableName}: " . $e->getMessage());
|
||
|
echo "❌ ERROR en {$tableName}: {$e->getMessage()}\n";
|
||
|
|
||
|
die();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* **Detecta si una columna es de tipo fecha.**
|
||
|
*/
|
||
|
private static function isDateColumn(string $columnName): bool
|
||
|
{
|
||
|
$dateFields = ['fecha_inicio_vigencia', 'fecha_fin_vigencia'];
|
||
|
|
||
|
return in_array($columnName, $dateFields);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* **Convierte fechas de diferentes formatos a `YYYY-MM-DD`.**
|
||
|
*/
|
||
|
private static function formatDate($value)
|
||
|
{
|
||
|
if (!$value || strtolower($value) === 'null') {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
if (is_numeric($value) && $value > 10000) {
|
||
|
return Carbon::instance(\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($value))->format('Y-m-d');
|
||
|
}
|
||
|
|
||
|
if (preg_match('/^\d{2}\/\d{2}\/\d{4}$/', $value)) {
|
||
|
return Carbon::createFromFormat('d/m/Y', $value)->format('Y-m-d');
|
||
|
}
|
||
|
|
||
|
return Carbon::parse($value)->format('Y-m-d');
|
||
|
|
||
|
} catch (\Exception $e) {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* **Obtiene las claves primarias de la tabla.**
|
||
|
*/
|
||
|
private static function getPrimaryKeys(string $tableName): array
|
||
|
{
|
||
|
$primaryKeys = [
|
||
|
'sat_forma_pago' => ['c_forma_pago'],
|
||
|
'sat_moneda' => ['c_moneda'],
|
||
|
'sat_codigo_postal' => ['c_codigo_postal'],
|
||
|
'sat_regimen_fiscal' => ['c_regimen_fiscal'],
|
||
|
'sat_pais' => ['c_pais'],
|
||
|
'sat_uso_cfdi' => ['c_uso_cfdi'],
|
||
|
'sat_clave_prod_serv' => ['c_clave_prod_serv'],
|
||
|
'sat_clave_unidad' => ['c_clave_unidad'],
|
||
|
'sat_aduana' => ['c_aduana'],
|
||
|
'sat_colonia' => ['c_colonia', 'c_codigo_postal'],
|
||
|
'sat_estado' => ['c_estado', 'c_pais'],
|
||
|
'sat_localidad' => ['c_localidad', 'c_estado'],
|
||
|
'sat_municipio' => ['c_municipio', 'c_estado'],
|
||
|
'sat_banco' => ['c_banco'],
|
||
|
'sat_deduccion' => ['c_deduccion'],
|
||
|
'sat_percepcion' => ['c_percepcion'],
|
||
|
'sat_regimen_contratacion' => ['c_regimen_contratacion'],
|
||
|
];
|
||
|
|
||
|
return $primaryKeys[$tableName] ?? ['id'];
|
||
|
}
|
||
|
}
|