Skip to content

발주서: 공급가 표 · 단가 없는 사양 차단 · 한 장 제약 해제 - #372

Merged
Jay722J merged 4 commits into
mainfrom
fix/local-seed-and-stale-tests
Sep 8, 2026
Merged

발주서: 공급가 표 · 단가 없는 사양 차단 · 한 장 제약 해제#372
Jay722J merged 4 commits into
mainfrom
fix/local-seed-and-stale-tests

Conversation

@Jay722J

@Jay722J Jay722J commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Closes #371

특장사 공급가 표

계약서 [별첨3] 양식이 요구하던 표다. 우리가 특장사에 지급하는 금액이라,
고객 견적가(option_price)와 아무 관계가 없는 다른 축이다.

  • maker_price 신설 — 근거는 특장사별 계약 [별첨1] 단가표(브레인특장 2026-07-29).
  • work_by 로 갈린다: MAKER = 특장사가 작업 → 발주서에 실린다 ·
    EVN = 인도 뒤 EV& 가 직접 → 실리지 않는다. 안 시킨 일의 대금을 청구받지 않는다.
  • 계약에 있으면 그 값으로 고정되어 자동 기입, 없으면 관리자가 직접 기입.
    0원으로 채우지 않는다 — 특장사가 「무상으로 해 주기로 한 일」로 읽는다.
  • 계약 줄은 서버가 다시 만든다. 화면의 「고칠 수 없음」은 타이핑만 막을 뿐이라
    API 를 직접 부르면 6,700,000 짜리를 1 원으로 적어 보낼 수 있었다(시험으로 확인).
  • 배정 시점의 표를 order.po_lines얼려 둔다. 단가표를 나중에 고쳤다고 이미 나간
    발주서 금액이 따라 바뀌면, 특장사가 받은 종이와 화면이 어긋난다.

🔴 단가가 없는 사양이 0원으로 고객에게 나가고 있었다

priceMap[code] ?? 0 이라 단가표에 행이 없는 사양도 0원으로 계산되고 저장됐다.
운영 DB 에 DOPT_*_COUPANG(미닫이) 4개와 SPL_LOW(저상 스포일러) 행이 없다
지금 그걸 고르면 그 값이 0원인 견적서가 나간다.

0원과 「정해지지 않음」은 다른 말이다. 앞은 팔 수 있고 뒤는 팔 수 없다.
화면과 서버가 같은 함수(makePriceLookup)로 판단하고, 무엇이 미책정인지
고른 값의 이름으로 말한다(「미닫이」).

⚠️ 운영 동작이 바뀐다. 지금 통과하던 미닫이·저상 스포일러 선택이 422 로 거부된다.
그게 이 변경의 목적이다(0원 견적을 내보내느니 막는다). 그 사양을 팔려면
option_price 에 값을 넣어야 한다.

발주서를 A4 한 장에 가두지 않는다

한 장에 맞추느라 내용이 길수록 통째로 축소돼 글씨가 작아졌고, 그래서 분량을 곳곳에서
막아야 했다(비고 4줄 · 별지 30줄 · 커스텀은 아예 2페이지). 세로 제약만 걷어냈다.

⚠️ 양식은 그대로다. 폭은 늘 BASE_W 로 조판하고 화면 폭에 맞춰 축소한다 —
폭까지 풀었더니 좁은 화면에서 표 머리글이 겹치고 값이 잘렸다.

  • 별지·장 넘기기 제거. 커스텀 요청사항은 서류 맨 아래 칸이 되고,
    「+ 커스텀 주문내용 추가」로 열고 「커스텀 요청사항 삭제」로 되돌린다(내용이 있으면 한 번 묻는다).
  • 확인 체크는 그 칸 바로 아래로. 읽고 나서 누르는 순서가 자리로 드러난다.
  • 표제부 값을 「브레…」로 자르지 않는다 — 서류에서 이름이 잘리면 어느 회사인지 모른다.

용어

숨긴 것을 도로 꺼내는 조작이 견적·고객에서 각각 「다시 보이기」였다. 둘 다 되돌리기
모았다. 같은 일에 이름이 둘이면 다른 기능처럼 읽힌다.

병렬 시험 flake

role-toggle-freeze 가 자기 임시 모듈을 지우는 사이 다른 시험이 「모든 모듈」을 권한으로
주면서 외래키에 걸려, 파일 전체가 가끔 빨개졌다. 모듈을 지우지 않고 꺼 두어 경합을
없앴다(「지우지 말고 상태로 관리」와 같은 방향). 5회 연속 전부 초록.

DB — 더하기만 한다

maker_price 표 신설 · order.po_lines · po_draft.po_lines.
DROP · ALTER COLUMN · DELETE 없음.

