diff --git a/app/routes.py b/app/routes.py index 6769c7ac099f1f7488b16639836e8bb78f918a4c..0c673de3496b68f7e6e13180c4758e4fbe430e35 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/services.py b/app/services.py index 43e27732a4d5154ffe6d9b44bbe71d74b68da50a..e103a1809d8c6d0cce7888553d8ac825f487996c 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/app/templates/admin_inventory.html b/app/templates/admin_inventory.html index 7c1e63cc7b100eae9d7546a7b429340419f577c5..f6d97a0689f31828f41e480ca4f2d686972b0e85 100644 --- a/app/templates/admin_inventory.html +++ b/app/templates/admin_inventory.html @@ -11,6 +11,10 @@ Warehouse Requests + | + + Worker Kiosk +
Error: {{ error }}
+ {% endif %} + + {% if step %} ++ Job Barcode: + {{ step.job.barcode }} +
+ ++ Station: + {{ step.station.name }} +
+ ++ Instruction: + {{ step.instruction }} +
+ + {% if step.photo_url %} ++ Instruction Photo: + {{ step.photo_url }} +
+ {% endif %} + +No parts required for this step.
+ {% endif %} + {% endif %} + + \ 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 0000000000000000000000000000000000000000..94625902ea3abe79c820ad74e9e1053916f10b79 --- /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