feat+fix: Bring removal, multi-expiry batches, FIFO in cooking steps

BRING! REMOVAL FIX (latte/aglio not removed after shopping):
- PHP addToInventory: replace exact strcasecmp with token-based fuzzy
  matching (same logic as _productOnBring) so custom Bring item names
  and translated catalog keys both match correctly
- JS submitAdd: add client-side fallback — if PHP removal missed the item,
  use _findSimilarItem against the loaded shoppingItems and call bring_remove

MULTI-EXPIRY BATCHES (when buying conf with different expiry dates):
- Add form (unit=conf): shows '+ Lotto con scadenza diversa' button
- Each extra batch has its own qty + expiry date input with +/- controls
- On submitAdd, extra batches are submitted as additional inventory_add calls
  (separate DB rows, separate expiry dates)
- Multi-batch section hidden in 'Ce l'avevo già' mode and for non-conf units
- Re-shown/hidden when switching unit via onAddUnitChange

RECIPE COOKING STEPS - FIFO ingredient display:
- renderCookingStep: each ingredient row now shows brand chip, location chip,
  and expiry date chip (color-coded: red ≤3d, yellow ≤7d)
- PHP already selected earliest-expiry inventory entry (ORDER BY days_left ASC
  with > not >= ensures first/earliest match wins)
- CSS: .cooking-ing-meta, .cooking-ing-chip, .exp-soon, .exp-close,
  .multi-batch-row, .multi-batch-qty, .multi-batch-date, .btn-icon-sm
