Allow editing orders in the order card
Cashiers can edit recipient, TTN, notes, goods and total in the order
modal and save via PATCH /orders/{id}. Edited orders get edited_at and
are no longer overwritten by CRM sync. Editing is blocked once a receipt
exists; changing the TTN resets Nova Poshta tracking fields.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -79,6 +79,19 @@ def _patch_orders_service(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(orders_router.orders_service, "list_orders", fake_list)
|
||||
monkeypatch.setattr(orders_router.orders_service, "delete_order", fake_delete)
|
||||
|
||||
async def fake_update(
|
||||
session: object, order_id: str, data: object
|
||||
) -> tuple[Order, list[str]] | None:
|
||||
if order_id == "locked":
|
||||
raise orders_router.orders_service.OrderEditError("По заказу уже создан чек")
|
||||
if order_id != "1":
|
||||
return None
|
||||
order = _order(order_id)
|
||||
order.edited_at = datetime(2026, 9, 24, tzinfo=UTC)
|
||||
return order, ["notes"]
|
||||
|
||||
monkeypatch.setattr(orders_router.orders_service, "update_order", fake_update)
|
||||
|
||||
async def fake_latest(session: object, order_ids: list[str]) -> dict:
|
||||
return {}
|
||||
|
||||
@@ -164,3 +177,56 @@ class TestDeleteOrder:
|
||||
response = client.delete("/api/v1/orders/does-not-exist")
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
_UPDATE_BODY = {
|
||||
"recipient_name": "Тест Тестов",
|
||||
"recipient_phone": "+380501112233",
|
||||
"waybill_number": "20450123456789",
|
||||
"notes": "правка",
|
||||
"total_amount": "1200.00",
|
||||
"goods": [
|
||||
{"id": "1", "sku": "SKU-1", "name": "Товар 1", "price": "1200.00", "quantity": "1"}
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
class TestUpdateOrder:
|
||||
@pytest.mark.parametrize("role", [UserRole.ADMIN, UserRole.CASHIER])
|
||||
def test_admin_and_cashier_can_update(self, client: TestClient, role: UserRole) -> None:
|
||||
app.dependency_overrides[get_current_user] = lambda: _user(role)
|
||||
|
||||
response = client.patch("/api/v1/orders/1", json=_UPDATE_BODY)
|
||||
|
||||
assert response.status_code == 200
|
||||
body = response.json()
|
||||
assert body["edited"] is True
|
||||
assert body["goods"][0]["sku"] == "SKU-1"
|
||||
|
||||
def test_viewer_cannot_update(self, client: TestClient) -> None:
|
||||
app.dependency_overrides[get_current_user] = lambda: _user(UserRole.VIEWER)
|
||||
|
||||
response = client.patch("/api/v1/orders/1", json=_UPDATE_BODY)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
def test_unknown_order_is_404(self, client: TestClient) -> None:
|
||||
app.dependency_overrides[get_current_user] = lambda: _user(UserRole.CASHIER)
|
||||
|
||||
response = client.patch("/api/v1/orders/nope", json=_UPDATE_BODY)
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_order_with_receipt_is_409(self, client: TestClient) -> None:
|
||||
app.dependency_overrides[get_current_user] = lambda: _user(UserRole.CASHIER)
|
||||
|
||||
response = client.patch("/api/v1/orders/locked", json=_UPDATE_BODY)
|
||||
|
||||
assert response.status_code == 409
|
||||
|
||||
def test_empty_goods_is_rejected(self, client: TestClient) -> None:
|
||||
app.dependency_overrides[get_current_user] = lambda: _user(UserRole.CASHIER)
|
||||
|
||||
response = client.patch("/api/v1/orders/1", json={**_UPDATE_BODY, "goods": []})
|
||||
|
||||
assert response.status_code == 422
|
||||
|
||||
Reference in New Issue
Block a user