검증

  • 실 API 통합시험: 공급가 표 7 · 미책정 차단 5 · 별지 8 · 임시저장 7
  • 소스 가드: 발주서 13 · 임시저장 8 · A4/팝업 9
  • 가드는 전부 버그를 되살려 빨간불을 확인했다 — 계약 단가 위조 · EV& 작업 혼입 ·
    탑 높이 무시 · 한 장 제약 부활 · 삭제 경로 제거 · 도어가 누락 · 탁송료 누락
  • 브라우저에서 한국어·영어 양쪽 확인
  • 로컬 650/650 초록 (이 브랜치 앞부분에서 낡은 검사 6개를 지금 의도로 고쳐 썼다)

새로 받은 사람의 로컬에서 통합시험 6개가 늘 빨갰다. 빨간불이 일상이 되면
**진짜 회귀가 그 사이에 섞여도 아무도 모른다.** 원인은 셋이었다.

**1. 시험이 남의 DB 에만 있는 계정에 기대고 있었다**
`db/seed/user.csv` 는 머리글만 있는 빈 파일이다(실계정 비커밋 — 그게 맞다).
그래서 시드를 돌려도 `sales1@evnsolution.com` 은 로컬에 생기지 않는다.
인증은 시험용 우회로 통과하지만 **소유권 검사는 행이 없으면 막힌다** —
영업이 만든 견적에 `sales_user_id` 가 붙지 않아 「자기 견적」이 되지 못했다.
`ensureFixtureUsers()` 로 시험이 스스로 마련하게 했다.
⚠️ **있는 계정은 건드리지 않는다** — 같은 주소가 실제로 쓰일 수 있어 없을 때만 만든다.

**2. 확정·배정이 갈라졌는데 옛 검사가 남아 있었다** (admin-orders 3개)
예전엔 `confirm` 하나가 확정·배정·주문생성을 다 했다. 지금은
`confirm`=영업의 견적 확정, `assign`=관리자의 제작 배정(계약완료에서만)으로 갈라져 있다.
서명 전 선배정을 허용하면 계약이 깨졌을 때 이미 제작에 들어가 있을 수 있어서다.
지우지 않고 **그 경계를 지키도록** 고쳐 썼다 — 확정은 주문을 만들지 않는다,
확정만으로는 배정되지 않는다, 계약완료라야 열린다.

**3. 못박은 숫자가 단가를 따라가지 못했다** (sales-api 2개)
`real_price = 46,471,818` 은 이 라우트가 처음 생기던 때의 단가표·옵션코드에서 나온 값이다.
그 뒤 단가는 견적서 Ver1.21 로 재구성됐고(코드 이름까지 바뀜) 등록비도 달라졌다
(증지대 2,500→2,000 · 번호판 25,000→28,000 · 등록대행 50,000→30,000 · 탁송료 179,000→188,000).
단가는 **바뀌라고 DB 에 둔 값**이라(CLAUDE.md) 관리자가 고칠 때마다 이 검사가 빨개진다.

엔진이 엑셀과 맞는지는 `shared/pricing` 이 **얼어붙은 픽스처**로 이미 지킨다
(범석환 ₩32,013,860 · 총견적서 — 입력까지 고정이라 단가가 바뀌어도 흔들리지 않는다).
그래서 여기서는 **이 길이 그 엔진과 같은 답을 내는가**를 본다 — 공급가가 DB 단가 합과
같은가(도어 규칙 포함), 부가세·보조금·환급·실구매가가 서로 들어맞는가, 빠진 비용은 없는가.
도어 규칙은 코드를 베끼지 않고 CLAUDE.md 에 적힌 규칙을 적었다.

「BODY_DRY → 422 unsupported」도 마찬가지다. 일반탑은 그때 단가가 없어 거부했는데
지금은 있다 — **DRY 도 제대로 값이 매겨지는가**로 바꿨다.

로컬 단가는 초기 더미(40,000,000 같은 반올림값)로 남아 있어 시드로 맞췄다.
검사는 전부 **버그를 되살려** 빨간불이 뜨는 것을 확인했다(도어가 누락 · 탁송료 누락).
배정 전에는 주문이 아직 없어 미리보기에 `orderId=0` 이 들어온다. 그대로 찍으니
문서번호 칸에 「주문 #0」이 나와 **0번이라는 문서가 있는 것처럼** 읽혔다.
번호는 배정하는 순간 붙으므로 그때까지는 「(배정 시 발급)」이라고 말한다.

두 장(발주서·별지)이 각자 문자열을 만들던 것도 한 곳으로 모았다 — 따로 만들면
한쪽만 고쳐져 같은 주문의 두 장이 다른 번호를 말할 수 있다.
**1. 특장사 공급가 표** — 계약서 [별첨3] 발주서 양식이 요구하던 표다.
우리가 특장사에 **지급하는** 금액이라, 고객 견적가(`option_price`)와 다른 축이다.

- `maker_price` 표 신설 — 근거는 특장사별 기본거래계약서 [별첨1] 단가표
  (브레인특장 2026-07-29: 저상 6,700,000 · 표준 6,900,000 + 옵션 3종).
