Carpeta models

This commit is contained in:
Axel Axt
2026-08-04 19:24:03 -03:00
parent ea2a40d0d5
commit 24d0bf55c9
12 changed files with 2944 additions and 0 deletions
+254
View File
@@ -0,0 +1,254 @@
<?php
require_once __DIR__ . '/../database/database.php';
class pedidoModel {
private PDO $db;
private ?string $error = null;
public function __construct() {
$this->db = Database::getConnection();
}
// MÉTODO PARA OBTENER ERRORES E INFORMARLOS A TRAVÉZ DEL CONTROLLER
public function getError(): ?string {
return $this->error;
}
// GUARDAR PEDIDO (retorna false o el ID del pedido generado)
public function guardarPedido(array $productosFinales, array $proveedor, array $vendedor, float $total): int|false {
// Validaciones (ya verifica el controller también)
if (empty($productosFinales)) {
$this->error = 'El pedido no tiene productos.';
return false;
}
if (empty($proveedor['id'])) {
$this->error = 'Proveedor inválido.';
return false;
}
if (empty($vendedor['id'])) {
$this->error = 'No se pudo vincular al usuario.';
return false;
}
if ($total <= 0) {
$this->error = 'Total de pedido inválido.';
return false;
}
foreach ($productosFinales as $p) {
if (empty($p['id_producto']) || empty($p['cantidad']) || empty($p['costo']) || empty($p['precio'])) {
$this->error = 'Producto inválido en el pedido.';
return false;
}
if ($p['cantidad'] <= 0 || $p['costo'] < 0) {
$this->error = 'Cantidad o costo inválido.';
return false;
}
}
try {
$this->db->beginTransaction();
// 1. INSERT PEDIDO
$sqlPedido = "
INSERT INTO pedidos (total, id_proveedor, id_usuario)
VALUES (:total, :id_proveedor, :id_usuario)
RETURNING id
";
$stmtPedido = $this->db->prepare($sqlPedido);
$stmtPedido->execute([
':total' => $total,
':id_proveedor' => $proveedor['id'],
':id_usuario' => $vendedor['id']
]);
$idPedido = $stmtPedido->fetchColumn();
if (!$idPedido) {
$this->error = 'No se pudo crear el pedido.';
$this->db->rollBack();
return false;
}
// 2. INSERT DETALLE (LOOP)
$sqlDetalle = "
INSERT INTO pedido_detalle
(precio_unitario, cantidad, subtotal, id_pedido, id_producto)
VALUES
(:precio_unitario, :cantidad, :subtotal, :id_pedido, :id_producto)
";
$stmtDetalle = $this->db->prepare($sqlDetalle);
foreach ($productosFinales as $p) {
$subtotal = $p['costo'] * $p['cantidad'];
if (!$stmtDetalle->execute([
':precio_unitario' => $p['costo'],
':cantidad' => $p['cantidad'],
':subtotal' => $subtotal,
':id_pedido' => $idPedido,
':id_producto' => $p['id_producto'],
])
) {
$this->error = 'Error al guardar el detalle del pedido.';
$this->db->rollBack();
return false;
}
}
// 3. ACTUALIZAR DATOS DE PRODUCTO
$sqlStock = "
UPDATE productos
SET
stock = COALESCE(stock, 0) + :cantidad,
precio = :precio
WHERE id = :id_producto
";
$stmtStock = $this->db->prepare($sqlStock);
foreach ($productosFinales as $p) {
if (!$stmtStock->execute([
':cantidad' => $p['cantidad'],
':precio' => $p['precio'],
':id_producto' => $p['id_producto'],
])
) {
$this->error = 'Error al actualizar el producto.';
$this->db->rollBack();
return false;
}
}
// 4. COMMIT
$this->db->commit();
return $idPedido;
} catch (Throwable $e) {
$this->db->rollBack();
// Log interno
error_log($e->getMessage());
$this->error = 'Error interno al guardar el pedido.';
return false;
}
}
// LISTAR PEDIDOS
public function listarPedidos(int $anio): ?array {
$sql = "SELECT p.id, p.fecha, p.total, pe.razon_social, u.nombre AS vendedor_nombre, u.apellido AS vendedor_apellido FROM pedidos AS p
INNER JOIN proveedores AS pe ON (p.id_proveedor = pe.id)
INNER JOIN usuarios AS u ON (p.id_usuario = u.id)
WHERE EXTRACT(YEAR FROM p.fecha) = :anio
ORDER BY p.id ASC";
$stmt = $this->db->prepare($sql);
$stmt->execute([':anio' => $anio]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// OBTENER DETALLE DE PEDIDO
public function getDetallePedido(int $idPedido): ?array {
$sql = "SELECT p.id AS id_producto, p.marca, p.modelo, pd.precio_unitario, pd.cantidad, pd.subtotal FROM pedido_detalle AS pd
INNER JOIN productos AS p ON (pd.id_producto = p.id)
WHERE (pd.id_pedido = :id_pedido)
ORDER BY p.id ASC";
$stmt = $this->db->prepare($sql);
$stmt->execute([':id_pedido' => $idPedido]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// OBTENER PREVIEW DE CODIGO DE REMITO (no se utiliza más)
/*
public function getNumeroRemitoPreview(): int {
$sql = "SELECT nextval('remito_preview_seq')";
$stmt = $this->db->prepare($sql);
$stmt->execute();
return (int) $stmt->fetchColumn();
}
*/
// OBTENER COSTO DE PRODUCTO
public function obtenerCosto(int $idProducto): ?float {
$sql = "SELECT pd.precio_unitario FROM pedido_detalle AS pd
INNER JOIN productos AS p ON (pd.id_producto = p.id)
WHERE (p.id = :idProducto)
AND pd.id_pedido = (
SELECT MAX(pd2.id_pedido)
FROM pedido_detalle AS pd2
WHERE pd2.id_producto = :idProducto2
)";
$stmt = $this->db->prepare($sql);
$stmt->execute([
':idProducto' => $idProducto,
':idProducto2' => $idProducto
]);
$resultado = $stmt->fetchColumn();
return $resultado !== false ? (float) $resultado : null;
}
// OBTENER COSTOS DE PRODUCTOS EN BASE A UNA CATEGORIA
public function obtenerCostosPorCategoria(int $idCategoria): array {
// Uso de LATERAL en LEFT JOIN -> permite traer el costo mas reciente (actúa como un foreach, ejecutando la subconsulta por cada producto)
$sql = "SELECT
p.id AS id_producto, pd.precio_unitario AS costo FROM productos AS p
LEFT JOIN LATERAL (
SELECT pd.precio_unitario FROM pedido_detalle AS pd
INNER JOIN pedidos AS pe ON (pe.id = pd.id_pedido)
WHERE (pd.id_producto = p.id)
ORDER BY pe.fecha DESC, pd.id DESC LIMIT 1
) pd ON true
WHERE (p.id_categoria = :idCategoria)";
$stmt = $this->db->prepare($sql);
$stmt->execute([':idCategoria' => $idCategoria]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}