feat(project): 업무 시간 지정(종일 기본) + 타임라인 시간 반영
- tasks 에 start_time/due_time TIME 컬럼 추가(마이그레이션 003). NULL=종일
- 업무 모달에 '시간 지정' 체크박스 → 체크 시 시작/마감 시간 입력란 표시.
미체크면 종일로 저장(시간 null)
- store.validate_time(HH:MM), db create/update + TIME 직렬화('HH:MM')
- 타임라인: 시간 있으면 date+time 으로 정확히 배치, 없으면 종일
DB: scripts/sql/project_db_003_task_times.sql 서버서 적용 필요.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from datetime import date, datetime
|
||||
from datetime import date, datetime, time
|
||||
from typing import Any
|
||||
|
||||
from psycopg.rows import dict_row
|
||||
@@ -331,12 +331,16 @@ class ProjectStore:
|
||||
priority: str = "normal",
|
||||
start_date: str | None = None,
|
||||
due_date: str | None = None,
|
||||
start_time: str | None = None,
|
||||
due_time: str | None = None,
|
||||
created_by: str = "",
|
||||
) -> dict[str, Any]:
|
||||
ttl = store.validate_task_title(title)
|
||||
pr = store.validate_priority(priority)
|
||||
sd = store.validate_date(start_date)
|
||||
dd = store.validate_date(due_date)
|
||||
sti = store.validate_time(start_time)
|
||||
dti = store.validate_time(due_time)
|
||||
assignee = store.norm_str(assignee_email, lower=True)
|
||||
with self._pool.connection() as conn:
|
||||
with conn.transaction():
|
||||
@@ -357,14 +361,15 @@ class ProjectStore:
|
||||
"""
|
||||
INSERT INTO tasks
|
||||
(project_id, stage_id, title, description, assignee_email,
|
||||
assignee_name, priority, start_date, due_date, sort_order, created_by)
|
||||
VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
|
||||
assignee_name, priority, start_date, due_date,
|
||||
start_time, due_time, sort_order, created_by)
|
||||
VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
|
||||
RETURNING *
|
||||
""",
|
||||
(
|
||||
project_id, stage_id, ttl, store.norm_str(description),
|
||||
assignee, store.norm_str(assignee_name), pr, sd, dd,
|
||||
int(nxt["n"]), store.norm_str(created_by, lower=True),
|
||||
sti, dti, int(nxt["n"]), store.norm_str(created_by, lower=True),
|
||||
),
|
||||
).fetchone()
|
||||
if assignee:
|
||||
@@ -406,6 +411,12 @@ class ProjectStore:
|
||||
if "due_date" in fields:
|
||||
sets.append("due_date = %s")
|
||||
params.append(store.validate_date(fields["due_date"]))
|
||||
if "start_time" in fields:
|
||||
sets.append("start_time = %s")
|
||||
params.append(store.validate_time(fields["start_time"]))
|
||||
if "due_time" in fields:
|
||||
sets.append("due_time = %s")
|
||||
params.append(store.validate_time(fields["due_time"]))
|
||||
if "assignee_email" in fields:
|
||||
new_assignee = store.norm_str(fields["assignee_email"], lower=True)
|
||||
sets.append("assignee_email = %s")
|
||||
@@ -714,6 +725,8 @@ class ProjectStore:
|
||||
for k, v in list(out.items()):
|
||||
if isinstance(v, datetime):
|
||||
out[k] = v.astimezone(KST).isoformat(timespec="seconds")
|
||||
elif isinstance(v, time):
|
||||
out[k] = v.strftime("%H:%M")
|
||||
elif isinstance(v, date):
|
||||
out[k] = v.isoformat()
|
||||
for k in ("id", "parent_id", "project_id", "stage_id", "task_id", "sort_order"):
|
||||
|
||||
Reference in New Issue
Block a user