- `work_by` 로 갈린다 — **MAKER**(특장사가 작업 → 발주서에 실린다) ·
  **EVN**(인도 뒤 EV& 가 직접 → 실리지 않는다). 안 시킨 일의 대금을 청구받지 않는다.
- 계약에 있는 항목은 그 값으로 **고정**되어 자동으로 채워지고, 계약에 없는 항목은
  관리자가 발주서에서 직접 적는다. **0원으로 채워 넣지 않는다** — 특장사가
  「무상으로 해 주기로 한 일」로 읽는다.
- 계약 줄은 **서버가 다시 만든다.** 화면의 「고칠 수 없음」은 타이핑만 막을 뿐이라,
  API 를 직접 부르면 6,700,000 짜리를 1 원으로 적어 보낼 수 있었다.
- 배정하는 순간의 표를 `order.po_lines` 에 **얼려 둔다.** 나중에 단가표를 고쳤다고
  이미 나간 발주서 금액이 따라 바뀌면, 특장사가 받은 종이와 화면이 어긋난다.

**2. 단가가 없는 사양은 0원이 아니다**

`priceMap[code] ?? 0` 이라 단가표에 **행이 없는** 사양도 0원으로 계산돼 견적이 그대로
저장됐다. 운영 DB 에는 미닫이(`DOPT_*_COUPANG`)와 저상 스포일러(`SPL_LOW`) 행이 없어,
고르면 그 값이 **0원으로 고객에게 나가고 있었다.**

0원과 「정해지지 않음」은 다른 말이다. 0원은 0으로 정해 둔 것이고(계약상 무상 등),
행이 없는 것은 아직 아무도 값을 정하지 않은 것이다. 앞은 팔 수 있고 뒤는 팔 수 없다.
화면과 서버가 **같은 함수**(`makePriceLookup`)로 판단하고, 무엇이 미책정인지
**고른 값의 이름으로** 말한다(「미닫이」 — 내부 코드는 아무도 못 고친다).

**3. 발주서를 A4 한 장에 가두지 않는다**

한 장에 맞추느라 내용이 길수록 통째로 축소돼 글씨가 작아졌고, 그래서 적을 수 있는
분량을 곳곳에서 막아야 했다(비고 4줄 · 별지 30줄 · 커스텀은 아예 2페이지로 분리).
**분량이 계속 골칫거리**가 됐다는 제보로 세로 제약을 걷어냈다.

⚠️ 양식은 그대로다. 폭은 늘 `BASE_W` 로 조판하고 화면 폭에 맞춰 축소한다 —
   폭까지 풀었더니 좁은 화면에서 표 머리글이 겹치고 값이 잘렸다. 푼 것은 **높이뿐**이다.
- 별지(2페이지)·장 넘기기 제거. 커스텀 요청사항은 서류 **맨 아래 칸**이 되고,
  「+ 커스텀 주문내용 추가」로 열고 「커스텀 요청사항 삭제」로 되돌린다(내용이 있으면 한 번 묻는다).
- 확인 체크는 그 칸 **바로 아래**로 옮겼다. 읽고 나서 누르는 순서가 자리로 드러난다.
- 표제부 값도 「브레…」로 자르지 않는다 — 서류에서 이름이 잘리면 어느 회사인지 모른다.

**4. 용어 통일** — 숨긴 것을 도로 꺼내는 조작이 견적·고객에서 각각 「다시 보이기」였다.
둘 다 **되돌리기**로 모았다. 같은 일에 이름이 둘이면 다른 기능처럼 읽힌다.

**5. 병렬 시험 flake** — `role-toggle-freeze` 가 자기 임시 모듈을 지우는 사이, 다른
시험이 「모든 모듈」을 권한으로 주면서 외래키에 걸려 파일 전체가 가끔 빨개졌다.
모듈을 지우지 않고 **꺼 두는** 것으로 경합 자체를 없앴다(5회 연속 전부 초록).

검사는 전부 **버그를 되살려** 빨간불을 확인했다 — 계약 단가 위조 · EV& 작업 혼입 ·
탑 높이 무시 · 한 장 제약 부활 · 삭제 경로 제거.
migration 이 `maker_price_org_group_value_top_key` 로 만들었는데 Prisma 는 `@@unique`
에서 `maker_price_maker_org_id_group_code_value_code_top_code_key` 를 만든다.
이름이 다르면 배포 전 검증이 **「인덱스가 바뀌었다」는 drift 로 잡는다** — 실제로 CI 에서 걸렸다.

손으로 쓴 SQL 과 스키마가 어긋나는 것을 배포 전에 잡으라고 있는 검사다.
막지 않았으면 운영 배포가 그 자리에서 멈췄을 것이다.
@Jay722J
Jay722J merged commit 9915a27 into main Sep 8, 2026
4 checks passed
@Jay722J
Jay722J deleted the fix/local-seed-and-stale-tests branch September 8, 2026 08:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

발주서: 특장사 공급가 표 · 단가 미책정 사양 차단 · 한 장 제약 해제

1 participant