mirror of
https://github.com/wasrusgen/zov-tech.git
synced 2026-06-03 17:44:48 +00:00
fix: write Measurements rows by column name, not by position
Root cause: _row_for_measurement() returned a positional list based on
_measurement_columns() order, but the actual Google Sheet may have
columns in a different order (if the sheet was created before new
columns were added and _ensure_measurements_sheet() appended them
at the end rather than in the middle). Values ended up in wrong
columns — client_name, manager_tg_id etc. were misaligned, so
_handle_clients couldn't match any rows and returned an empty list.
Fix:
- _row_for_measurement() now returns dict {col_name: value}
- sheets.append_named_row() reads real headers from the sheet and
builds the positional row accordingly — safe regardless of column order
- All three Measurements append calls updated to use append_named_row
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
46812620eb
commit
8318b25999
@ -921,10 +921,10 @@ def _ensure_measurements_sheet() -> None:
|
|||||||
log.info("Measurements: дополнили колонки: %s", missing)
|
log.info("Measurements: дополнили колонки: %s", missing)
|
||||||
|
|
||||||
|
|
||||||
def _row_for_measurement(measurement_id: str, ts: str, **fields) -> list[str]:
|
def _row_for_measurement(measurement_id: str, ts: str, **fields) -> dict[str, str]:
|
||||||
"""Собирает строку в нужном порядке колонок (для append_row)."""
|
"""Возвращает словарь колонка→значение для записи в Measurements.
|
||||||
cols = _measurement_columns()
|
Используется с sheets.append_named_row() — безопасно к порядку колонок."""
|
||||||
base = {
|
base: dict[str, Any] = {
|
||||||
"id": measurement_id, "ts": ts,
|
"id": measurement_id, "ts": ts,
|
||||||
"client_tg_id": "", "manager_tg_id": "", "filled_by": "",
|
"client_tg_id": "", "manager_tg_id": "", "filled_by": "",
|
||||||
"layout": "", "area_m2": "", "ceiling_mm": "",
|
"layout": "", "area_m2": "", "ceiling_mm": "",
|
||||||
@ -943,7 +943,8 @@ def _row_for_measurement(measurement_id: str, ts: str, **fields) -> list[str]:
|
|||||||
"podbor_decision": "", "podbor_decision_at": "", "podbor_lead_id": "",
|
"podbor_decision": "", "podbor_decision_at": "", "podbor_lead_id": "",
|
||||||
}
|
}
|
||||||
base.update(fields)
|
base.update(fields)
|
||||||
return [str(base.get(c, "")) for c in cols]
|
# Нормализуем: None → "", всё приводим к str
|
||||||
|
return {k: str(v) if v is not None else "" for k, v in base.items()}
|
||||||
|
|
||||||
|
|
||||||
def _handle_measurement(body: dict[str, Any]) -> dict[str, Any]:
|
def _handle_measurement(body: dict[str, Any]) -> dict[str, Any]:
|
||||||
@ -1066,7 +1067,7 @@ def _handle_measurement(body: dict[str, Any]) -> dict[str, Any]:
|
|||||||
for col, val in updates.items():
|
for col, val in updates.items():
|
||||||
sheets.update_cell_by_key("Measurements", "id", measurement_id, col, val)
|
sheets.update_cell_by_key("Measurements", "id", measurement_id, col, val)
|
||||||
else:
|
else:
|
||||||
sheets.append_row("Measurements", _row_for_measurement(
|
sheets.append_named_row("Measurements", _row_for_measurement(
|
||||||
measurement_id, _now_iso(),
|
measurement_id, _now_iso(),
|
||||||
client_tg_id=client_tg_id or "",
|
client_tg_id=client_tg_id or "",
|
||||||
manager_tg_id=manager_tg_id or "",
|
manager_tg_id=manager_tg_id or "",
|
||||||
@ -1528,7 +1529,7 @@ def _handle_measurement_request(body: dict[str, Any]) -> dict[str, Any]:
|
|||||||
return {"error": "assigned_not_measurer"}
|
return {"error": "assigned_not_measurer"}
|
||||||
|
|
||||||
measurement_id = _short_id()
|
measurement_id = _short_id()
|
||||||
sheets.append_row("Measurements", _row_for_measurement(
|
sheets.append_named_row("Measurements", _row_for_measurement(
|
||||||
measurement_id, _now_iso(),
|
measurement_id, _now_iso(),
|
||||||
manager_tg_id=tg_id,
|
manager_tg_id=tg_id,
|
||||||
filled_by="request",
|
filled_by="request",
|
||||||
@ -2431,7 +2432,7 @@ def _handle_client_create(body: dict[str, Any]) -> dict[str, Any]:
|
|||||||
client_no = _next_client_no(str(tg_id))
|
client_no = _next_client_no(str(tg_id))
|
||||||
|
|
||||||
# Создаём «карточку клиента» как заявку со статусом draft
|
# Создаём «карточку клиента» как заявку со статусом draft
|
||||||
sheets.append_row("Measurements", _row_for_measurement(
|
sheets.append_named_row("Measurements", _row_for_measurement(
|
||||||
measurement_id, _now_iso(),
|
measurement_id, _now_iso(),
|
||||||
manager_tg_id=str(tg_id),
|
manager_tg_id=str(tg_id),
|
||||||
requested_by_tg_id=str(tg_id),
|
requested_by_tg_id=str(tg_id),
|
||||||
|
|||||||
@ -60,6 +60,18 @@ def append_row(name: str, row: list[Any]) -> None:
|
|||||||
sheet(name).append_row(row, value_input_option="USER_ENTERED")
|
sheet(name).append_row(row, value_input_option="USER_ENTERED")
|
||||||
|
|
||||||
|
|
||||||
|
def append_named_row(name: str, data: dict[str, Any]) -> None:
|
||||||
|
"""Записывает строку по ИМЕНАМ колонок из реального заголовка листа.
|
||||||
|
Безопасно при любом порядке колонок — значения выставляются по имени,
|
||||||
|
а не по позиции, так что нет расхождения с _measurement_columns()."""
|
||||||
|
ws = sheet(name)
|
||||||
|
headers = ws.row_values(1)
|
||||||
|
if not headers:
|
||||||
|
raise ValueError(f"Sheet {name!r} has no header row")
|
||||||
|
row = [str(data.get(h, "") if data.get(h, "") is not None else "") for h in headers]
|
||||||
|
ws.append_row(row, value_input_option="USER_ENTERED")
|
||||||
|
|
||||||
|
|
||||||
def find_row(sheet_name: str, key_col: str, key_val: Any) -> dict[str, Any] | None:
|
def find_row(sheet_name: str, key_col: str, key_val: Any) -> dict[str, Any] | None:
|
||||||
"""Линейный поиск по колонке-ключу. Возвращает строку как dict или None."""
|
"""Линейный поиск по колонке-ключу. Возвращает строку как dict или None."""
|
||||||
s = sheet(sheet_name)
|
s = sheet(sheet_name)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user