From 56aed2f2aaac2ab228e939edefb780a01058feca Mon Sep 17 00:00:00 2001 From: umsangdon Date: Wed, 2 Sep 2026 10:28:25 +0900 Subject: [PATCH] =?UTF-8?q?feat(B01,B07):=20=ED=91=9C=EC=A7=80=20=EC=97=B0?= =?UTF-8?q?=EB=8F=84=C2=B7=EA=B8=B0=EB=B2=88=C2=B7=EC=82=AC=EC=97=85?= =?UTF-8?q?=EB=9F=89=EC=9D=84=20=EC=82=AC=EB=9E=8C=EC=9D=B4=20=EB=84=A3?= =?UTF-8?q?=EB=8A=94=20=EC=9E=90=EB=A6=AC=20=EC=8B=A0=EC=84=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 표제란에 남아 있던 빈칸 중 연도·기번과 사업량은 프로그램이 지어낼 수 없는 값이라 입력 자리를 만든다. 값의 출처를 정하지 않고 받을 칸만 세운다. DB (`012_title_block_inputs.sql`, ADD COLUMN·NULL 허용) - `projects.project_number` — 표지 연도·기번(실무문서 폴더명 관행: `2024년 간선임도(기번3-울진.대흥)`) - `projects.work_amount` — 표지 사업량. 단위·표기가 사업 종류마다 달라 자유 문자열. B01 프로젝트 수정 - 수정 모달에 시행청·연도기번·사업량 3칸 추가. 011 로 만든 시행청도 여기서 처음 입력 가능해짐(그전에는 스크립트로만 넣었음). - 목록·단건 조회 4곳과 UPDATE 에 세 칸을 함께 실음. USER 권한은 다른 칸과 같이 잠금. B07 - `_title_block_fields` 가 연도기번·사업량도 실어 보냄. 비어 있으면 종전대로 빈칸. 검증: `pytest tmp/tests/ -q` 139 passed / 0 failed(표지 값 1건 신규), 루트 `tsc --noEmit` 통과. 실사용자 경로 실측(5174) — 대시보드 wdw 행 [수정] 클릭 → 모달 라벨 9개에 새 3칸 확인 → 연도기번·사업량 입력 → [확인] 저장 → 표지 도면 Text 7개 **빈칸 0 · `{{` 잔존 0**, 화면에도 연도기번·`wdw 설계도`·위치·사업량·시행청이 모두 그려짐. Co-Authored-By: Claude Opus 5 (1M context) --- B01_Dashboard/B01_Dashboard_Api_Fetch.ts | 7 +++++ B01_Dashboard/B01_Dashboard_Repository.py | 14 +++++++-- B01_Dashboard/B01_Dashboard_Schema.py | 4 +++ B01_Dashboard/B01_Dashboard_UI_Modals.ts | 33 ++++++++++++++++++++- B07_DesignDetail/B07_DesignDetail_Router.py | 21 +++++++++++-- db_management/012_title_block_inputs.sql | 15 ++++++++++ 6 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 db_management/012_title_block_inputs.sql diff --git a/B01_Dashboard/B01_Dashboard_Api_Fetch.ts b/B01_Dashboard/B01_Dashboard_Api_Fetch.ts index 7e9a782a..46e87fa0 100644 --- a/B01_Dashboard/B01_Dashboard_Api_Fetch.ts +++ b/B01_Dashboard/B01_Dashboard_Api_Fetch.ts @@ -43,6 +43,10 @@ export interface ProjectItem { estimated_length_m?: number | null; memo?: string | null; status?: string | null; + /** 도면 표제란·표지에 실리는 값 — 프로그램이 지어낼 수 없어 사람이 넣는다 */ + client_org?: string | null; + project_number?: string | null; + work_amount?: string | null; owner_name?: string | null; workflow_stage: number; progress_percent: number; @@ -132,6 +136,9 @@ export interface UpdateProjectRequest { estimated_length_m?: number | null; memo?: string | null; status?: string | null; + client_org?: string | null; + project_number?: string | null; + work_amount?: string | null; } export interface AdminUpdateUserRequest extends UpdateUserRequest { diff --git a/B01_Dashboard/B01_Dashboard_Repository.py b/B01_Dashboard/B01_Dashboard_Repository.py index 1132aec2..a8f73bc6 100644 --- a/B01_Dashboard/B01_Dashboard_Repository.py +++ b/B01_Dashboard/B01_Dashboard_Repository.py @@ -148,7 +148,8 @@ async def list_user_projects(user_id: int) -> list[dict[str, Any]]: async with pool.acquire() as connection, connection.cursor(aiomysql.DictCursor) as cursor: await cursor.execute( """SELECT id, company_id, name, region, road_type, project_year, - estimated_length_m, memo, status, updated_at, created_at + estimated_length_m, memo, status, updated_at, created_at, + client_org, project_number, work_amount FROM projects WHERE user_id = %s AND deleted_at IS NULL ORDER BY updated_at DESC, created_at DESC""", (user_id,), @@ -170,6 +171,7 @@ async def list_company_projects(company_id: int) -> list[dict[str, Any]]: await cursor.execute( """SELECT p.id, p.company_id, p.name, p.region, p.road_type, p.project_year, p.estimated_length_m, p.memo, p.status, p.updated_at, p.created_at, + p.client_org, p.project_number, p.work_amount, u.name AS owner_name, u.email AS owner_email FROM projects p LEFT JOIN users u ON u.id = p.user_id WHERE p.company_id = %s AND p.deleted_at IS NULL @@ -193,6 +195,7 @@ async def list_all_projects() -> list[dict[str, Any]]: await cursor.execute( """SELECT p.id, p.company_id, p.name, p.region, p.road_type, p.project_year, p.estimated_length_m, p.memo, p.status, p.updated_at, p.created_at, + p.client_org, p.project_number, p.work_amount, u.name AS owner_name, u.email AS owner_email FROM projects p LEFT JOIN users u ON u.id = p.user_id WHERE p.deleted_at IS NULL @@ -214,7 +217,8 @@ async def get_project(project_id: str) -> dict[str, Any] | None: async with pool.acquire() as connection, connection.cursor(aiomysql.DictCursor) as cursor: await cursor.execute( """SELECT id, user_id, company_id, name, region, road_type, project_year, - estimated_length_m, memo, status + estimated_length_m, memo, status, + client_org, project_number, work_amount FROM projects WHERE id = %s AND deleted_at IS NULL""", (project_id,), ) @@ -228,7 +232,8 @@ async def update_project(project_id: str, data: dict[str, Any], actor_id: int) - await cursor.execute( """UPDATE projects SET name = %s, region = %s, road_type = %s, project_year = %s, - estimated_length_m = %s, memo = %s, status = COALESCE(%s, status) + estimated_length_m = %s, memo = %s, status = COALESCE(%s, status), + client_org = %s, project_number = %s, work_amount = %s WHERE id = %s AND deleted_at IS NULL""", ( data["name"], @@ -238,6 +243,9 @@ async def update_project(project_id: str, data: dict[str, Any], actor_id: int) - data.get("estimated_length_m"), data.get("memo"), data.get("status"), + data.get("client_org"), + data.get("project_number"), + data.get("work_amount"), project_id, ), ) diff --git a/B01_Dashboard/B01_Dashboard_Schema.py b/B01_Dashboard/B01_Dashboard_Schema.py index de9e931c..7b5ef6d9 100644 --- a/B01_Dashboard/B01_Dashboard_Schema.py +++ b/B01_Dashboard/B01_Dashboard_Schema.py @@ -46,6 +46,10 @@ class UpdateProjectRequest(BaseModel): estimated_length_m: float | None = Field(default=None, ge=0) memo: str | None = Field(default=None, max_length=5000) status: str | None = Field(default=None, max_length=50) + # 도면 표제란·표지에 실리는 값 (2026-09-02). 프로그램이 지어낼 수 없어 사람이 넣는다. + client_org: str | None = Field(default=None, max_length=255) + project_number: str | None = Field(default=None, max_length=100) + work_amount: str | None = Field(default=None, max_length=100) class AdminUpdateUserRequest(UpdateUserRequest): diff --git a/B01_Dashboard/B01_Dashboard_UI_Modals.ts b/B01_Dashboard/B01_Dashboard_UI_Modals.ts index d0f941a5..96535b2e 100644 --- a/B01_Dashboard/B01_Dashboard_UI_Modals.ts +++ b/B01_Dashboard/B01_Dashboard_UI_Modals.ts @@ -90,6 +90,21 @@ export function openEditProjectModal(user: DashboardUser, project: ProjectItem): value: String(project.estimated_length_m ?? ""), }); const memo = createInputField({ label: "비고", value: project.memo ?? "" }); + // 도면 표제란·표지에 그대로 실리는 값 — 프로그램이 지어낼 수 없어 여기서 받는다. + const clientOrg = createInputField({ + label: "시행청 (도면 표제란)", + value: project.client_org ?? "", + }); + const projectNumber = createInputField({ + label: "연도·기번 (표지)", + value: project.project_number ?? "", + placeholder: "예: 2026년 간선임도(기번3-울진.대흥)", + }); + const workAmount = createInputField({ + label: "사업량 (표지)", + value: project.work_amount ?? "", + placeholder: "예: L=2.14km", + }); if (isUserOnly) { name.input.disabled = true; @@ -98,11 +113,24 @@ export function openEditProjectModal(user: DashboardUser, project: ProjectItem): year.input.disabled = true; length.input.disabled = true; memo.input.disabled = true; + clientOrg.input.disabled = true; + projectNumber.input.disabled = true; + workAmount.input.disabled = true; } openModal( L("B01_Dashboard_EditProject"), - [name.root, region.root, roadType.root, year.root, length.root, memo.root], + [ + name.root, + region.root, + roadType.root, + year.root, + length.root, + memo.root, + clientOrg.root, + projectNumber.root, + workAmount.root, + ], async () => { await updateProject(project.id, { name: name.input.value.trim(), @@ -112,6 +140,9 @@ export function openEditProjectModal(user: DashboardUser, project: ProjectItem): estimated_length_m: length.input.value ? Number(length.input.value) : null, memo: memo.input.value.trim() || null, status: project.status, + client_org: clientOrg.input.value.trim() || null, + project_number: projectNumber.input.value.trim() || null, + work_amount: workAmount.input.value.trim() || null, }); showToast(L("B01_Dashboard_Saved"), "success"); }, diff --git a/B07_DesignDetail/B07_DesignDetail_Router.py b/B07_DesignDetail/B07_DesignDetail_Router.py index d7a77523..edc3f355 100644 --- a/B07_DesignDetail/B07_DesignDetail_Router.py +++ b/B07_DesignDetail/B07_DesignDetail_Router.py @@ -122,7 +122,7 @@ async def _title_block_fields(project_id: UUID) -> dict[str, str]: 배정이 없으면 프로젝트 소유자로 떨어진다 — 혼자 쓰는 계정에서도 칸이 차게. 아직 못 채우는 자리와 이유: - - 축척(A1/A3)·사업량·연도기번 — 값을 지어내지 않는다(임의 수치 금지). + - 사업량·연도기번은 사람이 넣는 값이다(B01 프로젝트 수정 화면). 비어 있으면 빈칸. - 설계일자 — "확정일"인데 도각은 **확정 전**에 그려져 저장본에 굳는다. 채울 시점 정의가 미결이라 비워 둔다. - 도면번호 — 단건 조회가 목록 순서를 모른다(목록을 다시 만들면 도면을 열 때마다 @@ -132,7 +132,8 @@ async def _title_block_fields(project_id: UUID) -> dict[str, str]: async with pool.acquire() as connection, connection.cursor() as cursor: await cursor.execute( """ - SELECT p.name, p.region, p.client_org, c.name, c.logo_path, + SELECT p.name, p.region, p.client_org, p.project_number, p.work_amount, + c.name, c.logo_path, COALESCE(designer.name, owner.name), COALESCE(designer.signature_path, owner.signature_path), pm.name, lead.name @@ -149,11 +150,25 @@ async def _title_block_fields(project_id: UUID) -> dict[str, str]: row = await cursor.fetchone() if not row: return {} - name, region, client_org, company, logo_path, designer, signature_path, pm, lead = row + ( + name, + region, + client_org, + project_number, + work_amount, + company, + logo_path, + designer, + signature_path, + pm, + lead, + ) = row fields = { "공사명": name, "위치": region, "시행청": client_org, + "연도기번": project_number, + "사업량": work_amount, "용역회사": company, "설계자": designer, "과업책임자": pm, diff --git a/db_management/012_title_block_inputs.sql b/db_management/012_title_block_inputs.sql new file mode 100644 index 00000000..832e9cd5 --- /dev/null +++ b/db_management/012_title_block_inputs.sql @@ -0,0 +1,15 @@ +-- 012_title_block_inputs.sql +-- 표지에 들어갈 사람이 넣는 값 (2026-09-02) +-- +-- 011 로 시행청·담당자 배정 자리를 냈고, 남은 표지 빈칸 둘은 **지어낼 수 없는 값**이라 +-- 사람이 넣을 칸을 만든다. 값 출처를 프로그램이 정하지 않는다. +-- 연도·기번 — 실무문서 폴더명 관행: `2024년 간선임도(기번3-울진.대흥)` +-- 사업량 — 표지 「- 사 업 량 :」 칸. 단위·표기가 사업 종류마다 달라 자유 문자열로 받는다. +-- +-- 전부 ADD COLUMN(NULL 허용)이라 기존 행·기존 동작은 그대로다. + +USE aislo_db; + +ALTER TABLE projects + ADD COLUMN IF NOT EXISTS project_number VARCHAR(100) NULL COMMENT '표지 연도·기번' AFTER client_org, + ADD COLUMN IF NOT EXISTS work_amount VARCHAR(100) NULL COMMENT '표지 사업량' AFTER project_number;