From ff38e9f78fc1c3db58c7e87e7e1f81cb6bd2b994 Mon Sep 17 00:00:00 2001
From: Nameru Thapa
- Production Jobs
+
+ Production Jobs
+
|
Warehouse Requests
|
- Worker Kiosk
+ Worker Kiosk
- No parts exist yet. Create a production job and add a step first.
+ No parts exist yet. Use the form above to add
+ the first part.
Station Inventory
+ Add Part to Station Inventory
+
+
+
Update Station Inventory
{% if parts %}
{% else %}
@@ -85,6 +176,7 @@
Station
Part
+ Unit
Quantity
{% endfor %}
diff --git a/app/templates/job_detail.html b/app/templates/job_detail.html
index 4fcace8..b87293c 100644
--- a/app/templates/job_detail.html
+++ b/app/templates/job_detail.html
@@ -153,8 +153,28 @@
{{ inventory.station.name }}
{{ inventory.part.name }}
+ {{ inventory.part.unit }}
{{ inventory.quantity }}
-
-
+
+
+
+
+
+
+ Select an existing part or enter a new name.
+
diff --git a/tests/test_part_management.py b/tests/test_part_management.py new file mode 100644 index 0000000..7884f1f --- /dev/null +++ b/tests/test_part_management.py @@ -0,0 +1,305 @@ +import pytest + +from app import create_app, db +from app.models import ( + Job, + JobStep, + Part, + StationInventory, + StepPart, +) + + +@pytest.fixture +def app(): + test_app = create_app( + { + "TESTING": True, + "SQLALCHEMY_DATABASE_URI": "sqlite:///:memory:", + } + ) + + yield test_app + + +def create_draft_job(name="Part Management Test"): + job = Job( + name=name, + quantity=1, + threshold_percent=20.0, + status="Draft", + ) + + db.session.add(job) + db.session.commit() + + return job.id + + +def test_tc19_create_part_directly_with_zero_quantity(app): + client = app.test_client() + + response = client.post( + "/admin/inventory", + data={ + "action": "create_part", + "part_name": "Direct Inventory Part", + "part_unit": "pcs", + "station_id": "ST-04", + "initial_quantity": "0", + }, + ) + + assert response.status_code == 302 + + with app.app_context(): + part = Part.query.filter_by( + name="Direct Inventory Part", + ).one() + + inventory = StationInventory.query.filter_by( + station_id="ST-04", + part_id=part.id, + ).one() + + assert part.unit == "pcs" + assert inventory.quantity == 0 + + +def test_tc20_existing_part_can_be_assigned_to_another_station(app): + with app.app_context(): + part = Part( + name="Reusable Part", + unit="kg", + ) + + db.session.add(part) + db.session.flush() + + db.session.add( + StationInventory( + station_id="ST-01", + part_id=part.id, + quantity=4, + ) + ) + + db.session.commit() + + part_id = part.id + + client = app.test_client() + + response = client.post( + "/admin/inventory", + data={ + "action": "create_part", + "part_name": " reusable PART ", + "part_unit": "KG", + "station_id": "ST-05", + "initial_quantity": "2", + }, + ) + + assert response.status_code == 302 + + with app.app_context(): + assert Part.query.count() == 1 + + original_inventory = ( + StationInventory.query.filter_by( + station_id="ST-01", + part_id=part_id, + ).one() + ) + + new_inventory = ( + StationInventory.query.filter_by( + station_id="ST-05", + part_id=part_id, + ).one() + ) + + assert original_inventory.quantity == 4 + assert new_inventory.quantity == 2 + + +def test_tc21_duplicate_station_part_is_rejected(app): + with app.app_context(): + part = Part( + name="Protected Part", + unit="pcs", + ) + + db.session.add(part) + db.session.flush() + + inventory = StationInventory( + station_id="ST-02", + part_id=part.id, + quantity=7, + ) + + db.session.add(inventory) + db.session.commit() + + part_id = part.id + + client = app.test_client() + + response = client.post( + "/admin/inventory", + data={ + "action": "create_part", + "part_name": "protected part", + "part_unit": "pcs", + "station_id": "ST-02", + "initial_quantity": "0", + }, + ) + + assert response.status_code == 400 + + with app.app_context(): + assert Part.query.count() == 1 + assert StationInventory.query.count() == 1 + + inventory = StationInventory.query.filter_by( + station_id="ST-02", + part_id=part_id, + ).one() + + assert inventory.quantity == 7 + + +def test_tc22_job_step_reuses_existing_part(app): + with app.app_context(): + part = Part( + name="Catalogue Part", + unit="kg", + ) + + db.session.add(part) + db.session.commit() + + part_id = part.id + job_id = create_draft_job( + "Existing Catalogue Part Test" + ) + + client = app.test_client() + + response = client.post( + f"/admin/jobs/{job_id}", + data={ + "station_id": "ST-03", + "instruction": "Install catalogue part", + "photo_url": "", + "part_name": " catalogue PART ", + "part_quantity": "1", + }, + ) + + assert response.status_code == 302 + + with app.app_context(): + assert Part.query.count() == 1 + + step = JobStep.query.one() + usage = StepPart.query.one() + + assert usage.step_id == step.id + assert usage.part_id == part_id + + inventory = StationInventory.query.filter_by( + station_id="ST-03", + part_id=part_id, + ).one() + + assert inventory.quantity == 0 + + +def test_tc23_job_step_creates_new_part_with_zero_stock(app): + with app.app_context(): + job_id = create_draft_job( + "New Job Part Test" + ) + + client = app.test_client() + + response = client.post( + f"/admin/jobs/{job_id}", + data={ + "station_id": "ST-04", + "instruction": "Install new part", + "photo_url": "", + "part_name": "New Step Part", + "part_quantity": "1", + }, + ) + + assert response.status_code == 302 + + with app.app_context(): + part = Part.query.filter_by( + name="New Step Part", + ).one() + + inventory = StationInventory.query.filter_by( + station_id="ST-04", + part_id=part.id, + ).one() + + assert part.unit == "pcs" + assert inventory.quantity == 0 + + +def test_tc24_job_step_form_contains_part_suggestions(app): + with app.app_context(): + db.session.add_all( + [ + Part(name="PA1", unit="pcs"), + Part(name="PA2", unit="kg"), + ] + ) + + db.session.commit() + + job_id = create_draft_job( + "Part Suggestion Test" + ) + + client = app.test_client() + + response = client.get( + f"/admin/jobs/{job_id}" + ) + + assert response.status_code == 200 + assert b'list="part_catalogue"' in response.data + assert b'value="PA1"' in response.data + assert b'value="PA2"' in response.data + assert b"Select an existing part or enter a new name." in ( + response.data + ) + + +def test_tc25_negative_initial_quantity_is_rejected(app): + client = app.test_client() + + response = client.post( + "/admin/inventory", + data={ + "action": "create_part", + "part_name": "Invalid Negative Part", + "part_unit": "pcs", + "station_id": "ST-01", + "initial_quantity": "-1", + }, + ) + + assert response.status_code == 400 + + with app.app_context(): + assert Part.query.count() == 0 + assert StationInventory.query.count() == 0 \ No newline at end of file -- GitLab