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 %} + + {% else %} +

No parts required for this step.

+ {% endif %} + {% endif %} + + \ No newline at end of file -- GitLab