diff --git a/data.db b/data.db
index 41c0d04..a63f63f 100644
Binary files a/data.db and b/data.db differ
diff --git a/main.py b/main.py
index 70b5246..3fa9cc6 100644
--- a/main.py
+++ b/main.py
@@ -3836,7 +3836,11 @@ def merge_project_external_fields(
item["hanmac_contract_amount"] = normalize_amount(contract_info.get("hanmac_contract_amount"))
item["billing_contract_amount"] = normalize_amount(billing_summary.get("contract_amount"))
item["billed_amount"] = normalize_amount(billing_summary.get("billed_amount"))
- item["collection_balance_amount"] = normalize_amount(billing_summary.get("balance_amount"))
+ # 수금잔액은 원천 balance 합계 대신 계약금액 대비 수금액 차이로 통일 계산한다.
+ computed_collection_balance = contract_amount - collection_amount
+ if abs(computed_collection_balance) < 1:
+ computed_collection_balance = 0.0
+ item["collection_balance_amount"] = computed_collection_balance
item["latest_billing_date"] = normalize_text(billing_summary.get("latest_billing_date"))
item["changed_contract_amount"] = latest_changed_contract_amount
item["changed_contract_date"] = normalize_text(latest_summary_change.get("change_date")) or normalize_text(latest_round_change.get("change_date"))
diff --git a/templates/process_cost.html b/templates/process_cost.html
index 2d077f3..33ae649 100644
--- a/templates/process_cost.html
+++ b/templates/process_cost.html
@@ -511,6 +511,34 @@
align-items: center;
}
+ .pc-related-head {
+ align-items: center;
+ justify-content: space-between;
+ gap: 10px;
+ }
+
+ .pc-related-head-main {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ flex: 1 1 auto;
+ min-width: 0;
+ flex-wrap: wrap;
+ }
+
+ .pc-related-form.pc-related-form--inline {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ flex: 1 1 460px;
+ min-width: 0;
+ }
+
+ .pc-related-form.pc-related-form--inline input[type="text"] {
+ flex: 1 1 260px;
+ min-width: 180px;
+ }
+
.pc-related-list {
border: 1px solid var(--line);
border-radius: 10px;
@@ -884,8 +912,15 @@
diff --git a/templates/projects.html b/templates/projects.html
index 4930353..b2db2ed 100644
--- a/templates/projects.html
+++ b/templates/projects.html
@@ -567,7 +567,21 @@
opacity: 0.72;
}
- .related-project-tag button {
+ .related-project-open {
+ border: 0;
+ background: transparent;
+ color: inherit;
+ font-size: 12px;
+ font-weight: 800;
+ line-height: 1.1;
+ padding: 0;
+ text-decoration: underline;
+ text-underline-offset: 2px;
+ cursor: pointer;
+ }
+
+ .related-project-tag .related-project-toggle,
+ .related-project-tag [data-remove-related] {
width: 14px;
height: 14px;
min-width: 14px;
@@ -589,7 +603,8 @@
justify-content: center;
}
- .related-project-tag button:hover {
+ .related-project-tag .related-project-toggle:hover,
+ .related-project-tag [data-remove-related]:hover {
transform: none;
background: rgba(41, 72, 95, 0.14);
}
@@ -7269,7 +7284,10 @@
const isInactive = inactiveCodes.has(item.support_dept_code);
return `
- ${escapeHtml(item.support_dept_code)} · ${escapeHtml(item.support_dept_name)}
+
+ · ${escapeHtml(item.support_dept_name)}
@@ -8671,6 +8689,17 @@
relatedBarBox.querySelectorAll("[data-remove-related]").forEach((button) => {
button.addEventListener("click", () => removeRelatedProject(button.dataset.removeRelated || ""));
});
+ relatedBarBox.querySelectorAll("[data-open-related-project]").forEach((button) => {
+ button.addEventListener("click", (event) => {
+ event.preventDefault();
+ event.stopPropagation();
+ const code = String(button.dataset.openRelatedProject || "").trim();
+ if (!code) return;
+ const targetItem = getAnalysisItem(code);
+ if (!targetItem) return;
+ renderAnalysis(targetItem);
+ });
+ });
relatedBarBox.querySelectorAll("[data-analysis-detail-note]").forEach((inputEl) => {
inputEl.addEventListener("focusout", async () => {
const baseCode = String(inputEl.dataset.analysisDetailNote || "").trim();