Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1

This commit is contained in:
2026-09-02 10:51:04 +09:00
6 changed files with 87 additions and 7 deletions
+7
View File
@@ -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 {
+11 -3
View File
@@ -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,
),
)
+4
View File
@@ -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):
+32 -1
View File
@@ -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");
},
+18 -3
View File
@@ -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,
+15
View File
@@ -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;