FEAT: Mejorar la importación de datos y evitar duplicados en ventas
This commit is contained in:
parent
f070777945
commit
39d1b3aee5
@ -19,6 +19,7 @@
|
||||
use Illuminate\Http\Request;
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Notsoweb\ApiResponse\Enums\ApiResponse;
|
||||
|
||||
/**
|
||||
@ -131,61 +132,54 @@ public function import(Request $request)
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$rows = $sheet->toArray();
|
||||
|
||||
DB::beginTransaction();
|
||||
// Leer encabezados de la primera fila
|
||||
$this->buildColumnMap($rows[0]);
|
||||
|
||||
try {
|
||||
// Leer encabezados de la primera fila
|
||||
$this->buildColumnMap($rows[0]);
|
||||
foreach ($rows as $index => $row) {
|
||||
if ($index === 0) continue; // Saltar encabezados
|
||||
|
||||
foreach ($rows as $index => $row) {
|
||||
if ($index === 0) continue; // Saltar encabezados
|
||||
// Iniciar transacción por fila
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$this->processRow([
|
||||
try {
|
||||
$this->processRow([
|
||||
'iccid' => $this->getColumnValue($row, 'ICCID'),
|
||||
'msisdn' => $this->getColumnValue($row, 'MSISDN'),
|
||||
'paquetes' => $this->getColumnValue($row, 'PAQUETES'),
|
||||
'usuario' => $this->getColumnValue($row, 'USUARIO'),
|
||||
'fecha_venta' => $this->getColumnValue($row, 'FECHA_VENTA'),
|
||||
'metodo_pago' => $this->getColumnValue($row, 'METODO_PAGO'),
|
||||
], $index + 1);
|
||||
|
||||
// Commit después de cada fila exitosa
|
||||
DB::commit();
|
||||
} catch (\Exception $e) {
|
||||
// Revertir solo esta fila
|
||||
DB::rollBack();
|
||||
|
||||
// Agregar error pero continuar con las demás filas
|
||||
$this->stats['errors'][] = [
|
||||
'fila' => $index + 1,
|
||||
'error' => $e->getMessage(),
|
||||
'datos' => [
|
||||
'iccid' => $this->getColumnValue($row, 'ICCID'),
|
||||
'msisdn' => $this->getColumnValue($row, 'MSISDN'),
|
||||
'paquetes' => $this->getColumnValue($row, 'PAQUETES'),
|
||||
'usuario' => $this->getColumnValue($row, 'USUARIO'),
|
||||
'fecha_venta' => $this->getColumnValue($row, 'FECHA_VENTA'),
|
||||
'metodo_pago' => $this->getColumnValue($row, 'METODO_PAGO'),
|
||||
], $index + 1);
|
||||
} catch (\Exception $e) {
|
||||
// Capturar información detallada del error antes de revertir
|
||||
DB::rollBack();
|
||||
|
||||
return ApiResponse::BAD_REQUEST->response([
|
||||
'success' => false,
|
||||
'message' => 'Error en la importación',
|
||||
'error' => $e->getMessage(),
|
||||
'fila' => $index + 1,
|
||||
'datos_fila' => [
|
||||
'iccid' => $this->getColumnValue($row, 'ICCID'),
|
||||
'msisdn' => $this->getColumnValue($row, 'MSISDN'),
|
||||
'paquetes' => $this->getColumnValue($row, 'PAQUETES'),
|
||||
'usuario' => $this->getColumnValue($row, 'USUARIO'),
|
||||
'fecha_venta' => $this->getColumnValue($row, 'FECHA_VENTA'),
|
||||
'metodo_pago' => $this->getColumnValue($row, 'METODO_PAGO'),
|
||||
],
|
||||
'stats' => $this->stats,
|
||||
], 500);
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return ApiResponse::OK->response([
|
||||
'success' => true,
|
||||
'message' => 'Importación completada',
|
||||
'stats' => $this->stats,
|
||||
'packages_created' => array_values(array_map(fn($p) => [
|
||||
'name' => $p->name,
|
||||
'price' => $p->price
|
||||
], $this->packageCache)),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return ApiResponse::OK->response([
|
||||
'success' => true,
|
||||
'message' => 'Importación completada',
|
||||
'stats' => $this->stats,
|
||||
'packages_created' => array_values(array_map(fn($p) => [
|
||||
'name' => $p->name,
|
||||
'price' => $p->price
|
||||
], $this->packageCache)),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return ApiResponse::BAD_REQUEST->response([
|
||||
'success' => false,
|
||||
@ -267,6 +261,16 @@ private function processSale(SimCard $sim, array $row)
|
||||
$packageInfo['price']
|
||||
);
|
||||
|
||||
//Evitar duplicados
|
||||
$existingSaleItem = SaleItem::where('sim_card_id', $sim->id)
|
||||
->where('package_id', $package->id)
|
||||
->first();
|
||||
|
||||
if ($existingSaleItem) {
|
||||
// Ya existe una venta para esta SIM+Paquete, salir
|
||||
return;
|
||||
}
|
||||
|
||||
// Buscar o crear el cliente
|
||||
$usuario = trim($row['usuario'] ?? '');
|
||||
$client = Client::where('full_name', $usuario)->first()
|
||||
@ -297,7 +301,7 @@ private function processSale(SimCard $sim, array $row)
|
||||
// Crear la venta
|
||||
$sale = Sale::create([
|
||||
'client_id' => $client->id,
|
||||
'cash_close_id' => null, // Se dejará null para importaciones
|
||||
'cash_close_id' => null,
|
||||
'total_amount' => $package->price,
|
||||
'payment_method' => $paymentMethod,
|
||||
'sale_date' => $saleDate,
|
||||
@ -325,37 +329,7 @@ private function processSale(SimCard $sim, array $row)
|
||||
]);
|
||||
}
|
||||
|
||||
// Verificar si ya existe una venta para esta SIM con este paquete
|
||||
// (para evitar duplicados en reimportaciones)
|
||||
$existingSale = Sale::whereHas('saleItems', function ($query) use ($sim, $package) {
|
||||
$query->where('sim_card_id', $sim->id)
|
||||
->where('package_id', $package->id);
|
||||
})->where('client_id', $client->id)
|
||||
->where('sale_date', $saleDate)
|
||||
->exists();
|
||||
|
||||
if (!$existingSale) {
|
||||
// Crear la venta
|
||||
$sale = Sale::create([
|
||||
'client_id' => $client->id,
|
||||
'cash_close_id' => null, // Importaciones no tienen corte de caja
|
||||
'total_amount' => $package->price,
|
||||
'payment_method' => $paymentMethod,
|
||||
'sale_date' => $saleDate,
|
||||
]);
|
||||
|
||||
// Crear el item de venta
|
||||
SaleItem::create([
|
||||
'sale_id' => $sale->id,
|
||||
'sim_card_id' => $sim->id,
|
||||
'package_id' => $package->id,
|
||||
]);
|
||||
|
||||
$this->stats['sales_created']++;
|
||||
}
|
||||
|
||||
// Asignar paquete a la SIM (usando syncWithoutDetaching para evitar duplicados)
|
||||
// Primero verificamos si ya existe la relación activa
|
||||
// Asignar paquete a la SIM
|
||||
$hasActivePackage = $sim->packages()
|
||||
->wherePivot('package_id', $package->id)
|
||||
->wherePivot('is_active', true)
|
||||
@ -371,30 +345,40 @@ private function processSale(SimCard $sim, array $row)
|
||||
// Actualizar status de la SIM
|
||||
$sim->update(['status' => SimCardStatus::ASSIGNED]);
|
||||
|
||||
// stats
|
||||
$this->stats['sales_created']++;
|
||||
$this->stats['assigned']++;
|
||||
}
|
||||
|
||||
private function parseSaleDate($dateValue)
|
||||
{
|
||||
if (empty($dateValue)) {
|
||||
return now();
|
||||
return now()->startOfDay()->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
// Si es numérico
|
||||
if (is_numeric($dateValue)) {
|
||||
try {
|
||||
$excelBaseDate = new \DateTime('1899-12-30');
|
||||
$excelBaseDate->modify("+{$dateValue} days");
|
||||
return $excelBaseDate->format('Y-m-d H:i:s');
|
||||
return $excelBaseDate->format('Y-m-d') . ' 00:00:00';
|
||||
} catch (\Exception $e) {
|
||||
return now();
|
||||
return now()->startOfDay()->format('Y-m-d H:i:s');
|
||||
}
|
||||
}
|
||||
|
||||
// Intentar parsear como fecha
|
||||
// Si es string, intentar parsear como DD/MM/YYYY
|
||||
try {
|
||||
return \Carbon\Carbon::parse($dateValue)->format('Y-m-d H:i:s');
|
||||
// Primero intentar formato DD/MM/YYYY explícitamente
|
||||
$date = \Carbon\Carbon::createFromFormat('d/m/Y', $dateValue);
|
||||
return $date->startOfDay()->format('Y-m-d H:i:s');
|
||||
} catch (\Exception $e) {
|
||||
return now();
|
||||
// Fallback: intentar parsear automáticamente
|
||||
try {
|
||||
return \Carbon\Carbon::parse($dateValue)->startOfDay()->format('Y-m-d H:i:s');
|
||||
} catch (\Exception $e) {
|
||||
return now()->startOfDay()->format('Y-m-d H:i:s');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user