From dc06cc7caa3fe0e7fe6ad8cfba845b6148ff66e8 Mon Sep 17 00:00:00 2001
From: Nameru Thapa
Date: Sun, 9 Aug 2026 15:10:57 +0200
Subject: [PATCH 1/2] feat: add barcode station validation
---
app/services.py | 31 ++++++++-
tests/test_barcode_scanning.py | 118 +++++++++++++++++++++++++++++++++
2 files changed, 148 insertions(+), 1 deletion(-)
create mode 100644 tests/test_barcode_scanning.py
diff --git a/app/services.py b/app/services.py
index 43e2773..e103a18 100644
--- a/app/services.py
+++ b/app/services.py
@@ -158,4 +158,33 @@ def fulfill_transfer_request(request_id, transferred_quantity):
else:
db.session.commit()
- return transfer_request
\ No newline at end of file
+ return transfer_request
+def scan_job_barcode(barcode, station_id):
+ normalized_barcode = (barcode or "").strip().upper()
+ normalized_station_id = (station_id or "").strip().upper()
+
+ if not normalized_barcode:
+ return None, "Enter a barcode."
+
+ if not normalized_station_id:
+ return None, "Select a station."
+
+ job = Job.query.filter_by(
+ barcode=normalized_barcode,
+ ).first()
+
+ if job is None:
+ return None, "Barcode not recognized."
+
+ if job.status != "Active":
+ return None, "This production job is not active."
+
+ current_step = job.steps[0] if job.steps else None
+
+ if current_step is None:
+ return None, "This production job has no production steps."
+
+ if current_step.station_id != normalized_station_id:
+ return None, "This job is not ready at this station."
+
+ return current_step, None
\ No newline at end of file
diff --git a/tests/test_barcode_scanning.py b/tests/test_barcode_scanning.py
new file mode 100644
index 0000000..9462590
--- /dev/null
+++ b/tests/test_barcode_scanning.py
@@ -0,0 +1,118 @@
+import pytest
+
+from app import create_app, db
+from app.models import Job, JobStep, Part, StepPart
+from app.services import scan_job_barcode
+
+
+@pytest.fixture
+def app():
+ test_app = create_app(
+ {
+ "TESTING": True,
+ "SQLALCHEMY_DATABASE_URI": "sqlite:///:memory:",
+ }
+ )
+
+ yield test_app
+
+
+def create_active_job():
+ part = Part(
+ name="Part A",
+ unit="pcs",
+ )
+
+ job = Job(
+ name="Day 3 Barcode Test",
+ quantity=2,
+ threshold_percent=20.0,
+ status="Active",
+ barcode="MES-DAY3-0001",
+ )
+
+ db.session.add_all([part, job])
+ db.session.flush()
+
+ step = JobStep(
+ job_id=job.id,
+ step_number=1,
+ station_id="ST-01",
+ instruction="Attach Part A",
+ photo_url="photo-step-1.jpg",
+ )
+
+ db.session.add(step)
+ db.session.flush()
+
+ usage = StepPart(
+ step_id=step.id,
+ part_id=part.id,
+ quantity=1,
+ )
+
+ db.session.add(usage)
+ db.session.commit()
+
+ return job.id
+
+
+def test_tc07_valid_barcode_at_correct_station(app):
+ with app.app_context():
+ create_active_job()
+
+ step, error = scan_job_barcode(
+ "MES-DAY3-0001",
+ "ST-01",
+ )
+
+ assert error is None
+ assert step is not None
+ assert step.step_number == 1
+ assert step.station_id == "ST-01"
+ assert step.instruction == "Attach Part A"
+ assert step.photo_url == "photo-step-1.jpg"
+
+ assert len(step.parts) == 1
+ assert step.parts[0].part.name == "Part A"
+ assert step.parts[0].quantity == 1
+
+
+def test_tc08_unknown_barcode_is_rejected_without_changes(app):
+ with app.app_context():
+ job_id = create_active_job()
+
+ job_before = db.session.get(Job, job_id)
+ status_before = job_before.status
+ barcode_before = job_before.barcode
+
+ step, error = scan_job_barcode(
+ "MES-UNKNOWN",
+ "ST-01",
+ )
+
+ assert step is None
+ assert error == "Barcode not recognized."
+
+ job_after = db.session.get(Job, job_id)
+ assert job_after.status == status_before
+ assert job_after.barcode == barcode_before
+ assert Job.query.count() == 1
+ assert JobStep.query.count() == 1
+
+
+def test_tc09_valid_barcode_at_wrong_station(app):
+ with app.app_context():
+ job_id = create_active_job()
+
+ step, error = scan_job_barcode(
+ "MES-DAY3-0001",
+ "ST-02",
+ )
+
+ assert step is None
+ assert error == "This job is not ready at this station."
+
+ job = db.session.get(Job, job_id)
+ assert job.status == "Active"
+ assert job.barcode == "MES-DAY3-0001"
\ No newline at end of file
--
GitLab
From a6231d6c31f3ce208f5f4a449ebf87973102d5c3 Mon Sep 17 00:00:00 2001
From: Nameru Thapa
Date: Sun, 9 Aug 2026 15:36:19 +0200
Subject: [PATCH 2/2] feat: add worker kiosk barcode interface
---
app/routes.py | 34 ++++++++++
app/templates/admin_inventory.html | 4 ++
app/templates/admin_jobs.html | 3 +-
app/templates/job_detail.html | 3 +-
app/templates/transfer_requests.html | 4 ++
app/templates/worker_kiosk.html | 96 ++++++++++++++++++++++++++++
6 files changed, 142 insertions(+), 2 deletions(-)
create mode 100644 app/templates/worker_kiosk.html
diff --git a/app/routes.py b/app/routes.py
index 6769c7a..0c673de 100644
--- a/app/routes.py
+++ b/app/routes.py
@@ -14,6 +14,7 @@ from app.models import (
from app.services import (
activate_job,
fulfill_transfer_request,
+ scan_job_barcode,
set_station_inventory,
)
@@ -209,4 +210,37 @@ def fulfill_request_route(request_id):
return redirect(
url_for("main.warehouse_requests")
+ )
+@bp.route("/kiosk", methods=["GET", "POST"])
+def worker_kiosk():
+ stations = Station.query.order_by(Station.id).all()
+
+ selected_station_id = ""
+ entered_barcode = ""
+ step = None
+ error = None
+
+ if request.method == "POST":
+ selected_station_id = request.form.get(
+ "station_id",
+ "",
+ ).strip()
+
+ entered_barcode = request.form.get(
+ "barcode",
+ "",
+ ).strip()
+
+ step, error = scan_job_barcode(
+ entered_barcode,
+ selected_station_id,
+ )
+
+ return render_template(
+ "worker_kiosk.html",
+ stations=stations,
+ selected_station_id=selected_station_id,
+ entered_barcode=entered_barcode,
+ step=step,
+ error=error,
)
\ No newline at end of file
diff --git a/app/templates/admin_inventory.html b/app/templates/admin_inventory.html
index 7c1e63c..f6d97a0 100644
--- a/app/templates/admin_inventory.html
+++ b/app/templates/admin_inventory.html
@@ -11,6 +11,10 @@
Warehouse Requests
+ |
+
+ Worker Kiosk
+
Station Inventory
diff --git a/app/templates/admin_jobs.html b/app/templates/admin_jobs.html
index 6ea3800..2ee97ff 100644
--- a/app/templates/admin_jobs.html
+++ b/app/templates/admin_jobs.html
@@ -10,7 +10,8 @@
|
Station Inventory
|
- Warehouse Requests
+ Warehouse Requests |
+ Worker Kiosk
Production Jobs
diff --git a/app/templates/job_detail.html b/app/templates/job_detail.html
index 9440225..de6ce40 100644
--- a/app/templates/job_detail.html
+++ b/app/templates/job_detail.html
@@ -10,7 +10,8 @@
|
Station Inventory
|
- Warehouse Requests
+ Warehouse Requests |
+Worker Kiosk
{{ job.name }}
diff --git a/app/templates/transfer_requests.html b/app/templates/transfer_requests.html
index e8c6753..0853727 100644
--- a/app/templates/transfer_requests.html
+++ b/app/templates/transfer_requests.html
@@ -11,6 +11,10 @@
Station Inventory
+ |
+
+ Worker Kiosk
+
Warehouse Transfer Requests
diff --git a/app/templates/worker_kiosk.html b/app/templates/worker_kiosk.html
new file mode 100644
index 0000000..29f870f
--- /dev/null
+++ b/app/templates/worker_kiosk.html
@@ -0,0 +1,96 @@
+
+
+
+
+ Worker Kiosk
+
+
+
+
+ Worker Kiosk
+
+
+
+ {% if error %}
+ Error: {{ error }}
+ {% endif %}
+
+ {% if step %}
+
+
+ {{ step.job.name }}
+
+
+ Job Barcode:
+ {{ step.job.barcode }}
+
+
+
+ Station:
+ {{ step.station.name }}
+
+
+ Production Step {{ step.step_number }}
+
+
+ Instruction:
+ {{ step.instruction }}
+
+
+ {% if step.photo_url %}
+
+ Instruction Photo:
+ {{ step.photo_url }}
+
+ {% endif %}
+
+ Required Parts
+
+ {% if step.parts %}
+
+ {% for usage in step.parts %}
+ -
+ {{ usage.part.name }} x {{ usage.quantity }}
+
+ {% endfor %}
+
+ {% else %}
+ No parts required for this step.
+ {% endif %}
+ {% endif %}
+
+
\ No newline at end of file
--
GitLab