Autosave: 20260724-100614
This commit is contained in:
parent
5437326dee
commit
9da54710dc
@ -586,26 +586,46 @@ try {
|
||||
throw new RuntimeException('El reparto intercalado solo está disponible en TUANI principal.');
|
||||
}
|
||||
|
||||
$repartoResult = cc_test_apply_daily_reparto($pdo, $storeKey, $orders, $assessors, $repartoSettings);
|
||||
$repartoResult = cc_test_run_locked_reparto_job(
|
||||
$pdo,
|
||||
$storeKey,
|
||||
$assessors,
|
||||
static function () use ($pdo, $storeKey): array {
|
||||
return cc_test_load_reparto_orders($pdo, $storeKey);
|
||||
},
|
||||
$repartoSettings,
|
||||
static function (array $orders) use ($storeKey, $storeConfig): array {
|
||||
return cc_test_filter_reparto_orders($orders, $storeKey, $storeConfig);
|
||||
}
|
||||
);
|
||||
$assignedCount = (int) ($repartoResult['assigned_count'] ?? 0);
|
||||
$noticeMessage = $assignedCount > 0
|
||||
? 'Reparto intercalado: ' . $assignedCount . ' pedido(s) asignado(s).'
|
||||
: (string) ($repartoResult['message'] ?? 'No había pedidos pendientes o no había asesoras disponibles.');
|
||||
if ($assignedCount > 0) {
|
||||
$tracking = drive_test_fetch_tracking($pdo, array_column($orders, 'source_key'));
|
||||
$orders = drive_test_merge_tracking($orders, $tracking);
|
||||
$orders = cc_test_load_reparto_orders($pdo, $storeKey);
|
||||
}
|
||||
$repartoSettings = $repartoResult['settings'] ?? $repartoSettings;
|
||||
}
|
||||
}
|
||||
|
||||
if ($isMainTuaniStore && ($repartoSettings['reparto_mode'] ?? 'manual') === 'automatico') {
|
||||
$autoRepartoResult = cc_test_apply_daily_reparto($pdo, $storeKey, $orders, $assessors, $repartoSettings);
|
||||
if ($isMainTuaniStore && cc_test_reparto_mode_is_auto($repartoSettings['reparto_mode'] ?? 'manual')) {
|
||||
$autoRepartoResult = cc_test_run_locked_reparto_job(
|
||||
$pdo,
|
||||
$storeKey,
|
||||
$assessors,
|
||||
static function () use ($pdo, $storeKey): array {
|
||||
return cc_test_load_reparto_orders($pdo, $storeKey);
|
||||
},
|
||||
$repartoSettings,
|
||||
static function (array $orders) use ($storeKey, $storeConfig): array {
|
||||
return cc_test_filter_reparto_orders($orders, $storeKey, $storeConfig);
|
||||
}
|
||||
);
|
||||
$autoAssignedCount = (int) ($autoRepartoResult['assigned_count'] ?? 0);
|
||||
if ($autoAssignedCount > 0) {
|
||||
$noticeMessage = trim((string) ($noticeMessage !== null ? $noticeMessage . ' ' : '') . 'Reparto automático: ' . $autoAssignedCount . ' pedido(s) asignado(s).');
|
||||
$tracking = drive_test_fetch_tracking($pdo, array_column($orders, 'source_key'));
|
||||
$orders = drive_test_merge_tracking($orders, $tracking);
|
||||
$orders = cc_test_load_reparto_orders($pdo, $storeKey);
|
||||
}
|
||||
$repartoSettings = $autoRepartoResult['settings'] ?? $repartoSettings;
|
||||
}
|
||||
|
||||
@ -775,7 +775,7 @@ try {
|
||||
$postedStates = [];
|
||||
}
|
||||
|
||||
$repartoSettings['reparto_mode'] = 'manual';
|
||||
$repartoSettings['reparto_mode'] = $postedMode;
|
||||
$repartoSettings['assessor_states'] = cc_test_reparto_normalize_states($postedStates, $assessors);
|
||||
cc_test_upsert_reparto_settings($pdo, $storeKey, $repartoSettings);
|
||||
$noticeMessage = 'Configuración de reparto guardada.';
|
||||
@ -784,42 +784,51 @@ try {
|
||||
throw new RuntimeException('El reparto intercalado solo está disponible en TUANI principal.');
|
||||
}
|
||||
|
||||
$repartoOrders = cc_test_filter_reparto_orders($orders, $storeKey, $storeConfig);
|
||||
if (empty($repartoOrders)) {
|
||||
$repartoResult = [
|
||||
'assigned_count' => 0,
|
||||
'settings' => $repartoSettings,
|
||||
'message' => 'No hay pedidos pendientes para repartir.',
|
||||
];
|
||||
} else {
|
||||
$repartoResult = cc_test_apply_daily_reparto($pdo, $storeKey, $repartoOrders, $assessors, $repartoSettings);
|
||||
}
|
||||
$repartoResult = cc_test_run_locked_reparto_job(
|
||||
$pdo,
|
||||
$storeKey,
|
||||
$assessors,
|
||||
static function () use ($pdo, $storeKey): array {
|
||||
return cc_test_load_reparto_orders($pdo, $storeKey);
|
||||
},
|
||||
$repartoSettings,
|
||||
static function (array $orders) use ($storeKey, $storeConfig): array {
|
||||
return cc_test_filter_reparto_orders($orders, $storeKey, $storeConfig);
|
||||
}
|
||||
);
|
||||
$assignedCount = (int) ($repartoResult['assigned_count'] ?? 0);
|
||||
$noticeMessage = $assignedCount > 0
|
||||
? 'Reparto intercalado: ' . $assignedCount . ' pedido(s) asignado(s).'
|
||||
: (string) ($repartoResult['message'] ?? 'No había pedidos pendientes o no había asesoras disponibles.');
|
||||
if ($assignedCount > 0) {
|
||||
$tracking = drive_test_fetch_tracking($pdo, array_column($orders, 'source_key'));
|
||||
$orders = drive_test_merge_tracking($orders, $tracking);
|
||||
$orders = cc_test_load_reparto_orders($pdo, $storeKey);
|
||||
}
|
||||
$repartoSettings = $repartoResult['settings'] ?? $repartoSettings;
|
||||
}
|
||||
|
||||
if ($isMainTuaniStore && ($repartoSettings['reparto_mode'] ?? 'manual') === 'automatico') {
|
||||
$repartoOrders = cc_test_filter_reparto_orders($orders, $storeKey, $storeConfig);
|
||||
$autoRepartoResult = cc_test_apply_daily_reparto($pdo, $storeKey, $repartoOrders, $assessors, $repartoSettings);
|
||||
if ($isMainTuaniStore && cc_test_reparto_mode_is_auto($repartoSettings['reparto_mode'] ?? 'manual')) {
|
||||
$autoRepartoResult = cc_test_run_locked_reparto_job(
|
||||
$pdo,
|
||||
$storeKey,
|
||||
$assessors,
|
||||
static function () use ($pdo, $storeKey): array {
|
||||
return cc_test_load_reparto_orders($pdo, $storeKey);
|
||||
},
|
||||
$repartoSettings,
|
||||
static function (array $orders) use ($storeKey, $storeConfig): array {
|
||||
return cc_test_filter_reparto_orders($orders, $storeKey, $storeConfig);
|
||||
}
|
||||
);
|
||||
$autoAssignedCount = (int) ($autoRepartoResult['assigned_count'] ?? 0);
|
||||
if ($autoAssignedCount > 0) {
|
||||
$noticeMessage = trim((string) ($noticeMessage !== null ? $noticeMessage . ' ' : '') . 'Reparto automático: ' . $autoAssignedCount . ' pedido(s) asignado(s).');
|
||||
$tracking = drive_test_fetch_tracking($pdo, array_column($orders, 'source_key'));
|
||||
$orders = drive_test_merge_tracking($orders, $tracking);
|
||||
$orders = cc_test_load_reparto_orders($pdo, $storeKey);
|
||||
}
|
||||
$repartoSettings = $autoRepartoResult['settings'] ?? $repartoSettings;
|
||||
}
|
||||
|
||||
if ($isMainTuaniStore) {
|
||||
$repartoSettings = cc_test_fetch_reparto_settings($pdo, $storeKey, $assessors);
|
||||
$repartoSettings['reparto_mode'] = 'manual';
|
||||
}
|
||||
$repartoPreview = cc_test_reparto_preview($assessors, $repartoSettings);
|
||||
$repartoEligibleOrders = cc_test_filter_reparto_orders($orders, $storeKey, $storeConfig);
|
||||
@ -1672,7 +1681,7 @@ require_once 'layout_header.php';
|
||||
<?php if ($isMainTuaniStore): ?>
|
||||
<?php
|
||||
$repartoModeLabel = (string) ($repartoPreview['mode_label'] ?? 'Manual');
|
||||
$repartoModeClass = ($repartoPreview['mode'] ?? 'manual') === 'automatico'
|
||||
$repartoModeClass = cc_test_reparto_mode_is_auto((string) ($repartoPreview['mode'] ?? 'manual'))
|
||||
? 'bg-success-subtle text-success-emphasis border'
|
||||
: 'bg-secondary-subtle text-secondary-emphasis border';
|
||||
$repartoNextLabel = (string) ($repartoPreview['next_label'] ?? '');
|
||||
@ -1721,8 +1730,14 @@ require_once 'layout_header.php';
|
||||
<div class="mb-2">
|
||||
<label class="form-label small text-muted fw-semibold mb-1" for="repartoMode">Modo de reparto</label>
|
||||
<select name="reparto_mode" id="repartoMode" class="form-select form-select-sm">
|
||||
<option value="manual"<?php echo ($repartoPreview['mode'] ?? 'manual') === 'manual' ? ' selected' : ''; ?>>Manual</option>
|
||||
<?php foreach ([
|
||||
'manual' => 'Manual',
|
||||
'automatico' => 'Automático',
|
||||
] as $modeValue => $modeLabel): ?>
|
||||
<option value="<?php echo htmlspecialchars($modeValue); ?>"<?php echo ($repartoPreview['mode'] ?? 'manual') === $modeValue ? ' selected' : ''; ?>><?php echo htmlspecialchars($modeLabel); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<div class="form-text">Automático reparte los pedidos pendientes al abrir o refrescar la pantalla.</div>
|
||||
</div>
|
||||
<div class="row row-cols-1 row-cols-md-2 g-2 cc-reparto-state-grid">
|
||||
<?php foreach (($repartoPreview['sequence'] ?? []) as $item): ?>
|
||||
|
||||
@ -392,12 +392,25 @@ if (!function_exists('cc_test_ensure_reparto_settings_table')) {
|
||||
function cc_test_normalize_reparto_mode(?string $value): string
|
||||
{
|
||||
$value = mb_strtolower(trim((string) $value));
|
||||
return in_array($value, ['automatico', 'automático', 'auto'], true) ? 'automatico' : 'manual';
|
||||
|
||||
if (in_array($value, ['automatico', 'automático', 'auto', 'hibrido', 'híbrido', 'hybrid'], true)) {
|
||||
return 'automatico';
|
||||
}
|
||||
|
||||
return 'manual';
|
||||
}
|
||||
|
||||
function cc_test_reparto_mode_label(string $mode): string
|
||||
{
|
||||
return cc_test_normalize_reparto_mode($mode) === 'automatico' ? 'Automático' : 'Manual';
|
||||
return match (cc_test_normalize_reparto_mode($mode)) {
|
||||
'automatico' => 'Automático',
|
||||
default => 'Manual',
|
||||
};
|
||||
}
|
||||
|
||||
function cc_test_reparto_mode_is_auto(?string $mode): bool
|
||||
{
|
||||
return cc_test_normalize_reparto_mode($mode) === 'automatico';
|
||||
}
|
||||
|
||||
function cc_test_normalize_reparto_state(?string $value): string
|
||||
@ -615,6 +628,121 @@ function cc_test_upsert_reparto_settings(PDO $pdo, string $storeKey, array $sett
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
function cc_test_reparto_lock_name(string $storeKey): string
|
||||
{
|
||||
$storeKey = trim(mb_strtolower($storeKey));
|
||||
return 'cc_test_auto_reparto_' . substr(sha1($storeKey), 0, 24);
|
||||
}
|
||||
|
||||
function cc_test_acquire_named_lock(PDO $pdo, string $lockName, int $timeoutSeconds = 0): bool
|
||||
{
|
||||
try {
|
||||
$stmt = $pdo->prepare('SELECT GET_LOCK(:lock_name, :timeout_seconds)');
|
||||
$stmt->bindValue(':lock_name', $lockName, PDO::PARAM_STR);
|
||||
$stmt->bindValue(':timeout_seconds', $timeoutSeconds, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
$result = $stmt->fetchColumn();
|
||||
|
||||
return (int) $result === 1;
|
||||
} catch (Throwable $exception) {
|
||||
error_log('cc_test_acquire_named_lock: ' . $exception->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function cc_test_release_named_lock(PDO $pdo, string $lockName): void
|
||||
{
|
||||
try {
|
||||
$stmt = $pdo->prepare('SELECT RELEASE_LOCK(:lock_name)');
|
||||
$stmt->bindValue(':lock_name', $lockName, PDO::PARAM_STR);
|
||||
$stmt->execute();
|
||||
} catch (Throwable $exception) {
|
||||
error_log('cc_test_release_named_lock: ' . $exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
function cc_test_load_reparto_orders(PDO $pdo, string $storeKey): array
|
||||
{
|
||||
$storeKey = trim(mb_strtolower($storeKey));
|
||||
if ($storeKey === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$availableStores = function_exists('drive_test_available_stores') ? drive_test_available_stores() : [];
|
||||
$storeConfig = $availableStores[$storeKey] ?? [];
|
||||
|
||||
if (!empty($storeConfig['incremental_sync']) && function_exists('drive_test_sync_orders_incremental')) {
|
||||
drive_test_sync_orders_incremental($pdo, $storeKey, 0);
|
||||
}
|
||||
|
||||
if (!function_exists('drive_test_fetch_orders_from_db') || !function_exists('drive_test_fetch_tracking') || !function_exists('drive_test_merge_tracking')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$orders = drive_test_fetch_orders_from_db($pdo, $storeKey);
|
||||
$tracking = drive_test_fetch_tracking($pdo, array_column($orders, 'source_key'));
|
||||
$orders = drive_test_merge_tracking($orders, $tracking);
|
||||
|
||||
return array_values(array_filter($orders, static function (array $order): bool {
|
||||
return empty($order['eliminado']);
|
||||
}));
|
||||
}
|
||||
|
||||
function cc_test_run_locked_reparto_job(PDO $pdo, string $storeKey, array $assessors, callable $loadOrders, array $settings = [], ?callable $filterOrders = null, int $lockTimeoutSeconds = 5): array
|
||||
{
|
||||
$lockName = cc_test_reparto_lock_name($storeKey);
|
||||
|
||||
if (!cc_test_acquire_named_lock($pdo, $lockName, $lockTimeoutSeconds)) {
|
||||
$currentSettings = cc_test_fetch_reparto_settings($pdo, $storeKey, $assessors);
|
||||
return [
|
||||
'assigned_count' => 0,
|
||||
'rotation_index' => (int) ($currentSettings['rotation_index'] ?? 0),
|
||||
'settings' => $currentSettings,
|
||||
'preview' => cc_test_reparto_preview($assessors, $currentSettings),
|
||||
'message' => 'El reparto automático ya se está ejecutando en otro proceso.',
|
||||
'locked' => false,
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
$currentSettings = cc_test_fetch_reparto_settings($pdo, $storeKey, $assessors);
|
||||
if (!empty($settings)) {
|
||||
$currentSettings = array_replace($currentSettings, $settings);
|
||||
$currentSettings['assessor_states'] = cc_test_reparto_normalize_states($currentSettings['assessor_states'] ?? [], $assessors);
|
||||
}
|
||||
|
||||
$orders = $loadOrders();
|
||||
if (!is_array($orders)) {
|
||||
$orders = [];
|
||||
}
|
||||
|
||||
if ($filterOrders !== null) {
|
||||
$filteredOrders = $filterOrders($orders);
|
||||
if (is_array($filteredOrders)) {
|
||||
$orders = array_values($filteredOrders);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($orders)) {
|
||||
return [
|
||||
'assigned_count' => 0,
|
||||
'rotation_index' => (int) ($currentSettings['rotation_index'] ?? 0),
|
||||
'settings' => $currentSettings,
|
||||
'preview' => cc_test_reparto_preview($assessors, $currentSettings),
|
||||
'message' => 'No hay pedidos pendientes para repartir.',
|
||||
'locked' => true,
|
||||
];
|
||||
}
|
||||
|
||||
$result = cc_test_apply_daily_reparto($pdo, $storeKey, $orders, $assessors, $currentSettings);
|
||||
$result['locked'] = true;
|
||||
|
||||
return $result;
|
||||
} finally {
|
||||
cc_test_release_named_lock($pdo, $lockName);
|
||||
}
|
||||
}
|
||||
|
||||
function cc_test_reparto_settings_signature(array $settings): string
|
||||
{
|
||||
$states = $settings['assessor_states'] ?? [];
|
||||
|
||||
42
reparto_auto_worker.php
Normal file
42
reparto_auto_worker.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
require_once __DIR__ . '/includes/callcenter_test_helpers.php';
|
||||
require_once __DIR__ . '/includes/drive_test_orders.php';
|
||||
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
http_response_code(403);
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
echo "Este worker solo se ejecuta por CLI." . PHP_EOL;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
$storeKey = 'otra_tienda';
|
||||
$availableStores = drive_test_available_stores();
|
||||
$storeConfig = $availableStores[$storeKey] ?? [];
|
||||
$assessors = cc_test_fetch_assessors($pdo);
|
||||
$repartoSettings = cc_test_fetch_reparto_settings($pdo, $storeKey, $assessors);
|
||||
|
||||
if (!cc_test_reparto_mode_is_auto($repartoSettings['reparto_mode'] ?? 'manual')) {
|
||||
echo '[' . date('Y-m-d H:i:s') . '] Reparto automático omitido: modo manual en ' . $storeKey . PHP_EOL;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$result = cc_test_run_locked_reparto_job(
|
||||
$pdo,
|
||||
$storeKey,
|
||||
$assessors,
|
||||
static function () use ($pdo, $storeKey): array {
|
||||
return cc_test_load_reparto_orders($pdo, $storeKey);
|
||||
},
|
||||
$repartoSettings,
|
||||
static function (array $orders) use ($storeKey, $storeConfig): array {
|
||||
return cc_test_filter_reparto_orders($orders, $storeKey, $storeConfig);
|
||||
}
|
||||
);
|
||||
|
||||
$assignedCount = (int) ($result['assigned_count'] ?? 0);
|
||||
$message = (string) ($result['message'] ?? '');
|
||||
$status = !empty($result['locked']) ? 'ok' : 'busy';
|
||||
|
||||
echo '[' . date('Y-m-d H:i:s') . '] ' . $storeKey . ' | estado=' . $status . ' | asignados=' . $assignedCount . ' | ' . $message . PHP_EOL;
|
||||
Loading…
x
Reference in New Issue
Block a user