feat: agregar reporte de ventas en formato Excel y ajustar lógica de descuentos en ventas
This commit is contained in:
parent
da92d0dc1a
commit
c5d8e3c65b
@ -25,7 +25,7 @@ public function index(Request $request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($request->has('client_number') && $request->client_number) {
|
if ($request->has('client_number') && $request->client_number) {
|
||||||
$query->where('client_number', $request->client_number);
|
$query->where('client_number', 'like', "%{$request->client_number}%");
|
||||||
}
|
}
|
||||||
|
|
||||||
elseif ($request->has('q') && $request->q) {
|
elseif ($request->has('q') && $request->q) {
|
||||||
@ -55,11 +55,16 @@ public function store(Request $request)
|
|||||||
'email' => 'nullable|email|max:255',
|
'email' => 'nullable|email|max:255',
|
||||||
'phone' => 'nullable|string|max:20',
|
'phone' => 'nullable|string|max:20',
|
||||||
'address' => 'nullable|string|max:500',
|
'address' => 'nullable|string|max:500',
|
||||||
'rfc' => 'nullable|string|max:13',
|
'rfc' => 'required|string|max:13',
|
||||||
|
'razon_social' => 'nullable|string|max:255',
|
||||||
|
'regimen_fiscal' => 'nullable|string|max:100',
|
||||||
|
'cp_fiscal' => 'nullable|string|max:5',
|
||||||
|
'uso_cfdi' => 'nullable|string|max:100',
|
||||||
],[
|
],[
|
||||||
'email.unique' => 'El correo electrónico ya está en uso por otro cliente.',
|
'email.unique' => 'El correo electrónico ya está en uso por otro cliente.',
|
||||||
'phone.unique' => 'El teléfono ya está en uso por otro cliente.',
|
'phone.unique' => 'El teléfono ya está en uso por otro cliente.',
|
||||||
'rfc.unique' => 'El RFC ya está en uso por otro cliente.',
|
'rfc.unique' => 'El RFC ya está en uso por otro cliente.',
|
||||||
|
'rfc.required' => 'El RFC es obligatorio.',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
try{
|
try{
|
||||||
@ -69,10 +74,14 @@ public function store(Request $request)
|
|||||||
'phone',
|
'phone',
|
||||||
'address',
|
'address',
|
||||||
'rfc',
|
'rfc',
|
||||||
|
'razon_social',
|
||||||
|
'regimen_fiscal',
|
||||||
|
'cp_fiscal',
|
||||||
|
'uso_cfdi',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Generar client_number automáticamente
|
// Usar RFC como client_number
|
||||||
$data['client_number'] = $this->clientTierService->generateClientNumber();
|
$data['client_number'] = $data['rfc'] ?? null;
|
||||||
|
|
||||||
$client = Client::create($data);
|
$client = Client::create($data);
|
||||||
|
|
||||||
@ -113,6 +122,10 @@ public function update(Request $request, Client $client)
|
|||||||
'phone',
|
'phone',
|
||||||
'address',
|
'address',
|
||||||
'rfc',
|
'rfc',
|
||||||
|
'razon_social',
|
||||||
|
'regimen_fiscal',
|
||||||
|
'cp_fiscal',
|
||||||
|
'uso_cfdi',
|
||||||
]));
|
]));
|
||||||
|
|
||||||
return ApiResponse::OK->response([
|
return ApiResponse::OK->response([
|
||||||
|
|||||||
@ -35,8 +35,7 @@ public function clientDiscountsReport(Request $request)
|
|||||||
$clients = Client::whereNotNull('tier_id')
|
$clients = Client::whereNotNull('tier_id')
|
||||||
->with('tier:id,tier_name,discount_percentage')
|
->with('tier:id,tier_name,discount_percentage')
|
||||||
->whereHas('sales', function($q) use ($fechaInicio, $fechaFin) {
|
->whereHas('sales', function($q) use ($fechaInicio, $fechaFin) {
|
||||||
$q->where('discount_amount', '>', 0)
|
$q->whereBetween('created_at', [$fechaInicio, $fechaFin]);
|
||||||
->whereBetween('created_at', [$fechaInicio, $fechaFin]);
|
|
||||||
})
|
})
|
||||||
->when($tierId, function($query) use ($tierId) {
|
->when($tierId, function($query) use ($tierId) {
|
||||||
$query->where('tier_id', $tierId);
|
$query->where('tier_id', $tierId);
|
||||||
@ -50,7 +49,6 @@ public function clientDiscountsReport(Request $request)
|
|||||||
// 3. MAPEO DE DATOS
|
// 3. MAPEO DE DATOS
|
||||||
$data = $clients->map(function($client) use ($fechaInicio, $fechaFin) {
|
$data = $clients->map(function($client) use ($fechaInicio, $fechaFin) {
|
||||||
$sales = $client->sales()
|
$sales = $client->sales()
|
||||||
->where('discount_amount', '>', 0)
|
|
||||||
->whereBetween('created_at', [$fechaInicio, $fechaFin])
|
->whereBetween('created_at', [$fechaInicio, $fechaFin])
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
@ -223,4 +221,249 @@ public function clientDiscountsReport(Request $request)
|
|||||||
|
|
||||||
return response()->download($filePath, $fileName)->deleteFileAfterSend(true);
|
return response()->download($filePath, $fileName)->deleteFileAfterSend(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generar reporte Excel de ventas
|
||||||
|
*/
|
||||||
|
public function salesReport(Request $request)
|
||||||
|
{
|
||||||
|
// 1. VALIDACIÓN
|
||||||
|
$request->validate([
|
||||||
|
'fecha_inicio' => 'required|date',
|
||||||
|
'fecha_fin' => 'required|date|after_or_equal:fecha_inicio',
|
||||||
|
'client_id' => 'nullable|exists:clients,id',
|
||||||
|
'user_id' => 'nullable|exists:users,id',
|
||||||
|
'status' => 'nullable|in:completed,cancelled',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$fechaInicio = Carbon::parse($request->fecha_inicio)->startOfDay();
|
||||||
|
$fechaFin = Carbon::parse($request->fecha_fin)->endOfDay();
|
||||||
|
|
||||||
|
// 2. CONSULTA DE VENTAS
|
||||||
|
$sales = Sale::with(['client:id,name,client_number,rfc', 'user:id,name', 'details'])
|
||||||
|
->whereBetween('created_at', [$fechaInicio, $fechaFin])
|
||||||
|
->when($request->client_id, function($query) use ($request) {
|
||||||
|
$query->where('client_id', $request->client_id);
|
||||||
|
})
|
||||||
|
->when($request->user_id, function($query) use ($request) {
|
||||||
|
$query->where('user_id', $request->user_id);
|
||||||
|
})
|
||||||
|
->when($request->status, function($query) use ($request) {
|
||||||
|
$query->where('status', $request->status);
|
||||||
|
})
|
||||||
|
->orderBy('created_at', 'desc')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
if ($sales->isEmpty()) {
|
||||||
|
return response()->json(['message' => 'No se encontraron ventas en el periodo especificado'], 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. MAPEO DE DATOS
|
||||||
|
$data = $sales->map(function($sale) {
|
||||||
|
return [
|
||||||
|
'folio' => $sale->invoice_number,
|
||||||
|
'fecha' => $sale->created_at->format('d/m/Y H:i'),
|
||||||
|
'cliente' => $sale->client?->name ?? 'N/A',
|
||||||
|
'rfc_cliente' => $sale->client?->rfc ?? 'N/A',
|
||||||
|
'vendedor' => $sale->user?->name ?? 'N/A',
|
||||||
|
'subtotal' => (float) $sale->subtotal,
|
||||||
|
'iva' => (float) $sale->tax,
|
||||||
|
'descuento' => (float) ($sale->discount_amount ?? 0),
|
||||||
|
'total' => (float) $sale->total,
|
||||||
|
'metodo_pago' => $this->translatePaymentMethod($sale->payment_method),
|
||||||
|
'status' => $sale->status === 'completed' ? 'Completada' : 'Cancelada',
|
||||||
|
'productos' => $sale->details->count(),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. CONFIGURACIÓN EXCEL
|
||||||
|
$fileName = 'Reporte_Ventas_' . $fechaInicio->format('Ymd') . '_' . $fechaFin->format('Ymd') . '.xlsx';
|
||||||
|
$filePath = storage_path('app/temp/' . $fileName);
|
||||||
|
if (!file_exists(dirname($filePath))) mkdir(dirname($filePath), 0755, true);
|
||||||
|
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
$sheet = $spreadsheet->getActiveSheet();
|
||||||
|
|
||||||
|
// Fuente Global
|
||||||
|
$sheet->getParent()->getDefaultStyle()->getFont()->setName('Arial');
|
||||||
|
$sheet->getParent()->getDefaultStyle()->getFont()->setSize(10);
|
||||||
|
|
||||||
|
// Estilos Comunes
|
||||||
|
$styleBox = [
|
||||||
|
'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN, 'color' => ['rgb' => '000000']]],
|
||||||
|
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER]
|
||||||
|
];
|
||||||
|
$styleLabel = [
|
||||||
|
'font' => ['size' => 12, 'bold' => true],
|
||||||
|
'alignment' => ['horizontal' => Alignment::HORIZONTAL_RIGHT, 'vertical' => Alignment::VERTICAL_CENTER]
|
||||||
|
];
|
||||||
|
$styleTableHeader = [
|
||||||
|
'font' => ['bold' => true, 'size' => 10, 'color' => ['rgb' => 'FFFFFF']],
|
||||||
|
'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => '2E75B6']],
|
||||||
|
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER, 'wrapText' => true],
|
||||||
|
'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN]]
|
||||||
|
];
|
||||||
|
|
||||||
|
// --- ESTRUCTURA DEL DOCUMENTO ---
|
||||||
|
$lastCol = 'L';
|
||||||
|
$sheet->getRowDimension(2)->setRowHeight(10);
|
||||||
|
$sheet->getRowDimension(3)->setRowHeight(25);
|
||||||
|
$sheet->getRowDimension(5)->setRowHeight(30);
|
||||||
|
|
||||||
|
// --- TÍTULO PRINCIPAL ---
|
||||||
|
$sheet->mergeCells("A3:{$lastCol}3");
|
||||||
|
$sheet->setCellValue('A3', 'REPORTE DE VENTAS');
|
||||||
|
$sheet->getStyle('A3')->applyFromArray([
|
||||||
|
'font' => ['bold' => true, 'size' => 16, 'color' => ['rgb' => '000000']],
|
||||||
|
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// --- INFORMACIÓN DEL PERIODO ---
|
||||||
|
Carbon::setLocale('es');
|
||||||
|
if ($fechaInicio->format('m/Y') === $fechaFin->format('m/Y')) {
|
||||||
|
$periodoTexto = 'del ' . $fechaInicio->format('d') . ' al ' . $fechaFin->format('d') . ' de ' . $fechaFin->translatedFormat('F \d\e Y');
|
||||||
|
} else {
|
||||||
|
$periodoTexto = 'del ' . $fechaInicio->format('d/m/Y') . ' al ' . $fechaFin->format('d/m/Y');
|
||||||
|
}
|
||||||
|
|
||||||
|
$sheet->mergeCells('A5:B5');
|
||||||
|
$sheet->setCellValue('A5', 'PERÍODO:');
|
||||||
|
$sheet->getStyle('A5')->applyFromArray($styleLabel);
|
||||||
|
|
||||||
|
$sheet->mergeCells("C5:{$lastCol}5");
|
||||||
|
$sheet->setCellValue('C5', $periodoTexto);
|
||||||
|
$sheet->getStyle("C5:{$lastCol}5")->applyFromArray($styleBox);
|
||||||
|
$sheet->getStyle('C5')->getFont()->setSize(12);
|
||||||
|
|
||||||
|
// --- RESUMEN DE TOTALES ---
|
||||||
|
$totalVentas = $data->count();
|
||||||
|
$totalSubtotal = $data->sum('subtotal');
|
||||||
|
$totalIva = $data->sum('iva');
|
||||||
|
$totalDescuentos = $data->sum('descuento');
|
||||||
|
$totalMonto = $data->sum('total');
|
||||||
|
|
||||||
|
$row = 7;
|
||||||
|
$sheet->setCellValue('A' . $row, 'TOTAL VENTAS:');
|
||||||
|
$sheet->setCellValue('B' . $row, $totalVentas);
|
||||||
|
$sheet->setCellValue('D' . $row, 'SUBTOTAL:');
|
||||||
|
$sheet->setCellValue('E' . $row, '$' . number_format($totalSubtotal, 2));
|
||||||
|
$sheet->setCellValue('G' . $row, 'IVA:');
|
||||||
|
$sheet->setCellValue('H' . $row, '$' . number_format($totalIva, 2));
|
||||||
|
$sheet->setCellValue('I' . $row, 'DESCUENTOS:');
|
||||||
|
$sheet->setCellValue('J' . $row, '$' . number_format($totalDescuentos, 2));
|
||||||
|
$sheet->setCellValue('K' . $row, 'TOTAL:');
|
||||||
|
$sheet->setCellValue('L' . $row, '$' . number_format($totalMonto, 2));
|
||||||
|
|
||||||
|
$sheet->getStyle("A{$row}:{$lastCol}{$row}")->getFont()->setBold(true);
|
||||||
|
$sheet->getStyle('B' . $row)->getFont()->setSize(12)->getColor()->setRGB('2E75B6');
|
||||||
|
$sheet->getStyle('E' . $row)->getFont()->setSize(12)->getColor()->setRGB('2E75B6');
|
||||||
|
$sheet->getStyle('H' . $row)->getFont()->setSize(12)->getColor()->setRGB('2E75B6');
|
||||||
|
$sheet->getStyle('J' . $row)->getFont()->setSize(12)->getColor()->setRGB('FF6600');
|
||||||
|
$sheet->getStyle('L' . $row)->getFont()->setSize(12)->getColor()->setRGB('008000');
|
||||||
|
|
||||||
|
// --- ENCABEZADOS DE TABLA ---
|
||||||
|
$h = 9;
|
||||||
|
$headers = [
|
||||||
|
'A' => 'No.',
|
||||||
|
'B' => 'FOLIO',
|
||||||
|
'C' => 'FECHA',
|
||||||
|
'D' => 'CLIENTE',
|
||||||
|
'E' => 'RFC',
|
||||||
|
'F' => 'VENDEDOR',
|
||||||
|
'G' => 'SUBTOTAL',
|
||||||
|
'H' => 'IVA',
|
||||||
|
'I' => 'DESCUENTO',
|
||||||
|
'J' => 'TOTAL',
|
||||||
|
'K' => "MÉTODO\nPAGO",
|
||||||
|
'L' => 'ESTADO',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($headers as $col => $text) {
|
||||||
|
$sheet->setCellValue("{$col}{$h}", $text);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sheet->getStyle("A{$h}:{$lastCol}{$h}")->applyFromArray($styleTableHeader);
|
||||||
|
$sheet->getRowDimension($h)->setRowHeight(35);
|
||||||
|
|
||||||
|
// --- LLENADO DE DATOS ---
|
||||||
|
$row = 10;
|
||||||
|
$i = 1;
|
||||||
|
|
||||||
|
foreach ($data as $item) {
|
||||||
|
$sheet->setCellValue('A' . $row, $i);
|
||||||
|
$sheet->setCellValue('B' . $row, $item['folio']);
|
||||||
|
$sheet->setCellValue('C' . $row, $item['fecha']);
|
||||||
|
$sheet->setCellValue('D' . $row, $item['cliente']);
|
||||||
|
$sheet->setCellValue('E' . $row, $item['rfc_cliente']);
|
||||||
|
$sheet->setCellValue('F' . $row, $item['vendedor']);
|
||||||
|
$sheet->setCellValue('G' . $row, '$' . number_format($item['subtotal'], 2));
|
||||||
|
$sheet->setCellValue('H' . $row, '$' . number_format($item['iva'], 2));
|
||||||
|
$sheet->setCellValue('I' . $row, '$' . number_format($item['descuento'], 2));
|
||||||
|
$sheet->setCellValue('J' . $row, '$' . number_format($item['total'], 2));
|
||||||
|
$sheet->setCellValue('K' . $row, $item['metodo_pago']);
|
||||||
|
$sheet->setCellValue('L' . $row, $item['status']);
|
||||||
|
|
||||||
|
// Estilos de fila
|
||||||
|
$sheet->getStyle("A{$row}:{$lastCol}{$row}")->applyFromArray([
|
||||||
|
'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN]],
|
||||||
|
'alignment' => ['vertical' => Alignment::VERTICAL_CENTER],
|
||||||
|
'font' => ['size' => 10]
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Centrados
|
||||||
|
$sheet->getStyle("A{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
||||||
|
$sheet->getStyle("B{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
||||||
|
$sheet->getStyle("C{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
||||||
|
$sheet->getStyle("K{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
||||||
|
$sheet->getStyle("L{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
||||||
|
|
||||||
|
// Color de estado
|
||||||
|
if ($item['status'] === 'Cancelada') {
|
||||||
|
$sheet->getStyle("L{$row}")->getFont()->getColor()->setRGB('FF0000');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color alterno de filas
|
||||||
|
if ($i % 2 == 0) {
|
||||||
|
$sheet->getStyle("A{$row}:{$lastCol}{$row}")->getFill()
|
||||||
|
->setFillType(Fill::FILL_SOLID)
|
||||||
|
->getStartColor()->setRGB('F2F2F2');
|
||||||
|
}
|
||||||
|
|
||||||
|
$row++;
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- ANCHOS DE COLUMNA ---
|
||||||
|
$sheet->getColumnDimension('A')->setWidth(5);
|
||||||
|
$sheet->getColumnDimension('B')->setWidth(22);
|
||||||
|
$sheet->getColumnDimension('C')->setWidth(18);
|
||||||
|
$sheet->getColumnDimension('D')->setWidth(22);
|
||||||
|
$sheet->getColumnDimension('E')->setWidth(16);
|
||||||
|
$sheet->getColumnDimension('F')->setWidth(20);
|
||||||
|
$sheet->getColumnDimension('G')->setWidth(14);
|
||||||
|
$sheet->getColumnDimension('H')->setWidth(12);
|
||||||
|
$sheet->getColumnDimension('I')->setWidth(14);
|
||||||
|
$sheet->getColumnDimension('J')->setWidth(14);
|
||||||
|
$sheet->getColumnDimension('K')->setWidth(14);
|
||||||
|
$sheet->getColumnDimension('L')->setWidth(14);
|
||||||
|
|
||||||
|
$writer = new Xlsx($spreadsheet);
|
||||||
|
$writer->save($filePath);
|
||||||
|
|
||||||
|
return response()->download($filePath, $fileName)->deleteFileAfterSend(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traducir método de pago
|
||||||
|
*/
|
||||||
|
private function translatePaymentMethod(?string $method): string
|
||||||
|
{
|
||||||
|
return match($method) {
|
||||||
|
'cash' => 'Efectivo',
|
||||||
|
'credit_card' => 'T. Crédito',
|
||||||
|
'debit_card' => 'T. Débito',
|
||||||
|
'transfer' => 'Transferencia',
|
||||||
|
default => $method ?? 'N/A',
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,17 +30,9 @@ public function show(string $invoiceNumber)
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si ya tiene datos de facturación
|
|
||||||
if ($sale->client_id) {
|
|
||||||
return ApiResponse::NO_CONTENT->response([
|
|
||||||
'message' => 'Esta venta ya tiene datos de facturación registrados',
|
|
||||||
'client' => $sale->client,
|
|
||||||
'sale' => $this->formatSaleData($sale)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ApiResponse::OK->response([
|
return ApiResponse::OK->response([
|
||||||
'sale' => $this->formatSaleData($sale)
|
'sale' => $this->formatSaleData($sale),
|
||||||
|
'client' => $sale->client,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,21 +5,9 @@
|
|||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Models\ClientTier;
|
use App\Models\ClientTier;
|
||||||
use App\Models\ClientTierHistory;
|
use App\Models\ClientTierHistory;
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
|
|
||||||
class ClientTierService
|
class ClientTierService
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Generar número de cliente único secuencial
|
|
||||||
*/
|
|
||||||
public function generateClientNumber(): string
|
|
||||||
{
|
|
||||||
$lastClient = Client::orderBy('id', 'desc')->first();
|
|
||||||
$nextNumber = $lastClient ? ((int) substr($lastClient->client_number, 4)) + 1 : 1;
|
|
||||||
|
|
||||||
return 'CLI-' . str_pad($nextNumber, 4, '0', STR_PAD_LEFT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calcular y actualizar el tier del cliente basado en sus compras totales
|
* Calcular y actualizar el tier del cliente basado en sus compras totales
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -40,12 +40,11 @@ public function createSale(array $data)
|
|||||||
$discountPercentage = $this->clientTierService->getApplicableDiscount($client);
|
$discountPercentage = $this->clientTierService->getApplicableDiscount($client);
|
||||||
$clientTierName = $client->tier->tier_name;
|
$clientTierName = $client->tier->tier_name;
|
||||||
|
|
||||||
// Calcular descuento sobre el subtotal + tax
|
// Calcular descuento solo sobre el subtotal (sin IVA)
|
||||||
$totalBeforeDiscount = $data['subtotal'] + $data['tax'];
|
$discountAmount = round($data['subtotal'] * ($discountPercentage / 100), 2);
|
||||||
$discountAmount = round($totalBeforeDiscount * ($discountPercentage / 100), 2);
|
|
||||||
|
|
||||||
// Recalcular total con descuento
|
// Recalcular total: subtotal - descuento + IVA
|
||||||
$data['total'] = $totalBeforeDiscount - $discountAmount;
|
$data['total'] = ($data['subtotal'] - $discountAmount) + $data['tax'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calcular el cambio si es pago en efectivo
|
// Calcular el cambio si es pago en efectivo
|
||||||
@ -60,7 +59,7 @@ public function createSale(array $data)
|
|||||||
// 1. Crear la venta principal
|
// 1. Crear la venta principal
|
||||||
$sale = Sale::create([
|
$sale = Sale::create([
|
||||||
'user_id' => $data['user_id'],
|
'user_id' => $data['user_id'],
|
||||||
'client_id' => $data['client_id'] ?? null,
|
'client_id' => $client?->id ?? $data['client_id'] ?? null,
|
||||||
'cash_register_id' => $data['cash_register_id'] ?? $this->getCurrentCashRegister($data['user_id']),
|
'cash_register_id' => $data['cash_register_id'] ?? $this->getCurrentCashRegister($data['user_id']),
|
||||||
'invoice_number' => $data['invoice_number'] ?? $this->generateInvoiceNumber(),
|
'invoice_number' => $data['invoice_number'] ?? $this->generateInvoiceNumber(),
|
||||||
'subtotal' => $data['subtotal'],
|
'subtotal' => $data['subtotal'],
|
||||||
|
|||||||
@ -75,6 +75,7 @@
|
|||||||
Route::get('top-selling-product', [ReportController::class, 'topSellingProduct']);
|
Route::get('top-selling-product', [ReportController::class, 'topSellingProduct']);
|
||||||
Route::get('products-without-movement', [ReportController::class, 'productsWithoutMovement']);
|
Route::get('products-without-movement', [ReportController::class, 'productsWithoutMovement']);
|
||||||
Route::get('client-discounts/excel', [ExcelController::class, 'clientDiscountsReport']);
|
Route::get('client-discounts/excel', [ExcelController::class, 'clientDiscountsReport']);
|
||||||
|
Route::get('sales/excel', [ExcelController::class, 'salesReport']);
|
||||||
});
|
});
|
||||||
|
|
||||||
//CLIENTES
|
//CLIENTES
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user