발주서 금액: 계약가는 DB·자동 기입, 계약 밖은 입력 칸이 저절로 - #374
Merged
Merged
Conversation
**1. 옵션과 단가표를 빠짐없이 맞물린다** `maker_price` 행이 **옵션마다 하나씩** 미리 있다(활성 옵션값 27개 전부 · 32행). 행을 만들거나 지우지 않는다 — 아무 코드로나 행을 만들 수 있으면 어느 선택에도 걸리지 않는 유령 줄이 쌓이고, 「이 옵션은 누가 하기로 했더라」를 표에서 답할 수 없게 된다. `value_code` 에 외래키를 걸어 없는 옵션을 가리키는 행 자체가 생기지 않는다. 분류는 셋이다. **EVN 과 NONE 은 발주서에 안 실리는 결과가 같지만 뜻이 다르다** — 앞은 우리가 하는 일이고 뒤는 아무도 안 하는 일이다. · MAKER 15 — 특장사가 한다(계약가 8 · 미책정 7) · EVN 6 — 인도 뒤 EV& 가 직접(차량 트림 포함 — 계약 제7조: 베이스 차량은 갑이 지급) · NONE 11 — 「없음」·「추가없음」, 기본형에 포함된 여닫이 도어 **2. 입력 칸이 저절로 생긴다** 예전엔 계약에 없는 항목이면 관리자가 「+ 항목 추가」를 눌러 **품목명부터 타이핑**해야 했다. 무엇을 적어야 하는지 화면이 이미 아는데 사람에게 다시 묻던 셈이다(제보). 이제 고른 옵션 중 특장사 몫인데 계약가가 없으면 그 줄이 **금액만 빈 칸으로** 뜬다. 품목명·단위·수량은 옵션이 정하고 관리자는 금액만 채운다. 서버도 그렇게 받는다 — 품목명을 딴 것으로 보내도 옵션이 정한 이름이 남는다. **3. 빈 금액으로는 배정되지 않는다** 0 원짜리 줄이 실린 발주서는 특장사에게 「무상으로 해 주기로 했다」로 읽힌다. 화면은 어느 줄이 비었는지 먼저 말하고 배정 버튼을 잠그며, 서버도 같은 것을 본다. `unit_price` 를 NULL 허용으로 넓혔다 — 「계약에 값이 없다」와 「0 원에 하기로 했다」는 다른 말이라 0 으로 채워 두지 않는다. **4. 관리 화면**(관리자 > 특장사 단가) 특장사를 고르면 그 계약의 표가 뜨고, 분류·단가·품목명·단위·수량을 **그 자리에서** 고친다. 저장 버튼을 따로 두면 고쳐 놓고 안 누른 채 떠나 옛 단가가 발주서에 나간다. 옵션과의 연결은 고칠 수 없다(행이 옵션에 매여 있다). **5. `top_code` 를 NULL 대신 빈 문자열로** Postgres 는 유일 인덱스에서 NULL 을 **서로 다른 값으로** 본다. NULL 로 두면 「같은 옵션·같은 탑에 단가는 하나뿐」이 그 줄들에는 걸리지 않아 같은 자리에 행이 여럿 생길 수 있었다(Prisma 도 복합 유일키 조회에 NULL 을 받지 않는다). ## DB — 더하기만 한다 `unit_price` NOT NULL 해제(넓히는 쪽) · `value_code` 외래키 추가. `DROP` · `DELETE` · 컬럼 삭제 없음. ## 검증 - 실 API 10 · 시드 맞물림 가드 9(옵션 누락 · 유령 행 · 분류 · 0원 · 중복 · 계약가 대조) - 가드는 버그를 되살려 확인했다 — 옵션 추가 후 미등록 · 계약가 오기 · EV& 몫에 단가 - 로컬 662/662 초록
**1. 없는 모듈을 요구하고 있었다**
새로 만든 「특장사 단가」 라우트가 `requirePermission('basedata.edit')` 를 걸었는데
실제 모듈은 `basedata.manage` 다. 없는 모듈을 요구하면 **아무도 그 기능을 못 쓴다** —
화면은 그럭저럭 그려지고 「권한 없음」만 나와 원인을 찾기 어렵다.
**2. 🔴 시드가 운영 모듈을 지우고 있었다**
`feature_module.csv` 에 11개뿐인데 운영에는 17개다. 시드는 이 표를 `deleteMany({})` 로
**통째로 비우고** CSV 대로 다시 만든다 — 그래서 로컬에서 시드를 한 번 돌렸더니
`basedata.manage` · `quote.edit` · `quote.delete` · `doc.send.email` · `doc.send.sign` ·
`notify.push` · `stats.own` · `stats.all` 이 **권한과 함께 사라졌고**, 옵션DB·무게상수·
영업 성과 탭이 통째로 없어졌다(그래서 찾았다).
운영 값을 그대로 옮겨 맞췄다. `access_control.csv` 도 마찬가지로 맞췄다 —
이미 없어진 `view.all` · `subsidy.manage` 를 가리키고 있어 시드가 외래키에서 멈췄다
(그 둘은 운영에도 없고 코드도 안 쓴다. 시험 주석에 「켜고 꺼도 아무 일 없었다」고 남아 있다).
⚠️ 이 시드를 **운영에서 돌리면 안 된다**. 모듈과 계정별 권한이 전부 지워진다.
(memory/feedback_prod_seed 의 경고가 이 경우다)
**3. 되풀이되지 않게**
`permission-seed-sync.test.ts` — 라우트가 요구하는 모듈이 시드에 다 있는가,
권한 부여가 없는 모듈을 가리키지 않는가, 세 역할 모두 기본 권한이 있는가.
시드가 「지우고 다시 만든다」는 사실 자체도 검사에 박아 뒀다 — upsert 로 바뀌면
이 검사들이 지키던 것도 뜻을 잃으므로 그때 같이 고쳐야 한다.
두 버그를 되살려 빨간불을 확인했다(모듈 이름 오타 · 시드에서 모듈 제거).
**4. 관리 화면 마무리** — 「분류」 칸이 좁아 「특장사」가 「사」로 잘렸다. 칸을 넓혔다.
**1. `onDelete` 를 안 적어 migration 과 스키마가 어긋났다** migration SQL 은 `ON DELETE RESTRICT` 로 만들었는데 스키마에는 안 적었다. 선택 관계(`OptionValue?`)의 Prisma 기본값은 **SetNull** 이라, 배포 전 검증이 「외래키를 지웠다가 다시 만든다」는 drift 로 잡았다(CI 에서 걸렸다). Restrict 가 맞다 — SetNull 이면 옵션을 지웠을 때 `value_code` 가 NULL 인 **유령 줄**이 남아 어느 선택에도 걸리지 않는다. 옵션은 지우지 않고 `active` 로 끄는 것이 이 저장소의 방식이라, 지우기를 막는 쪽이 그 방식과도 맞는다. **2. high 취약점 3건 — 이 변경과 무관하다** `@xmldom/xmldom` · `multer` · `nodemailer`. 어제까지 초록이었고 이 브랜치는 의존성을 건드리지 않았다(`package.json` 무변경) — 그 사이 새로 공지된 것이다. `npm audit fix` 로 **패치만** 올렸다(`--force` 없음 · `package-lock.json` 만 변경). 남은 6건은 moderate 라 검사 기준(high) 밖이다.
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 #373
옵션과 단가표를 빠짐없이 맞물린다
maker_price행이 옵션마다 하나씩 미리 있다(활성 옵션 27개 전부 · 탑크기별 분화 포함 32행).value_code에 외래키를 걸어 없는 옵션을 가리키는 행 자체가 생기지 않고, 만들거나 지우는API 도 없다. 관리자는 값만 고친다.
분류가 셋인 이유 — EV& 직접과 해당 없음은 발주서에 안 실리는 결과가 같지만 뜻이 다르다.
앞은 우리가 하는 일이고 뒤는 아무도 안 하는 일이다. 나중에 「이거 누가 하기로 했더라」를
표에서 답하려면 구분돼 있어야 한다.
입력 칸이 저절로 생긴다
예전엔 계약에 없는 항목이면 「+ 항목 추가」를 눌러 품목명부터 타이핑해야 했다.
이제 고른 옵션 중 특장사 몫인데 계약가가 없으면 그 줄이 금액만 빈 칸으로 뜬다.
품목명·단위·수량은 옵션이 정하고 관리자는 금액만 채운다 — 서버도 그렇게 받아서,
품목명을 딴 것으로 보내도 옵션이 정한 이름이 남는다(시험으로 확인).
빈 금액으로는 배정되지 않는다. 화면은 어느 줄이 비었는지 먼저 말하고 버튼을 잠그며,
서버도 같은 것을 본다. 0 원짜리 줄은 특장사에게 「무상으로 해 주기로 했다」로 읽힌다.
unit_price를 NULL 허용으로 넓혔다 — 「계약에 값이 없다」와 「0 원에 하기로 했다」는 다른 말이다.🔴 덤으로 찾은 것: 시드가 운영 모듈을 지운다
feature_module.csv에 11개뿐인데 운영에는 17개다. 시드는 이 표를deleteMany({})로통째로 비우고 CSV 대로 다시 만든다 — 로컬에서 시드를 한 번 돌렸더니
basedata.manage·quote.edit·quote.delete·doc.send.*·notify.push·stats.*가 권한과 함께사라졌고, 옵션DB·무게상수·영업 성과 탭이 통째로 없어졌다(그래서 발견했다).
운영 값으로 맞췄고,
permission-seed-sync.test.ts로 다시는 어긋나지 않게 했다 —라우트가 요구하는 모듈이 시드에 다 있는가, 권한이 없는 모듈을 가리키지 않는가.
이 PR 이 그걸 바꾸지는 않는다 — 별도로 다룰 일이다.
DB — 더하기만 한다
unit_priceNOT NULL 해제 ·value_code외래키 추가.DROP·DELETE없음.배포 뒤 필요한 일
운영
maker_price에는 계약 8줄만 있다. 나머지 24줄(분류 정보)은 배포 뒤 격리 SQL 로 넣어야관리 화면이 온전히 보인다 — 전체 시드는 돌리지 않는다.
검증
모듈 이름 오타 · 시드에서 모듈 제거
다시 비우면 「선택 옵션」 태그와 함께 빈 금액 칸 + 배정 잠김 → 채우면 열림