고이장례연구소
경력근거: 파트너와 주고받는 금액이 어긋나지 않도록 정산 시스템을 설계하고 운영합니다.
※ 실제 산출물이 아닌 AI 예측 시안입니다.
고이는 숨김 없는 장례를 내걸고, 장례 업계를 가격 정보 불일치·과도한 비용·계약 불이행·추가금 요구로 소비자 피해가 빈번한 시장이라고 적습니다. 백엔드팀 소개는 상담 시스템과 견적 로직, 출동 단계의 장례식장·파트너 매칭, 파트너와 주고받는 금액이 어긋나지 않게 하는 정산 시스템을 한 흐름으로 나열하고, 상담사와 운영팀이 쓰는 어드민도 같은 팀이 만든다고 밝힙니다. 견적 로직과 정산 시스템을 같은 팀이 만드는 회사라서, 정산서 한 줄을 상담 때 동의한 견적까지 거슬러 견주는 판정이 그 팀 안에서 완결됩니다. 회사 브리프는 후불제 장례에서 쓰지 않은 품목을 환불한다고 적고 있어 판정 결과가 지급 보류와 유가족 환불 두 쪽으로 나뉩니다. 유가족 앱의 품목 수량 확인과 장례지도사·도우미 출동 체크인은 공고에 나오지 않는 기록이라, 지원자가 확인 화면과 체크인 API 를 직접 만들고 재생 스크립트가 장례 한 건씩 그 API 를 불러 기록을 쌓습니다. 이 포트폴리오는 네 가지 기록을 장례 번호와 품목으로 묶어 지급 보류와 환불을 계산하는 Django 백엔드와 어드민을 보여줍니다. 장례 실무나 상조 규제 전문성은 주장하지 않습니다.
백엔드팀 소개는 고객이 고이를 찾는 순간부터 장례를 마치고 정산이 끝나는 순간까지를 팀이 만들고 운영한다고 적고, 정산 단계를 파트너와 주고받는 금액이 어긋나지 않게 하는 일로 정의합니다. 정산서 판정은 그 단계 한가운데에 놓이는 기능입니다.
공고는 장례 업계의 소비자 피해로 가격 정보 불일치, 과도한 비용, 추가금 요구를 들고, 고이를 그 구조적 비효율을 없애는 회사로 소개합니다. 유가족이 확인하지 않은 품목과 정찰 단가를 넘는 청구를 지급 전에 멈추는 일이 그 약속을 운영에서 지키는 자리입니다.
주요업무에 사내 어드민과 운영 시스템의 기획·개발·운영, 반복되는 운영 업무 개선, 데이터 정합성 같은 기술 리스크 관리가 나란히 적혀 있습니다. 정산 담당자가 정산서를 줄마다 손으로 맞춰 보던 일을 어드민의 판정으로 옮기는 이 포트폴리오가 세 업무에 모두 걸칩니다.
장례가 끝나고 현장 파트너가 정산서를 올리면 이 화면은 정산서 줄마다 상담 때 유가족이 동의한 견적, 장례 중 유가족이 앱에서 확인한 최종 수량, 장례지도사·도우미의 출동 체크인을 맞대어 지급해도 되는 줄인지와 유가족에게 돌려줄 금액이 생겼는지를 판정합니다. 견적과 앱 확인 어디에도 없는 품목이나 확인 수량을 넘긴 수량은 추가 품목으로, 견적 단가를 넘긴 단가는 정찰 가격 초과로, 체크인으로 센 인일보다 많은 인력은 출동 초과로 잡고 그만큼을 지급 보류 금액으로 냅니다. 환불은 유가족이 앱에서 줄인 수량이나 체크인으로 센 인일이 견적에 못 미칠 때만 계산하고, 파트너가 정산서에 덜 적은 수량은 환불 근거로 쓰지 않습니다. 정산 담당자는 걸린 줄을 파트너에게 되묻고, 상담사는 같은 화면의 환불 금액으로 유가족 환불을 처리합니다.
고이 운영팀의 정산 담당자와 후불제 장례를 맡은 상담사. 장례가 끝난 뒤 현장 파트너가 올린 정산서를 같은 어드민 화면에서 보고, 걸린 줄은 파트너에게 되묻고 돌려줄 금액은 유가족 환불로 처리한다
후불제 장례의 정산 담당자는 정산서를 견적서와 나란히 놓고 줄을 하나씩 맞춰 보지만, 장례 중에 유가족이 앱에서 바꾼 수량과 실제로 출동한 인원은 다른 화면에 있어 세 기록을 한 번에 견주기 어렵습니다. 그 사이 유가족이 동의하지 않은 품목이 정산서에 섞여 그대로 지급되고, 유가족이 줄인 식사 수량의 환불은 상담사에게 전해지지 않습니다. 정산서 줄을 장례 번호와 품목 기준으로 견적·앱 확인·체크인 세 기록에 묶어 판정하고 지급 보류와 환불 금액을 같은 화면에 내면, 정산 담당자와 상담사가 각자 처리할 줄을 그 자리에서 가져갑니다.
지원자가 만든 견적 동의·유가족 앱 확인·출동 체크인·정산서 업로드 API 를 재생 스크립트가 장례 한 건씩 호출해 네 표를 채우고, Django 어드민의 정산서 판정 화면이 줄마다 다섯 판정과 지급 보류·환불 금액을 보여줍니다. 정산 담당자는 걸린 줄을 판정별로 보고, 상담사는 같은 화면에서 환불 금액이 생긴 장례를 고릅니다. 모든 행이 API 호출의 결과라 사람이 손으로 적은 표는 입력에 없습니다.
재생 스크립트로 장례 40건을 새로 만들며 정산서 줄 160개에 어긋남(확인 없는 품목·단가 인상·출동보다 많은 인일·유가족 감량·파트너의 덜 청구)을 심고, 설계한 판정으로 돌아온 줄을 재생 시험 탭에 셉니다. 2주차부터 Docker 이미지를 GitHub Actions 로 AWS 에 배포하고, 정산서가 올라오면 Celery 작업이 판정을 계산하며 실패는 Sentry 에 남습니다.
Claude Haiku 4.5 가 걸린 줄의 판정과 근거 수치로 파트너에게 보낼 문의 초안을 쓰고, 정산 담당자가 고쳐서 기존 연락 창구로 보냅니다. 판정 결과는 BigQuery 로 적재해 Holistics 에서 파트너별 지급 보류 금액과 걸린 줄 비율을 주 단위로 보고, 새로 걸린 줄은 Slack 으로 정산 담당자에게 알립니다.
10월 첫째 주에 파트너들이 올린 정산서를 정산 담당자가 판정에 돌리면 장례 6건의 판정 13줄 가운데 8줄이 걸리고 화면 위 합계에 지급 보류 1,164,000원과 유가족 환불 710,000원이 찍힙니다. 걸린 줄 맨 앞은 견적에도 앱 확인에도 없는 F-24031 수의(고급) 450,000원과 정찰 600,000원보다 80,000원 비싼 F-24040 제단 꽃장식입니다.
상담사가 F-24044 식사를 열면 유가족이 앱에서 50인분을 30인분으로 줄인 기록과 환불 360,000원이 보이고, F-24046 식사는 정산서가 확인 40인분보다 8인분 적은 32인분이어도 환불이 잡히지 않은 채 파트너에게 빠뜨린 청구인지 묻는 메모만 붙어 있습니다.
앱 확인이 비어 있는 F-24046 수의는 견적 1벌로 견줘 그대로 지급되고, 유가족이 60인분으로 늘려 확인한 F-24040 식사는 넘긴 10인분만 보류되며, 자정을 넘긴 SH-219 의 근무는 이틀로 세어져 F-24038 도우미의 대표 판정은 출동 초과, 같이 걸린 환불은 1인일로 나옵니다.
재생 시험을 돌리면 장례 40건의 정산서 줄 160개마다 심어 둔 어긋남과 화면의 판정이 나란히 기록되고, 정산 담당자는 둘이 다른 줄만 골라 봅니다. 단, 어긋남과 데이터를 모두 작성자가 만든 합성 기준이라 성능 검증이 아니라 데모 조건 동작입니다.
위 수치는 재생 스크립트가 만든 더미데이터 자가채점이며, 판정이 실제로 잘못 나갈 지급과 놓친 환불을 잡는지는 고이의 실제 견적·유가족 확인·출동 기록과 파트너 정산서가 있어야 측정됩니다. 이 포트폴리오는 그 검증을 바로 돌릴 수 있는 상태로 만듭니다.
수치는 합성 데이터 자가채점이라 성능 증명이 아닙니다. 지급 보류 1,164,000원과 환불 710,000원은 재생 스크립트에 제가 넣은 어긋남 여덟 줄을 판정이 다시 찾아낸 합계라, 고이 파트너들이 실제로 어떤 품목을 얼마나 자주 덧붙여 청구하는지는 이 합계에 담겨 있지 않습니다.
식이 틀려도 금액이 맞아 보이는 줄을 골라 넣었습니다. 정산서만 8인분 적은 F-24046 식사는 환불 식에 정산서 수량이 섞이면 144,000원 환불로, 앱 확인이 빈 F-24046 수의는 빈 값을 0 으로 읽으면 300,000원 보류로 바뀝니다. 자정을 넘긴 SH-219 근무를 건수로 세면 F-24038 도우미 보류가 300,000원으로 늘고, 점심·저녁 두 줄로 나뉜 F-24050 식사는 줄을 합치지 않으면 아예 걸리지 않습니다.
인일을 날짜 단위로 세므로 자정을 넘긴 3시간 반 근무가 이틀로 잡힙니다. F-24038 도우미의 환불이 1인일에 그친 이유가 이것이고 근무 시간으로 세면 2인일입니다. 어느 셈법이 유가족과 파트너 모두에게 맞는지는 운영팀이 정할 일이라 셈법을 설정으로 빼 둡니다.
정산서에 덜 적힌 수량을 환불 근거로 쓰지 않는 것은 파트너가 청구를 빠뜨렸을 가능성을 지키려는 선택입니다. 파트너가 실제로 덜 쓴 것을 정직하게 적은 경우에도 유가족 확인이나 체크인이 그 감량을 받쳐 주지 않으면 환불이 나가지 않으므로, 그런 줄에는 메모를 달아 상담사가 유가족에게 다시 확인하게 둡니다.
현장에서 유가족이 앱으로 더한 품목은 견적에 단가가 없어 정찰 가격 판정을 하지 않습니다. 정찰 가격표를 별도 표로 받아 견줄지는 도입 단계 과제로 둡니다.
유가족 앱 확인 기록과 출동 체크인 기록은 공고에 나오지 않는 가정입니다. 고이의 실제 운영이 확인을 전화로 받거나 체크인을 남기지 않는다면 환불 판정의 근거가 사라지므로, 도입 전에 두 기록이 어디에 어떤 모양으로 남는지부터 봅니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
상담 단계에서 유가족이 동의한 견적의 품목별 단가와 수량입니다. 정찰 가격 판정의 기준 단가이고, 앱 확인이 비어 있는 품목에서는 확인 수량을 대신합니다. 장례지도사·도우미는 인일 단위로 들어 있습니다.
| funeral_id | item_code | item_name | unit | unit_price | qty |
|---|---|---|---|---|---|
| F-24031 | SHROUD-B | 수의(기본) | 벌 | 300,000 | 1 |
| F-24031 | STF-DIR | 장례지도사 | 인일 | 250,000 | 3 |
| F-24038 | STF-DIR | 장례지도사 | 인일 | 250,000 | 3 |
| F-24038 | STF-HLP | 도우미 | 인일 | 150,000 | 6 |
| F-24038 | COFFIN-B | 관(오동나무) | 개 | 400,000 | 1 |
아래 JSON Schema 에 맞는 견적 품목 행을 더 만들어 주세요. {"type":"object","properties":{"funeral_id":{"type":"string"},"item_code":{"type":"string"},"item_name":{"type":"string"},"unit":{"type":"string"},"unit_price":{"type":"integer","minimum":0},"qty":{"type":"integer","minimum":0}},"required":["funeral_id","item_code","item_name","unit","unit_price","qty"]} 쉬운 데이터만 만들지 말 것 — 다음 경우를 의도적으로 섞으세요: (1) 장례지도사(STF-DIR)·도우미(STF-HLP)를 인일로 담은 인력 줄, (2) 식사처럼 수십 단위로 줄거나 늘기 쉬운 품목, (3) 같은 장례에 같은 품목이 한 줄만 있는 정상 견적. 모든 행은 위 스키마를 통과해야 하고, 형식을 깨뜨리는 시도는 따로 3건만 표시해 두세요.{"type":"object","properties":{"funeral_id":{"type":"string"},"item_code":{"type":"string"},"item_name":{"type":"string"},"unit":{"type":"string"},"unit_price":{"type":"integer","minimum":0},"qty":{"type":"integer","minimum":0}},"required":["funeral_id","item_code","item_name","unit","unit_price","qty"]}장례 중 유가족이 앱에서 확인한 품목별 최종 수량입니다. 견적보다 늘린 수량(F-24040 식사 60인분)과 줄인 수량(F-24044 식사 30인분), 견적에 없던 현장 추가 품목, 확인하지 않아 수량이 비어 있는 품목(F-24046 수의)이 섞여 있습니다.
| funeral_id | item_code | qty | confirmed_at |
|---|---|---|---|
| F-24031 | SHROUD-B | 1 | 2026-10-01T15:10:00+09:00 |
| F-24038 | COFFIN-B | 1 | 2026-10-03T13:40:00+09:00 |
| F-24040 | ALTAR-FL | 1 | 2026-10-04T10:05:00+09:00 |
| F-24040 | MEAL | 60 | 2026-10-05T18:30:00+09:00 |
| F-24044 | MEAL | 30 | 2026-10-06T19:20:00+09:00 |
아래 JSON Schema 에 맞는 유가족 앱 확인 행을 더 만들어 주세요. {"type":"object","properties":{"funeral_id":{"type":"string"},"item_code":{"type":"string"},"qty":{"type":["integer","null"],"minimum":0},"confirmed_at":{"type":["string","null"],"format":"date-time"}},"required":["funeral_id","item_code","qty","confirmed_at"]} 쉬운 데이터만 만들지 말 것 — 다음 경우를 의도적으로 섞으세요: (1) qty 와 confirmed_at 이 둘 다 null 인 미확인 품목, (2) 견적보다 줄인 수량과 늘린 수량, (3) qty 가 0 인 품목, (4) 견적에 없는 item_code 를 현장에서 더한 행. 모든 행은 위 스키마를 통과해야 하고, 형식을 깨뜨리는 시도는 따로 3건만 표시해 두세요.{"type":"object","properties":{"funeral_id":{"type":"string"},"item_code":{"type":"string"},"qty":{"type":["integer","null"],"minimum":0},"confirmed_at":{"type":["string","null"],"format":"date-time"}},"required":["funeral_id","item_code","qty","confirmed_at"]}장례지도사·도우미가 출동해 남긴 날짜별 체크인·체크아웃 시각입니다. 판정은 (staff_id, 날짜) 짝을 한 인일로 세고, 자정을 넘긴 근무(F-24038 SH-219)는 걸친 날짜마다 인일이 됩니다.
| funeral_id | staff_id | staff_role | checkin_at | checkout_at |
|---|---|---|---|---|
| F-24031 | SD-101 | 장례지도사 | 2026-10-01T08:00:00+09:00 | 2026-10-01T21:00:00+09:00 |
| F-24031 | SD-101 | 장례지도사 | 2026-10-02T08:30:00+09:00 | 2026-10-02T20:30:00+09:00 |
| F-24031 | SD-101 | 장례지도사 | 2026-10-03T07:00:00+09:00 | 2026-10-03T14:00:00+09:00 |
| F-24038 | SD-114 | 장례지도사 | 2026-10-03T09:00:00+09:00 | 2026-10-03T21:00:00+09:00 |
| F-24038 | SD-114 | 장례지도사 | 2026-10-04T08:30:00+09:00 | 2026-10-04T20:00:00+09:00 |
아래 JSON Schema 에 맞는 출동 체크인 행을 더 만들어 주세요. {"type":"object","properties":{"funeral_id":{"type":"string"},"staff_id":{"type":"string"},"staff_role":{"type":"string","enum":["장례지도사","도우미"]},"checkin_at":{"type":"string","format":"date-time"},"checkout_at":{"type":"string","format":"date-time"}},"required":["funeral_id","staff_id","staff_role","checkin_at","checkout_at"]} 쉬운 데이터만 만들지 말 것 — 다음 경우를 의도적으로 섞으세요: (1) 22시 이후 체크인해 다음 날 새벽 체크아웃한 근무, (2) 견적 인일보다 적게 출동한 장례, (3) 한 사람이 사흘 연속 출동한 장례. 시각은 모두 +09:00 을 붙이고, 모든 행은 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 따로 3건만 표시해 두세요.{"type":"object","properties":{"funeral_id":{"type":"string"},"staff_id":{"type":"string"},"staff_role":{"type":"string","enum":["장례지도사","도우미"]},"checkin_at":{"type":"string","format":"date-time"},"checkout_at":{"type":"string","format":"date-time"}},"required":["funeral_id","staff_id","staff_role","checkin_at","checkout_at"]}현장 파트너가 장례가 끝난 뒤 올린 정산서 줄입니다. 견적에 없는 고급 수의, 정찰보다 비싼 단가, 체크인보다 많은 인일, 확인 수량을 넘긴 식사, 확인보다 덜 적은 식사, 점심·저녁 두 줄로 나눠 적어 한 줄씩은 확인 수량 안이지만 합치면 넘는 식사(F-24050)가 들어 있습니다.
| invoice_line_no | funeral_id | partner_id | item_code | unit_price | qty | memo |
|---|---|---|---|---|---|---|
| INV-24031-01 | F-24031 | PT-0412 | SHROUD-P | 450,000 | 1 | 유가족 요청으로 고급 수의 사용 |
| INV-24031-02 | F-24031 | PT-0412 | STF-DIR | 250,000 | 3 | — |
| INV-24038-01 | F-24038 | PT-0377 | STF-DIR | 250,000 | 4 | — |
| INV-24038-02 | F-24038 | PT-0377 | STF-HLP | 150,000 | 6 | 도우미 2명 3일 |
| INV-24038-03 | F-24038 | PT-0377 | COFFIN-B | 400,000 | 1 | — |
아래 JSON Schema 에 맞는 파트너 정산서 줄을 더 만들어 주세요. {"type":"object","properties":{"invoice_line_no":{"type":"string"},"funeral_id":{"type":"string"},"partner_id":{"type":"string"},"item_code":{"type":"string"},"unit_price":{"type":"integer","minimum":0},"qty":{"type":"integer","minimum":0},"memo":{"type":["string","null"]}},"required":["invoice_line_no","funeral_id","partner_id","item_code","unit_price","qty","memo"]} 쉬운 데이터만 만들지 말 것 — 다음 경우를 의도적으로 섞으세요: (1) 견적과 앱 확인 어디에도 없는 item_code, (2) 견적 단가보다 비싼 unit_price, (3) 체크인 인일보다 많은 인력 qty, (4) 유가족 확인 수량보다 적게 적은 qty, (5) 같은 품목을 두 줄로 나눠 적은 정산서. 모든 행은 위 스키마를 통과해야 하고, 형식을 깨뜨리는 시도는 따로 3건만 표시해 두세요.{"type":"object","properties":{"invoice_line_no":{"type":"string"},"funeral_id":{"type":"string"},"partner_id":{"type":"string"},"item_code":{"type":"string"},"unit_price":{"type":"integer","minimum":0},"qty":{"type":"integer","minimum":0},"memo":{"type":["string","null"]}},"required":["invoice_line_no","funeral_id","partner_id","item_code","unit_price","qty","memo"]}| Python | 판정 계산과 장례 한 건의 흐름을 API 로 재생하는 스크립트의 언어입니다. |
| Django | 정산 담당자와 상담사가 쓰는 어드민입니다. 판정별 줄 목록과 펼침 패널, 주차·파트너 필터를 둡니다. |
| Django REST Framework | 견적 동의, 유가족 앱 확인, 출동 체크인, 정산서 업로드 네 API 입니다. 재생 스크립트가 이 API 로 네 표를 채웁니다. |
| PostgreSQL | 네 표를 담고 장례 번호와 품목으로 정산서 줄을 합쳐 판정 입력을 만듭니다. |
| Celery | 2주차부터 정산서 업로드 뒤 판정 계산을 비동기 작업으로 돌립니다. |
| Redis | Celery 작업 브로커입니다. |
| AWS | 2주차부터 어드민과 API 를 배포하고 정산서 원본 파일을 S3 에 남깁니다. |
| Docker | 어드민·API·Celery 워커를 같은 이미지로 묶습니다. |
| GitHub Actions | 덜 청구한 줄·자정을 넘긴 근무·비어 있는 앱 확인 테스트가 통과해야 AWS 로 배포합니다. |
| Sentry | 판정 작업이 실패하면 그 정산서의 장례 번호와 오류를 남깁니다. |
| BigQuery | 3주차에 판정 결과를 주 단위로 적재합니다. |
| Holistics | 파트너별 지급 보류 금액과 걸린 줄 비율 추이를 봅니다. |
| Slack | 새로 걸린 줄이 생기면 정산 담당자 채널에 알립니다. |
| Claude Code | API 와 판정 식 구현을 맡기고, 결과 구조와 테스트를 직접 검토해 받아들입니다. |
| Claude Haiku 4.5 | 3주차에 걸린 줄마다 파트너 문의 초안을 씁니다. |
✗ fail → 덜 청구한 줄이 환불로 잡히면 환불 식에 정산서 수량이 섞인 것이라, 환불 식의 입력을 유가족 확인과 체크인 두 표로만 좁히고 다시 돌립니다.
✓ pass → 그대로 멈춰도 정산 담당자가 이번 주 지급을 이 화면으로 결정하는 완결품입니다. 더 키우려면 심어 둔 어긋남이 판정으로 돌아오는지 장례 40건으로 세고 AWS 로 배포합니다.
✗ fail → 어긋난 줄이 인일 셈에 몰려 있으면 체크인 시각의 시간대 처리부터 고치고, 문의 초안은 미룹니다.
✓ pass → 여기까지도 완결입니다. 더 키우려면 파트너 문의 초안과 파트너별 보류 추이로 넓힙니다.
위 예측 UI 는 3주차까지 완주했을 때의 화면입니다. 1주차에 실제로 만드는 것은 아래 표시된 화면 하나입니다.
verdict사유 라벨 · funeral_id + 한 줄 요약(근거 수치 포함)화면·코드·면접에서 이 단어를 그대로 씁니다. 키는 출력 스키마의 enum 과 같습니다.
확인한 내역대로 청구함확인대로 청구유가족 확인 없는 추가 품목확인 없는 추가정찰 가격보다 비싸게 청구함정찰가 초과실제 출동보다 많은 인력을 청구함출동보다 많은 인력쓰지 않아 유가족에게 돌려줄 품목유가족 환불파트너 정산서 판정입니다. 장례 한 건의 품목 하나(같은 품목의 정산서 줄은 합치고, 정산서에 없더라도 환불이 생긴 품목은 넣습니다)가 다섯 판정 중 어디에 서는지와 지급 보류·환불 금액, 계산에 쓴 견적·앱 확인·체크인 수치를 남기는 형식입니다. summary 는 규칙이 수치를 채우는 고정 문형입니다.
{"type":"object","properties":{"funeral_id":{"type":"string"},"item_code":{"type":"string"},"verdict":{"type":"string","enum":["확인한 내역대로 청구함","유가족 확인 없는 추가 품목","정찰 가격보다 비싸게 청구함","실제 출동보다 많은 인력을 청구함","쓰지 않아 유가족에게 돌려줄 품목"]},"other_verdicts":{"type":"array","items":{"type":"string","enum":["확인한 내역대로 청구함","유가족 확인 없는 추가 품목","정찰 가격보다 비싸게 청구함","실제 출동보다 많은 인력을 청구함","쓰지 않아 유가족에게 돌려줄 품목"]},"description":"대표 판정 말고 같은 줄에 같이 걸린 판정입니다."},"item_name":{"type":"string"},"unit":{"type":"string"},"invoice_line_nos":{"type":"array","items":{"type":"string"},"description":"이 품목으로 합친 정산서 줄 번호입니다. 정산서에 없는 환불 품목이면 빈 배열입니다."},"billed_qty":{"type":"integer","minimum":0,"description":"같은 funeral_id·item_code 의 partner_invoice_lines.qty 합입니다."},"billed_unit_price":{"type":["integer","null"],"minimum":0},"billed_krw":{"type":"integer","minimum":0,"description":"정산서 줄마다 qty × unit_price 를 더한 값입니다."},"quote_qty":{"type":["integer","null"],"minimum":0},"quote_unit_price":{"type":["integer","null"],"minimum":0},"family_confirmed_qty":{"type":["integer","null"],"minimum":0,"description":"family_confirmations.qty 원문입니다. 기록이 없거나 비어 있으면 null 입니다."},"confirmed_qty":{"type":"integer","minimum":0,"description":"판정에 쓴 확인 수량입니다. family_confirmed_qty 가 있으면 그 값, 없으면 quote_qty, 둘 다 없으면 0 입니다."},"counted_person_days":{"type":["integer","null"],"minimum":0,"description":"인력 줄만 채웁니다. item_code 에 맞는 staff_role 의 staff_checkins 에서 checkin_at 날짜부터 checkout_at 날짜까지 걸친 (staff_id, 날짜) 짝의 개수를 한국 시각으로 셉니다."},"hold_krw":{"type":"integer","minimum":0,"description":"지급 보류 금액입니다. billed_krw − min(billed_qty, 지급 기준 수량) × min(billed_unit_price, quote_unit_price) 이고, 지급 기준 수량은 confirmed_qty(인력 줄은 confirmed_qty 와 counted_person_days 중 작은 값, 견적과 확인 어디에도 없는 품목은 0)입니다. quote_unit_price 가 없으면 billed_unit_price 를 씁니다."},"refund_krw":{"type":"integer","minimum":0,"description":"유가족 환불 금액입니다. 사용 수량(family_confirmed_qty, 없으면 quote_qty / 인력 줄은 counted_person_days)이 quote_qty 보다 적을 때 (quote_qty − 사용 수량) × quote_unit_price 이고, partner_invoice_lines.qty 는 이 식에 들어가지 않습니다."},"evidence":{"type":"array","items":{"type":"object","properties":{"table":{"type":"string","enum":["quote_items","family_confirmations","staff_checkins","partner_invoice_lines"]},"field":{"type":"string"},"agg":{"type":"string","enum":["count","sum","avg","min","max"]},"value":{"type":["string","integer","null"]}},"required":["table","field","value"]}},"summary":{"type":"string"},"note":{"type":["string","null"]}},"required":["funeral_id","item_code","verdict","other_verdicts","item_name","unit","invoice_line_nos","billed_qty","billed_unit_price","billed_krw","quote_qty","quote_unit_price","family_confirmed_qty","confirmed_qty","counted_person_days","hold_krw","refund_krw","evidence","summary","note"]}[
{
"funeral_id": "F-24031",
"item_code": "SHROUD-P",
"verdict": "유가족 확인 없는 추가 품목",
"other_verdicts": [],
"item_name": "수의(고급)",
"unit": "벌",
"invoice_line_nos": [
"INV-24031-01"
],
"billed_qty": 1,
"billed_unit_price": 450000,
"billed_krw": 450000,
"quote_qty": null,
"quote_unit_price": null,
"family_confirmed_qty": null,
"confirmed_qty": 0,
"counted_person_days": null,
"hold_krw": 450000,
"refund_krw": 0,
"evidence": [
{
"table": "partner_invoice_lines",
"field": "invoice_line_no",
"value": "INV-24031-01"
},
{
"table": "partner_invoice_lines",
"field": "item_code",
"value": "SHROUD-P"
},
{
"table": "partner_invoice_lines",
"field": "unit_price",
"value": 450000
},
{
"table": "partner_invoice_lines",
"field": "qty",
"value": 1
},
{
"table": "quote_items",
"field": "item_code",
"value": "SHROUD-B"
},
{
"table": "family_confirmations",
"field": "item_code",
"value": "SHROUD-B"
},
{
"table": "family_confirmations",
"field": "qty",
"value": 1
}
],
"summary": "F-24031 의 수의(고급) 1벌 450,000원은 견적에도 유가족 앱 확인에도 없는 품목이라 지급을 보류합니다.",
"note": "견적과 앱 확인에는 수의(기본) 1벌이 있고 그 줄은 정산서에 없습니다. 파트너 메모의 유가족 요청은 앱 기록으로 확인되지 않아 파트너에게 동의 근거를 묻습니다."
},
{
"funeral_id": "F-24040",
"item_code": "MEAL",
"verdict": "유가족 확인 없는 추가 품목",
"other_verdicts": [],
"item_name": "식사",
"unit": "인분",
"invoice_line_nos": [
"INV-24040-02"
],
"billed_qty": 70,
"billed_unit_price": 18000,
"billed_krw": 1260000,
"quote_qty": 50,
"quote_unit_price": 18000,
"family_confirmed_qty": 60,
"confirmed_qty": 60,
"counted_person_days": null,
"hold_krw": 180000,
"refund_krw": 0,
"evidence": [
{
"table": "partner_invoice_lines",
"field": "invoice_line_no",
"value": "INV-24040-02"
},
{
"table": "partner_invoice_lines",
"field": "qty",
"value": 70
},
{
"table": "partner_invoice_lines",
"field": "unit_price",
"value": 18000
},
{
"table": "quote_items",
"field": "qty",
"value": 50
},
{
"table": "family_confirmations",
"field": "qty",
"value": 60
},
{
"table": "family_confirmations",
"field": "confirmed_at",
"value": "2026-10-05T18:30:00+09:00"
}
],
"summary": "F-24040 식사는 유가족이 앱에서 50인분을 60인분으로 늘려 확인했는데 70인분이 청구돼, 넘친 10인분 180,000원의 지급을 보류합니다.",
"note": "견적보다 늘어난 10인분은 유가족이 확인한 추가라 지급 대상이고, 확인 수량을 넘긴 10인분만 확인 없는 추가로 봅니다."
},
{
"funeral_id": "F-24040",
"item_code": "ALTAR-FL",
"verdict": "정찰 가격보다 비싸게 청구함",
"other_verdicts": [],
"item_name": "제단 꽃장식",
"unit": "식",
"invoice_line_nos": [
"INV-24040-01"
],
"billed_qty": 1,
"billed_unit_price": 680000,
"billed_krw": 680000,
"quote_qty": 1,
"quote_unit_price": 600000,
"family_confirmed_qty": 1,
"confirmed_qty": 1,
"counted_person_days": null,
"hold_krw": 80000,
"refund_krw": 0,
"evidence": [
{
"table": "partner_invoice_lines",
"field": "invoice_line_no",
"value": "INV-24040-01"
},
{
"table": "partner_invoice_lines",
"field": "unit_price",
"value": 680000
},
{
"table": "quote_items",
"field": "unit_price",
"value": 600000
},
{
"table": "partner_invoice_lines",
"field": "qty",
"value": 1
},
{
"table": "family_confirmations",
"field": "qty",
"value": 1
}
],
"summary": "F-24040 제단 꽃장식은 견적 정찰 단가 600,000원보다 80,000원 비싼 680,000원으로 청구돼 차액의 지급을 보류합니다.",
"note": "파트너 메모는 꽃 시세 반영이지만 유가족이 동의한 단가는 견적 단가입니다."
},
{
"funeral_id": "F-24038",
"item_code": "STF-HLP",
"verdict": "실제 출동보다 많은 인력을 청구함",
"other_verdicts": [
"쓰지 않아 유가족에게 돌려줄 품목"
],
"item_name": "도우미",
"unit": "인일",
"invoice_line_nos": [
"INV-24038-02"
],
"billed_qty": 6,
"billed_unit_price": 150000,
"billed_krw": 900000,
"quote_qty": 6,
"quote_unit_price": 150000,
"family_confirmed_qty": null,
"confirmed_qty": 6,
"counted_person_days": 5,
"hold_krw": 150000,
"refund_krw": 150000,
"evidence": [
{
"table": "partner_invoice_lines",
"field": "invoice_line_no",
"value": "INV-24038-02"
},
{
"table": "partner_invoice_lines",
"field": "qty",
"value": 6
},
{
"table": "partner_invoice_lines",
"field": "unit_price",
"value": 150000
},
{
"table": "quote_items",
"field": "qty",
"value": 6
},
{
"table": "staff_checkins",
"field": "staff_id",
"value": "SH-207"
},
{
"table": "staff_checkins",
"field": "staff_id",
"value": "SH-219"
},
{
"table": "staff_checkins",
"field": "checkin_at",
"value": "2026-10-04T22:00:00+09:00"
},
{
"table": "staff_checkins",
"field": "checkout_at",
"value": "2026-10-05T01:30:00+09:00"
},
{
"table": "staff_checkins",
"field": "staff_id",
"agg": "count",
"value": 4
}
],
"summary": "F-24038 도우미는 체크인으로 센 5인일보다 많은 6인일이 청구돼 1인일 150,000원의 지급을 보류하고, 견적 6인일 중 쓰지 않은 1인일 150,000원은 유가족에게 돌려줍니다.",
"note": "SH-219 의 10월 4일 22시부터 5일 1시 반까지 근무는 날짜 단위로 세어 이틀이 됐습니다. 근무 시간으로 세면 인일은 4, 환불은 2인일입니다."
},
{
"funeral_id": "F-24044",
"item_code": "MEAL",
"verdict": "쓰지 않아 유가족에게 돌려줄 품목",
"other_verdicts": [],
"item_name": "식사",
"unit": "인분",
"invoice_line_nos": [
"INV-24044-01"
],
"billed_qty": 30,
"billed_unit_price": 18000,
"billed_krw": 540000,
"quote_qty": 50,
"quote_unit_price": 18000,
"family_confirmed_qty": 30,
"confirmed_qty": 30,
"counted_person_days": null,
"hold_krw": 0,
"refund_krw": 360000,
"evidence": [
{
"table": "partner_invoice_lines",
"field": "invoice_line_no",
"value": "INV-24044-01"
},
{
"table": "partner_invoice_lines",
"field": "qty",
"value": 30
},
{
"table": "quote_items",
"field": "qty",
"value": 50
},
{
"table": "quote_items",
"field": "unit_price",
"value": 18000
},
{
"table": "family_confirmations",
"field": "qty",
"value": 30
},
{
"table": "family_confirmations",
"field": "confirmed_at",
"value": "2026-10-06T19:20:00+09:00"
}
],
"summary": "F-24044 식사는 유가족이 앱에서 견적 50인분을 30인분으로 줄여 확인해, 쓰지 않은 20인분 360,000원을 유가족에게 돌려줍니다.",
"note": "파트너는 확인한 30인분만 청구해 지급할 금액은 그대로입니다."
},
{
"funeral_id": "F-24046",
"item_code": "MEAL",
"verdict": "확인한 내역대로 청구함",
"other_verdicts": [],
"item_name": "식사",
"unit": "인분",
"invoice_line_nos": [
"INV-24046-02"
],
"billed_qty": 32,
"billed_unit_price": 18000,
"billed_krw": 576000,
"quote_qty": 40,
"quote_unit_price": 18000,
"family_confirmed_qty": 40,
"confirmed_qty": 40,
"counted_person_days": null,
"hold_krw": 0,
"refund_krw": 0,
"evidence": [
{
"table": "partner_invoice_lines",
"field": "invoice_line_no",
"value": "INV-24046-02"
},
{
"table": "partner_invoice_lines",
"field": "qty",
"value": 32
},
{
"table": "quote_items",
"field": "qty",
"value": 40
},
{
"table": "family_confirmations",
"field": "qty",
"value": 40
},
{
"table": "family_confirmations",
"field": "confirmed_at",
"value": "2026-10-07T17:45:00+09:00"
}
],
"summary": "F-24046 식사는 청구 32인분이 유가족 확인 40인분 안이라 확인한 내역대로 청구한 줄입니다.",
"note": "정산서만 확인보다 8인분 적고 유가족 확인과 견적은 40인분이라 환불 근거가 없습니다. 파트너가 청구를 빠뜨렸는지 묻는 메모만 남깁니다."
},
{
"funeral_id": "F-24046",
"item_code": "SHROUD-B",
"verdict": "확인한 내역대로 청구함",
"other_verdicts": [],
"item_name": "수의(기본)",
"unit": "벌",
"invoice_line_nos": [
"INV-24046-01"
],
"billed_qty": 1,
"billed_unit_price": 300000,
"billed_krw": 300000,
"quote_qty": 1,
"quote_unit_price": 300000,
"family_confirmed_qty": null,
"confirmed_qty": 1,
"counted_person_days": null,
"hold_krw": 0,
"refund_krw": 0,
"evidence": [
{
"table": "partner_invoice_lines",
"field": "invoice_line_no",
"value": "INV-24046-01"
},
{
"table": "partner_invoice_lines",
"field": "qty",
"value": 1
},
{
"table": "partner_invoice_lines",
"field": "unit_price",
"value": 300000
},
{
"table": "quote_items",
"field": "qty",
"value": 1
},
{
"table": "quote_items",
"field": "unit_price",
"value": 300000
},
{
"table": "family_confirmations",
"field": "qty",
"value": null
},
{
"table": "family_confirmations",
"field": "confirmed_at",
"value": null
}
],
"summary": "F-24046 수의(기본)는 유가족 앱 확인이 비어 있어 견적 수량 1벌로 견줬고, 청구가 그 안이라 확인한 내역대로 청구한 줄입니다.",
"note": "앱 확인이 없다는 사실만으로는 감량 근거가 되지 않아 환불을 계산하지 않았습니다."
},
{
"funeral_id": "F-24031",
"item_code": "STF-DIR",
"verdict": "확인한 내역대로 청구함",
"other_verdicts": [],
"item_name": "장례지도사",
"unit": "인일",
"invoice_line_nos": [
"INV-24031-02"
],
"billed_qty": 3,
"billed_unit_price": 250000,
"billed_krw": 750000,
"quote_qty": 3,
"quote_unit_price": 250000,
"family_confirmed_qty": null,
"confirmed_qty": 3,
"counted_person_days": 3,
"hold_krw": 0,
"refund_krw": 0,
"evidence": [
{
"table": "partner_invoice_lines",
"field": "invoice_line_no",
"value": "INV-24031-02"
},
{
"table": "partner_invoice_lines",
"field": "qty",
"value": 3
},
{
"table": "quote_items",
"field": "qty",
"value": 3
},
{
"table": "staff_checkins",
"field": "staff_id",
"value": "SD-101"
},
{
"table": "staff_checkins",
"field": "staff_id",
"agg": "count",
"value": 3
},
{
"table": "staff_checkins",
"field": "checkin_at",
"value": "2026-10-01T08:00:00+09:00"
},
{
"table": "staff_checkins",
"field": "checkout_at",
"value": "2026-10-03T14:00:00+09:00"
}
],
"summary": "F-24031 장례지도사는 청구 3인일이 체크인으로 센 3인일과 견적 3인일에 맞아 확인한 내역대로 청구한 줄입니다.",
"note": null
},
{
"funeral_id": "F-24050",
"item_code": "MEAL",
"verdict": "유가족 확인 없는 추가 품목",
"other_verdicts": [],
"item_name": "식사",
"unit": "인분",
"invoice_line_nos": [
"INV-24050-01",
"INV-24050-02"
],
"billed_qty": 38,
"billed_unit_price": 18000,
"billed_krw": 684000,
"quote_qty": 35,
"quote_unit_price": 18000,
"family_confirmed_qty": 35,
"confirmed_qty": 35,
"counted_person_days": null,
"hold_krw": 54000,
"refund_krw": 0,
"evidence": [
{
"table": "partner_invoice_lines",
"field": "invoice_line_no",
"value": "INV-24050-01"
},
{
"table": "partner_invoice_lines",
"field": "qty",
"value": 20
},
{
"table": "partner_invoice_lines",
"field": "invoice_line_no",
"value": "INV-24050-02"
},
{
"table": "partner_invoice_lines",
"field": "qty",
"value": 18
},
{
"table": "partner_invoice_lines",
"field": "qty",
"agg": "sum",
"value": 38
},
{
"table": "family_confirmations",
"field": "qty",
"value": 35
}
],
"summary": "F-24050 식사는 점심 20인분과 저녁 18인분 두 줄을 합친 38인분이 유가족 확인 35인분을 넘어, 넘친 3인분 54,000원의 지급을 보류합니다.",
"note": "두 줄을 따로 보면 어느 줄도 35인분을 넘지 않아 걸리지 않습니다. 파트너에게 점심과 저녁 인원이 겹쳐 적혔는지 묻습니다."
}
][자동 검증 · 코드가 네 표에서 다시 계산합니다] 1. verdict 와 other_verdicts 의 값이 enum 다섯 값 중 하나인지 확인합니다. 2. evidence 의 field 가 quote_items·family_confirmations·staff_checkins·partner_invoice_lines 에 실재하는 컬럼인지 대조합니다. 3. agg 가 없는 evidence 의 value 가 그 컬럼에 수록된 샘플 행 값 그대로인지 대조합니다. F-24046 수의의 family_confirmations.qty 는 null 그대로 실립니다. 4. billed_qty 는 같은 funeral_id·item_code 의 partner_invoice_lines.qty 합, billed_krw 는 줄마다 qty × unit_price 의 합으로 다시 계산합니다. F-24050 식사는 점심 20인분·저녁 18인분 두 줄이 한 품목 38인분으로 합쳐집니다. 5. confirmed_qty 를 family_confirmations.qty(null 이 아니면) → quote_items.qty → 0 순서로 다시 계산합니다. 6. counted_person_days 를 STF-DIR 은 장례지도사, STF-HLP 는 도우미의 staff_checkins 에서 checkin_at 날짜부터 checkout_at 날짜까지 걸친 (staff_id, 날짜) 짝의 개수로 다시 계산합니다. 7. hold_krw = billed_krw − min(billed_qty, 지급 기준 수량) × min(billed_unit_price, quote_unit_price) 를 다시 계산합니다. 8. refund_krw = (quote_qty − 사용 수량) × quote_unit_price 를 사용 수량이 quote_qty 보다 적을 때만 다시 계산하고, 이 식에 partner_invoice_lines.qty 가 들어가지 않았는지 봅니다. 9. item_code 가 같은 funeral_id 의 quote_items·family_confirmations 어디에도 없거나 billed_qty 가 confirmed_qty 를 넘으면 판정에 유가족 확인 없는 추가 품목이 들어갑니다. 성립 사례는 F-24031 수의(고급)와 F-24040 식사 70인분, 두 줄을 합쳐 확인 35인분을 넘는 F-24050 식사, 불성립 사례는 F-24046 수의(기본)입니다. F-24050 식사는 줄마다 따로 견주면 어느 줄도 35인분을 넘지 않아, 줄을 합치는지 가릅니다. 10. billed_unit_price 가 quote_unit_price 를 넘으면 판정에 정찰 가격보다 비싸게 청구함이 들어갑니다. 성립 사례는 F-24040 제단 꽃장식, 불성립 사례는 단가가 같은 F-24046 수의(기본)입니다. 11. 인력 줄에서 billed_qty 가 counted_person_days 를 넘으면 판정에 실제 출동보다 많은 인력을 청구함이 들어갑니다. 성립 사례는 F-24038 도우미(6인일 대 5인일), 불성립 사례는 F-24031 장례지도사(3인일 대 3인일)입니다. 12. refund_krw 가 양수이면 판정에 쓰지 않아 유가족에게 돌려줄 품목이 들어갑니다. 성립 사례는 F-24044 식사와 F-24038 도우미, 불성립 사례는 정산서만 8인분 적은 F-24046 식사입니다. 13. 판정이 둘 이상 걸리면 verdict 는 출동 초과 → 추가 품목 → 정찰 가격 → 환불 순서로 먼저 걸린 값이고 나머지는 other_verdicts 에 남습니다. 성립 사례는 F-24038 도우미(대표 출동 초과, 같이 걸린 환불), 불성립 사례는 하나만 걸린 F-24040 제단 꽃장식입니다. 14. summary 가 존댓말 한 문장으로 끝나는지 확인합니다. [수동 검증 · 사람이 봅니다] 1. 정산 담당자가 걸린 줄의 summary 와 note 만 읽고 파트너에게 무엇을 되물을지 정하는지 봅니다. 2. 상담사가 환불 금액이 생긴 장례를 유가족에게 안내할 순서로 고를 수 있는지 봅니다. [평가 러너 · 2주차] 1. 재생 스크립트가 장례 40건의 정산서 줄 160개에 심은 어긋남과 화면의 판정이 같은 줄을 셉니다. 2. 다른 줄은 인일 셈·확인 수량 대체·단가 비교 중 어느 계산에서 어긋났는지 남깁니다.