Fix contract linkage and persist related project groups

This commit is contained in:
b17301
2026-04-09 08:50:17 +09:00
parent 8f6f6533dc
commit ae658a332b
7 changed files with 1838 additions and 156 deletions
BIN
View File
Binary file not shown.
+1044 -8
View File
File diff suppressed because it is too large Load Diff
+101 -57
View File
@@ -1,13 +1,25 @@
{% extends "base.html" %}
{% block title %}연도별 수익 비용 정리{% endblock %}
{% block title %}연도별 수익/비용{% endblock %}
{% block head_extra %}
<style>
.summary-layout {
display: grid;
grid-template-columns: 320px minmax(0, 1fr);
gap: 16px;
align-items: start;
}
.summary-stack {
display: grid;
gap: 16px;
}
.filter-grid {
display: grid;
grid-template-columns: 220px 220px 1fr;
gap: 12px;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
align-items: end;
}
@@ -15,7 +27,8 @@
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 10px;
margin-top: 0;
justify-content: center;
}
.legend-item {
@@ -43,7 +56,7 @@
radial-gradient(circle at top left, rgba(17, 17, 17, 0.045), transparent 36%);
border: 1px solid var(--line);
border-radius: 16px;
padding: 16px;
padding: 14px;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.92);
}
@@ -51,12 +64,6 @@
margin-bottom: 10px;
}
.chart-title {
font-size: 18px;
font-weight: 700;
margin-bottom: 12px;
}
.chart-svg {
width: 100%;
height: auto;
@@ -78,11 +85,47 @@
.metric-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-template-columns: 1fr;
gap: 8px;
}
.chart-stack {
display: grid;
gap: 16px;
}
.summary-stack .panel {
padding: 14px;
gap: 12px;
}
.summary-stack .section-title h2,
.chart-stack .section-title h2 {
font-size: 17px;
}
.summary-stack .field select {
min-width: 0;
padding: 10px 12px;
font-size: 13px;
}
.summary-stack .stat-card {
padding: 10px 12px;
border-radius: 12px;
gap: 2px;
}
.summary-stack .stat-card .label {
font-size: 11px;
}
.summary-stack .stat-card .value {
font-size: 20px;
}
@media (max-width: 1000px) {
.summary-layout,
.filter-grid,
.metric-grid {
grid-template-columns: 1fr;
@@ -93,52 +136,51 @@
{% block content %}
<section class="panel">
<div class="section-title">
<h2>연도별 수익 비용 정리</h2>
</div>
<div class="filter-grid">
<div class="field">
<select id="granularity" aria-label="보기 기준">
<option value="yearly">연간</option>
<option value="monthly">월간</option>
</select>
<div class="summary-layout">
<div class="summary-stack">
<section class="panel">
<div class="section-title">
<h2>수익/비용 현황</h2>
</div>
<div class="filter-grid">
<div class="field">
<select id="granularity" aria-label="보기 기준">
<option value="yearly">연간</option>
<option value="monthly">월간</option>
</select>
</div>
<div class="field">
<select id="yearFilter" aria-label="연도 선택">
<option value="recent10">최근 10개년</option>
{% for year in available_years %}
<option value="{{ year }}">{{ year }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="metric-grid" id="metricGrid"></div>
</section>
</div>
<div class="field">
<select id="yearFilter" aria-label="연도 선택">
<option value="recent10">최근 10개년</option>
{% for year in available_years %}
<option value="{{ year }}">{{ year }}</option>
{% endfor %}
</select>
<div class="chart-stack">
<section class="panel">
<div class="section-title">
<h2>비용 구조</h2>
</div>
<div class="chart-box">
<div class="legend chart-legend" id="expenseLegend"></div>
<svg id="expenseChart" class="chart-svg expense-chart-svg" viewBox="0 0 1600 420" preserveAspectRatio="xMidYMid meet"></svg>
</div>
</section>
<section class="panel">
<div class="section-title">
<h2>수금/비용/영업수지 그래프</h2>
</div>
<div class="chart-box">
<div class="legend chart-legend" id="balanceLegend"></div>
<svg id="balanceChart" class="chart-svg" viewBox="0 0 1200 420" preserveAspectRatio="xMidYMid meet"></svg>
</div>
</section>
</div>
<div></div>
</div>
</section>
<section class="panel">
<div class="section-title">
<h2>선택 구간 요약</h2>
</div>
<div class="metric-grid" id="metricGrid"></div>
</section>
<section class="panel">
<div class="section-title">
<h2>비용 구조</h2>
</div>
<div class="chart-box">
<div class="legend chart-legend" id="expenseLegend"></div>
<svg id="expenseChart" class="chart-svg expense-chart-svg" viewBox="0 0 1600 420" preserveAspectRatio="xMidYMid meet"></svg>
</div>
</section>
<section class="panel">
<div class="section-title">
<h2>수금/비용/영업수지 그래프</h2>
</div>
<div class="chart-box">
<div class="legend chart-legend" id="balanceLegend"></div>
<svg id="balanceChart" class="chart-svg" viewBox="0 0 1200 420" preserveAspectRatio="xMidYMid meet"></svg>
</div>
</section>
{% endblock %}
@@ -293,7 +335,9 @@
}
function buildSignedAxis(maxPositiveValue, minNegativeValue, width, height, margin) {
const rangeMax = Math.max(Math.abs(maxPositiveValue || 0), Math.abs(minNegativeValue || 0), 1);
const forcedNegativeFloor = -20000000000;
const normalizedNegative = Math.min(Number(minNegativeValue || 0), forcedNegativeFloor);
const rangeMax = Math.max(Math.abs(maxPositiveValue || 0), Math.abs(normalizedNegative || 0), 1);
const { tickStep, tickMax } = buildPositiveAxisScale(rangeMax, 4);
const plotHeight = height - margin.top - margin.bottom;
const zeroY = margin.top + (plotHeight * tickMax) / (tickMax * 2);
+2 -1
View File
@@ -477,7 +477,7 @@
<nav class="nav">
<a href="/" class="{% if request.url.path == '/' %}active{% endif %}">대시보드</a>
<a href="/projects" class="{% if request.url.path == '/projects' %}active{% endif %}">프로젝트 정보</a>
<a href="/annual-summary" class="{% if request.url.path == '/annual-summary' %}active{% endif %}">연도별 수익 비용 정리</a>
<a href="/annual-summary" class="{% if request.url.path == '/annual-summary' %}active{% endif %}">연도별 수익/비용</a>
<div class="nav-spacer"></div>
<aside
class="sync-status"
@@ -496,6 +496,7 @@
<div>마지막 확인: <strong id="syncLastChecked">-</strong></div>
<div>서버 시간: <strong id="syncServerTime">{{ server_time or '-' }}</strong></div>
<div>데이터 버전: <strong id="syncDataVersion">{{ data_version or '-' }}</strong></div>
<div>계약/청구 동기화: <strong>{{ import_sync_summary.contract_project_count or 0 }}</strong> / <strong>{{ import_sync_summary.billing_project_count or 0 }}</strong></div>
</div>
</aside>
</nav>
+10
View File
@@ -228,6 +228,16 @@
</div>
</div>
<div class="chart-shell" style="margin-bottom: 16px;">
<div class="legend-box" style="justify-content:flex-start;">
<span class="legend-item"><span class="legend-swatch" style="background:#111827;"></span>계약 프로젝트 {{ import_sync_summary.contract_project_count or 0 }}건</span>
<span class="legend-item"><span class="legend-swatch" style="background:#0f766e;"></span>청구 프로젝트 {{ import_sync_summary.billing_project_count or 0 }}건</span>
<span class="legend-item"><span class="legend-swatch" style="background:#f59e0b;"></span>변경계약 검토 {{ import_sync_summary.review_needed_count or 0 }}건</span>
<span class="legend-item"><span class="legend-swatch" style="background:#1d4ed8;"></span>한맥 계약합계 {{ "{:,.0f}".format(import_sync_summary.total_hanmac_contract_amount or 0) }}</span>
<span class="legend-item"><span class="legend-swatch" style="background:#7c3aed;"></span>청구 수금합계 {{ "{:,.0f}".format(import_sync_summary.total_collected_amount or 0) }}</span>
</div>
</div>
<div class="dashboard-layout">
<section class="dashboard-status-panel">
<div class="dashboard-status-grid">
+29
View File
@@ -431,6 +431,35 @@
</div>
</section>
<section class="panel">
<div class="section-title">
<h2>계약·청구 동기화 현황</h2>
<p>계약현황 / 기성청구현황 파일 기준</p>
</div>
<div class="stats">
<div class="stat-card">
<div class="label">계약 프로젝트</div>
<div class="value">{{ import_sync_summary.contract_project_count or 0 }}</div>
</div>
<div class="stat-card">
<div class="label">청구 프로젝트</div>
<div class="value">{{ import_sync_summary.billing_project_count or 0 }}</div>
</div>
<div class="stat-card">
<div class="label">청구 행 수</div>
<div class="value">{{ import_sync_summary.billing_entry_count or 0 }}</div>
</div>
<div class="stat-card">
<div class="label">변경계약 검토</div>
<div class="value">{{ import_sync_summary.review_needed_count or 0 }}</div>
</div>
<div class="stat-card">
<div class="label">청구 수금합계</div>
<div class="value">{{ "{:,.0f}".format(import_sync_summary.total_collected_amount or 0) }}</div>
</div>
</div>
</section>
<section class="two-col">
<div class="stack">
<section class="panel">
+652 -90
View File
File diff suppressed because it is too large Load Diff