Merge pull request #13 from Axia4/copilot/add-supercafe-module
Add SuperCafe module inside EntreAulas
This commit is contained in:
@@ -13,9 +13,13 @@ DATA/
|
|||||||
│ └── user2.json
|
│ └── user2.json
|
||||||
└── Centros/ # Centro data
|
└── Centros/ # Centro data
|
||||||
├── centro1/
|
├── centro1/
|
||||||
│ └── Aularios/
|
│ ├── Aularios/
|
||||||
│ ├── aulario_abc123.json
|
│ │ ├── aulario_abc123.json
|
||||||
│ └── aulario_xyz456.json
|
│ │ └── aulario_xyz456.json
|
||||||
|
│ └── SuperCafe/ # SuperCafe data (per centro)
|
||||||
|
│ ├── Menu.json # Menu items / categories (optional)
|
||||||
|
│ └── Comandas/ # One JSON file per order
|
||||||
|
│ └── sc<id>.json
|
||||||
└── centro2/
|
└── centro2/
|
||||||
└── Aularios/
|
└── Aularios/
|
||||||
└── aulario_def789.json
|
└── aulario_def789.json
|
||||||
@@ -23,6 +27,42 @@ DATA/
|
|||||||
|
|
||||||
## File Examples
|
## File Examples
|
||||||
|
|
||||||
|
### SuperCafe (persons come from the existing Alumnos system)
|
||||||
|
|
||||||
|
Persons are loaded automatically from the aulario Alumnos directories — no separate configuration file is needed.
|
||||||
|
See the **Aulario Student Names** section for the alumnos data format.
|
||||||
|
|
||||||
|
### SuperCafe Menu (DATA/entreaulas/Centros/{centro_id}/SuperCafe/Menu.json)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Bebidas": {
|
||||||
|
"Café": 1,
|
||||||
|
"Zumo": 1.5
|
||||||
|
},
|
||||||
|
"Comida": {
|
||||||
|
"Bocadillo": 2.5,
|
||||||
|
"Ensalada": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### SuperCafe Order (DATA/entreaulas/Centros/{centro_id}/SuperCafe/Comandas/{id}.json)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Fecha": "2024-01-15",
|
||||||
|
"Persona": "aulario_abc123:Juan",
|
||||||
|
"Comanda": "1x Café, 1x Bocadillo",
|
||||||
|
"Notas": "Sin azúcar",
|
||||||
|
"Estado": "Pedido"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`Persona` is stored as `{aulario_id}:{alumno_name}` and resolved to a display name at render time.
|
||||||
|
Order statuses: `Pedido`, `En preparación`, `Listo`, `Entregado`, `Deuda`.
|
||||||
|
A person with 3 or more orders in `Deuda` status cannot place new orders.
|
||||||
|
|
||||||
### Main Users (DATA/Usuarios.json)
|
### Main Users (DATA/Usuarios.json)
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|||||||
@@ -4,4 +4,8 @@
|
|||||||
|
|
||||||
<a class="sidebar-link" href="/entreaulas/">
|
<a class="sidebar-link" href="/entreaulas/">
|
||||||
<span>Mi aula</span>
|
<span>Mi aula</span>
|
||||||
|
</a>
|
||||||
|
<a class="sidebar-link" href="/entreaulas/supercafe.php">
|
||||||
|
<img src="/static/iconexperience/purchase_order_cart.png" alt="">
|
||||||
|
<span>SuperCafe</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -55,6 +55,15 @@ function safe_aulario_config_path($centro_id, $aulario_id)
|
|||||||
' . htmlspecialchars($aulario_name) . '
|
' . htmlspecialchars($aulario_name) . '
|
||||||
</a>';
|
</a>';
|
||||||
} ?>
|
} ?>
|
||||||
|
<?php if (in_array('supercafe:access', $_SESSION['auth_data']['permissions'] ?? [])): ?>
|
||||||
|
<a href="/entreaulas/supercafe.php" class="btn btn-warning grid-item">
|
||||||
|
<img src="/static/iconexperience/purchase_order_cart.png" height="125"
|
||||||
|
style="background: white; padding: 5px; border-radius: 10px;"
|
||||||
|
alt="Icono SuperCafe">
|
||||||
|
<br>
|
||||||
|
SuperCafe
|
||||||
|
</a>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
292
public_html/entreaulas/supercafe.php
Normal file
292
public_html/entreaulas/supercafe.php
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
<?php
|
||||||
|
require_once "_incl/auth_redir.php";
|
||||||
|
require_once "../_incl/tools.security.php";
|
||||||
|
|
||||||
|
if (!in_array('supercafe:access', $_SESSION['auth_data']['permissions'] ?? [])) {
|
||||||
|
header('HTTP/1.1 403 Forbidden');
|
||||||
|
die('Acceso denegado');
|
||||||
|
}
|
||||||
|
|
||||||
|
function safe_centro_id_sc($value)
|
||||||
|
{
|
||||||
|
return preg_replace('/[^0-9]/', '', (string)$value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sc_safe_order_id($value)
|
||||||
|
{
|
||||||
|
return preg_replace('/[^a-zA-Z0-9_-]/', '', basename((string)$value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load personas from the existing Alumnos system.
|
||||||
|
* Returns array keyed by "{aulario_id}:{alumno_name}" with
|
||||||
|
* ['Nombre', 'Region' (aulario display name), 'AularioID', 'HasPhoto'] entries.
|
||||||
|
*/
|
||||||
|
function sc_load_personas_from_alumnos($centro_id)
|
||||||
|
{
|
||||||
|
$aularios_path = "/DATA/entreaulas/Centros/$centro_id/Aularios";
|
||||||
|
$personas = [];
|
||||||
|
if (!is_dir($aularios_path)) {
|
||||||
|
return $personas;
|
||||||
|
}
|
||||||
|
foreach (glob("$aularios_path/*.json") ?: [] as $aulario_file) {
|
||||||
|
$aulario_id = basename($aulario_file, '.json');
|
||||||
|
$aulario_data = json_decode(file_get_contents($aulario_file), true);
|
||||||
|
$aulario_name = $aulario_data['name'] ?? $aulario_id;
|
||||||
|
$alumnos_path = "$aularios_path/$aulario_id/Alumnos";
|
||||||
|
if (!is_dir($alumnos_path)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
foreach (glob("$alumnos_path/*/", GLOB_ONLYDIR) ?: [] as $alumno_dir) {
|
||||||
|
$alumno_name = basename($alumno_dir);
|
||||||
|
$key = $aulario_id . ':' . $alumno_name;
|
||||||
|
$personas[$key] = [
|
||||||
|
'Nombre' => $alumno_name,
|
||||||
|
'Region' => $aulario_name,
|
||||||
|
'AularioID' => $aulario_id,
|
||||||
|
'HasPhoto' => file_exists("$alumno_dir/photo.jpg"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $personas;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a human-readable label for a persona key.
|
||||||
|
* Falls back to showing the raw stored value for legacy orders.
|
||||||
|
*/
|
||||||
|
function sc_persona_label($persona_key, $personas)
|
||||||
|
{
|
||||||
|
if (isset($personas[$persona_key])) {
|
||||||
|
$p = $personas[$persona_key];
|
||||||
|
return $p['Nombre'] . ' (' . $p['Region'] . ')';
|
||||||
|
}
|
||||||
|
return $persona_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
$centro_id = safe_centro_id_sc($_SESSION['auth_data']['entreaulas']['centro'] ?? '');
|
||||||
|
if ($centro_id === '') {
|
||||||
|
require_once "_incl/pre-body.php";
|
||||||
|
echo '<div class="card pad"><h1>SuperCafe</h1><p>No tienes un centro asignado.</p></div>';
|
||||||
|
require_once "_incl/post-body.php";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
define('SC_DATA_DIR', "/DATA/entreaulas/Centros/$centro_id/SuperCafe/Comandas");
|
||||||
|
define('SC_MAX_DEBTS', 3);
|
||||||
|
|
||||||
|
$estados_colores = [
|
||||||
|
'Pedido' => '#FFFFFF',
|
||||||
|
'En preparación' => '#FFCCCB',
|
||||||
|
'Listo' => 'gold',
|
||||||
|
'Entregado' => 'lightgreen',
|
||||||
|
'Deuda' => '#f5d3ff',
|
||||||
|
];
|
||||||
|
|
||||||
|
$can_edit = in_array('supercafe:edit', $_SESSION['auth_data']['permissions'] ?? []);
|
||||||
|
$personas = sc_load_personas_from_alumnos($centro_id);
|
||||||
|
|
||||||
|
// Handle POST actions (requires edit permission)
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $can_edit) {
|
||||||
|
$action = $_POST['action'] ?? '';
|
||||||
|
|
||||||
|
if ($action === 'change_status') {
|
||||||
|
$order_id = sc_safe_order_id($_POST['order_id'] ?? '');
|
||||||
|
$new_status = $_POST['status'] ?? '';
|
||||||
|
if ($order_id !== '' && array_key_exists($new_status, $estados_colores)) {
|
||||||
|
$order_file = SC_DATA_DIR . '/' . $order_id . '.json';
|
||||||
|
if (is_readable($order_file)) {
|
||||||
|
$data = json_decode(file_get_contents($order_file), true);
|
||||||
|
if (is_array($data)) {
|
||||||
|
$data['Estado'] = $new_status;
|
||||||
|
file_put_contents(
|
||||||
|
$order_file,
|
||||||
|
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE),
|
||||||
|
LOCK_EX
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header('Location: /entreaulas/supercafe.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($action === 'delete') {
|
||||||
|
$order_id = sc_safe_order_id($_POST['order_id'] ?? '');
|
||||||
|
if ($order_id !== '') {
|
||||||
|
$order_file = SC_DATA_DIR . '/' . $order_id . '.json';
|
||||||
|
if (is_file($order_file)) {
|
||||||
|
unlink($order_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header('Location: /entreaulas/supercafe.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load all orders
|
||||||
|
$orders = [];
|
||||||
|
if (is_dir(SC_DATA_DIR)) {
|
||||||
|
$files = glob(SC_DATA_DIR . '/*.json') ?: [];
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$data = json_decode(file_get_contents($file), true);
|
||||||
|
if (!is_array($data)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$data['_id'] = basename($file, '.json');
|
||||||
|
$orders[] = $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort newest first (by Fecha desc)
|
||||||
|
usort($orders, function ($a, $b) {
|
||||||
|
return strcmp($b['Fecha'] ?? '', $a['Fecha'] ?? '');
|
||||||
|
});
|
||||||
|
|
||||||
|
$orders_active = array_filter($orders, fn($o) => ($o['Estado'] ?? '') !== 'Deuda');
|
||||||
|
$orders_deuda = array_filter($orders, fn($o) => ($o['Estado'] ?? '') === 'Deuda');
|
||||||
|
|
||||||
|
require_once "_incl/pre-body.php";
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="card pad">
|
||||||
|
<div style="display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px;">
|
||||||
|
<h1 style="margin: 0;">SuperCafe – Cafetería</h1>
|
||||||
|
<?php if ($can_edit): ?>
|
||||||
|
<a href="/entreaulas/supercafe_edit.php" class="btn btn-success">+ Nueva comanda</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Active orders -->
|
||||||
|
<details class="card pad" open
|
||||||
|
style="background: beige; border: 2px solid black; border-radius: 15px;">
|
||||||
|
<summary style="font-weight: bold; font-size: 1.1rem; cursor: pointer;">
|
||||||
|
Todas las comandas (<?= count($orders_active) ?>)
|
||||||
|
</summary>
|
||||||
|
<div style="margin-top: 10px;">
|
||||||
|
<?php if (empty($orders_active)): ?>
|
||||||
|
<p style="color: #666;">No hay comandas activas.</p>
|
||||||
|
<?php else: ?>
|
||||||
|
<div style="overflow-x: auto;">
|
||||||
|
<table class="table table-bordered" style="margin-bottom: 0;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Fecha</th>
|
||||||
|
<th>Persona</th>
|
||||||
|
<th>Comanda</th>
|
||||||
|
<th>Notas</th>
|
||||||
|
<th>Estado</th>
|
||||||
|
<?php if ($can_edit): ?><th>Acciones</th><?php endif; ?>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($orders_active as $order):
|
||||||
|
$estado = $order['Estado'] ?? 'Pedido';
|
||||||
|
$bg = $estados_colores[$estado] ?? '#FFFFFF';
|
||||||
|
?>
|
||||||
|
<tr style="background: <?= htmlspecialchars($bg) ?>;">
|
||||||
|
<td><?= htmlspecialchars($order['Fecha'] ?? '') ?></td>
|
||||||
|
<td><?= htmlspecialchars(sc_persona_label($order['Persona'] ?? '', $personas)) ?></td>
|
||||||
|
<td style="white-space: pre-wrap; max-width: 250px;"><?= htmlspecialchars($order['Comanda'] ?? '') ?></td>
|
||||||
|
<td><?= htmlspecialchars($order['Notas'] ?? '') ?></td>
|
||||||
|
<td><strong><?= htmlspecialchars($estado) ?></strong></td>
|
||||||
|
<?php if ($can_edit): ?>
|
||||||
|
<td style="white-space: nowrap;">
|
||||||
|
<a href="/entreaulas/supercafe_edit.php?id=<?= urlencode($order['_id']) ?>"
|
||||||
|
class="btn btn-sm btn-primary">Editar</a>
|
||||||
|
<form method="post" style="display: inline;">
|
||||||
|
<input type="hidden" name="action" value="change_status">
|
||||||
|
<input type="hidden" name="order_id" value="<?= htmlspecialchars($order['_id']) ?>">
|
||||||
|
<select name="status" class="form-select form-select-sm"
|
||||||
|
style="display: inline; width: auto;"
|
||||||
|
onchange="this.form.submit();">
|
||||||
|
<?php foreach (array_keys($estados_colores) as $st): ?>
|
||||||
|
<option value="<?= htmlspecialchars($st) ?>"
|
||||||
|
<?= $st === $estado ? 'selected' : '' ?>>
|
||||||
|
<?= htmlspecialchars($st) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</form>
|
||||||
|
<form method="post" style="display: inline;"
|
||||||
|
onsubmit="return confirm('¿Borrar esta comanda?');">
|
||||||
|
<input type="hidden" name="action" value="delete">
|
||||||
|
<input type="hidden" name="order_id" value="<?= htmlspecialchars($order['_id']) ?>">
|
||||||
|
<button type="submit" class="btn btn-sm btn-danger">Borrar</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<!-- Debts -->
|
||||||
|
<details class="card pad" open
|
||||||
|
style="background: lightpink; border: 2px solid black; border-radius: 15px; margin-top: 10px;">
|
||||||
|
<summary style="font-weight: bold; font-size: 1.1rem; cursor: pointer;">
|
||||||
|
Deudas (<?= count($orders_deuda) ?>)
|
||||||
|
</summary>
|
||||||
|
<div style="margin-top: 10px;">
|
||||||
|
<?php if (empty($orders_deuda)): ?>
|
||||||
|
<p style="color: #666;">No hay comandas en deuda.</p>
|
||||||
|
<?php else: ?>
|
||||||
|
<div style="overflow-x: auto;">
|
||||||
|
<table class="table table-bordered" style="margin-bottom: 0;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Fecha</th>
|
||||||
|
<th>Persona</th>
|
||||||
|
<th>Comanda</th>
|
||||||
|
<th>Notas</th>
|
||||||
|
<?php if ($can_edit): ?><th>Acciones</th><?php endif; ?>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($orders_deuda as $order): ?>
|
||||||
|
<tr style="background: #f5d3ff;">
|
||||||
|
<td><?= htmlspecialchars($order['Fecha'] ?? '') ?></td>
|
||||||
|
<td><?= htmlspecialchars(sc_persona_label($order['Persona'] ?? '', $personas)) ?></td>
|
||||||
|
<td style="white-space: pre-wrap; max-width: 250px;"><?= htmlspecialchars($order['Comanda'] ?? '') ?></td>
|
||||||
|
<td><?= htmlspecialchars($order['Notas'] ?? '') ?></td>
|
||||||
|
<?php if ($can_edit): ?>
|
||||||
|
<td style="white-space: nowrap;">
|
||||||
|
<a href="/entreaulas/supercafe_edit.php?id=<?= urlencode($order['_id']) ?>"
|
||||||
|
class="btn btn-sm btn-primary">Editar</a>
|
||||||
|
<form method="post" style="display: inline;">
|
||||||
|
<input type="hidden" name="action" value="change_status">
|
||||||
|
<input type="hidden" name="order_id" value="<?= htmlspecialchars($order['_id']) ?>">
|
||||||
|
<select name="status" class="form-select form-select-sm"
|
||||||
|
style="display: inline; width: auto;"
|
||||||
|
onchange="this.form.submit();">
|
||||||
|
<?php foreach (array_keys($estados_colores) as $st): ?>
|
||||||
|
<option value="<?= htmlspecialchars($st) ?>"
|
||||||
|
<?= $st === 'Deuda' ? 'selected' : '' ?>>
|
||||||
|
<?= htmlspecialchars($st) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</form>
|
||||||
|
<form method="post" style="display: inline;"
|
||||||
|
onsubmit="return confirm('¿Borrar esta comanda?');">
|
||||||
|
<input type="hidden" name="action" value="delete">
|
||||||
|
<input type="hidden" name="order_id" value="<?= htmlspecialchars($order['_id']) ?>">
|
||||||
|
<button type="submit" class="btn btn-sm btn-danger">Borrar</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<?php require_once "_incl/post-body.php"; ?>
|
||||||
340
public_html/entreaulas/supercafe_edit.php
Normal file
340
public_html/entreaulas/supercafe_edit.php
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
<?php
|
||||||
|
require_once "_incl/auth_redir.php";
|
||||||
|
require_once "../_incl/tools.security.php";
|
||||||
|
|
||||||
|
if (!in_array('supercafe:edit', $_SESSION['auth_data']['permissions'] ?? [])) {
|
||||||
|
header('HTTP/1.1 403 Forbidden');
|
||||||
|
die('Acceso denegado');
|
||||||
|
}
|
||||||
|
|
||||||
|
function safe_centro_id_sc($value)
|
||||||
|
{
|
||||||
|
return preg_replace('/[^0-9]/', '', (string)$value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sc_safe_order_id($value)
|
||||||
|
{
|
||||||
|
return preg_replace('/[^a-zA-Z0-9_-]/', '', basename((string)$value));
|
||||||
|
}
|
||||||
|
|
||||||
|
$centro_id = safe_centro_id_sc($_SESSION['auth_data']['entreaulas']['centro'] ?? '');
|
||||||
|
if ($centro_id === '') {
|
||||||
|
require_once "_incl/pre-body.php";
|
||||||
|
echo '<div class="card pad"><h1>SuperCafe</h1><p>No tienes un centro asignado.</p></div>';
|
||||||
|
require_once "_incl/post-body.php";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sc_base = "/DATA/entreaulas/Centros/$centro_id/SuperCafe";
|
||||||
|
define('SC_DATA_DIR', "$sc_base/Comandas");
|
||||||
|
define('SC_MAX_DEBTS', 3);
|
||||||
|
|
||||||
|
$valid_statuses = ['Pedido', 'En preparación', 'Listo', 'Entregado', 'Deuda'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load personas from the existing Alumnos system (alumnos.php).
|
||||||
|
* Returns array keyed by "{aulario_id}:{alumno_name}" with
|
||||||
|
* ['Nombre', 'Region' (aulario display name), 'AularioID'] entries.
|
||||||
|
* Groups are sorted by aulario name, alumnos sorted alphabetically.
|
||||||
|
*/
|
||||||
|
function sc_load_personas_from_alumnos($centro_id)
|
||||||
|
{
|
||||||
|
$aularios_path = "/DATA/entreaulas/Centros/$centro_id/Aularios";
|
||||||
|
$personas = [];
|
||||||
|
if (!is_dir($aularios_path)) {
|
||||||
|
return $personas;
|
||||||
|
}
|
||||||
|
$aulario_files = glob("$aularios_path/*.json") ?: [];
|
||||||
|
foreach ($aulario_files as $aulario_file) {
|
||||||
|
$aulario_id = basename($aulario_file, '.json');
|
||||||
|
$aulario_data = json_decode(file_get_contents($aulario_file), true);
|
||||||
|
$aulario_name = $aulario_data['name'] ?? $aulario_id;
|
||||||
|
$alumnos_path = "$aularios_path/$aulario_id/Alumnos";
|
||||||
|
if (!is_dir($alumnos_path)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$alumno_dirs = glob("$alumnos_path/*/", GLOB_ONLYDIR) ?: [];
|
||||||
|
usort($alumno_dirs, function ($a, $b) {
|
||||||
|
return strcasecmp(basename($a), basename($b));
|
||||||
|
});
|
||||||
|
foreach ($alumno_dirs as $alumno_dir) {
|
||||||
|
$alumno_name = basename($alumno_dir);
|
||||||
|
// Key uses ':' as separator; safe_id_segment chars [A-Za-z0-9_-] exclude ':'
|
||||||
|
$key = $aulario_id . ':' . $alumno_name;
|
||||||
|
$personas[$key] = [
|
||||||
|
'Nombre' => $alumno_name,
|
||||||
|
'Region' => $aulario_name,
|
||||||
|
'AularioID' => $aulario_id,
|
||||||
|
'HasPhoto' => file_exists("$alumno_dir/photo.jpg"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $personas;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sc_load_menu($sc_base)
|
||||||
|
{
|
||||||
|
$path = "$sc_base/Menu.json";
|
||||||
|
if (!file_exists($path)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$data = json_decode(file_get_contents($path), true);
|
||||||
|
return is_array($data) ? $data : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function sc_count_debts($persona_key)
|
||||||
|
{
|
||||||
|
if (!is_dir(SC_DATA_DIR)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
$count = 0;
|
||||||
|
foreach (glob(SC_DATA_DIR . '/*.json') ?: [] as $file) {
|
||||||
|
$data = json_decode(file_get_contents($file), true);
|
||||||
|
if (is_array($data)
|
||||||
|
&& ($data['Persona'] ?? '') === $persona_key
|
||||||
|
&& ($data['Estado'] ?? '') === 'Deuda') {
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if creating or editing
|
||||||
|
$order_id = sc_safe_order_id($_GET['id'] ?? '');
|
||||||
|
$is_new = $order_id === '';
|
||||||
|
if ($is_new) {
|
||||||
|
$raw_id = uniqid('sc', true);
|
||||||
|
$order_id = preg_replace('/[^a-zA-Z0-9_-]/', '', $raw_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$order_file = SC_DATA_DIR . '/' . $order_id . '.json';
|
||||||
|
|
||||||
|
$order_data = [
|
||||||
|
'Fecha' => date('Y-m-d'),
|
||||||
|
'Persona' => '',
|
||||||
|
'Comanda' => '',
|
||||||
|
'Notas' => '',
|
||||||
|
'Estado' => 'Pedido',
|
||||||
|
];
|
||||||
|
if (!$is_new && is_readable($order_file)) {
|
||||||
|
$existing = json_decode(file_get_contents($order_file), true);
|
||||||
|
if (is_array($existing)) {
|
||||||
|
$order_data = array_merge($order_data, $existing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$personas = sc_load_personas_from_alumnos($centro_id);
|
||||||
|
$menu = sc_load_menu($sc_base);
|
||||||
|
|
||||||
|
// Group personas by aulario for the optgroup picker
|
||||||
|
$personas_by_aulario = [];
|
||||||
|
foreach ($personas as $key => $pinfo) {
|
||||||
|
$personas_by_aulario[$pinfo['Region']][$key] = $pinfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
$error = '';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$persona_key = $_POST['Persona'] ?? '';
|
||||||
|
$notas = trim($_POST['Notas'] ?? '');
|
||||||
|
$estado = $_POST['Estado'] ?? 'Pedido';
|
||||||
|
|
||||||
|
if (!in_array($estado, $valid_statuses, true)) {
|
||||||
|
$estado = 'Pedido';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate that the submitted persona key exists in the loaded list.
|
||||||
|
// When no alumnos are configured ($personas is empty), accept any non-empty free-text value.
|
||||||
|
if ($persona_key === '' || (!empty($personas) && !array_key_exists($persona_key, $personas))) {
|
||||||
|
$error = '¡Hay que elegir una persona válida!';
|
||||||
|
} else {
|
||||||
|
// Build comanda string from selected menu items
|
||||||
|
$comanda_parts = [];
|
||||||
|
if (!empty($menu)) {
|
||||||
|
foreach ($menu as $category => $items) {
|
||||||
|
foreach ($items as $item_name => $item_price) {
|
||||||
|
$qty_key = 'item_' . md5($category . '_' . $item_name);
|
||||||
|
$qty = (int)($_POST[$qty_key] ?? 0);
|
||||||
|
if ($qty > 0) {
|
||||||
|
$comanda_parts[] = $qty . 'x ' . $item_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$manual = trim($_POST['Comanda_manual'] ?? '');
|
||||||
|
if ($manual !== '') {
|
||||||
|
$comanda_parts[] = $manual;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$comanda_str = implode(', ', $comanda_parts);
|
||||||
|
|
||||||
|
// Debt check: only for new orders or when the person changes
|
||||||
|
$prev_persona = $order_data['Persona'] ?? '';
|
||||||
|
if ($is_new || $prev_persona !== $persona_key) {
|
||||||
|
$debt_count = sc_count_debts($persona_key);
|
||||||
|
if ($debt_count >= SC_MAX_DEBTS) {
|
||||||
|
$error = 'Esta persona tiene ' . $debt_count . ' comandas en deuda. No se puede realizar el pedido.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($error === '') {
|
||||||
|
$new_data = [
|
||||||
|
'Fecha' => date('Y-m-d'),
|
||||||
|
'Persona' => $persona_key,
|
||||||
|
'Comanda' => $comanda_str,
|
||||||
|
'Notas' => $notas,
|
||||||
|
'Estado' => $is_new ? 'Pedido' : $estado,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!is_dir(SC_DATA_DIR)) {
|
||||||
|
mkdir(SC_DATA_DIR, 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tmp = SC_DATA_DIR . '/.' . $order_id . '.tmp';
|
||||||
|
$bytes = file_put_contents(
|
||||||
|
$tmp,
|
||||||
|
json_encode($new_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE),
|
||||||
|
LOCK_EX
|
||||||
|
);
|
||||||
|
if ($bytes === false || !rename($tmp, $order_file)) {
|
||||||
|
@unlink($tmp);
|
||||||
|
$error = 'Error al guardar la comanda.';
|
||||||
|
} else {
|
||||||
|
header('Location: /entreaulas/supercafe.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once "_incl/pre-body.php";
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="card pad">
|
||||||
|
<h1><?= $is_new ? 'Nueva comanda' : 'Editar comanda' ?></h1>
|
||||||
|
<a href="/entreaulas/supercafe.php" class="btn btn-secondary">← Volver</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($error !== ''): ?>
|
||||||
|
<div class="card pad" style="background: #f8d7da; color: #842029;">
|
||||||
|
<?= htmlspecialchars($error) ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
<fieldset class="card pad">
|
||||||
|
<legend>
|
||||||
|
<strong>Rellenar comanda</strong>
|
||||||
|
<code><?= htmlspecialchars($order_id) ?></code>
|
||||||
|
</legend>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label"><strong>Persona</strong></label>
|
||||||
|
<?php if (!empty($personas_by_aulario)): ?>
|
||||||
|
<select name="Persona" class="form-select" required>
|
||||||
|
<option value="">-- Selecciona una persona --</option>
|
||||||
|
<?php foreach ($personas_by_aulario as $region_name => $group): ?>
|
||||||
|
<optgroup label="<?= htmlspecialchars($region_name) ?>">
|
||||||
|
<?php foreach ($group as $pkey => $pinfo): ?>
|
||||||
|
<option value="<?= htmlspecialchars($pkey) ?>"
|
||||||
|
<?= ($order_data['Persona'] === $pkey) ? 'selected' : '' ?>>
|
||||||
|
<?= htmlspecialchars($pinfo['Nombre']) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</optgroup>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<?php
|
||||||
|
// Show photo of the currently selected person (if editing)
|
||||||
|
$sel_key = $order_data['Persona'];
|
||||||
|
$sel_info = $personas[$sel_key] ?? null;
|
||||||
|
if ($sel_info && $sel_info['HasPhoto']):
|
||||||
|
?>
|
||||||
|
<div id="sc-persona-photo" style="margin-top: 8px;">
|
||||||
|
<?php $photo_url = '/entreaulas/_filefetch.php?type=alumno_photo'
|
||||||
|
. '¢ro=' . urlencode($centro_id)
|
||||||
|
. '&aulario=' . urlencode($sel_info['AularioID'])
|
||||||
|
. '&alumno=' . urlencode($sel_info['Nombre']); ?>
|
||||||
|
<img src="<?= htmlspecialchars($photo_url) ?>"
|
||||||
|
alt="Foto de <?= htmlspecialchars($sel_info['Nombre']) ?>"
|
||||||
|
style="height: 80px; border-radius: 8px; border: 2px solid #dee2e6;">
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<input type="text" name="Persona" class="form-control"
|
||||||
|
value="<?= htmlspecialchars($order_data['Persona']) ?>"
|
||||||
|
placeholder="Nombre de la persona" required>
|
||||||
|
<small class="text-muted">
|
||||||
|
No hay alumnos registrados en los aularios de este centro.
|
||||||
|
Añade alumnos desde
|
||||||
|
<a href="/entreaulas/">EntreAulas</a>.
|
||||||
|
</small>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (!empty($menu)): ?>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label"><strong>Artículos</strong></label>
|
||||||
|
<?php foreach ($menu as $category => $items): ?>
|
||||||
|
<div style="margin-bottom: 15px; padding: 10px; background: #f8f9fa; border-radius: 8px;">
|
||||||
|
<strong><?= htmlspecialchars($category) ?></strong>
|
||||||
|
<div style="display: flex; flex-wrap: wrap; gap: 10px; margin-top: 8px;">
|
||||||
|
<?php foreach ($items as $item_name => $item_price):
|
||||||
|
$qty_key = 'item_' . md5($category . '_' . $item_name);
|
||||||
|
?>
|
||||||
|
<label style="display: flex; align-items: center; gap: 6px; background: white; padding: 6px 10px; border-radius: 6px; border: 1px solid #dee2e6;">
|
||||||
|
<input type="number" name="<?= htmlspecialchars($qty_key) ?>"
|
||||||
|
min="0" max="99" value="0"
|
||||||
|
style="width: 55px;" class="form-control form-control-sm">
|
||||||
|
<?= htmlspecialchars($item_name) ?>
|
||||||
|
<?php if ($item_price > 0): ?>
|
||||||
|
<small style="color: #6c757d;">(<?= number_format((float)$item_price, 2) ?>c)</small>
|
||||||
|
<?php endif; ?>
|
||||||
|
</label>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label"><strong>Comanda (texto libre)</strong></label>
|
||||||
|
<input type="text" name="Comanda_manual" class="form-control"
|
||||||
|
value="<?= htmlspecialchars($order_data['Comanda']) ?>"
|
||||||
|
placeholder="Ej. 1x Café, 1x Bocadillo">
|
||||||
|
<small class="text-muted">
|
||||||
|
No hay menú configurado en
|
||||||
|
<code>/DATA/entreaulas/Centros/<?= htmlspecialchars($centro_id) ?>/SuperCafe/Menu.json</code>.
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label"><strong>Notas</strong></label>
|
||||||
|
<textarea name="Notas" class="form-control" rows="2"><?= htmlspecialchars($order_data['Notas']) ?></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (!$is_new): ?>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label"><strong>Estado</strong></label>
|
||||||
|
<select name="Estado" class="form-select">
|
||||||
|
<?php foreach ($valid_statuses as $st): ?>
|
||||||
|
<option value="<?= htmlspecialchars($st) ?>"
|
||||||
|
<?= $st === $order_data['Estado'] ? 'selected' : '' ?>>
|
||||||
|
<?= htmlspecialchars($st) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<small class="text-muted">El estado también se puede cambiar desde el listado.</small>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div style="display: flex; gap: 10px;">
|
||||||
|
<button type="submit" class="btn btn-success">Guardar</button>
|
||||||
|
<a href="/entreaulas/supercafe.php" class="btn btn-secondary">Cancelar</a>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<?php require_once "_incl/post-body.php"; ?>
|
||||||
|
|
||||||
@@ -165,10 +165,60 @@ switch ($_GET['action'] ?? '') {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="accordion-item">
|
||||||
|
<h2 class="accordion-header" id="headingSupercafe">
|
||||||
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSupercafe" aria-expanded="false" aria-controls="collapseSupercafe">
|
||||||
|
SuperCafe
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
<div id="collapseSupercafe" class="accordion-collapse collapse" aria-labelledby="headingSupercafe" data-bs-parent="#permissionsAccordion">
|
||||||
|
<div class="accordion-body">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="supercafe:access" id="supercafe-access">
|
||||||
|
<label class="form-check-label" for="supercafe-access">
|
||||||
|
Acceso
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="supercafe:edit" id="supercafe-edit">
|
||||||
|
<label class="form-check-label" for="supercafe-edit">
|
||||||
|
Editar comandas
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary mt-3">Crear Usuario</button>
|
<button type="submit" class="btn btn-primary mt-3">Crear Usuario</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="card pad">
|
||||||
|
<div>
|
||||||
|
<h2>EntreAulas: Configuración</h2>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="centro" class="form-label">Centro asociado:</label>
|
||||||
|
<select id="centro" name="centro" class="form-select">
|
||||||
|
<option value="">-- Selecciona un centro --</option>
|
||||||
|
<?php
|
||||||
|
$centros_folders_add = glob("/DATA/entreaulas/Centros/*", GLOB_ONLYDIR) ?: [];
|
||||||
|
foreach ($centros_folders_add as $centro_folder) {
|
||||||
|
$centro_id_opt = basename($centro_folder);
|
||||||
|
echo '<option value="' . htmlspecialchars($centro_id_opt) . '">' . htmlspecialchars($centro_id_opt) . '</option>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="role" class="form-label">Rol en EntreAulas:</label>
|
||||||
|
<select id="role" name="role" class="form-select">
|
||||||
|
<option value="">-- Selecciona un rol --</option>
|
||||||
|
<option value="teacher">Profesor</option>
|
||||||
|
<option value="student">Estudiante</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<p class="text-muted"><small>Las aulas podrán asignarse tras guardar el usuario.</small></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<?php
|
<?php
|
||||||
require_once "_incl/post-body.php";
|
require_once "_incl/post-body.php";
|
||||||
@@ -252,6 +302,29 @@ switch ($_GET['action'] ?? '') {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="accordion-item">
|
||||||
|
<h2 class="accordion-header" id="headingSupercafe">
|
||||||
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSupercafe" aria-expanded="false" aria-controls="collapseSupercafe">
|
||||||
|
SuperCafe
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
<div id="collapseSupercafe" class="accordion-collapse collapse" aria-labelledby="headingSupercafe" data-bs-parent="#permissionsAccordion">
|
||||||
|
<div class="accordion-body">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="supercafe:access" id="supercafe-access" <?php if (in_array('supercafe:access', $userdata['permissions'] ?? [])) echo 'checked'; ?>>
|
||||||
|
<label class="form-check-label" for="supercafe-access">
|
||||||
|
Acceso
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="supercafe:edit" id="supercafe-edit" <?php if (in_array('supercafe:edit', $userdata['permissions'] ?? [])) echo 'checked'; ?>>
|
||||||
|
<label class="form-check-label" for="supercafe-edit">
|
||||||
|
Editar comandas
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" name="username" value="<?php echo htmlspecialchars($username); ?>">
|
<input type="hidden" name="username" value="<?php echo htmlspecialchars($username); ?>">
|
||||||
<button type="submit" class="btn btn-primary mt-3">Guardar Cambios</button>
|
<button type="submit" class="btn btn-primary mt-3">Guardar Cambios</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user