254 lines
7.6 KiB
PHP
254 lines
7.6 KiB
PHP
<?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);
|
|
}
|
|
} |