40097-vm/calculo_costos_v3.php
2026-05-19 01:22:09 +00:00

500 lines
24 KiB
PHP

<?php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
if (!isset($_SESSION['user_role']) || !in_array($_SESSION['user_role'], ['Administrador', 'admin'])) {
header('Location: dashboard.php');
exit();
}
$pageTitle = "Cálculo de Costos V3";
include 'db/config.php';
include 'layout_header.php';
$db = db();
// Obtener productos para el select del modal
$stmt_products = $db->query("SELECT id, nombre FROM products ORDER BY nombre ASC");
$productos_select = $stmt_products->fetchAll(PDO::FETCH_ASSOC);
// Obtener videos y sus costos asociados de las tablas V3
$stmt = $db->query("SELECT mv.id, mv.orden, mv.foto_producto, p.nombre as nombre_producto,
mc.costo_producto, mc.costo_fijo_film, mc.comision_asesora,
mc.delivery, mc.costo_publicitario, mc.inversion_total,
mc.promo_1, mc.promo_2, mc.promo_3,
mc.comision_asesora_provincia, mc.delivery_provincia
FROM marketing_videos_v3 mv
LEFT JOIN products p ON mv.producto_id = p.id
LEFT JOIN marketing_costos_v3 mc ON mv.id = mc.video_id
ORDER BY mv.orden ASC, mv.id DESC");
$costos = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<style>
.table-v3 {
font-size: 0.8rem;
border-collapse: separate;
border-spacing: 0 5px;
}
.table-v3 th {
background-color: #f8f9fa;
border-bottom: 2px solid #dee2e6;
white-space: nowrap;
padding: 10px 5px;
position: sticky;
top: 0;
z-index: 10;
}
.table-v3 td {
vertical-align: middle;
padding: 6px 3px;
background-color: #fff;
}
.input-cell {
width: 100%;
min-width: 70px;
padding: 6px;
border: 2px solid #e9ecef;
border-radius: 6px;
text-align: center;
font-size: 0.85rem;
font-weight: 500;
transition: all 0.2s;
}
.input-cell:focus {
border-color: #0d6efd;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.15);
outline: 0;
}
.input-cell.saving { background-color: #fff3cd !important; border-color: #ffc107 !important; }
.input-cell.saved { background-color: #d1e7dd !important; border-color: #198754 !important; }
.recaudo-v3 {
background-color: #f8f9fa;
font-weight: bold;
text-align: center;
padding: 6px 4px;
border-radius: 6px;
min-width: 85px;
border: 1px solid #dee2e6;
font-size: 0.8rem;
}
.recaudo-label {
font-size: 0.65rem;
display: block;
color: #6c757d;
margin-bottom: 2px;
}
.img-v3 {
width: 120px;
height: 120px;
object-fit: cover;
border-radius: 12px;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
box-shadow: 0 4px 12px rgba(0,0,0,0.12);
}
.img-v3:hover {
transform: scale(2.2);
z-index: 100;
position: relative;
box-shadow: 0 15px 30px rgba(0,0,0,0.2);
}
.img-placeholder {
width: 120px;
height: 120px;
cursor: pointer;
transition: background-color 0.2s;
border-radius: 12px;
}
.img-placeholder:hover {
background-color: #e2e6ea !important;
}
.btn-action {
width: 28px;
height: 28px;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 6px;
}
.bg-provincia {
background-color: #fff4e6 !important;
}
.header-provincia {
background-color: #ffe8cc !important;
color: #d9480f !important;
}
</style>
<div class="row align-items-center mb-4 mt-2">
<div class="col-md-6">
<p class="text-muted mb-0">Total de registros encontrados: <span class="fw-bold text-dark"><?php echo count($costos); ?></span></p>
</div>
<div class="col-md-6 text-end">
<button type="button" class="btn btn-outline-secondary me-2" onclick="location.reload()">
<i class="fas fa-sync-alt"></i> Actualizar
</button>
<button type="button" id="btnAbrirModalV3" class="btn btn-success btn-lg shadow-sm px-4">
<i class="fas fa-plus-circle me-2"></i> CREAR NUEVO PRODUCTO
</button>
</div>
</div>
<?php if (isset($_GET['success'])): ?>
<div class="alert alert-success alert-dismissible fade show shadow-sm" role="alert">
<i class="fas fa-check-circle me-2"></i> ¡Operación realizada con éxito!
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<?php if (isset($_GET['error'])): ?>
<div class="alert alert-danger alert-dismissible fade show shadow-sm" role="alert">
<i class="fas fa-exclamation-triangle me-2"></i> <?php echo htmlspecialchars($_GET['error']); ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<div class="card shadow-sm border-0">
<div class="card-body p-0">
<div class="table-responsive" style="max-height: 70vh;">
<table class="table table-v3 mb-0">
<thead>
<tr>
<th class="text-center">Orden</th>
<th>Producto</th>
<th class="text-center">Imagen</th>
<th class="text-center">Costo Prod.</th>
<th class="text-center">Film</th>
<th class="text-center">Asesora (L)</th>
<th class="text-center header-provincia">Asesora (P)</th>
<th class="text-center">Delivery (L)</th>
<th class="text-center header-provincia">Delivery (P)</th>
<th class="text-center">Publicidad</th>
<th class="text-center">Promo 1</th>
<th class="text-center">Recaudo 1</th>
<th class="text-center">Promo 2</th>
<th class="text-center">Recaudo 2</th>
<th class="text-center">Promo 3</th>
<th class="text-center">Recaudo 3</th>
<th class="text-center">Acción</th>
</tr>
</thead>
<tbody>
<?php if (empty($costos)): ?>
<tr>
<td colspan="17" class="text-center py-5 text-muted">
<i class="fas fa-info-circle me-2"></i> No hay productos en esta lista. Haz clic en "Nuevo Producto" para empezar.
</td>
</tr>
<?php endif; ?>
<?php
if (!function_exists('getValV3')) {
function getValV3($promo) {
if (empty($promo)) return 0;
preg_match('/[\d.]+/', $promo, $matches);
return isset($matches[0]) ? floatval($matches[0]) : 0;
}
}
?>
<?php foreach ($costos as $c): ?>
<?php
$costo_prod = ($c['costo_producto'] ?? 0);
$costo_film = ($c['costo_fijo_film'] ?? 0);
$costo_asesora = ($c['comision_asesora'] ?? 0);
$costo_asesora_p = ($c['comision_asesora_provincia'] ?? 0);
$costo_delivery = ($c['delivery'] ?? 0);
$costo_delivery_p = ($c['delivery_provincia'] ?? 0);
$costo_publicidad = ($c['costo_publicitario'] ?? 0);
$p1 = getValV3($c['promo_1']);
$p2 = getValV3($c['promo_2']);
$p3 = getValV3($c['promo_3']);
// Cálculos Local
$r1_l = $p1 > 0 ? $p1 - ($costo_prod + $costo_film + $costo_asesora + $costo_delivery + $costo_publicidad) : null;
$r2_l = $p2 > 0 ? $p2 - (($costo_prod + $costo_film) * 2 + $costo_asesora + $costo_delivery + $costo_publicidad) : null;
$r3_l = $p3 > 0 ? $p3 - (($costo_prod + $costo_film) * 3 + $costo_asesora + $costo_delivery + $costo_publicidad) : null;
// Cálculos Provincia
$r1_p = $p1 > 0 ? $p1 - ($costo_prod + $costo_film + $costo_asesora_p + $costo_delivery_p + $costo_publicidad) : null;
$r2_p = $p2 > 0 ? $p2 - (($costo_prod + $costo_film) * 2 + $costo_asesora_p + $costo_delivery_p + $costo_publicidad) : null;
$r3_p = $p3 > 0 ? $p3 - (($costo_prod + $costo_film) * 3 + $costo_asesora_p + $costo_delivery_p + $costo_publicidad) : null;
?>
<tr id="row-<?php echo $c['id']; ?>" data-id="<?php echo $c['id']; ?>">
<td class="text-center fw-bold text-primary"><?php echo $c['orden']; ?></td>
<td style="max-width: 120px;">
<div class="text-truncate fw-bold" title="<?php echo htmlspecialchars($c['nombre_producto']); ?>">
<?php echo htmlspecialchars($c['nombre_producto'] ?: 'General'); ?>
</div>
</td>
<td class="text-center">
<div class="position-relative d-inline-block">
<?php if ($c['foto_producto']): ?>
<img src="<?php echo $c['foto_producto']; ?>" class="img-v3" onclick="triggerImageUpload(<?php echo $c['id']; ?>)">
<?php else: ?>
<div class="bg-light rounded d-flex align-items-center justify-content-center img-placeholder" onclick="triggerImageUpload(<?php echo $c['id']; ?>)">
<i class="fas fa-camera text-muted"></i>
</div>
<?php endif; ?>
<input type="file" id="file-input-<?php echo $c['id']; ?>" class="d-none" accept="image/*" onchange="uploadImage(<?php echo $c['id']; ?>)">
</div>
</td>
<td><input type="number" step="0.01" class="input-cell cost-input" data-field="costo_producto" value="<?php echo $c['costo_producto']; ?>"></td>
<td><input type="number" step="0.01" class="input-cell cost-input" data-field="costo_fijo_film" value="<?php echo $c['costo_fijo_film']; ?>"></td>
<td><input type="number" step="0.01" class="input-cell cost-input" data-field="comision_asesora" value="<?php echo $c['comision_asesora']; ?>"></td>
<td><input type="number" step="0.01" class="input-cell cost-input bg-provincia" data-field="comision_asesora_provincia" value="<?php echo $c['comision_asesora_provincia']; ?>"></td>
<td><input type="number" step="0.01" class="input-cell cost-input" data-field="delivery" value="<?php echo $c['delivery']; ?>"></td>
<td><input type="number" step="0.01" class="input-cell cost-input bg-provincia" data-field="delivery_provincia" value="<?php echo $c['delivery_provincia']; ?>"></td>
<td><input type="number" step="0.01" class="input-cell cost-input" data-field="costo_publicitario" value="<?php echo $c['costo_publicitario']; ?>"></td>
<td><input type="text" class="input-cell promo-input" data-field="promo_1" value="<?php echo htmlspecialchars($c['promo_1']); ?>"></td>
<td>
<div class="recaudo-v3 mb-1 <?php echo $r1_l >= 0 ? 'text-success' : 'text-danger'; ?>" id="r1l-<?php echo $c['id']; ?>">
<span class="recaudo-label">LOCAL</span>
<?php echo $r1_l !== null ? 'S/ '.number_format($r1_l, 2) : '-'; ?>
</div>
<div class="recaudo-v3 bg-provincia <?php echo $r1_p >= 0 ? 'text-success' : 'text-danger'; ?>" id="r1p-<?php echo $c['id']; ?>">
<span class="recaudo-label">PROVINCIA</span>
<?php echo $r1_p !== null ? 'S/ '.number_format($r1_p, 2) : '-'; ?>
</div>
</td>
<td><input type="text" class="input-cell promo-input" data-field="promo_2" value="<?php echo htmlspecialchars($c['promo_2']); ?>"></td>
<td>
<div class="recaudo-v3 mb-1 <?php echo $r2_l >= 0 ? 'text-success' : 'text-danger'; ?>" id="r2l-<?php echo $c['id']; ?>">
<span class="recaudo-label">LOCAL</span>
<?php echo $r2_l !== null ? 'S/ '.number_format($r2_l, 2) : '-'; ?>
</div>
<div class="recaudo-v3 bg-provincia <?php echo $r2_p >= 0 ? 'text-success' : 'text-danger'; ?>" id="r2p-<?php echo $c['id']; ?>">
<span class="recaudo-label">PROVINCIA</span>
<?php echo $r2_p !== null ? 'S/ '.number_format($r2_p, 2) : '-'; ?>
</div>
</td>
<td><input type="text" class="input-cell promo-input" data-field="promo_3" value="<?php echo htmlspecialchars($c['promo_3']); ?>"></td>
<td>
<div class="recaudo-v3 mb-1 <?php echo $r3_l >= 0 ? 'text-success' : 'text-danger'; ?>" id="r3l-<?php echo $c['id']; ?>">
<span class="recaudo-label">LOCAL</span>
<?php echo $r3_l !== null ? 'S/ '.number_format($r3_l, 2) : '-'; ?>
</div>
<div class="recaudo-v3 bg-provincia <?php echo $r3_p >= 0 ? 'text-success' : 'text-danger'; ?>" id="r3p-<?php echo $c['id']; ?>">
<span class="recaudo-label">PROVINCIA</span>
<?php echo $r3_p !== null ? 'S/ '.number_format($r3_p, 2) : '-'; ?>
</div>
</td>
<td class="text-center">
<button class="btn btn-danger btn-action" onclick="eliminarRow(<?php echo $c['id']; ?>)" title="Eliminar">
<i class="fas fa-trash-alt"></i>
</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<!-- Modal Nuevo -->
<div class="modal fade" id="modalNuevoV3" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 shadow">
<form action="save_marketing_video_v3.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="redirect" value="calculo_costos_v3.php?success=1">
<div class="modal-header bg-primary text-white">
<h5 class="modal-title"><i class="fas fa-plus-circle me-2"></i> Nuevo Producto (V3)</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body p-4">
<div class="mb-3">
<label class="form-label fw-bold">Seleccionar Producto</label>
<select name="producto_id" class="form-select form-select-lg" onchange="fetchProductCost(this.value)">
<option value="">General / Sin producto específico</option>
<?php foreach ($productos_select as $p): ?>
<option value="<?php echo $p['id']; ?>"><?php echo htmlspecialchars($p['nombre']); ?></option>
<?php endforeach; ?>
</select>
<small class="text-muted">Si no seleccionas uno, se marcará como "General".</small>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">Costo Base (S/)</label>
<input type="number" name="costo_producto" id="costo_producto_nuevo_v3" class="form-control" step="0.01" placeholder="0.00">
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">Orden de Lista</label>
<input type="number" name="orden" class="form-control" value="1">
</div>
</div>
<div class="mb-0">
<label class="form-label fw-bold">Imagen del Producto</label>
<input type="file" name="foto_producto" class="form-control">
</div>
</div>
<div class="modal-footer bg-light">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
<button type="submit" class="btn btn-primary px-4">Crear Registro</button>
</div>
</form>
</div>
</div>
</div>
<script>
function fetchProductCost(productId) {
if (!productId) return;
fetch(`get_product_details.php?id=${productId}`)
.then(response => response.json())
.then(data => {
if (data.success && data.product.costo) {
document.getElementById('costo_producto_nuevo_v3').value = data.product.costo;
}
});
}
document.querySelectorAll('.input-cell').forEach(input => {
// Actualización instantánea visual
input.addEventListener('input', function() {
const row = this.closest('tr');
recalculateRowVisual(row);
});
// Guardado real al salir del cuadro
input.addEventListener('change', function() {
const row = this.closest('tr');
const id = row.dataset.id;
const field = this.dataset.field;
const value = this.value;
this.classList.add('saving');
fetch('update_marketing_costos_field_v3.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id, field, value })
})
.then(response => response.json())
.then(data => {
this.classList.remove('saving');
if (data.success) {
this.classList.add('saved');
setTimeout(() => this.classList.remove('saved'), 1000);
}
});
});
});
function recalculateRowVisual(row) {
const id = row.dataset.id;
const costProd = parseFloat(row.querySelector('[data-field="costo_producto"]').value) || 0;
const costFilm = parseFloat(row.querySelector('[data-field="costo_fijo_film"]').value) || 0;
const costAsesora = parseFloat(row.querySelector('[data-field="comision_asesora"]').value) || 0;
const costAsesoraP = parseFloat(row.querySelector('[data-field="comision_asesora_provincia"]').value) || 0;
const costDelivery = parseFloat(row.querySelector('[data-field="delivery"]').value) || 0;
const costDeliveryP = parseFloat(row.querySelector('[data-field="delivery_provincia"]').value) || 0;
const costPublicidad = parseFloat(row.querySelector('[data-field="costo_publicitario"]').value) || 0;
for (let i = 1; i <= 3; i++) {
const promoVal = row.querySelector(`[data-field="promo_${i}"]`).value;
const match = promoVal.match(/[\d.]+/);
const pVal = match ? parseFloat(match[0]) : 0;
const recaudoElL = row.querySelector(`#r${i}l-${id}`);
const recaudoElP = row.querySelector(`#r${i}p-${id}`);
if (pVal > 0) {
// Local
const recaudoL = pVal - ((costProd + costFilm) * i + costAsesora + costDelivery + costPublicidad);
recaudoElL.innerHTML = `<span class="recaudo-label">LOCAL</span>S/ ${recaudoL.toFixed(2)}`;
recaudoElL.className = 'recaudo-v3 mb-1 ' + (recaudoL >= 0 ? 'text-success' : 'text-danger');
// Provincia
const recaudoP = pVal - ((costProd + costFilm) * i + costAsesoraP + costDeliveryP + costPublicidad);
recaudoElP.innerHTML = `<span class="recaudo-label">PROVINCIA</span>S/ ${recaudoP.toFixed(2)}`;
recaudoElP.className = 'recaudo-v3 bg-provincia ' + (recaudoP >= 0 ? 'text-success' : 'text-danger');
} else {
recaudoElL.innerHTML = `<span class="recaudo-label">LOCAL</span>-`;
recaudoElP.innerHTML = `<span class="recaudo-label">PROVINCIA</span>-`;
}
}
}
function eliminarRow(id) {
if (confirm("¿Eliminar este registro de la V3?")) {
window.location.href = "delete_marketing_video_v3.php?id=" + id;
}
}
function triggerImageUpload(id) {
document.getElementById(`file-input-${id}`).click();
}
function uploadImage(id) {
const fileInput = document.getElementById(`file-input-${id}`);
if (!fileInput.files || !fileInput.files[0]) return;
const formData = new FormData();
formData.append('id', id);
formData.append('foto_producto', fileInput.files[0]);
const cell = fileInput.closest('td');
const originalContent = cell.innerHTML;
cell.innerHTML = '<div class="spinner-border spinner-border-sm text-primary" role="status"></div>';
fetch('update_marketing_video_image_v3.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload(); // Recargamos para ver la nueva imagen
} else {
alert('Error al subir imagen: ' + data.error);
cell.innerHTML = originalContent;
}
})
.catch(err => {
console.error(err);
alert('Error técnico al subir imagen');
cell.innerHTML = originalContent;
});
}
// Script de apertura forzada y diagnóstico
document.addEventListener('DOMContentLoaded', function() {
const btn = document.getElementById('btnAbrirModalV3');
const modalEl = document.getElementById('modalNuevoV3');
if (btn && modalEl) {
btn.addEventListener('click', function() {
console.log('Intento de apertura manual del modal...');
try {
// Intento 1: Usando la API de Bootstrap 5
if (typeof bootstrap !== 'undefined') {
const modalInstance = new bootstrap.Modal(modalEl);
modalInstance.show();
console.log('Modal abierto con bootstrap.Modal');
} else {
// Intento 2: Si bootstrap no está definido, intentar vía jQuery si existe
if (typeof $ !== 'undefined' && typeof $.fn.modal !== 'undefined') {
$(modalEl).modal('show');
console.log('Modal abierto con jQuery');
} else {
alert('Error: No se encontró la librería de Bootstrap. Por favor, recarga la página.');
}
}
} catch (err) {
console.error('Error al abrir modal:', err);
alert('Hubo un problema técnico al abrir la ventana. Error: ' + err.message);
}
});
} else {
console.error('No se encontró el botón o el modal en el DOM');
}
});
</script>
<?php include 'layout_footer.php'; ?>