This commit is contained in:
dadaloop82
2026-04-07 12:10:14 +00:00
parent 22ae3abf47
commit b7ed9899fa
6 changed files with 551 additions and 5 deletions
+35 -3
View File
@@ -667,13 +667,45 @@ function addToInventory(PDO $db): void {
$bringKey = italianToBring($prodName); $bringKey = italianToBring($prodName);
$listData = bringRequest('GET', "https://api.getbring.com/rest/v2/bringlists/{$listUUID}"); $listData = bringRequest('GET', "https://api.getbring.com/rest/v2/bringlists/{$listUUID}");
if ($listData && isset($listData['purchase'])) { if ($listData && isset($listData['purchase'])) {
// Token-based matching — same logic as _productOnBring() in smart_shopping
$stop = ['di','del','della','dei','degli','dalle','delle','da','in','con','per',
'a','e','il','lo','la','i','gli','le','un','uno','una','al','alle','agli','allo'];
$tokenize = function(string $s) use ($stop): array {
$clean = mb_strtolower(preg_replace('/[^\p{L}\s]/u', ' ', $s));
return array_values(array_filter(
preg_split('/\s+/', trim($clean)),
fn($t) => mb_strlen($t) > 2 && !in_array($t, $stop)
));
};
$prodTokens = $tokenize($prodName);
$keyTokens = $tokenize($bringKey);
$prodFirst = $prodTokens[0] ?? '';
$keyFirst = $keyTokens[0] ?? '';
foreach ($listData['purchase'] as $item) { foreach ($listData['purchase'] as $item) {
if (strcasecmp($item['name'] ?? '', $bringKey) === 0) { $rawName = $item['name'] ?? '';
$body = http_build_query(['uuid' => $listUUID, 'remove' => $bringKey]); // 1. Exact match on translated catalog key or original Italian name
bringRequest('PUT', "https://api.getbring.com/rest/v2/bringlists/{$listUUID}", $body); if (strcasecmp($rawName, $bringKey) === 0 || strcasecmp($rawName, $prodName) === 0) {
bringRequest('PUT', "https://api.getbring.com/rest/v2/bringlists/{$listUUID}",
http_build_query(['uuid' => $listUUID, 'remove' => $rawName]));
$removedFromBring = true; $removedFromBring = true;
break; break;
} }
// 2. Token-based fuzzy: first significant word must match
if ($prodFirst || $keyFirst) {
$rawTokens = $tokenize($rawName);
$rawFirst = $rawTokens[0] ?? '';
if ($rawFirst && (
$rawFirst === $prodFirst ||
$rawFirst === $keyFirst ||
in_array($prodFirst, $rawTokens) ||
in_array($keyFirst, $rawTokens)
)) {
bringRequest('PUT', "https://api.getbring.com/rest/v2/bringlists/{$listUUID}",
http_build_query(['uuid' => $listUUID, 'remove' => $rawName]));
$removedFromBring = true;
break;
}
}
} }
} }
} }
+67
View File
@@ -2671,6 +2671,47 @@ body {
font-weight: 600; font-weight: 600;
} }
/* ===== MULTI-BATCH EXPIRY (conf products with different expiry dates) ===== */
.multi-batch-row {
display: flex;
align-items: center;
gap: 8px;
margin-top: 8px;
padding: 8px 10px;
background: rgba(0,0,0,0.04);
border: 1px dashed var(--border);
border-radius: var(--radius-sm);
}
.multi-batch-qty {
display: flex;
align-items: center;
gap: 4px;
flex-shrink: 0;
}
.multi-batch-unit {
font-size: 0.8rem;
color: var(--text-muted);
}
.multi-batch-date {
flex: 1;
min-width: 0;
font-size: 0.85rem !important;
}
.btn-icon-sm {
background: none;
border: 1px solid var(--border);
border-radius: 6px;
padding: 4px 8px;
font-size: 0.75rem;
cursor: pointer;
color: var(--text-muted);
flex-shrink: 0;
}
/* ===== PRODUCT DETAILS CARD (Action Page) ===== */ /* ===== PRODUCT DETAILS CARD (Action Page) ===== */
.product-details-card { .product-details-card {
background: var(--bg-card); background: var(--bg-card);
@@ -3489,6 +3530,32 @@ body {
min-width: 0; min-width: 0;
} }
.cooking-ing-meta {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin-top: 5px;
}
.cooking-ing-chip {
font-size: 0.72rem;
background: rgba(255,255,255,0.12);
border-radius: 5px;
padding: 2px 7px;
color: rgba(255,255,255,0.75);
white-space: nowrap;
}
.cooking-ing-chip.exp-soon {
background: rgba(239,68,68,0.3);
color: #fca5a5;
}
.cooking-ing-chip.exp-close {
background: rgba(234,179,8,0.3);
color: #fde047;
}
.cooking-use-btn { .cooking-use-btn {
flex-shrink: 0; flex-shrink: 0;
background: #16a34a; background: #16a34a;
+120 -1
View File
@@ -3269,6 +3269,8 @@ function showAddForm() {
// Reset historical expiry for this product; will be fetched async // Reset historical expiry for this product; will be fetched async
window._historyExpiryDays = null; window._historyExpiryDays = null;
window._historyExpiryCount = 0; window._historyExpiryCount = 0;
// Reset extra batches from previous add
window._addExtraBatches = [];
// Store base expiry for vacuum recalculation // Store base expiry for vacuum recalculation
window._addBaseExpiryDays = estimatedDays; window._addBaseExpiryDays = estimatedDays;
@@ -3293,6 +3295,12 @@ function showAddForm() {
</div> </div>
<p class="form-hint">📝 Puoi modificare la data o scansionarla con la fotocamera</p> <p class="form-hint">📝 Puoi modificare la data o scansionarla con la fotocamera</p>
</div> </div>
<div id="multi-batch-section" style="display:${unit === 'conf' ? 'block' : 'none'}">
<div id="multi-batch-container"></div>
<button type="button" class="btn btn-outline btn-small full-width" style="margin-top:8px" onclick="addExpiryBatch()">
📦 + Lotto con scadenza diversa
</button>
</div>
`; `;
showPage('add'); showPage('add');
@@ -3407,6 +3415,10 @@ function onAddUnitChange() {
if (isConf) setTimeout(() => confRow.scrollIntoView({ behavior: 'smooth', block: 'nearest' }), 100); if (isConf) setTimeout(() => confRow.scrollIntoView({ behavior: 'smooth', block: 'nearest' }), 100);
} }
// Show/hide multi-batch section (only for conf unit)
const mbSection = document.getElementById('multi-batch-section');
if (mbSection) mbSection.style.display = unit === 'conf' ? 'block' : 'none';
// If switching units, suggest a sensible quantity // If switching units, suggest a sensible quantity
// BUT only if the user hasn't manually changed the quantity in this form // BUT only if the user hasn't manually changed the quantity in this form
if (qtyInput.dataset.manuallySet === 'true') return; // User already edited qty, don't overwrite if (qtyInput.dataset.manuallySet === 'true') return; // User already edited qty, don't overwrite
@@ -3457,6 +3469,11 @@ function selectPurchaseType(btn, type) {
btn.parentElement.querySelectorAll('.purchase-type-btn').forEach(b => b.classList.remove('active')); btn.parentElement.querySelectorAll('.purchase-type-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active'); btn.classList.add('active');
// Reset extra batches when switching purchase type
window._addExtraBatches = [];
const mbContainer = document.getElementById('multi-batch-container');
if (mbContainer) mbContainer.innerHTML = '';
const detailDiv = document.getElementById('expiry-detail'); const detailDiv = document.getElementById('expiry-detail');
// Save current quantity before switching, so we can preserve it // Save current quantity before switching, so we can preserve it
@@ -3489,6 +3506,9 @@ function selectPurchaseType(btn, type) {
`; `;
// Restore quantity - switching purchase type should NOT change it // Restore quantity - switching purchase type should NOT change it
document.getElementById('add-quantity').value = currentQty; document.getElementById('add-quantity').value = currentQty;
// Show multi-batch section only in "new" mode (and only for conf unit)
const mbSection = document.getElementById('multi-batch-section');
if (mbSection) mbSection.style.display = (document.getElementById('add-unit')?.value === 'conf') ? 'block' : 'none';
} else { } else {
detailDiv.innerHTML = ` detailDiv.innerHTML = `
<div class="form-group"> <div class="form-group">
@@ -3511,6 +3531,9 @@ function selectPurchaseType(btn, type) {
</div> </div>
`; `;
// DON'T auto-set remaining percentage - keep the quantity the user already entered // DON'T auto-set remaining percentage - keep the quantity the user already entered
// Hide multi-batch section in "existing" mode
const mbSection = document.getElementById('multi-batch-section');
if (mbSection) mbSection.style.display = 'none';
} }
} }
@@ -3528,6 +3551,50 @@ function setRemainingPct(pct) {
document.getElementById('add-quantity').value = adjustedQty; document.getElementById('add-quantity').value = adjustedQty;
} }
// ===== MULTI-EXPIRY BATCHES (for conf products with different expiry dates) =====
window._addExtraBatches = [];
function addExpiryBatch() {
const loc = document.getElementById('add-location')?.value || '';
const baseDays = window._historyExpiryDays ?? estimateExpiryDays(currentProduct, loc);
const estimatedDate = addDays(baseDays);
window._addExtraBatches.push({ qty: 1, expiry: estimatedDate });
_rebuildMultiBatchUI();
}
function removeExpiryBatch(i) {
window._addExtraBatches.splice(i, 1);
_rebuildMultiBatchUI();
}
function adjustBatchQty(i, delta) {
window._addExtraBatches[i].qty = Math.max(1, (window._addExtraBatches[i].qty || 1) + delta);
_rebuildMultiBatchUI();
}
function _rebuildMultiBatchUI() {
const container = document.getElementById('multi-batch-container');
if (!container) return;
if (window._addExtraBatches.length === 0) {
container.innerHTML = '';
return;
}
container.innerHTML = window._addExtraBatches.map((b, i) => `
<div class="multi-batch-row">
<div class="multi-batch-qty">
<button type="button" class="qty-btn" onclick="adjustBatchQty(${i}, -1)"></button>
<input type="number" class="qty-input" value="${b.qty}" min="1" step="1" style="width:60px"
onchange="window._addExtraBatches[${i}].qty = parseInt(this.value)||1">
<button type="button" class="qty-btn" onclick="adjustBatchQty(${i}, 1)">+</button>
<span class="multi-batch-unit">conf</span>
</div>
<input type="date" class="form-input multi-batch-date" value="${b.expiry}"
onchange="window._addExtraBatches[${i}].expiry = this.value">
<button type="button" class="btn-icon-sm" onclick="removeExpiryBatch(${i})" title="Rimuovi"></button>
</div>
`).join('');
}
function selectLocation(btn, loc) { function selectLocation(btn, loc) {
btn.parentElement.querySelectorAll('.loc-btn').forEach(b => b.classList.remove('active')); btn.parentElement.querySelectorAll('.loc-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active'); btn.classList.add('active');
@@ -3583,8 +3650,46 @@ async function submitAdd(e) {
showToast(`${currentProduct.name} aggiunto!${qtyInfo}`, 'success'); showToast(`${currentProduct.name} aggiunto!${qtyInfo}`, 'success');
if (result.removed_from_bring) { if (result.removed_from_bring) {
setTimeout(() => showToast('🛒 Rimosso dalla lista della spesa', 'info'), 1500); setTimeout(() => showToast('🛒 Rimosso dalla lista della spesa', 'info'), 1500);
} else if (shoppingItems.length > 0 && shoppingListUUID) {
// PHP matching may have missed the item (custom name / no catalog match) —
// try a client-side fuzzy remove using the already-loaded shoppingItems
const match = _findSimilarItem(currentProduct.name, shoppingItems);
if (match) {
api('bring_remove', {}, 'POST', {
name: match.name,
rawName: match.rawName || '',
listUUID: shoppingListUUID
}).then(r => {
if (r && r.success) {
shoppingItems = shoppingItems.filter(i => i !== match);
setTimeout(() => showToast('🛒 Rimosso dalla lista della spesa', 'info'), 1500);
}
}).catch(() => {});
}
} }
if (!spesaModeAfterAdd()) showPage('dashboard'); if (!spesaModeAfterAdd()) showPage('dashboard');
// Submit extra batches (different expiry dates) in the background, silently
if ((window._addExtraBatches || []).length > 0) {
const loc = document.getElementById('add-location')?.value || result.location || 'dispensa';
const selectedUnit = document.getElementById('add-unit').value;
const productUnit = currentProduct.unit || 'pz';
const confUnit = document.getElementById('add-conf-unit')?.value || null;
const confSize = parseFloat(document.getElementById('add-conf-size')?.value) || null;
for (const batch of window._addExtraBatches) {
if (!batch.qty || batch.qty <= 0) continue;
api('inventory_add', {}, 'POST', {
product_id: currentProduct.id,
quantity: batch.qty,
location: loc,
expiry_date: batch.expiry || null,
unit: selectedUnit !== productUnit ? selectedUnit : null,
package_unit: selectedUnit === 'conf' ? confUnit : null,
package_size: selectedUnit === 'conf' ? confSize : null,
}).catch(() => {});
}
window._addExtraBatches = [];
}
} else { } else {
showToast(result.error || 'Errore', 'error'); showToast(result.error || 'Errore', 'error');
} }
@@ -6911,11 +7016,25 @@ function renderCookingStep() {
const ingsEl = document.getElementById('cooking-step-ings'); const ingsEl = document.getElementById('cooking-step-ings');
if (ings.length > 0) { if (ings.length > 0) {
const LOC_LABELS = { dispensa: '🏠 Dispensa', frigo: '❄️ Frigo', freezer: '🧊 Freezer' };
ingsEl.innerHTML = ings.map(ing => { ingsEl.innerHTML = ings.map(ing => {
const loc = (ing.location || 'dispensa').replace(/'/g, "\\'"); const loc = (ing.location || 'dispensa').replace(/'/g, "\\'");
const qtyNum = ing.qty_number || 0; const qtyNum = ing.qty_number || 0;
// Build info chips: brand, location, expiry
const chips = [];
if (ing.brand) chips.push(`<span class="cooking-ing-chip">${escapeHtml(ing.brand)}</span>`);
const locLabel = LOC_LABELS[ing.location] || (ing.location ? `📍 ${ing.location}` : '🏠 Dispensa');
chips.push(`<span class="cooking-ing-chip">${locLabel}</span>`);
if (ing.expiry_date) {
const daysLeft = Math.round((new Date(ing.expiry_date) - new Date()) / 86400000);
const expClass = daysLeft <= 3 ? 'exp-soon' : daysLeft <= 7 ? 'exp-close' : '';
chips.push(`<span class="cooking-ing-chip ${expClass}">📅 scade ${formatDate(ing.expiry_date)}</span>`);
}
return `<div class="cooking-ing-row"> return `<div class="cooking-ing-row">
<span class="cooking-ing-name">📦 <strong>${escapeHtml(ing.name)}</strong>: ${escapeHtml(ing.qty)}</span> <div style="flex:1;min-width:0">
<span class="cooking-ing-name">📦 <strong>${escapeHtml(ing.name)}</strong>: ${escapeHtml(ing.qty)}</span>
<div class="cooking-ing-meta">${chips.join('')}</div>
</div>
<button class="cooking-use-btn" onclick="cookingUseIngredient(${ing._idx}, ${ing.product_id}, '${loc}', ${qtyNum}, this)">📤 Usa</button> <button class="cooking-use-btn" onclick="cookingUseIngredient(${ing._idx}, ${ing.product_id}, '${loc}', ${qtyNum}, this)">📤 Usa</button>
</div>`; </div>`;
}).join(''); }).join('');
+328
View File
@@ -1474,3 +1474,331 @@
[2026-04-06 08:40:02] OK — 13 items cached [2026-04-06 08:40:02] OK — 13 items cached
[2026-04-06 08:45:02] OK — 13 items cached [2026-04-06 08:45:02] OK — 13 items cached
[2026-04-06 08:50:01] OK — 13 items cached [2026-04-06 08:50:01] OK — 13 items cached
[2026-04-06 08:55:02] OK — 12 items cached
[2026-04-06 09:00:02] OK — 13 items cached
[2026-04-06 09:05:01] OK — 13 items cached
[2026-04-06 09:10:02] OK — 13 items cached
[2026-04-06 09:15:02] OK — 12 items cached
[2026-04-06 09:20:01] OK — 12 items cached
[2026-04-06 09:25:02] OK — 12 items cached
[2026-04-06 09:30:02] OK — 12 items cached
[2026-04-06 09:35:01] OK — 12 items cached
[2026-04-06 09:40:02] OK — 12 items cached
[2026-04-06 09:45:02] OK — 12 items cached
[2026-04-06 09:50:01] OK — 12 items cached
[2026-04-06 09:55:02] OK — 12 items cached
[2026-04-06 10:00:02] OK — 12 items cached
[2026-04-06 10:05:01] OK — 12 items cached
[2026-04-06 10:10:02] OK — 12 items cached
[2026-04-06 10:15:02] OK — 12 items cached
[2026-04-06 10:20:02] OK — 12 items cached
[2026-04-06 10:25:01] OK — 12 items cached
[2026-04-06 10:30:02] OK — 11 items cached
[2026-04-06 10:35:02] OK — 11 items cached
[2026-04-06 10:40:01] OK — 11 items cached
[2026-04-06 10:45:02] OK — 29 items cached
[2026-04-06 10:50:02] OK — 29 items cached
[2026-04-06 10:55:01] OK — 29 items cached
[2026-04-06 11:00:02] OK — 29 items cached
[2026-04-06 11:05:02] OK — 29 items cached
[2026-04-06 11:10:01] OK — 29 items cached
[2026-04-06 11:15:03] OK — 29 items cached
[2026-04-06 11:20:01] OK — 29 items cached
[2026-04-06 11:25:02] OK — 29 items cached
[2026-04-06 11:30:02] OK — 29 items cached
[2026-04-06 11:35:01] OK — 29 items cached
[2026-04-06 11:40:02] OK — 29 items cached
[2026-04-06 11:45:02] OK — 29 items cached
[2026-04-06 11:50:01] OK — 29 items cached
[2026-04-06 11:55:02] OK — 29 items cached
[2026-04-06 12:00:02] OK — 29 items cached
[2026-04-06 12:05:02] OK — 29 items cached
[2026-04-06 12:10:01] OK — 29 items cached
[2026-04-06 12:15:02] OK — 29 items cached
[2026-04-06 12:20:02] OK — 29 items cached
[2026-04-06 12:25:02] OK — 29 items cached
[2026-04-06 12:30:01] OK — 29 items cached
[2026-04-06 12:35:02] OK — 29 items cached
[2026-04-06 12:40:01] OK — 29 items cached
[2026-04-06 12:45:02] OK — 29 items cached
[2026-04-06 12:50:02] OK — 29 items cached
[2026-04-06 12:55:01] OK — 29 items cached
[2026-04-06 13:00:02] OK — 29 items cached
[2026-04-06 13:05:02] OK — 29 items cached
[2026-04-06 13:10:01] OK — 29 items cached
[2026-04-06 13:15:03] OK — 29 items cached
[2026-04-06 13:20:01] OK — 29 items cached
[2026-04-06 13:25:02] OK — 29 items cached
[2026-04-06 13:30:01] OK — 29 items cached
[2026-04-06 13:35:01] OK — 29 items cached
[2026-04-06 13:40:02] OK — 29 items cached
[2026-04-06 13:45:01] OK — 29 items cached
[2026-04-06 13:50:02] OK — 29 items cached
[2026-04-06 13:55:02] OK — 29 items cached
[2026-04-06 14:00:01] OK — 29 items cached
[2026-04-06 14:05:02] OK — 29 items cached
[2026-04-06 14:10:02] OK — 29 items cached
[2026-04-06 14:15:02] OK — 29 items cached
[2026-04-06 14:20:02] OK — 29 items cached
[2026-04-06 14:25:01] OK — 29 items cached
[2026-04-06 14:30:02] OK — 29 items cached
[2026-04-06 14:35:02] OK — 29 items cached
[2026-04-06 14:40:01] OK — 29 items cached
[2026-04-06 14:45:01] OK — 29 items cached
[2026-04-06 14:50:02] OK — 29 items cached
[2026-04-06 14:55:02] OK — 29 items cached
[2026-04-06 15:00:07] OK — 29 items cached
[2026-04-06 15:05:01] OK — 29 items cached
[2026-04-06 15:10:02] OK — 29 items cached
[2026-04-06 15:15:02] OK — 29 items cached
[2026-04-06 15:20:02] OK — 29 items cached
[2026-04-06 15:25:02] OK — 29 items cached
[2026-04-06 15:30:02] OK — 29 items cached
[2026-04-06 15:35:01] OK — 29 items cached
[2026-04-06 15:40:02] OK — 29 items cached
[2026-04-06 15:45:02] OK — 29 items cached
[2026-04-06 15:50:01] OK — 29 items cached
[2026-04-06 15:55:02] OK — 29 items cached
[2026-04-06 16:00:02] OK — 29 items cached
[2026-04-06 16:05:02] OK — 29 items cached
[2026-04-06 16:10:01] OK — 29 items cached
[2026-04-06 16:15:02] OK — 29 items cached
[2026-04-06 16:20:02] OK — 29 items cached
[2026-04-06 16:25:02] OK — 29 items cached
[2026-04-06 16:30:01] OK — 29 items cached
[2026-04-06 16:35:01] OK — 29 items cached
[2026-04-06 16:40:02] OK — 29 items cached
[2026-04-06 16:45:02] OK — 29 items cached
[2026-04-06 16:50:01] OK — 29 items cached
[2026-04-06 16:55:02] OK — 29 items cached
[2026-04-06 17:00:02] OK — 29 items cached
[2026-04-06 17:05:02] OK — 29 items cached
[2026-04-06 17:10:01] OK — 29 items cached
[2026-04-06 17:15:02] OK — 29 items cached
[2026-04-06 17:20:02] OK — 29 items cached
[2026-04-06 17:25:02] OK — 29 items cached
[2026-04-06 17:30:01] OK — 29 items cached
[2026-04-06 17:35:02] OK — 29 items cached
[2026-04-06 17:40:02] OK — 29 items cached
[2026-04-06 17:45:01] OK — 29 items cached
[2026-04-06 17:50:02] OK — 29 items cached
[2026-04-06 17:55:02] OK — 29 items cached
[2026-04-06 18:00:01] OK — 29 items cached
[2026-04-06 18:05:02] OK — 29 items cached
[2026-04-06 18:10:02] OK — 29 items cached
[2026-04-06 18:15:02] OK — 29 items cached
[2026-04-06 18:20:01] OK — 29 items cached
[2026-04-06 18:25:02] OK — 29 items cached
[2026-04-06 18:30:02] OK — 29 items cached
[2026-04-06 18:35:01] OK — 29 items cached
[2026-04-06 18:40:02] OK — 29 items cached
[2026-04-06 18:45:02] OK — 29 items cached
[2026-04-06 18:50:01] OK — 29 items cached
[2026-04-06 18:55:02] OK — 29 items cached
[2026-04-06 19:00:02] OK — 29 items cached
[2026-04-06 19:05:02] OK — 29 items cached
[2026-04-06 19:10:01] OK — 29 items cached
[2026-04-06 19:15:02] OK — 29 items cached
[2026-04-06 19:20:02] OK — 29 items cached
[2026-04-06 19:25:02] OK — 29 items cached
[2026-04-06 19:30:02] OK — 29 items cached
[2026-04-06 19:35:01] OK — 29 items cached
[2026-04-06 19:40:02] OK — 29 items cached
[2026-04-06 19:45:02] OK — 29 items cached
[2026-04-06 19:50:01] OK — 29 items cached
[2026-04-06 19:55:02] OK — 29 items cached
[2026-04-06 20:00:02] OK — 29 items cached
[2026-04-06 20:05:01] OK — 29 items cached
[2026-04-06 20:10:02] OK — 29 items cached
[2026-04-06 20:15:02] OK — 29 items cached
[2026-04-06 20:20:02] OK — 29 items cached
[2026-04-06 20:25:02] OK — 29 items cached
[2026-04-06 20:30:01] OK — 29 items cached
[2026-04-06 20:35:02] OK — 29 items cached
[2026-04-06 20:40:02] OK — 29 items cached
[2026-04-06 20:45:01] OK — 29 items cached
[2026-04-06 20:50:02] OK — 29 items cached
[2026-04-06 20:55:01] OK — 29 items cached
[2026-04-06 21:00:02] OK — 29 items cached
[2026-04-06 21:05:02] OK — 29 items cached
[2026-04-06 21:10:01] OK — 29 items cached
[2026-04-06 21:15:02] OK — 29 items cached
[2026-04-06 21:20:02] OK — 29 items cached
[2026-04-06 21:25:02] OK — 29 items cached
[2026-04-06 21:30:01] OK — 29 items cached
[2026-04-06 21:35:02] OK — 29 items cached
[2026-04-06 21:40:02] OK — 29 items cached
[2026-04-06 21:45:01] OK — 29 items cached
[2026-04-06 21:50:02] OK — 29 items cached
[2026-04-06 21:55:02] OK — 29 items cached
[2026-04-06 22:00:02] OK — 29 items cached
[2026-04-06 22:05:01] OK — 29 items cached
[2026-04-06 22:10:02] OK — 29 items cached
[2026-04-06 22:15:02] OK — 29 items cached
[2026-04-06 22:20:02] OK — 29 items cached
[2026-04-06 22:25:01] OK — 29 items cached
[2026-04-06 22:30:02] OK — 29 items cached
[2026-04-06 22:35:01] OK — 29 items cached
[2026-04-06 22:40:02] OK — 29 items cached
[2026-04-06 22:45:02] OK — 29 items cached
[2026-04-06 22:50:02] OK — 29 items cached
[2026-04-06 22:55:01] OK — 29 items cached
[2026-04-06 23:00:02] OK — 29 items cached
[2026-04-06 23:05:01] OK — 29 items cached
[2026-04-06 23:10:02] OK — 29 items cached
[2026-04-06 23:15:02] OK — 29 items cached
[2026-04-06 23:20:02] OK — 29 items cached
[2026-04-06 23:25:02] OK — 29 items cached
[2026-04-06 23:30:01] OK — 29 items cached
[2026-04-06 23:35:02] OK — 29 items cached
[2026-04-06 23:40:02] OK — 29 items cached
[2026-04-06 23:45:01] OK — 29 items cached
[2026-04-06 23:50:02] OK — 29 items cached
[2026-04-06 23:55:02] OK — 29 items cached
[2026-04-07 00:00:01] OK — 30 items cached
[2026-04-07 00:05:02] OK — 30 items cached
[2026-04-07 00:10:01] OK — 30 items cached
[2026-04-07 00:15:03] OK — 30 items cached
[2026-04-07 00:20:01] OK — 30 items cached
[2026-04-07 00:25:02] OK — 30 items cached
[2026-04-07 00:30:02] OK — 30 items cached
[2026-04-07 00:35:02] OK — 30 items cached
[2026-04-07 00:40:01] OK — 30 items cached
[2026-04-07 00:45:02] OK — 30 items cached
[2026-04-07 00:50:01] OK — 30 items cached
[2026-04-07 00:55:01] OK — 30 items cached
[2026-04-07 01:00:02] OK — 30 items cached
[2026-04-07 01:05:01] OK — 30 items cached
[2026-04-07 01:10:02] OK — 30 items cached
[2026-04-07 01:15:02] OK — 30 items cached
[2026-04-07 01:20:02] OK — 30 items cached
[2026-04-07 01:25:01] OK — 30 items cached
[2026-04-07 01:30:02] OK — 30 items cached
[2026-04-07 01:35:02] OK — 30 items cached
[2026-04-07 01:40:01] OK — 30 items cached
[2026-04-07 01:45:02] OK — 30 items cached
[2026-04-07 01:50:02] OK — 30 items cached
[2026-04-07 01:55:02] OK — 30 items cached
[2026-04-07 02:00:01] OK — 30 items cached
[2026-04-07 02:05:02] OK — 30 items cached
[2026-04-07 02:10:02] OK — 30 items cached
[2026-04-07 02:15:02] OK — 30 items cached
[2026-04-07 02:20:01] OK — 30 items cached
[2026-04-07 02:25:02] OK — 30 items cached
[2026-04-07 02:30:02] OK — 30 items cached
[2026-04-07 02:35:01] OK — 30 items cached
[2026-04-07 02:40:02] OK — 30 items cached
[2026-04-07 02:45:01] OK — 30 items cached
[2026-04-07 02:50:02] OK — 30 items cached
[2026-04-07 02:55:02] OK — 30 items cached
[2026-04-07 03:00:02] OK — 30 items cached
[2026-04-07 03:05:02] OK — 30 items cached
[2026-04-07 03:10:01] OK — 30 items cached
[2026-04-07 03:15:02] OK — 30 items cached
[2026-04-07 03:20:02] OK — 30 items cached
[2026-04-07 03:25:02] OK — 30 items cached
[2026-04-07 03:30:01] OK — 30 items cached
[2026-04-07 03:35:02] OK — 30 items cached
[2026-04-07 03:40:02] OK — 30 items cached
[2026-04-07 03:45:01] OK — 30 items cached
[2026-04-07 03:50:02] OK — 30 items cached
[2026-04-07 03:55:02] OK — 30 items cached
[2026-04-07 04:00:01] OK — 30 items cached
[2026-04-07 04:05:01] OK — 30 items cached
[2026-04-07 04:10:02] OK — 30 items cached
[2026-04-07 04:15:02] OK — 30 items cached
[2026-04-07 04:20:02] OK — 30 items cached
[2026-04-07 04:25:01] OK — 30 items cached
[2026-04-07 04:30:02] OK — 30 items cached
[2026-04-07 04:35:02] OK — 30 items cached
[2026-04-07 04:40:01] OK — 30 items cached
[2026-04-07 04:45:02] OK — 30 items cached
[2026-04-07 04:50:02] OK — 30 items cached
[2026-04-07 04:55:01] OK — 31 items cached
[2026-04-07 05:00:02] OK — 31 items cached
[2026-04-07 05:05:02] OK — 31 items cached
[2026-04-07 05:10:02] OK — 31 items cached
[2026-04-07 05:15:02] OK — 31 items cached
[2026-04-07 05:20:02] OK — 31 items cached
[2026-04-07 05:25:01] OK — 31 items cached
[2026-04-07 05:30:02] OK — 31 items cached
[2026-04-07 05:35:02] OK — 31 items cached
[2026-04-07 05:40:01] OK — 31 items cached
[2026-04-07 05:45:02] OK — 31 items cached
[2026-04-07 05:50:01] OK — 31 items cached
[2026-04-07 05:55:02] OK — 31 items cached
[2026-04-07 06:00:02] OK — 31 items cached
[2026-04-07 06:05:02] OK — 31 items cached
[2026-04-07 06:10:01] OK — 31 items cached
[2026-04-07 06:15:02] OK — 31 items cached
[2026-04-07 06:20:02] OK — 31 items cached
[2026-04-07 06:25:02] OK — 31 items cached
[2026-04-07 06:30:01] OK — 31 items cached
[2026-04-07 06:35:02] OK — 31 items cached
[2026-04-07 06:40:02] OK — 31 items cached
[2026-04-07 06:45:01] OK — 31 items cached
[2026-04-07 06:50:02] OK — 31 items cached
[2026-04-07 06:55:02] OK — 31 items cached
[2026-04-07 07:00:02] OK — 31 items cached
[2026-04-07 07:05:01] OK — 31 items cached
[2026-04-07 07:10:02] OK — 31 items cached
[2026-04-07 07:15:02] OK — 31 items cached
[2026-04-07 07:20:02] OK — 31 items cached
[2026-04-07 07:25:01] OK — 31 items cached
[2026-04-07 07:30:02] OK — 31 items cached
[2026-04-07 07:35:01] OK — 31 items cached
[2026-04-07 07:40:02] OK — 31 items cached
[2026-04-07 07:45:02] OK — 31 items cached
[2026-04-07 07:50:01] OK — 31 items cached
[2026-04-07 07:55:02] OK — 31 items cached
[2026-04-07 08:00:02] OK — 31 items cached
[2026-04-07 08:05:01] OK — 31 items cached
[2026-04-07 08:10:02] OK — 31 items cached
[2026-04-07 08:15:02] OK — 31 items cached
[2026-04-07 08:20:02] OK — 31 items cached
[2026-04-07 08:25:02] OK — 31 items cached
[2026-04-07 08:30:01] OK — 31 items cached
[2026-04-07 08:35:02] OK — 31 items cached
[2026-04-07 08:40:02] OK — 31 items cached
[2026-04-07 08:45:01] OK — 31 items cached
[2026-04-07 08:50:02] OK — 31 items cached
[2026-04-07 08:55:01] OK — 31 items cached
[2026-04-07 09:00:02] OK — 31 items cached
[2026-04-07 09:05:02] OK — 31 items cached
[2026-04-07 09:10:01] OK — 31 items cached
[2026-04-07 09:15:03] OK — 31 items cached
[2026-04-07 09:20:01] OK — 31 items cached
[2026-04-07 09:25:02] OK — 31 items cached
[2026-04-07 09:30:02] OK — 31 items cached
[2026-04-07 09:35:01] OK — 31 items cached
[2026-04-07 09:40:02] OK — 31 items cached
[2026-04-07 09:45:01] OK — 31 items cached
[2026-04-07 09:50:02] OK — 31 items cached
[2026-04-07 09:55:02] OK — 31 items cached
[2026-04-07 10:00:01] OK — 31 items cached
[2026-04-07 10:05:02] OK — 31 items cached
[2026-04-07 10:10:02] OK — 31 items cached
[2026-04-07 10:15:02] OK — 31 items cached
[2026-04-07 10:20:01] OK — 31 items cached
[2026-04-07 10:25:02] OK — 31 items cached
[2026-04-07 10:30:02] OK — 31 items cached
[2026-04-07 10:35:01] OK — 31 items cached
[2026-04-07 10:40:02] OK — 31 items cached
[2026-04-07 10:45:02] OK — 31 items cached
[2026-04-07 10:50:01] OK — 31 items cached
[2026-04-07 10:55:02] OK — 31 items cached
[2026-04-07 11:00:02] OK — 31 items cached
[2026-04-07 11:05:01] OK — 31 items cached
[2026-04-07 11:10:02] OK — 31 items cached
[2026-04-07 11:15:02] OK — 31 items cached
[2026-04-07 11:20:02] OK — 31 items cached
[2026-04-07 11:25:01] OK — 31 items cached
[2026-04-07 11:30:02] OK — 31 items cached
[2026-04-07 11:35:02] OK — 31 items cached
[2026-04-07 11:40:01] OK — 31 items cached
[2026-04-07 11:45:02] OK — 31 items cached
[2026-04-07 11:50:01] OK — 31 items cached
[2026-04-07 11:55:02] OK — 31 items cached
[2026-04-07 12:00:02] OK — 31 items cached
[2026-04-07 12:05:02] OK — 31 items cached
[2026-04-07 12:10:01] OK — 30 items cached
BIN
View File
Binary file not shown.
File diff suppressed because one or more lines are too long