feat(b08): 초류종자살포 잎 고르기 칸(브레인 ㉮) — 매핑이 부모 5-24(갈래 고르기형)라 고를 칸이 없어 금액이 영영 안 서던 자리
- 산출 조건 「초류종자살포 비탈면 토질」(5-24-1 기계/일반 · 5-24-2 기계/마사토 · 서버 선택지 · 제안값 없음) · 비면 금액 없이 입력 사유 - 매핑 `leaf_from`·`leaf_codes` 가 설정값으로 잎 코드를 고름(인계 한 곳 · 갈래 없는 매핑은 종전과 같음) - 936be972 일반 고름 → 인계 FP-05-24-01 막힘 풀림 · B09 는 다음 막힘 「트럭 4.5ton(장비 줄) 카탈로그에 없음 · 종자 규격 미정」으로 아직 금액 없음 · 면고르기 성토면 12,350.32㎡ 40,373,196원 · 절토면 3,087.51㎡ 7,731,123원(파종 면적 그대로 따라감) · 되돌림 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
This commit is contained in:
@@ -52,6 +52,9 @@ FACE_DRESSING_FILL_SUGGESTED = (
|
||||
"영월 실무 설계내역 「성토사면고르기 06M3 B/H」(백호 = 무한궤도 굴착기) · 임도는 산지라"
|
||||
" 타이어식이 잘 안 들어감 — 타이어식도 고를 수 있음(2026-09-14 브레인 ②)",
|
||||
)
|
||||
#: 초류종자살포 비탈면 토질 — 품셈 5-24 씨앗뿜어붙이기의 잎 둘(5-24-1 일반 · 5-24-2 마사토) 이름 그대로.
|
||||
#: 매핑 `leaf_from` 이 잎 코드로 잇고 비면 금액 없이 사유 · 제안값 없음(2026-09-14 브레인 ㉮).
|
||||
SEED_SPRAY_GROUNDS = ("일반", "마사토")
|
||||
#: 지장목제거 뿌리뽑기(9-21 제근) 굴착기 크기 — 품셈 9-21 표 갈래 0.2·0.7(무한궤도). 등급과 한 갈래.
|
||||
#: ⚠ 제안값은 칸 곁에만(스스로 안 고름 · 비면 금액 없이 사유 — 2026-09-14 브레인 판정).
|
||||
ROOT_REMOVAL_EXCAVATOR_SIZES = ("0.2", "0.7")
|
||||
|
||||
@@ -116,6 +116,7 @@ def build_handoff(
|
||||
face_dressing_cut_class: str | None = None,
|
||||
face_dressing_fill_class: str | None = None,
|
||||
root_removal_excavator_m3: str | None = None,
|
||||
seed_spray_ground: str | None = None,
|
||||
priced_sheets: list[dict[str, Any]] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""B09 가 그대로 받는 모양. 없는 표는 건너뛰되 **빈 표와 구별해 적는다**.
|
||||
@@ -137,6 +138,8 @@ def build_handoff(
|
||||
"face_dressing_fill_class": face_dressing_fill_class,
|
||||
# 제근 굴착기 크기 — 등급과 「크기·등급」 한 갈래로(매핑 `variant_template` · 09-14).
|
||||
"root_removal_excavator_m3": root_removal_excavator_m3,
|
||||
# 초류종자살포 비탈면 토질 — 부모 5-24 의 잎(일반·마사토)을 고름(매핑 `leaf_from` · 09-14 ㉮).
|
||||
"seed_spray_ground": seed_spray_ground,
|
||||
}
|
||||
rows, misses = _earthwork_rows(
|
||||
summary_table, table, methods, bench_cut_depth_m, variant_inputs
|
||||
|
||||
@@ -164,12 +164,17 @@ def _earthwork_rows(
|
||||
template_ready = all(inputs.get(name) for name in needs)
|
||||
if template and template_ready:
|
||||
variant_value = template.format(**inputs)
|
||||
# 부모(갈래 고르기형)를 가리키는 매핑 — 설정값으로 잎 코드를 고름(초류종자살포 5-24 · 09-14 ㉮).
|
||||
leaf_from = str((entry or {}).get("leaf_from") or "")
|
||||
leaf = (
|
||||
((entry or {}).get("leaf_codes") or {}).get(inputs.get(leaf_from))
|
||||
if leaf_from
|
||||
else None
|
||||
)
|
||||
code = leaf or code
|
||||
missing = not leaf if leaf_from else (not variant_value or not template_ready)
|
||||
# 매핑이 「이 칸이 비면 못 고름」이라 적은 갈래 — 금액 없이 입력 사유(면고르기 · 09-14 Ⓒ).
|
||||
if (
|
||||
code
|
||||
and (not variant_value or not template_ready)
|
||||
and (entry or {}).get("variant_missing_reason")
|
||||
):
|
||||
if code and missing and (entry or {}).get("variant_missing_reason"):
|
||||
blocked_kind = blocked_kind or BLOCKED_INPUT_MISSING
|
||||
blocked_reason = blocked_reason or str(entry["variant_missing_reason"])
|
||||
if code is None and not is_subtotal:
|
||||
|
||||
@@ -36,6 +36,7 @@ from B08_Quantity.B08_Quantity_Engine_EarthworkSummary import (
|
||||
FACE_DRESSING_FILL_SUGGESTED,
|
||||
ROOT_REMOVAL_EXCAVATOR_SIZES,
|
||||
ROOT_REMOVAL_EXCAVATOR_SUGGESTED,
|
||||
SEED_SPRAY_GROUNDS,
|
||||
SummaryInput,
|
||||
)
|
||||
from B08_Quantity.B08_Quantity_Engine_EarthworkSummary import build_table as build_summary_table
|
||||
@@ -183,6 +184,8 @@ async def get_earthwork_table(project_id: UUID, route_id: int) -> JSONResponse:
|
||||
"basis": ROOT_REMOVAL_EXCAVATOR_SUGGESTED[1],
|
||||
},
|
||||
}
|
||||
# 초류종자살포 비탈면 토질 — 5-24 잎 둘(서버 한 곳) · 제안값 없음(2026-09-14 브레인 ㉮).
|
||||
table["seed_spray_choices"] = {"choices": list(SEED_SPRAY_GROUNDS)}
|
||||
# 준비공·사방공 — 못 서는 줄도 사유와 함께 남긴다(빈 표는 「빠뜨림」과 구별이 안 됨).
|
||||
structures = await _route_structures(project_id)
|
||||
table["preparation"] = build_preparation_table(
|
||||
@@ -490,6 +493,8 @@ class QuantitySettingsBody(BaseModel):
|
||||
face_dressing_cut_class: str | None = None
|
||||
# 제근 굴착기 크기 — "0.2"·"0.7"(품셈 9-21 갈래). `""` 는 「안 정함」.
|
||||
root_removal_excavator_m3: str | None = None
|
||||
# 초류종자살포 비탈면 토질 — "일반"·"마사토"(품셈 5-24 잎). `""` 는 「안 정함」.
|
||||
seed_spray_ground: str | None = None
|
||||
face_dressing_fill_class: str | None = None
|
||||
# 면고르기 면적 덮어쓰기(㎡) — `None` 은 파종 면적을 그대로(2026-09-14 판정 Ⓐ).
|
||||
face_dressing_fill_area_m2: float | None = None
|
||||
@@ -569,6 +574,7 @@ async def save_quantity_settings(project_id: UUID, body: QuantitySettingsBody) -
|
||||
("face_dressing_cut_class", FACE_DRESSING_CUT_CLASSES),
|
||||
("face_dressing_fill_class", FACE_DRESSING_FILL_CLASSES),
|
||||
("root_removal_excavator_m3", ROOT_REMOVAL_EXCAVATOR_SIZES),
|
||||
("seed_spray_ground", SEED_SPRAY_GROUNDS),
|
||||
):
|
||||
if key in values and values[key] not in choices:
|
||||
values[key] = "" # 선택지 밖·빈 값은 「안 정함」 — 가까운 갈래로 안 고침
|
||||
|
||||
@@ -499,6 +499,8 @@ async def get_handoff(project_id: UUID) -> JSONResponse:
|
||||
face_dressing_fill_class=settings.get("face_dressing_fill_class") or None,
|
||||
# 제근 굴착기 크기(0.2·0.7) — 비면 뿌리뽑기 줄이 입력 사유로 막힘(제안 0.7 은 칸 곁에만).
|
||||
root_removal_excavator_m3=settings.get("root_removal_excavator_m3") or None,
|
||||
# 초류종자살포 비탈면 토질 — 부모 5-24 의 잎을 고름. 비면 그 줄이 입력 사유로 막힘(09-14 ㉮).
|
||||
seed_spray_ground=settings.get("seed_spray_ground") or None,
|
||||
# 구조물도 양식 일위대가로 셀 장 — 그 구조물은 호표 `AX-ST` 줄 하나로(PLAN 6장 ②).
|
||||
priced_sheets=_priced_sheets(project_root, unit_table, modes, settings),
|
||||
)
|
||||
|
||||
@@ -102,6 +102,8 @@ export interface QuantitySettings {
|
||||
stand_volume_class?: string | null;
|
||||
/** 제근 굴착기 크기 — `"0.2"`·`"0.7"`(서버 선택지 안) · 비면 「안 정함」. */
|
||||
root_removal_excavator_m3?: string | null;
|
||||
/** 초류종자살포 비탈면 토질 — `"일반"`·`"마사토"`(5-24 잎) · 비면 「안 정함」. */
|
||||
seed_spray_ground?: string | null;
|
||||
/** 면고르기 갈래(서버 선택지 안) · 면적 덮어쓰기(㎡, 없음 = 파종 면적). */
|
||||
face_dressing_cut_class?: string | null;
|
||||
face_dressing_fill_class?: string | null;
|
||||
@@ -164,6 +166,8 @@ export interface EarthworkTable {
|
||||
conversion_factor_choices?: Record<string, ConversionFactorChoice>;
|
||||
/** 면고르기 갈래 선택지 — 품셈 9-19-1 원문 표 두 벌(서버 한 곳). 화면은 그대로 보임. */
|
||||
face_dressing_choices?: { cut: string[]; fill: string[] };
|
||||
/** 초류종자살포 비탈면 토질 선택지(5-24 잎 둘) — 서버 한 곳 · 제안값 없음. */
|
||||
seed_spray_choices?: { choices: string[] };
|
||||
/** 제근 굴착기 크기 선택지·제안(회색 · [제안값 넣기]) — 서버 한 곳. */
|
||||
root_removal_excavator_choices?: {
|
||||
choices: string[];
|
||||
|
||||
@@ -43,6 +43,7 @@ import { renderStructureSummary } from "./B08_Quantity_UI_StructureSummary";
|
||||
import { appendTreeWasteFields } from "./B08_Quantity_UI_Side_TreeWaste";
|
||||
import { appendFaceDressingFields } from "./B08_Quantity_UI_Side_FaceDressing";
|
||||
import { appendRootRemovalFields } from "./B08_Quantity_UI_Side_RootRemoval";
|
||||
import { appendSeedSprayField } from "./B08_Quantity_UI_Side_SeedSpray";
|
||||
import { appendBenchCutFields } from "./B08_Quantity_UI_Side_BenchCut";
|
||||
|
||||
/** locale 헬퍼 */
|
||||
@@ -111,6 +112,7 @@ async function saveQuantitySettings(projectId: string, draft: DraftSettings): Pr
|
||||
topsoil_target: draft.topsoil_target,
|
||||
stand_volume_class: draft.stand_volume_class,
|
||||
root_removal_excavator_m3: draft.root_removal_excavator_m3,
|
||||
seed_spray_ground: draft.seed_spray_ground,
|
||||
// 면고르기 — 갈래 `""`·면적 `null` 도 그대로(「안 정함」·「파종 면적 그대로」로 되돌리는 길).
|
||||
face_dressing_cut_class: draft.face_dressing_cut_class,
|
||||
face_dressing_fill_class: draft.face_dressing_fill_class,
|
||||
@@ -311,6 +313,8 @@ interface DraftSettings {
|
||||
stand_volume_class: string;
|
||||
// 제근 굴착기 크기 — "0.2"·"0.7"(서버 선택지) · `""` 는 「안 정함」. 칸은 `_Side_RootRemoval`.
|
||||
root_removal_excavator_m3: string;
|
||||
// 초류종자살포 비탈면 토질 — "일반"·"마사토"(5-24 잎) · `""` 는 「안 정함」. 칸은 `_Side_SeedSpray`.
|
||||
seed_spray_ground: string;
|
||||
// 면고르기 — 갈래 둘(서버 선택지) · 면적 덮어쓰기 둘(`null` = 파종 면적). 칸은 `_Side_FaceDressing`.
|
||||
face_dressing_cut_class: string;
|
||||
face_dressing_fill_class: string;
|
||||
@@ -525,13 +529,10 @@ function buildQuantitySidePanel(
|
||||
}
|
||||
}
|
||||
|
||||
// ── 면고르기 — 밑수가 파종 면적이라 반영률 바로 밑. 선택지는 서버 목록 그대로(칸은 따로 뺀 파일).
|
||||
appendFaceDressingFields(panel, draft, table?.face_dressing_choices, {
|
||||
field,
|
||||
optionalNumberField,
|
||||
selectField,
|
||||
hintRow,
|
||||
});
|
||||
// ── 초류종자살포 토질 · 면고르기 — 밑수가 파종 면적이라 반영률 바로 밑. 선택지는 서버 목록 그대로.
|
||||
const sideHelpers = { field, optionalNumberField, selectField, hintRow };
|
||||
appendSeedSprayField(panel, draft, table?.seed_spray_choices, sideHelpers);
|
||||
appendFaceDressingFields(panel, draft, table?.face_dressing_choices, sideHelpers);
|
||||
|
||||
// ── 표토 두께 — 표토 운반 부피(제거 ㎡ × T)에 씀 · 제거 줄은 ㎡ 라 안 곱함(2026-09-13 「실무대로」) ──
|
||||
// ⚠ 2026-09-08 ㉘ 자기 감사: 서버·엔진은 이 값을 받고 있었는데 **화면에 넣을 칸이 없었다.**
|
||||
@@ -1032,6 +1033,7 @@ export async function renderB08Quantity(root: HTMLElement): Promise<void> {
|
||||
topsoil_target: (stored.topsoil_target as string) ?? "",
|
||||
stand_volume_class: (stored.stand_volume_class as string) ?? "",
|
||||
root_removal_excavator_m3: (stored.root_removal_excavator_m3 as string) ?? "",
|
||||
seed_spray_ground: (stored.seed_spray_ground as string) ?? "",
|
||||
face_dressing_cut_class: (stored.face_dressing_cut_class as string) ?? "",
|
||||
face_dressing_fill_class: (stored.face_dressing_fill_class as string) ?? "",
|
||||
face_dressing_fill_area_m2: (stored.face_dressing_fill_area_m2 as number | null) ?? null,
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/* =============================================================================
|
||||
* B08_Quantity_UI_Side_SeedSpray.ts
|
||||
* 산출 조건 「초류종자살포 비탈면 토질」 칸 — 반영률 밑 · 면고르기 위(2026-09-14 브레인 ㉮).
|
||||
*
|
||||
* 매핑이 부모 공종(5-24 씨앗뿜어붙이기 · 갈래 고르기형)을 가리켜 잎(일반·마사토)을 고를 칸이 없었음
|
||||
* → 금액이 영영 안 섬. 이 칸이 잎을 고름(서버 매핑 `leaf_from`).
|
||||
* 페이지 본문(`B08_Quantity_UI_Page.ts`)이 700줄을 넘어 새 칸을 이 파일로 뺌(CLAUDE.md 4장).
|
||||
* ⚠ 선택지는 **서버가 내려준 그대로**(`seed_spray_choices`) · 제안값 없음 — 비면 내역 줄이 입력 사유로 섬.
|
||||
* ========================================================================== */
|
||||
|
||||
import { ui_locales, currentLanguageIndex } from "@ui/ui_template_locale";
|
||||
import type { SideFieldHelpers } from "./B08_Quantity_UI_Side_TreeWaste";
|
||||
|
||||
function L(key: keyof typeof ui_locales): string {
|
||||
return ui_locales[key][currentLanguageIndex];
|
||||
}
|
||||
|
||||
export function appendSeedSprayField(
|
||||
panel: HTMLElement,
|
||||
draft: { seed_spray_ground: string; dirty: boolean },
|
||||
choices: { choices: string[] } | undefined,
|
||||
h: SideFieldHelpers,
|
||||
): void {
|
||||
if (!choices) return;
|
||||
panel.append(
|
||||
h.selectField(
|
||||
L("B08_Quantity_Side_SeedSpray_Label"),
|
||||
draft.seed_spray_ground,
|
||||
[
|
||||
{ value: "", label: L("B08_Quantity_SeedSpray_Unset") },
|
||||
...choices.choices.map((name) => ({ value: name, label: name })),
|
||||
],
|
||||
(picked) => {
|
||||
draft.seed_spray_ground = picked;
|
||||
draft.dirty = true;
|
||||
},
|
||||
),
|
||||
h.hintRow(L("B08_Quantity_Side_SeedSpray_Hint")),
|
||||
);
|
||||
}
|
||||
@@ -130,7 +130,11 @@
|
||||
"work_item_code": "FP-05-24",
|
||||
"master_name": "씨앗뿜어붙이기",
|
||||
"basis_unit": "㎡",
|
||||
"basis_source": "산림사업 표준품셈(고시 2025-82) 원문 L2769·L2794 — 5-24-1·5-24-2 절 머리 바로 아래 「(㎡당)」. ⚠ 「단위:」 글자 없이 **괄호만** 적힌 모양이라 마스터가 못 읽은 자리(2026-09-08 원문 대조)."
|
||||
"basis_source": "산림사업 표준품셈(고시 2025-82) 원문 L2769·L2794 — 5-24-1·5-24-2 절 머리 바로 아래 「(㎡당)」. ⚠ 「단위:」 글자 없이 **괄호만** 적힌 모양이라 마스터가 못 읽은 자리(2026-09-08 원문 대조).",
|
||||
"leaf_from": "seed_spray_ground",
|
||||
"leaf_codes": {"일반": "FP-05-24-01", "마사토": "FP-05-24-02"},
|
||||
"variant_missing_reason": "초류종자살포 비탈면 토질(일반·마사토)이 아직 입력되지 않았습니다 — 산출 조건에서 고르면 단가가 섭니다(품셈 5-24 씨앗뿜어붙이기: 5-24-1 기계/일반 · 5-24-2 기계/마사토)",
|
||||
"leaf_note": "2026-09-14 브레인 ㉮ — 매핑이 부모(갈래 고르기형)라 B09 가 「잎 미선택」으로 막는데 고를 칸이 없었음 · 산출 조건 칸이 잎 코드를 고름 · 제안값 없음."
|
||||
},
|
||||
{
|
||||
"group": "되메우기",
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
"""초류종자살포 잎 고르기 — 2026-09-14 브레인 차례 ㉮(B08 나머지 탭 훑기).
|
||||
|
||||
매핑이 부모(FP-05-24 씨앗뿜어붙이기 · 갈래 고르기형)를 가리켜 B09 가 「잎 미선택」으로 막는데 고를 칸이 없었음
|
||||
→ 금액이 영영 안 섬(면고르기와 같은 병). 산출 조건 「초류종자살포 비탈면 토질」(5-24-1 일반 · 5-24-2 마사토)
|
||||
칸 · 매핑 `leaf_from`·`leaf_codes` 가 잎 코드로 잇고 · 비면 금액 없이 입력 사유 · 제안값 없음(지어내지 않음).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from B08_Quantity.B08_Quantity_Engine_EarthworkSummary import SEED_SPRAY_GROUNDS # noqa: E402
|
||||
from B08_Quantity.B08_Quantity_Engine_Handoff import build_handoff # noqa: E402
|
||||
|
||||
SUMMARY = {"rows": [{"group": "초류종자살포", "item": "", "spec": "씨드스프레이", "unit": "㎡",
|
||||
"amount": 1500.0, "application_ratio_pct": 100.0}]} # fmt: skip
|
||||
|
||||
|
||||
def _row(**inputs) -> dict:
|
||||
rows = build_handoff(summary_table=SUMMARY, **inputs)["work_items"]
|
||||
return next(r for r in rows if r["name"] == "초류종자살포")
|
||||
|
||||
|
||||
def test_토질을_고르면_잎_코드로_감() -> None:
|
||||
assert _row(seed_spray_ground="일반")["work_item_code"] == "FP-05-24-01"
|
||||
row = _row(seed_spray_ground="마사토")
|
||||
assert row["work_item_code"] == "FP-05-24-02" and not row["blocked_kind"], row
|
||||
|
||||
|
||||
def test_토질이_비면_부모_코드에_금액_없이_입력_사유() -> None:
|
||||
row = _row()
|
||||
assert row["work_item_code"] == "FP-05-24"
|
||||
assert row["blocked_kind"] == "input_missing", row
|
||||
assert "일반·마사토" in row["blocked_reason"] and "산출 조건" in row["blocked_reason"]
|
||||
|
||||
|
||||
def test_선택지는_마스터_잎_이름과_같음() -> None:
|
||||
from B09_Estimation.B09_Estimation_ResourceAxis import load_work_item_master
|
||||
|
||||
leaves = {
|
||||
n["work_item_code"]: n["name"].split("/")[-1]
|
||||
for n in load_work_item_master()["work_items"]
|
||||
if n.get("parent_code") == "FP-05-24"
|
||||
}
|
||||
assert leaves == {"FP-05-24-01": "일반", "FP-05-24-02": "마사토"}
|
||||
assert set(SEED_SPRAY_GROUNDS) == set(leaves.values())
|
||||
|
||||
|
||||
def test_산출_조건이_받고_선택지_밖은_안_정함으로() -> None:
|
||||
source = (ROOT / "B08_Quantity" / "B08_Quantity_Router_Earthwork.py").read_text("utf-8")
|
||||
assert '("seed_spray_ground", SEED_SPRAY_GROUNDS)' in source
|
||||
assert 'table["seed_spray_choices"]' in source
|
||||
material = (ROOT / "B08_Quantity" / "B08_Quantity_Router_Material.py").read_text("utf-8")
|
||||
assert 'seed_spray_ground=settings.get("seed_spray_ground") or None' in material
|
||||
|
||||
|
||||
def test_화면_칸이_있고_저장_읽기에_실림() -> None:
|
||||
page = (ROOT / "B08_Quantity" / "B08_Quantity_UI_Page.ts").read_text("utf-8")
|
||||
assert "seed_spray_ground: draft.seed_spray_ground" in page
|
||||
assert "seed_spray_ground: (stored.seed_spray_ground" in page
|
||||
assert "appendSeedSprayField(" in page
|
||||
@@ -693,6 +693,12 @@ export const ui_locales_b2 = {
|
||||
"⚠ 「육상」은 통상값이고 사용자 확정이 아닙니다(확정 3차 ④) — 품셈 9-13 의 18구분이 이 값으로 갈립니다",
|
||||
"⚠ “Dry” is a customary default, not a user decision — it selects one of the 18 sub-items",
|
||||
],
|
||||
B08_Quantity_Side_SeedSpray_Label: ["초류종자살포 비탈면 토질", "Seed spray slope soil"],
|
||||
B08_Quantity_SeedSpray_Unset: ["안 정함(금액 안 섬)", "Not set (no amount)"],
|
||||
B08_Quantity_Side_SeedSpray_Hint: [
|
||||
"품셈 5-24 씨앗뿜어붙이기가 5-24-1 기계/일반 · 5-24-2 기계/마사토 둘로 갈림 — 제안값 없음, 안 고르면 내역 줄이 입력 사유로 섭니다",
|
||||
"Pumsem 5-24 splits into general / decomposed-granite soil — no suggestion; unset leaves the bill row with an input reason",
|
||||
],
|
||||
B08_Quantity_Side_RootRemoval_Label: ["제근 굴착기 크기", "Root removal excavator size"],
|
||||
B08_Quantity_RootRemoval_Unset: ["안 정함(금액 안 섬)", "Not set (no amount)"],
|
||||
B08_Quantity_RootRemoval_Suggest: ["제안(비우면 안 정함):", "Suggested (blank = not set):"],
|
||||
|
||||
Reference in New Issue
Block a user