feat(workshop): restrict status flow, add mechanic notes, inventory part picker
Some checks failed
CI / lint-and-test (3.11) (push) Has been cancelled
CI / lint-and-test (3.13) (push) Has been cancelled

- Service orders can no longer move backwards through the workshop pipeline.

- Added editable Reception / Diagnosis / Repair / Delivery notes in the detail modal.

- Added inventory-aware autocomplete when attaching parts to a service order.
This commit is contained in:
2026-06-30 17:43:19 +00:00
parent bd5f733d6f
commit 2863cf8e9f
4 changed files with 208 additions and 13 deletions

View File

@@ -356,6 +356,60 @@ def assign_mechanic_endpoint(so_id):
conn.close()
@service_order_bp.route('/inventory-search', methods=['GET'])
@require_auth('workshop.view')
def inventory_search():
"""Search active inventory for attaching parts to a service order.
Does not require inventory.view so workshop users can pick parts.
"""
q = request.args.get('q', '').strip()
if not q:
return jsonify({'data': []})
branch_id = request.args.get('branch_id', g.branch_id)
conn = get_tenant_conn(g.tenant_id)
try:
cur = conn.cursor()
like = f'%{q}%'
if branch_id:
cur.execute("""
SELECT i.id, i.part_number, i.name, i.brand, i.unit,
COALESCE((SELECT stock FROM inventory_stock
WHERE inventory_id = i.id AND branch_id = %s), 0) AS stock,
i.cost, i.price_1
FROM inventory i
WHERE i.is_active = true
AND (i.part_number ILIKE %s OR i.name ILIKE %s OR i.barcode ILIKE %s)
ORDER BY i.name
LIMIT 20
""", (branch_id, like, like, like))
else:
cur.execute("""
SELECT i.id, i.part_number, i.name, i.brand, i.unit,
COALESCE((SELECT stock FROM inventory_stock_summary
WHERE inventory_id = i.id), 0) AS stock,
i.cost, i.price_1
FROM inventory i
WHERE i.is_active = true
AND (i.part_number ILIKE %s OR i.name ILIKE %s OR i.barcode ILIKE %s)
ORDER BY i.name
LIMIT 20
""", (like, like, like))
items = []
for r in cur.fetchall():
items.append({
'id': r[0], 'part_number': r[1], 'name': r[2], 'brand': r[3], 'unit': r[4],
'stock': float(r[5]) if r[5] else 0,
'cost': float(r[6]) if r[6] else 0,
'price_1': float(r[7]) if r[7] else 0,
})
cur.close()
return jsonify({'data': items})
finally:
conn.close()
# ─── Service catalog (reusable labor) ─────────────