V0.8.6 - Frefractor: Counting.py

This commit is contained in:
Javier
2026-01-23 11:45:35 -06:00
parent 6789f0899a
commit 53158e76e4
10 changed files with 705 additions and 669 deletions

View File

@@ -62,6 +62,8 @@
<footer class="footer">
<div class="footer-content">
<p>&copy; 2026 Javier Torres. All Rights Reserved.</p>
<p class="text-muted"><small>v{{ version }}</small></p>
</div>
</footer>
</html>

View File

@@ -97,9 +97,9 @@
{% set row_class = 'weight_discrepancy' %}
{% endif %}
<div class="scan-row scan-row-{{ row_class }}"
data-entry-id="{{ scan.entry_id }}"
onclick="openScanDetail({{ scan.entry_id }})">
<div class="scan-row scan-row-{{ row_class }}"
data-entry-id="{{ scan.entry_id }}"
onclick="openScanDetail('{{ scan.entry_id }}')">
<div class="scan-row-lot">{{ scan.lot_number }}</div>
<div class="scan-row-item">{{ scan.item or 'N/A' }}</div>
<div class="scan-row-weight">{{ scan.actual_weight }} lbs</div>
@@ -160,7 +160,7 @@
<div class="finish-section">
<div class="action-buttons-row">
<a href="{{ url_for('my_counts', session_id=session_id) }}" class="btn btn-secondary btn-block btn-lg">
<a href="{{ url_for('counting.my_counts', session_id=session_id) }}" class="btn btn-secondary btn-block btn-lg">
← Back to My Counts
</a>
<button id="finishBtn" class="btn btn-success btn-block btn-lg" onclick="finishLocation()">
@@ -191,7 +191,7 @@ document.getElementById('lotScanForm').addEventListener('submit', function(e) {
});
function checkDuplicate() {
fetch('{{ url_for("scan_lot", session_id=session_id, location_count_id=location.location_count_id) }}', {
fetch('{{ url_for("counting.scan_lot", session_id=session_id, location_count_id=location.location_count_id) }}', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
@@ -260,7 +260,7 @@ function submitScan(weight) {
return;
}
fetch('{{ url_for("scan_lot", session_id=session_id, location_count_id=location.location_count_id) }}', {
fetch('{{ url_for("counting.scan_lot", session_id=session_id, location_count_id=location.location_count_id) }}', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
@@ -560,7 +560,7 @@ function deleteFromDetail(entryId) {
function finishLocation() {
if (!confirm('Are you finished counting this location?')) return;
fetch('{{ url_for("finish_location", session_id=session_id, location_count_id=location.location_count_id) }}', {
fetch('{{ url_for("counting.finish_location", session_id=session_id, location_count_id=location.location_count_id) }}', {
method: 'POST',
headers: {'Content-Type': 'application/json'}
})

View File

@@ -37,14 +37,14 @@
</div>
</div>
<div class="bin-actions">
<a href="{{ url_for('count_location', session_id=count_session.session_id, location_count_id=bin.location_count_id) }}" class="btn btn-primary btn-block">
<a href="{{ url_for('counting.count_location', session_id=count_session.session_id, location_count_id=bin.location_count_id) }}" class="btn btn-primary btn-block">
Resume Counting
</a>
<div class="bin-actions-row">
<button class="btn btn-secondary" onclick="markComplete({{ bin.location_count_id }})">
<button class="btn btn-secondary" onclick="markComplete('{{ bin.location_count_id }}')">
✓ Mark Complete
</button>
<button class="btn btn-danger" onclick="deleteBinCount({{ bin.location_count_id }}, '{{ bin.location_name }}')">
<button class="btn btn-danger" onclick="deleteBinCount('{{ bin.location_count_id }}', '{{ bin.location_name }}')">
🗑️ Delete
</button>
</div>
@@ -105,7 +105,7 @@
<button type="button" class="btn-close-modal" onclick="closeStartBinModal()"></button>
</div>
<form id="startBinForm" action="{{ url_for('start_bin_count', session_id=count_session.session_id) }}" method="POST">
<form id="startBinForm" action="{{ url_for('counting.start_bin_count', session_id=count_session.session_id) }}" method="POST">
<div class="form-group">
<label class="form-label">Bin Number *</label>
<input type="text" name="location_name" class="form-input scan-input" required autofocus placeholder="Scan or type bin number">

View File

@@ -8,8 +8,9 @@
<div>
<a href="{{ url_for('dashboard') }}" class="breadcrumb">← Back to Dashboard</a>
<h1 class="page-title">{{ count_session.session_name }}</h1>
<!-- Fixed variable name from session.session_type to count_session.session_type -->
<span class="session-type-badge session-type-{{ count_session.session_type }}">
{{ 'Full Physical' if session.session_type == 'full_physical' else 'Cycle Count' }}
{{ 'Full Physical' if count_session.session_type == 'full_physical' else 'Cycle Count' }}
</span>
</div>
</div>
@@ -30,6 +31,7 @@
{% endif %}
</div>
{% if not count_session.master_baseline_timestamp %}
<!-- Note: Using data_imports blueprint URL -->
<form method="POST" action="{{ url_for('data_imports.upload_master', session_id=count_session.session_id) }}" enctype="multipart/form-data" class="upload-form">
<input type="file" name="csv_file" accept=".csv" required class="file-input">
<button type="submit" class="btn btn-primary btn-sm">Upload MASTER</button>
@@ -41,8 +43,8 @@
<div class="baseline-label">CURRENT Baseline (Optional)</div>
<div class="baseline-status">
{% if count_session.current_baseline_timestamp %}
<span class="status-badge status-success">Last Updated:
<div>{{ count_session.current_baseline_timestamp[:16] if count_session.current_baseline_timestamp else 'Never' }}</div>
<span class="status-badge status-success">✓ Uploaded</span>
<small class="baseline-time">{{ count_session.current_baseline_timestamp[:16] }}</small>
{% else %}
<span class="status-badge status-neutral">Not Uploaded</span>
{% endif %}
@@ -65,27 +67,27 @@
<h2 class="section-title">Real-Time Statistics</h2>
<div class="stats-grid">
<div class="stat-card stat-match" onclick="showStatusDetails('match', {{ count_session.session_id }})">
<div class="stat-card stat-match" onclick="showStatusDetails('match')">
<div class="stat-number">{{ stats.matched or 0 }}</div>
<div class="stat-label">✓ Matched</div>
</div>
<div class="stat-card stat-duplicate" onclick="showStatusDetails('duplicates', {{ count_session.session_id }})">
<div class="stat-card stat-duplicate" onclick="showStatusDetails('duplicates')">
<div class="stat-number">{{ stats.duplicates or 0 }}</div>
<div class="stat-label">🔵 Duplicates</div>
</div>
<div class="stat-card stat-weight-disc" onclick="showStatusDetails('weight_discrepancy', {{ count_session.session_id }})">
<div class="stat-card stat-weight-disc" onclick="showStatusDetails('weight_discrepancy')">
<div class="stat-number">{{ stats.weight_discrepancy or 0 }}</div>
<div class="stat-label">⚖️ Weight Discrepancy</div>
</div>
<div class="stat-card stat-wrong" onclick="showStatusDetails('wrong_location', {{ count_session.session_id }})">
<div class="stat-card stat-wrong" onclick="showStatusDetails('wrong_location')">
<div class="stat-number">{{ stats.wrong_location or 0 }}</div>
<div class="stat-label">⚠ Wrong Location</div>
</div>
<div class="stat-card stat-ghost" onclick="showStatusDetails('ghost_lot', {{ count_session.session_id }})">
<div class="stat-card stat-ghost" onclick="showStatusDetails('ghost_lot')">
<div class="stat-number">{{ stats.ghost_lots or 0 }}</div>
<div class="stat-label">🟣 Ghost Lots</div>
</div>
<div class="stat-card stat-missing" onclick="showStatusDetails('missing', {{ count_session.session_id }})">
<div class="stat-card stat-missing" onclick="showStatusDetails('missing')">
<div class="stat-number">{{ stats.missing_lots or 0 }}</div>
<div class="stat-label">🔴 Missing</div>
</div>
@@ -129,7 +131,12 @@
</thead>
<tbody>
{% for loc in locations %}
<tr class="location-row-clickable" onclick="showLocationDetails({{ loc.location_count_id }}, '{{ loc.location_name }}', '{{ loc.status }}')">
<!-- Refactored to use data attributes instead of direct Jinja injection in onclick -->
<tr class="location-row-clickable"
data-id="{{ loc.location_count_id }}"
data-name="{{ loc.location_name }}"
data-status="{{ loc.status }}"
onclick="handleLocationClick(this)">
<td><strong>{{ loc.location_name }}</strong></td>
<td>
<span class="status-badge status-{{ loc.status }}">
@@ -246,7 +253,10 @@
</div>
<script>
function showStatusDetails(status, sessionId) {
// Store the Session ID globally to use in functions without passing it every time
const CURRENT_SESSION_ID = "{{ count_session.session_id }}";
function showStatusDetails(status) {
document.getElementById('statusModal').style.display = 'flex';
document.getElementById('statusDetailContent').innerHTML = '<div class="loading-spinner">Loading...</div>';
@@ -261,8 +271,8 @@ function showStatusDetails(status, sessionId) {
};
document.getElementById('statusModalTitle').textContent = titles[status] || 'Details';
// Fetch details
fetch(`/session/${sessionId}/status-details/${status}`)
// Fetch details using the blueprint URL structure
fetch(`/session/${CURRENT_SESSION_ID}/status-details/${status}`)
.then(response => response.json())
.then(data => {
if (data.success) {
@@ -421,6 +431,14 @@ let currentLocationName = '';
let currentLocationStatus = '';
let currentLocationData = null;
// New helper function to handle click from data attributes
function handleLocationClick(row) {
const id = row.getAttribute('data-id');
const name = row.getAttribute('data-name');
const status = row.getAttribute('data-status');
showLocationDetails(id, name, status);
}
function showLocationDetails(locationCountId, locationName, status) {
currentLocationId = locationCountId;
currentLocationName = locationName;
@@ -594,6 +612,7 @@ function closeFinalizeConfirm() {
}
function confirmFinalize() {
// Note: The /complete endpoint is handled by blueprints/counting.py
fetch(`/location/${currentLocationId}/complete`, {
method: 'POST',
headers: {
@@ -625,6 +644,7 @@ function closeReopenConfirm() {
}
function confirmReopen() {
// Note: The /reopen endpoint is handled by blueprints/admin_locations.py
fetch(`/location/${currentLocationId}/reopen`, {
method: 'POST',
headers: {
@@ -656,4 +676,4 @@ document.addEventListener('keydown', function(e) {
}
});
</script>
{% endblock %}
{% endblock %}

View File

@@ -7,11 +7,11 @@
<!-- Mode Selector (only for admins) -->
{% if session.role in ['owner', 'admin'] %}
<div class="mode-selector">
<button class="mode-btn" onclick="window.location.href='{{ url_for('dashboard') }}'">
👔 Admin Console
</button>
<a href="{{ url_for('dashboard') }}" class="mode-btn">
Admin Console
</a>
<button class="mode-btn mode-btn-active">
📦 Scanning Mode
Scanning Mode
</button>
</div>
{% endif %}
@@ -22,11 +22,11 @@
{% if sessions %}
<div class="sessions-list">
{% for session in sessions %}
<a href="{{ url_for('count_session', session_id=session.session_id) }}" class="session-list-item">
{% for s in sessions %}
<a href="{{ url_for('counting.count_session', session_id=s.session_id) }}" class="session-list-item">
<div class="session-list-info">
<h3 class="session-list-name">{{ session.session_name }}</h3>
<span class="session-list-type">{{ 'Full Physical' if session.session_type == 'full_physical' else 'Cycle Count' }}</span>
<h3 class="session-list-name">{{ s.session_name }}</h3>
<span class="session-list-type">{{ 'Full Physical' if s.session_type == 'full_physical' else 'Cycle Count' }}</span>
</div>
<div class="session-list-action">
<span class="arrow-icon"></span>