feat(pnl): 경영관리 손익 탭 + 기능모듈 전수조사 - #464
Merged
Merged
Conversation
경영관리가 쓰는 엑셀(차량판매_손익_양식)을 그대로 옮겼다. 달을 가르는 기준은 **세금계산서 발행일**이고, 발행일을 적는 순간 그 달 표로 들어간다. ## 화면 셋 · **요약** — 「주문 진행」처럼 맨 위에 그 달 전체(건수·공급가액·VAT·공급대가· 계약금·캐피탈·입금 차액·원가·수익·수익률) · **입력 필요** — 발행일을 아직 안 적은 건을 맨 위에 따로 모은다. 여기서 날짜를 적으면 그 달 표로 자동으로 들어간다 · **그 달의 표** — 적으면서 보는 자리. 칸을 벗어나면 저장한다(줄마다 따로) — 「저장」 버튼을 누르게 하면 스무 줄을 고칠 때 안 누른 줄이 생긴다 PC·태블릿은 표(칸이 열셋이라 태블릿은 옆으로 민다), 휴대폰은 줄마다 카드다. 접힌 카드에도 **입금 차액과 수익**은 보인다 — 그게 이 표를 보는 이유다. ## 어디서 오는 값인가 · 공급가액 = 계약서상 **특장 결제금액**에서 VAT 를 덜어 낸 값. VAT 가 버림이라 그냥 1.1 로 나누면 1원이 새서, 딱 맞는 값을 골라 채운다(supplyFromGross) · 계약금 = 시스템에 등록된 특장 계약금. **고칠 수 있다** · 캐피탈·입금일·비고 = 경영관리가 직접 적는다 · 원가(외주·사급·내부·기타) = **별도 시스템에서 불러올 자리**(구성 중). 그때까지는 손으로 적고, `cost_source` 로 손으로 적은 값인지 남긴다⚠️ **처음 만들 때 계약 금액을 굳힌다.** 단가표를 나중에 고쳤다고 이미 세금계산서가 나간 줄의 금액이 소급해 바뀌면 안 된다(발주서 금액을 얼리는 것과 같은 이유).⚠️ VAT·공급대가·입금 차액·수익은 **저장하지 않는다.** 적어 두면 공급가액을 고쳤을 때 둘이 어긋나고 어느 쪽이 맞는지 알 수 없어진다. 화면과 서버가 같은 함수로 낸다 (shared/finance/pnl — 엑셀의 실제 줄로 검사한다). ## 권한 기능모듈 둘을 더했다 — `pnl.view`(조회) · `pnl.manage`(입력). 돈에 관한 표라 **보는 것부터** 권한을 건다. 경영관리·마스터 자리에 기본으로 켠다. ## DB `order_pnl` 표를 **새로 더한다.** 기존 표·칸은 건드리지 않는다. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
아래 월별 표에서 새로 들어온 줄을 하나씩 찾아 열고 적는 일은, 주문이 쌓이면 할 수 없는 일이 된다(지시). 그래서 **새 건은 위에서 끝낸다.** · 「입력 필요」의 줄을 펴면 **그 건의 모든 칸**이 나온다 — 발행일·사업자명·공급가액· 계약금·캐피탈·입금일 둘·원가 넷·메모·비고 · VAT·공급대가·입금 차액·수익은 **치는 대로** 따라온다 — 다 적고 맞는지 볼 수 있다 · 저장하면 적은 발행일의 달로 화면이 옮겨 가고, 그 표에 줄이 내려가 있다 · 아래 월별 표는 이제 **보는 자리**다(고칠 수는 있다) 적는 중에는 그 건만 남긴다 — 밀린 건이 백 줄이면 목록이 화면을 다 먹어 적다가 어디를 보고 있는지 잃는다. 닫힌 목록은 높이를 묶어 아래 표를 밀어내지 않는다. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
손으로 padding 을 줘서 입력칸·날짜칸·버튼이 저마다 1~8px 씩 어긋났다(제보). 칸이 열몇 개인 표에서는 그 어긋남이 줄마다 쌓여 눈에 띈다. · 폼(입력 필요·휴대폰 카드·원가 창) = `--h-control` · `--fs-input` → 36px·14px 한 벌 · 표(PC·태블릿) = `--h-control-sm` · `--fs-label` → 28px·13px 한 벌 · **계산해서 보여만 주는 값도 같은 자리**를 차지한다(테두리만 투명) — 줄이 맞는다 · 값은 전부 오른쪽 끝에 맞춘다 — 적는 칸 옆에서 계산값만 왼쪽이면 어긋나 보인다 · 수익은 「금액 · 비율」이라 한 칸에 안 들어간다 — 줄을 통째로 쓴다⚠️ `minHeight` 도 함께 준다. DateField 의 방아쇠가 `minHeight: --h-control` 을 들고 있어 height 만 주면 **그 칸만 8px 더 컸다**(표에서 실제로 그랬다 — 36 vs 28). 실측: 폼 36px/14px 전부 동일 · 표 28px/13px 전부 동일 · 표 줄 높이 69px 균일. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
## 원가 한 칸(지시) 외주비·사급비·내부비용·기타로 나눠 두고 팝업에서 적게 했는데, 별도 시스템이 **원가를 하나로** 내려 줄 것이라 나눌 이유가 없어졌다. 칸 하나로 줄이고 표에서 바로 적는다 — 팝업도 없앴다. 아직 병합 전인 마이그레이션이라 컬럼을 지우지 않고 **처음부터 한 칸으로** 고쳤다 (운영에는 이 표가 아직 없다). 어디서 온 값인지(`cost_source`)는 남긴다 — 나중에 자동으로 채울 때 손으로 적은 값을 덮지 않기 위해서다. ## 한 화면에 들어가게(지시) PC 에서 가로 스크롤은 잘 안 되고, 밀지 않으면 글자가 잘린 채로 읽힌다. 칸을 열셋에서 **아홉**으로 줄였다 — 값을 지운 것이 아니라 **나오는 값을 나온 자리 아래**로 붙였다. · 고객명 아래 사업자명 (이름이 길어도 안 잘리게 폭을 준다) · 공급가액 아래 VAT · 공급대가 · 계약금·캐피탈 아래 각각의 입금일 (따로 있던 「입금일」 칸이 없어졌다) `table-layout: fixed` + colgroup 으로 폭을 못 박는다. 숫자 칸은 잘리지 않을 만큼 주고 남는 폭은 비고가 먹는다. 견적번호는 줄지 않고 **고객명이 줄어든다**(번호가 잘리면 못 찾는다). 실측 — 1440px: 1359/1359 · 1024px: 943/943, **가로 스크롤 없음**. 줄 높이 68px 균일. 남는 잘림은 서른 자짜리 상호와 긴 비고뿐인데, 둘 다 적는 칸이라 안에서 밀려 다 읽힌다. ## 곁들여 · DB 없이 도는 보안 검사 둘(로컬 자동 로그인 차단 · 마이페이지 접근 규칙)을 **CI 목록에 넣었다** · 손익 시험이 켜는 모듈을 둘로 줄였다 — 스물아홉 줄 × 세 계정이 다른 시험과 DB 를 놓고 다퉜다 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
## ① 한 번 저장하면 「입력 필요」가 다시 안 열렸다(제보) 저장하면 그 건은 목록에서 빠지는데 「지금 펴 둔 줄」 표시는 그 번호를 들고 있었다. 그러면 「편 줄만 보여 준다」는 규칙에 걸려 **목록이 통째로 비고**, 누를 줄조차 없어진다. 저장이 끝나면 표시를 비우고, **펴 둔 줄이 목록에 없으면 접은 것으로 본다** — 두 겹으로 막는다. ## ② 계약에서 오는 값은 못 고친다(지시) 고객명 · 공급가액 · VAT · 공급대가 · 계약금은 계약서에서 그대로 가져와 **줄을 만들 때 굳힌다.** 사업자명 칸은 없앴다(고객명으로 통일). 화면에서 빼는 것만으로는 막은 것이 아니라 **서버가 받지 않는다** — 보내도 안 바뀐다(검사로 못 박음). 고쳐야 하는 예외가 생기면 그때 다시 본다. 적는 칸은 이제 여섯이다 — 세금계산서 발행일 · 캐피탈 · 입금일 둘 · 원가 · 비고. ## ③ 표 정렬(지시) 값을 위아래로 쌓았더니 칸마다 높이가 달라지고 숫자가 세로로 몰렸다. **쌓지 않는다** — 칸을 하나씩 옆으로 늘어놓고, 글자로만 보여 주는 숫자도 적는 칸과 **같은 높이**를 차지하게 했다. 비고도 넓혔다. 다 넣으면 폭이 남으므로 좁은 화면에서는 **표만** 옆으로 민다(페이지가 아니라). 못 적는 칸은 흐리게 — 적을 수 있는 것처럼 보이면 눌러 보다가 헛수고한다. ## ④ 「입력 필요」 기준 = 특장사 수락(지시) 계약만 끝난 건은 아직 만들 차가 없어 세금계산서를 끊을 일이 없다. **수락한 뒤부터** 선다 (로컬 실데이터로 106건 → 3건). **오래 기다린 것이 맨 위**로 오게 서버에서 못 박았다. ## ⑤ 삭제 — 지우지 않는다(지시) 사유를 반드시 적게 하고(서버가 강제), 줄은 **회색으로 남긴다.** 그 줄 위에 반투명 레이어를 덮어 사유를 적는다 — 줄 전체를 정확히 덮는다(실측: 같은 top·left·너비·높이). 합계에서는 빠지고, 달 제목 옆에 「삭제 N건」으로 센다. 잘못 지웠으면 되돌린다. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
## 고객명이 잘렸다(제보) 옆에 붙인 견적번호가 자리를 먹고 있었다. **번호를 뺐다** — 번호로 찾는 일은 견적 목록에서 한다. 저장 표시(✓)만 이름 뒤에 남는다. ## 사업자명은 따로 적는다(지시) 계약서에서 끌어오려다 **적는 건과 안 적는 건이 섞여 있어** 되돌렸다. 표에 칸을 따로 두고 손으로 적는다(비워 둬도 된다). 미리 채우지 않는다 — 채워 두면 안 적기로 한 건까지 적힌 것처럼 보인다. ## 요약 열 칸 · 수익과 수익률 강조(지시) 한 칸에 「금액 · %」로 붙여 놨더니 긴 글자가 되어 눈에 안 들어왔다. 갈라서 각자 한 칸씩 준다. **숫자만** 브랜드 라임(`--lime-ink`)으로 키운다 — 컨피규레이터 **실구매가와 같은 색·같은 크기(22px)**. 칸은 칠하지 않는다. 칠하면 덩어리가 되어 옆 칸들과 무게가 뒤집힌다(가격바가 배경을 걷은 것과 같은 이유).⚠️ 이 색은 흰 바탕 대비가 낮아 **큰 글씨 전용**이다(가격바 주석). 그래서 요약의 그 둘만 키우고, 표 안의 수익은 본문 크기라 **검정 굵게**로 둔다. 칸 수를 다섯으로 못 박았던 것도 풀었다 — 좁은 화면에서 칸이 131px 이 되어 22px 짜리 수익 금액(143px)이 옆 칸을 침범했다. 이제 폭에 맞춰 칸 수가 준다. ## 곁들여 로컬 확인용 더미 생성 스크립트(`backend/src/scripts/pnl-dummy.ts`) — 「입력 필요」에 설 건 (특장사 수락 완료)을 몇 개 만든다. 운영에서는 시작과 동시에 멈추고, 시드·배포에 들어가지 않는다. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
## 무엇이 꼬여 있었나 이름은 DB 시드에, 설명은 화면 상수에, 코드는 라우트에 — **세 곳으로 갈라져** 서로 어긋났다. 그래서 이런 것들이 쌓였다. · **아무 데서도 검사하지 않는 모듈 넷**이 목록에 남아 켜고 끌 수 있었다 — 견적 삭제(삭제 기능 자체가 없어졌다) · 앱 알림(기기에서 허용하는 것으로 바뀌었다) · 제작 배정 알림 메일(역할 프리셋이 대신한다) · 기준데이터 옛 우산(다섯으로 쪼갰다) · **정렬 번호가 셋씩 겹쳐**(10·11·12) 목록 순서가 뜰 때마다 달라졌다 · 이름이 하는 일과 어긋났다 — 「고객 목록」인데 서류함까지, 「주문 상태 변경」인데 제작 단계 진행 ## 한 벌로 모았다 `shared/rbac/modules.ts` 하나가 **코드·이름·설명·묶음·물러남**을 갖는다. 순서는 배열 순서다(번호를 매기지 않는다 — 매기면 겹친다). DB 의 `feature_module` 은 권한 행이 가리키는 **자리**일 뿐이고, 목록은 라우트가 카탈로그로 실어 보낸다. 이제 화면 글자를 고치는 데 마이그레이션이 필요 없다. ## 이름·설명 물갈이 스물넷 전부 **실제로 하는 일**로 다시 적고 한 줄 설명을 달았다. 묶음(견적·서류·고객·주문·제작· 성과·손익·기준데이터·운영)으로 갈라 목록에 구분 줄을 넣었다 — 스물넷이 한 덩어리면 못 찾는다. ## 물러난 모듈 **행은 지우지 않는다**(이미 켜 둔 계정의 기록이다). `active=false` 로 내리고 목록에서 빼고, 프리셋·권한 시드에서도 뺐다. **새로 켤 수 없다**(서버가 400) — 끄는 것은 언제나 된다. 옛 우산은 이미 켜 둔 계정에서 다섯을 대신하던 동작을 그대로 지킨다. ## 지키는 검사(permission-catalog.test.ts) · 살아 있는 모듈은 **어디선가 실제로 검사한다** — 켜도 아무 일 없는 토글을 못 만든다 · 카탈로그와 시드가 한 글자도 어긋나지 않는다(이름·화면·순서·활성) · 정렬 번호가 겹치지 않는다 · 설명이 이름을 되풀이하지 않는다 · 물러난 모듈이 프리셋·권한 시드에 없다 · 이름·설명·묶음이 **영문 사전에 있다** — 값으로 부르는 자리라 i18n 검사가 못 보던 구멍이다 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #463
① 차량 판매건별 손익 — 경영관리 탭 (새 기능)
경영관리가 쓰는 엑셀(차량판매_손익_양식)을 그대로 옮겼다.
달을 가르는 기준은 세금계산서 발행일이고, 발행일을 적는 순간 그 달 표로 들어간다.
화면 셋
수익과 수익률은 숫자만 브랜드 라임(
--lime-ink)으로 키운다 — 컨피규레이터 실구매가와 같은 색·크기저장하면 적은 달로 화면이 옮겨 가며 아래 표에 내려간다. 적는 중에는 그 건만 남는다
「입력 필요」에 서는 시점은 특장사 수락이다 — 계약만 끝난 건은 아직 만들 차가 없어 세금계산서를 끊을 일이 없다
(로컬 실데이터 106건 → 3건). 오래 기다린 것이 맨 위로 오게 서버에서 못 박았다.
어디서 오는 값인가
VAT 가 버림이라 공급대가를 그냥 1.1 로 나누면 1원이 샌다 — 딱 맞는 값을 고른다(
supplyFromGross).계산값(VAT·공급대가·입금 차액·수익)은 저장하지 않는다 — 적어 두면 공급가액을 고쳤을 때 둘이 어긋난다.
화면과 서버가 같은 함수를 쓴다(
shared/finance/pnl).삭제 — 지우지 않는다. 사유를 반드시 적게 하고(서버가 강제) 줄은 회색으로 남으며,
그 위에 반투명 레이어가 덮여 사유가 적힌다(줄 전체를 정확히 덮는다). 합계에서만 빠진다. 되돌릴 수 있다.
권한 —
pnl.view(손익 보기) ·pnl.manage(손익 입력). 돈에 관한 표라 보는 것부터 막는다.경영관리·마스터 자리에 기본으로 켠다.
UI — PC·태블릿은 표(아홉 칸 → 사업자명이 붙어 열 칸, 좁으면 표만 옆으로 민다), 휴대폰은 줄마다 카드.
적는 칸은 앱 토큰 한 벌로 맞췄다(폼 36px/14px · 표 28px/13px). 못 적는 칸은 흐리게.
② 기능모듈 전수조사 (정리)
이름은 DB 시드에, 설명은 화면 상수에, 코드는 라우트에 — 세 곳으로 갈라져 서로 어긋나 있었다.
(견적 삭제 · 앱 알림 · 제작 배정 알림 메일 · 기준데이터 옛 우산)
shared/rbac/modules.ts한 벌로 모았다 — 코드·이름·설명·묶음·물러남. 순서는 배열 순서다.DB 는 권한이 가리킬 자리일 뿐이고 목록은 라우트가 카탈로그로 실어 보낸다
(이름을 고치는 데 마이그레이션이 더 필요 없다).
스물넷 전부 실제로 하는 일로 다시 적고 한 줄 설명을 붙였다.
견적 · 서류 · 고객 · 주문·제작 · 성과·손익 · 기준데이터 · 운영 일곱 묶음으로 갈라 구분 줄을 넣었다.
물러난 모듈은 행을 지우지 않는다(이미 켜 둔 계정의 기록이다). 비활성으로 내리고 목록·프리셋·시드에서
빼고, 새로 켜는 것은 서버가 막는다(끄는 것은 언제나 된다). 옛 우산이 다섯을 대신하던 동작은 그대로 지킨다.
DB
order_pnl표를 새로 더한다. 기능모듈 두 줄(pnl.view·pnl.manage)을 더하고,기존 기능모듈 행의 이름·순서·활성을 카탈로그에 맞춘다(UPDATE 만 — 행을 지우지 않는다).
검증
물러난 모듈이 프리셋·시드에 없음 · 이름·설명이 영문 사전에 있다(값으로 부르는 자리라 기존 검사가 못 보던 구멍)
남은 것
원가 연동. 칸과 저장 구조는 만들어 뒀고, 손으로 적은 값인지(
cost_source)를 남겨나중에 자동으로 채울 때 사람이 적은 값을 덮지 않게 한다.
🤖 Generated with Claude Code