기능 목록
| ID | 기능명 | 담당자 | 우선순위 | 상태 | 관련 테스트 |
|---|---|---|---|---|---|
| NEW-001 | 입력값 자동 대문자 변환 | A 개발자(김동현) | 중 | ✅ 완료 | NEW-001-Unit/, NEW-001-Integration/ |
| NEW-002 | 인보이스 AED(디르함) 통화 선택 | B 개발자(하지혁) | 상 | ✅ 완료 | NEW-002-Unit/, NEW-002-Integration/ |
| NEW-003 | 인보이스 IBK 은행계좌 선택 | B 개발자(하지혁) | 상 | ✅ 완료 | NEW-003-Unit/, NEW-003-Integration/ |
| NEW-004 | 이메일 주소 자동 매핑 | A 개발자(김동현) | 중 | ✅ 완료 | Mails/ |
| NEW-005 | 지급기한 태그 색상 표시 | A 개발자(김동현) | 중 | ✅ 완료 | NEW-005-Unit/ |
| NEW-006 | 바지선명 검색 개선 | A 개발자(김동현) | 하 | ✅ 완료 | - |
| NEW-007 | 인쿼리 텍스트 자동 파싱 → 주문 등록 | B 개발자(하지혁) | 상 | ✅ 완료 | NEW-007-Unit/, NEW-007-Integration/ |
| NEW-008 | 요청사항 텍스트 영역 자동 높이 조절 | A 개발자(김동현) | 중 | ✅ 완료 | NEW-008-Unit/ |
| NEW-009 | 검색 필드 부분 매칭(%LIKE%) 전체 점검 | A 개발자(김동현) | 중 | ✅ 완료 | - |
| NEW-010 | 인보이스 발행 시 선박명 제거 체크박스 | B 개발자(하지혁) | 중 | ✅ 완료 | - |
| NEW-011 | 아랍권 인보이스 2건 동시 발송 | A 개발자(김동현) | 상 | ✅ 완료 | - |
| NEW-012 | 인보이스/BDR 발송 후 발행완료 처리 | A 개발자(김동현) | 상 | ✅ 완료 | - |
| NEW-013 | ETA 필드 추가 + 3단계 DDD 자동 계산 | B 개발자(하지혁) | 상 | ✅ 완료 | - |
| NEW-014 | 웹 페이지 로딩 속도 개선 | B 개발자(하지혁) | 상 | ✅ 완료 | - |
| NEW-015 | 주문 수정 이력 로그 (수정자 + 시간) | B 개발자(하지혁) | 중 | ✅ 완료 | - |
| NEW-016 | 홈페이지 문의 크롤링 (Playwright) | B 개발자(하지혁) | 상 | 대기 | - |
| NEW-017 | 서비스 상태 모니터링 대시보드 | B 개발자(하지혁) | 중 | 대기 | - |
| NEW-018 | 서류(PDF/이미지) 내용 편집기 | B 개발자(하지혁) | 중 | ✅ 완료 | - |
| NEW-019 | 입금 자동 확인 + 페이먼트 리마인더 | B 개발자(하지혁) | 상 | 대기 | - |
| NEW-020 | 휴먼에러 UI/UX 방지책 (정렬/IMO/필수필드) | A 개발자(김동현) | 상 | 대기 | - |
| NEW-021 | 브로커리지 태그 + 필터 기능 | A 개발자(김동현) | 중 | 대기 | - |
| NEW-022 | 디머리지/에이전트피 추가비용 항목 | B 개발자(하지혁) | 중 | 대기 | - |
| NEW-023 | 메일 발송 상태 콜백 + UI 반영 | B 개발자(하지혁) | 상 | 대기 | - |
| NEW-024 | 환율 조회 UI (날짜별 환율 표시) | B 개발자(하지혁) | 중 | 대기 | - |
배경/목적
해운업에서 선박명, 항구명, 업체명 등은 관례상 대문자(UPPER CASE)로 작성. 현재는 수동 입력이라 소문자가 섞여 들어가는 경우 많음.
상세 요구사항
- 바이어/셀러 관리에서 저장(PUT/POST) 시 문자열 필드를 자동 대문자 변환
- 예외 필드: 담당자명 (이름 원형 유지), 이메일 주소
- 대문자 변환 대상: 선박명, 항구명, 업체명, 터미널명, 유종명, 메모/비고
- 백엔드 API에서 변환 (저장 시점)
영향 범위
- 바이어 관리 — OrderController PUT/POST
- 셀러 관리 — SalesOrderController PUT/POST
- 정산 관리
- 이메일 시스템
- 업체 관리
- 회원 관리
- DB 스키마 변경
- 기존 API 수정
API 설계
| Method | Endpoint | 설명 | 신규/수정 |
|---|---|---|---|
| PUT | /api/v2/order | 주문 수정 시 대문자 변환 | 수정 |
| POST | /api/v2/order | 주문 등록 시 대문자 변환 | 수정 |
| PUT | /api/Sales/Order | 판매주문 수정 시 대문자 변환 | 수정 |
| POST | /api/Sales/Order/AddExclusiveReg | 판매주문 등록 시 대문자 변환 | 수정 |
참고
한글은 ToUpper() 해도 변화 없으므로 별도 처리 불필요. 이메일 판별: @ 포함 여부. 기존 데이터는 건드리지 않음.
배경/목적
아랍권(UAE 등) 거래 시 인보이스를 디르함(AED)으로 발행해야 함. 현재 USD만 지원. 체크박스로 AED 변환 발행 가능해야 함.
상세 요구사항
- 인보이스/BDR 생성 화면에 "AED(디르함) 발행" 체크박스 추가
- 기본값: 해제 (USD 기본)
- 체크 시: USD → AED 환율 적용 변환 후 발행
- 인보이스 PDF에 통화 단위 표시: USD → AED
- AED 환율: 고정(3.6725) 또는 실시간 선택
영향 범위
- 바이어 관리
- 셀러 관리
- 문서/파일 — 인보이스 PDF 생성 로직
- DB 스키마 변경 — 환율 테이블 AED 추가
- 신규 API — AED 환율 조회
- 기존 API 수정 — 인보이스 생성 API에 currency 파라미터
API 설계
| Method | Endpoint | 설명 | 신규/수정 |
|---|---|---|---|
| POST | /api/invoice/Generate | currency 파라미터 추가 (USD/AED) | 수정 |
| POST | /api/invoice/PreviewOrder | currency 파라미터 추가 | 수정 |
| GET | /api/etc/ExchangeRate | AED 환율 지원 추가 | 수정 |
참고
USD/AED 고정환율: 1 USD = 3.6725 AED (UAE 페그제). 큰 변동 없음.
배경/목적
인보이스 은행 정보가 하나은행 고정. 홍콩이마린 거래 시 수동으로 IBK로 변경 중. 체크박스로 선택해야 함.
상세 요구사항
- "IBK 계좌로 발행" 체크박스 추가 (기본: 하나은행)
- IBK 정보: Account
054-160034-56-00011, BankIndustrial Bank of Korea, SWIFTIBKOKRSEXXX - 향후 은행 추가 가능하도록 BankAccount 테이블 신설
영향 범위
- 문서/파일 — 인보이스 PDF 은행 정보 동적 변경
- DB 스키마 변경 — BankAccount 테이블 신설
- 기존 API 수정 — bankAccountId 파라미터 추가
DB 변경
| 테이블 | 컬럼 | 타입 | 설명 |
|---|---|---|---|
| BankAccount | BankAccountId | int (PK) | ID |
| BankAccount | BankName | nvarchar(64) | 은행명 |
| BankAccount | AccountNumber | varchar(32) | 계좌번호 |
| BankAccount | SwiftCode | varchar(16) | SWIFT 코드 |
| BankAccount | IsDefault | bit | 기본 계좌 여부 |
| BankAccount | Label | nvarchar(32) | 표시명 |
초기 데이터
| Label | BankName | AccountNumber | SwiftCode | Default |
|---|---|---|---|---|
| 하나은행 | KEB Hana Bank | (현재 계좌) | (현재 SWIFT) | Y |
| IBK | Industrial Bank of Korea | 054-160034-56-00011 | IBKOKRSEXXX | N |
배경/목적
메일 발송 시 수신자/참조 이메일을 수동 입력 중. 주문업체명+선박명으로 검색하여 이메일을 자동으로 가져오고 업체/회원 정보에 반영해야 함.
현재 이메일 DB 구조
| 위치 | 필드 | 설명 |
|---|---|---|
| ManagedCompany | ContactEmail | 업체 연락 이메일 (필수) |
| ManagedCompany | RepresentEmail | 대표자 이메일 |
| ManagedCompany | SupportEmail | 서포트 이메일 |
| UserAccount | 사용자 이메일 (필수) | |
| OrderOilSupplier | OilSalesManagerEmail | 공급사 담당자 |
| OrderOilSupplier | BargeManagerEmail | 바지 담당자 |
상세 요구사항
- 메일 발송 화면에서 주문업체명+선박명 기준으로 이메일 이력 자동 조회
- 조회된 이메일을 To, CC에 자동 채움
- 새 이메일 발견 시 업체/회원 정보에 업데이트
- 벙커 아카이브 연동: 방식 확인 필요 (API? DB? 크롤링?)
영향 범위
- 바이어 관리
- 셀러 관리
- 이메일 시스템
- 업체 관리
- 회원 관리
- DB 스키마 변경
- 신규 API
- 기존 API 수정
참고
1차: 내부 이메일 이력(EmailRelatedToOrder)에서 추출. 2차: 벙커 아카이브 외부 연동.
배경/목적
지급기한(PayDueDate)이 언제인지 한눈에 파악하기 어려움. 태그 형태로 날짜를 표시하고 기한 임박 시 색상 경고 필요.
색상 규칙
| 조건 | 색상 | 예시 |
|---|---|---|
| 3일 이상 여유 | 회색 | 3/28 |
| 2일 전 | 노란색 | 3/25 |
| 1일 전 | 주황색 | 3/24 |
| 당일 (D-Day) | 빨간색 | 3/23 |
| 기한 초과 | 진한 빨강 | 3/20 |
영향 범위
- 바이어 관리 — OrderList.razor 태그 추가
- 셀러 관리 — SalesOrderList.razor 태그 추가
참고
결제 완료된 건(PayCompleteDate 있으면)은 태그 표시 안 함. SngPayDueTag.razor 컴포넌트 신규 생성.
코드 분석 결과
현재 검색 로직 (OrdersController.cs:428):
Ship.ShipName.Contains(bargeShipName) // = SQL %LIKE%
코드 자체는 맞음. 추정 원인:
- Ship 테이블에 해당 바지선이
ShipType="BARGE"로 등록 안 되어 있을 수 있음 OrderOilSupplier.BargeShipId가 null이면 매핑 안 됨- 비활성화(
IsActivated) 필터로 걸러질 수 있음
해결 방안
- DB에서 바지선 데이터 상태 확인 필요
- 검색 결과 0건 시
OrderOilSupplier.BargeShipName(비정규화 필드) fallback 검색 추가
영향 범위
- 바이어 관리 — OrdersController 검색 로직
- 셀러 관리 — SalesOrdersController 검색 로직
- 기존 API 수정
배경/목적
인쿼리 텍스트를 보고 수동으로 바이어/셀러 주문을 등록 중. 자동 파싱하여 주문을 자동 생성해야 함.
처리 순서
- 텍스트 입력 (붙여넣기)
- 파싱 → 구조화된 데이터로 변환
- DB 매칭 (업체관리/회원관리에 있는 것만)
- 미리보기 표시 (매칭 결과 + 미매칭 하이라이트)
- 사용자 확인/수정
- 바이어 주문 생성 (POST /api/order/OrderForAdmin)
- 셀러 주문 생성 (POST /api/Sales/Order/AddExclusiveReg) — 바이어 OrderInfoId 연결
기본 스펙 매핑 (스펙 미명시 시 2010 기준 자동 적용)
| 입력 | OilName | 기본 스펙 |
|---|---|---|
| VLSFO | VLSFO | ISO 8217:2010 RMG 380 Max 0.5% S |
| VLSFO 120VIS/CST | VLSFO | ISO 8217:2010 RMG 380 Max 0.5% S, Max 120 Vis. |
| VLSFO 180VIS/CST | VLSFO | ISO 8217:2010 RMG 380 Max 0.5% S, Max 180 Vis. |
| HSFO / HSFO 380CST | HSFO | ISO 8217:2010 RMG 380 Max 3.5% S |
| HSFO 500CST | HSFO | ISO 8217:2010 RMK 500 Max 3.5% S |
| HSFO 700CST | HSFO | ISO 8217:2010 RMK 700 Max 3.5% S |
| LSMGO | LSMGO | ISO 8217:2010 DMA Max 0.1% S |
| LSMGO 0.5% | LSMGO | ISO 8217:2010 DMA Max 0.5% S |
| MGO / MDO | LSMGO | ISO 8217:2010 DMA Max 0.1% S |
업체명 매핑 (한국어 약칭 → 영문)
| 한국어 | 영문 정식명 |
|---|---|
| 홍콩이마린 | HONGKONG E-MARINE SUPPLY SERVICE CORP., LTD. |
| 금진 | KEUMJIN MARINE CO., LTD. |
| 동림 | DONGRIM TRADING CO., LTD. |
| 프라임 | PRIME BUNKER CO., LTD. |
| 로인스 | Roins |
| 글로벌오프쇼어 | GLOBAL OFFSHORE MARINE SERVICE |
| 코모션 | COMOCEAN SHIPPING DMCC |
| 파노코 | PANOCO KOREA CO., LTD. |
매핑에 없으면 아래 매칭 로직으로 처리
업체명 매칭 우선순위
1순위: 한국어 매핑 테이블 (홍콩이마린 → 정확 매칭)
2순위: 영문 입력 시 앞 5~10글자 + 뒤 5~10글자로 DB 2중 확인
예: "HONGKONG E-MARINE SUPPLY SERVICE CORP., LTD."
→ 앞: "HONGKONG E-" → DB StartsWith 매칭
→ 뒤: "CORP., LTD." → DB EndsWith 매칭
→ 둘 다 일치하면 확정
3순위: 못 찾으면 → 미리보기에서 드롭다운 수동 선택단가/결제조건 파싱
| 유종 수 | 부대비용 | 예시 |
|---|---|---|
| 1개 | 없음 | 1400 (30DDD) |
| 1개 | 있음 | 1400+500 (30DDD) |
| 2개 | 없음 | 1370/1620 (30DDD) |
| 2개 | 있음 | 1370/1620+500 (30DDD) |
| 3개 | 없음 | 1400/1650/1800 (30DDD) |
| 3개 | 있음 | 1400/1650/1800+500 (30DDD) |
단가 슬래시(/) 순서 = 유종 나열 순서
결제조건
| 형식 | 의미 |
|---|---|
숫자DDD | 공급 후 N일 결제 (7, 15, 30, 45, 60, 90 등 숫자 자유) |
CIA | Cash In Advance - 공급 전 선결제 |
COD | Cash On Delivery - 공급 완료 즉시 송금 |
에이전트 정보 (4개 필드 각각 독립, 없으면 NULL)
| 필드 | 파싱 키워드 | 저장 방식 | NULL |
|---|---|---|---|
| 업체명 | AGENT / 에이전트 | 업체명 텍스트 | O |
| 담당자명 | AGENT NAME / MR. / MS. | MR./MS. 포함 전체 저장 | O |
| 전화번호 | T. / TEL | 번호 텍스트 | O |
| 이메일 | E-mail / EMAIL | 이메일 텍스트 | O |
담당자명: AGENT NAME : MR.HA → MR.HA 전체 저장 (MR./MS. 떼지 않음)
NULL 가능 필드 전체
| 필드 | 필수 | NULL시 |
|---|---|---|
| VESSEL NAME, IMO, PORT, DELIVERY WINDOW | O | - |
| 유종 (최소 1개), 바이어, 셀러 | O | - |
| 서플, AGENT, T., E-mail, MR./MS., 비고 | 안 넣음 |
DDD 결제기한일 자동 계산
DDD(Days after Delivery Date)는 달력일(calendar days) 기준. 공급 시작일 = Day 1. 만기일이 주말/공휴일이면 다음 영업일로 이동.
| 결제조건 | 계산 방식 | 예시 (공급일 2026-03-24) |
|---|---|---|
30DDD | 공급일 + 29 달력일 = Day 30 | 2026-04-22 (수) |
7DDD | 공급일 + 6 달력일 = Day 7 | 2026-03-30 (월) |
CIA | 공급 시작일 당일 | 2026-03-24 |
COD | 공급 시작일 당일 | 2026-03-24 |
주의: DDD는 영업일(business days)이 아니라 달력일(calendar days)임. 만기일이 주말/공휴일에 걸리면 다음 영업일로 밀림.
공휴일 자동 조회 (Nager.Date API)
DDD 계산 시 공휴일은 Nager.Date 무료 REST API를 통해 자동 조회됨.
- API:
GET https://date.nager.at/api/v3/PublicHolidays/{year}/{countryCode} - 한국(KR) 공휴일 무조건 포함 + 바이어/셀러 업체 소속국가 공휴일 추가
- 메모리 캐싱: 한번 조회하면 서버 재시작까지 재호출 안 함
- API 실패 시 하드코딩 fallback 사용 (한국/UAE/싱가포르 2025~2030)
- 서버 시작 시 주요 10개국 캐시 프리로드 (백그라운드)
지원 국가 (50개국+): 대한민국(KR), 아랍에미리트(AE), 싱가포르(SG), 일본(JP), 중국(CN), 홍콩(HK), 대만(TW), 말레이시아(MY), 태국(TH), 인도네시아(ID), 필리핀(PH), 베트남(VN), 인도(IN), 사우디아라비아(SA), 미국(US), 영국(GB), 노르웨이(NO), 그리스(GR), 터키(TR), 호주(AU) 등
바이어/셀러 PayDueDate 각각 독립 계산
| 바이어 | 셀러 | |
|---|---|---|
| PayType 소스 | 파싱 결과 Buyer.PayType | 파싱 결과 Seller.PayType |
| PayDueDate 저장 | 2단계 PUT OrderForAdmin | 3단계 POST UpdateSellerPayDueDate |
| 공휴일 국가 | 한국 + 바이어 업체 국가 | 한국 + 셀러 업체 국가 |
주문 생성 흐름 (3단계)
신규 파일
| 파일 | 설명 |
|---|---|
Controllers/InquiryParseController.cs | 파싱/매칭/주문생성 API |
Services/InquiryParser/InquiryTextParser.cs | 텍스트 파서 핵심 로직 |
Services/InquiryParser/DddCalculator.cs | DDD 결제기한 계산기 (Nager API + fallback) |
Services/InquiryParser/InquiryParseResult.cs | 파싱 결과 모델 |
Models/InquiryParseResultDto.cs | 프론트 DTO |
테스트 현황 (196개 전체 통과)
| 카테고리 | 테스트 수 | 파일 위치 |
|---|---|---|
| NEW-002-Unit | 6개 | NEW-002-Unit/AedCurrencyTests.cs |
| NEW-002-Integration | 4개 | NEW-002-Integration/ |
| NEW-003-Unit | 6개 | NEW-003-Unit/BankAccountTests.cs |
| NEW-003-Integration | 5개 | NEW-003-Integration/ |
| NEW-007-Unit | 150개+ | NEW-007-Unit/ (9개 파일) |
| NEW-007-Integration | 12개 | NEW-007-Integration/ |
예시 (15개)
예시 1: 유종 1개
VESSEL NAME: SEA PIONEER IMO : 9334567 DELIVERY PORT/AREA NAME: BUSAN DELIVERY WINDOW: 10-12TH APR 2026 VLSFO 0.5% : 100MT 바이어 : 홍콩이마린 1400 (30DDD) 셀러 : 금진 1300 (7DDD)
예시 2: 유종 1개 + 부대비용
VESSEL NAME: BRAVE SAILOR IMO : 9667788 DELIVERY PORT/AREA NAME: YOSU DELIVERY WINDOW: 01-05TH APR 2026 VLSFO : 200MT 바이어 : 홍콩이마린 1400+500 (60DDD) 셀러 : 동림 1300+500 (7DDD)
예시 3: 유종 2개 (기본, 풀 정보)
VESSEL NAME: TONG RUN 7 IMO : 9673563 DELIVERY PORT/AREA NAME: BUSAN DELIVERY WINDOW: 16-20TH MAR 2026 VLSFO : 55MT LSMGO : 22MT 바이어 : HONGKONG E-MARINE SUPPLY SERVICE CORP., LTD. 1370/1620+500 (30DDD) 셀러 : KEUMJIN MARINE CO., LTD. 1290/1540+500 (7DDD) 서플 : Roins AGENT : GLOBAL OFFSHORE MARINE SERVICE T.+82 51 715 9679 E-mail : ops@globaloms.co.kr MR. SONG, HYO SUNG
예시 4: 유종 2개 + 에이전트/서플 없음 (NULL)
VESSEL NAME: PACIFIC STAR IMO : 9512345 DELIVERY PORT/AREA NAME: FUJAIRAH DELIVERY WINDOW: 05-10TH APR 2026 VLSFO 0.5% : 150MT LSMGO 0.1% : 30MT 바이어 : 프라임 1500/1700 (15DDD) 셀러 : KEUMJIN MARINE CO., LTD. 1400/1600 (7DDD)
예시 5: 유종 2개 + 한국어 약칭
VESSEL NAME: OCEAN GRACE IMO : 9876543 DELIVERY PORT/AREA NAME: BUSAN DELIVERY WINDOW: 20-25TH MAR 2026 VLSFO : 100MT LSMGO : 40MT 바이어 : 홍콩이마린 1370/1620+500 (30DDD) 셀러 : 금진 1290/1540+500 (7DDD) 서플 : 로인스
예시 6: 유종 2개 + 비고 포함
VESSEL NAME: DONG-A VENUS IMO : 9556677 DELIVERY PORT/AREA NAME: BUSAN DELIVERY WINDOW: 20-23RD MAR 2026 VLSFO : 150MT LSMGO : 40MT 바이어 : 홍콩이마린 1370/1620+500 (30DDD) 셀러 : 동림 1290/1540+500 (7DDD) 비고 : STEM CONFIRMED, FIXED
예시 7: 유종 2개 + COD 결제조건
VESSEL NAME: MORNING STAR IMO : 9334422 DELIVERY PORT/AREA NAME: BUSAN DELIVERY WINDOW: 15-18TH OCT 2026 VLSFO : 80MT LSMGO : 20MT 바이어 : 홍콩이마린 1400/1650 (COD) 셀러 : 금진 1300/1550 (7DDD)
예시 8: 유종 3개 + VIS 스펙 + 부대비용
VESSEL NAME: XIN SHENG SHENG 6 IMO : 8358013 DELIVERY PORT/AREA NAME: YOSU DELIVERY WINDOW: 01-03RD AUG 2026 HSFO 380CST : 200MT VLSFO 180VIS : 55~100MT LSMGO 0.1% : 22~50MT 바이어 : 홍콩이마린 1400/1650/1800+500 (30DDD) 셀러 : 동림 1300/1550/1700+500 (7DDD) 서플 : Roins AGENT : GLOBAL OFFSHORE MARINE SERVICE T.+82 51 715 9679 E-mail : ops@globaloms.co.kr MR. SONG, HYO SUNG
예시 9: 유종 3개 + 부대비용 없음
VESSEL NAME: STAR EXPLORER IMO : 9887766 DELIVERY PORT/AREA NAME: SINGAPORE DELIVERY WINDOW: 10-15TH JUN 2026 HSFO : 300MT VLSFO : 100~150MT LSMGO : 30MT 바이어 : 프라임 1200/1500/1700 (45DDD) 셀러 : 금진 1100/1400/1600 (15DDD)
예시 10: 유종 3개 + HSFO 700CST
VESSEL NAME: BLUE OCEAN IMO : 9112233 DELIVERY PORT/AREA NAME: SINGAPORE DELIVERY WINDOW: 05-08TH MAY 2026 HSFO 700CST : 500MT VLSFO : 100MT LSMGO : 30MT 바이어 : COMOCEAN SHIPPING DMCC 1200/1500/1700+600 (30DDD) 셀러 : 프라임 1100/1400/1600+500 (15DDD)
예시 11: 유종 3개 + 수량 혼합 + 90DDD (풀 정보)
VESSEL NAME: GOLDEN WAVE IMO : 9556688 DELIVERY PORT/AREA NAME: YOSU DELIVERY WINDOW: 20-25TH NOV 2026 HSFO 380CST : 500MT VLSFO : 100~200MT LSMGO : 30MT 바이어 : 홍콩이마린 1200/1500/1700+500 (90DDD) 셀러 : 동림 1100/1400/1600+500 (7DDD) 서플 : 로인스 AGENT : 글로벌오프쇼어 T.+82 51 715 9679 MR. SONG, HYO SUNG
예시 12: 영문 키워드 (BUYER/SELLER)
VESSEL NAME: STAR VOYAGER IMO : 9998877 DELIVERY PORT/AREA NAME: YOSU DELIVERY WINDOW: 10-15TH JUN 2026 VLSFO : 80~120MT BUYER : HONGKONG E-MARINE SUPPLY SERVICE CORP., LTD. 1400/1650 (30DDD) SELLER : KEUMJIN MARINE CO., LTD. 1300/1550 (7DDD) SUPPLIER : Roins AGENT : GLOBAL OFFSHORE MARINE SERVICE
예시 13: CIA 결제조건
VESSEL NAME: PACIFIC DREAM IMO : 9223311 DELIVERY PORT/AREA NAME: FUJAIRAH DELIVERY WINDOW: 01-03RD SEP 2026 VLSFO : 100MT 바이어 : COMOCEAN SHIPPING DMCC 1500 (CIA) 셀러 : 프라임 1400 (7DDD)
예시 14: 에이전트 업체명만 (연락처 없음)
VESSEL NAME: OCEAN STAR IMO : 9445566 DELIVERY PORT/AREA NAME: BUSAN DELIVERY WINDOW: 15-18TH MAR 2026 VLSFO : 200MT 바이어 : 홍콩이마린 1400+500 (30DDD) 셀러 : 동림 1300+500 (7DDD) AGENT : GLOBAL OFFSHORE MARINE SERVICE
예시 15: 에이전트 업체명 + 이메일만
VESSEL NAME: GRAND EXPLORER IMO : 9778899 DELIVERY PORT/AREA NAME: BUSAN DELIVERY WINDOW: 10-15TH JUL 2026 HSFO 380CST : 500MT VLSFO : 100~200MT LSMGO : 30MT 바이어 : HONGKONG E-MARINE SUPPLY SERVICE CORP., LTD. 1200/1500/1700+500 (30DDD) 셀러 : KEUMJIN MARINE CO., LTD. 1100/1400/1600+500 (7DDD) AGENT : GLOBAL OFFSHORE MARINE SERVICE E-mail : ops@globaloms.co.kr
영향 범위
- 바이어 관리 — 주문 자동 등록
- 셀러 관리 — 판매주문 자동 등록
- 업체 관리 — 매칭 조회 + 매핑 테이블
- 회원 관리 — 담당자 매칭
- 견적(RFQ) — 입력 화면
- DB 스키마 변경 — CompanyAlias 테이블
- 신규 API — Parse, Match, CreateOrders
API 설계
| Method | Endpoint | 설명 | 신규/수정 |
|---|---|---|---|
| POST | /api/inquiry/Parse | 텍스트 파싱 → 구조화 데이터 | 신규 |
| POST | /api/inquiry/Match | DB 매칭 (업체/선박/유종) | 신규 |
| POST | /api/inquiry/CreateOrders | 바이어+셀러 주문 자동 생성 | 신규 |
참고
정규식 기반 파싱. 업체명: 한국어 매핑 우선 → 없으면 DB Contains. 반드시 바이어 먼저 → 셀러는 OrderInfoId 연결.
배경/목적
바이어/셀러 관리 화면에서 요청사항(RequestMemo) 입력 시 고정 높이에 스크롤이 생김. 입력한 만큼 영역이 자동으로 늘어나야 내용 전체가 한눈에 보임.
상세 요구사항
- 요청사항 텍스트 입력 영역을 내용에 따라 자동 확장으로 변경
- 스크롤 없이 입력한 텍스트 전체가 보여야 함
- 최소 높이는 현재와 동일 유지
- OrderList.razor, SalesOrderList.razor 양쪽 모두 적용
영향 범위
- 바이어 관리 — OrderList.razor
- 셀러 관리 — SalesOrderList.razor
참고
프론트만 수정, 백엔드 변경 없음. MudBlazor AutoGrow 또는 CSS/JS auto-resize 활용.
배경/목적
일부 검색 필드가 정확 일치(==)로 되어있어 부분 검색이 안 됨. 텍스트 검색은 모두 Contains()(%LIKE%)로 동작해야 함.
코드 분석 결과
| 검색 필드 | 현재 방식 | 문제 |
|---|---|---|
| 주문번호 | Contains() %LIKE% | 정상 |
| 선사명 | Contains() %LIKE% | 정상 |
| 선박명 | Contains() %LIKE% | 정상 |
| 항구명 | == portName 정확 일치 | 부분 검색 안 됨 |
| 에이전트명 | Contains() %LIKE% | 정상 |
| 바지업체명 | Contains() %LIKE% | 정상 |
| 바지선명 | Contains() (Ship 테이블) | 데이터 누락 가능 (NEW-006) |
| 터미널명 | Contains() %LIKE% | 정상 |
| 유종구매타입 | == 정확 일치 | 정상 (드롭다운) |
| 급유방식 | == 정확 일치 | 정상 (드롭다운) |
| 결제방식 | == 정확 일치 | 정상 (드롭다운) |
수정 대상
| 파일 | 줄 | 현재 | 변경 |
|---|---|---|---|
| OrdersController.cs | 400 | order.PortName == portName | order.PortName.Contains(portName) |
| SalesOrdersController.cs | 515 | order.OrderInfo.PortName == portName | order.OrderInfo.PortName.Contains(portName) |
영향 범위
- 바이어 관리 — OrdersController 검색 쿼리
- 셀러 관리 — SalesOrdersController 검색 쿼리
- 기존 API 수정
참고
NEW-006(바지선명 검색)과 같이 진행하면 효율적. 규칙: 텍스트 입력 → Contains, 드롭다운 선택 → 정확 일치.
배경/목적
인보이스 발행 시 선박명을 포함하지 않아야 하는 경우가 있음. 기존 AED/IBK 체크박스와 동일한 패턴으로 "선박명 제거" 옵션 추가.
상세 요구사항
- 인보이스(INVOICE) 서류 발행 영역에 "선박명 제거" 체크박스 추가
- 체크 시 인보이스 PDF에서 선박명(ShipName)이 빈 값으로 생성
- AED, IBK 체크박스와 조합 가능 (3개 독립 동작)
- 발행 완료 후 체크박스 자동 초기화
수정 파일
| 파일 | 변경 내용 |
|---|---|
SngDocUploadPanel.razor | "선박명 제거" 체크박스 + RemoveShipName 파라미터 추가 |
OrderList.razor | @bind-RemoveShipName 바인딩 |
OrderList.razor.cs | RemoveShipName 프로퍼티 + API 쿼리스트링 추가 |
InvoiceController.cs | removeShipName=true일 때 ShipName 빈 값 설정 |
배경/목적
아랍권(UAE) 거래 시 USD + AED 인보이스 2건을 발행해야 함. 현재 1건씩만 생성/발송 가능하여 2번 작업 필요. 한 번에 2건 생성 + 1개 메일로 동시 발송해야 함.
현재 구조 분석
- 인보이스 생성:
POST /api/Invoice/Generate→useAed파라미터로 USD/AED 구분 - 메일 발송:
POST /api/Mail/SendMail→AttachFiles가IEnumerable<string>이라 복수 첨부 이미 백엔드 지원 - 아랍권 업체 GUID 하드코딩: PETRA PRIME, FOUCHER TRADE, COMOCEAN, ADELE SHIPPING
상세 요구사항
- 아랍권 업체 감지 시 "USD + AED 동시 생성" 버튼 추가
- 클릭 시: Generate(useAed=false) + Generate(useAed=true) → PDF 2건 생성
- 메일 발송 시 2건의 PDF를
AttachFiles에 넣어서 1회 발송 - 아랍권 감지: 기존
isAedCultureCompGuids배열 활용 - 메일 미리보기에서 첨부파일 2건 표시
API 설계
| Method | Endpoint | 설명 |
|---|---|---|
| POST | /api/Invoice/GenerateBatch | USD+AED 동시 생성 (2건 반환) — 신규 |
| POST | /api/Mail/SendMail | AttachFiles에 2건 전달 — 기존 유지 |
주요 수정 파일
| 파일 | 변경 내용 |
|---|---|
InvoiceController.cs | GenerateBatch 엔드포인트 추가 |
OrderList.razor | 동시 생성 버튼 UI |
OrderList.razor.cs | 2건 생성 + 메일 발송 로직 |
MailContentMaker.cs | "USD + AED Invoice attached" 안내 문구 |
참고: NEW-002(AED 디르함) 완료 — useAed=true 파라미터 활용 가능.
배경/목적
인보이스/BDR 메일 발송 후 주문 상태가 자동 변경되지 않음. 발송 성공 시 "발행완료" 상태로 변경되어야 정산 흐름이 이어짐.
현재 주문 상태 체계
관련 DB 필드
| 필드 | 타입 | 설명 |
|---|---|---|
OrderInfo.StatusCode | int | 비트 시프트 기반 상태 코드 |
OrderSettlement.IsIssueInvoice | bool | 인보이스 발행 여부 |
OrderSettlement.InvoiceLink | string | 인보이스 링크 |
OrderSettlement.IsRecvBdr | bool | BDR 수신 여부 |
OutboundMail.Status | int | -1:실패, 0:대기, 100:성공 |
상세 요구사항
- 인보이스 메일 발송 성공 시 (
OutboundMail.Status = 100):OrderInfo.StatusCode→ "발행완료" (STR_STATUS_PUBLICATION_DONE)OrderSettlement.IsIssueInvoice = trueOrderSettlement.InvoiceLink= 발송된 PDF 링크
- 발송 실패 시 상태 변경 금지 —
Status = 100확인 후에만 - 발행완료 건 목록에서 시각적 구분 (뱃지/색상)
- 수동 발행완료 취소 + 재발행 가능
주요 수정 파일
| 파일 | 변경 내용 |
|---|---|
MailController.cs | SendMail 발송 성공 후 상태 변경 로직 (line 231+) |
InvoiceController.cs | ResendMail 상태 유지 확인 |
OrderList.razor | 발행완료 뱃지 UI |
SalesOrderList.razor | 발행완료 뱃지 UI |
핵심: MailController.SendMail에서 OutboundMailer 성공 후 → EmailRelatedToOrder.OrderInfoId로 주문 찾아서 상태 변경.
셀러 주문은 EmailRelatedToSalesOrder 테이블로 연결.
배경/목적
주문 진행 과정에서 3번의 DDD 재계산이 필요. 현재 ETA는 별도 필드 없이 요청사항에 "ETA: 3/7" 식으로 수기 입력 중. ETA 필드 추가 + 각 단계에서 자동 DDD 계산하면 수동 DDD 계산기 사용 불필요.
업무 흐름과 3단계 DDD
| 단계 | 시점 | 트리거 필드 | 현재 |
|---|---|---|---|
| 1단계 | 에이전트 통화 후 | ETA (신규) | 필드 없음 |
| 2단계 | 공급일정 확인 후 | 공급예정일 (EstSupplyStartDate) | DDD 안 됨 |
| 3단계 | BDR 수령 후 | 공급확정일 (SupplyCompleteDate) | DDD 안 됨 |
DDD 계산 우선순위
| 우선순위 | 필드 | DB |
|---|---|---|
| 1 (최우선) | 공급확정일 | SupplyCompleteDate (기존) |
| 2 | 공급예정일 | EstSupplyStartDate (기존) |
| 3 | ETA | Eta (신규) |
| 4 (기본) | 공급요청시작일 | ReqSupplyStartDate (기존) |
→ 값이 있는 것 중 가장 우선순위 높은 날짜로 DDD 계산
상세 요구사항
- ETA 필드 추가 —
OrderInfo.Eta(DateTime?, nullable), UI DatePicker 추가 - 3개 필드 변경 시 DDD 자동 재계산 — ETA, 공급예정일, 공급확정일
- 바이어/셀러 각각 PayType 기준, 기존
/api/Etc/CalcPayDate사용 - PayType 비어있으면 계산 안 함
주요 수정 파일
| 파일 | 변경 내용 |
|---|---|
OrderInfo.cs | Eta 필드 추가 (DateTime?, nullable) |
EditOrder.cs | Eta, EstSupplyStartDate, SupplyCompleteDate에 PropertyChanged 추가 |
OrderList.razor | ETA 입력 DatePicker UI 추가 |
OrderList.razor.cs | 3개 필드 변경 감지 → ReCalcPayDueDate 호출 |
| DB 마이그레이션 | OrderInfo 테이블에 Eta 컬럼 추가 |
논의 (김동현/하지혁 2026-03-27): CIA 건은 ETA 기준 DDD가 중요. NEW-012(발행완료)와 연결성 있지만 NEW-013은 독립 구현 가능.
배경/목적
주문 관리 페이지(OrderList, SalesOrderList) 초기 로딩이 느림. 구조적 문제 여러 개 확인.
현재 문제점
| 문제 | 위치 | 영향 |
|---|---|---|
| 순차 API 호출 (8~9개) | OrderList.razor.cs L229-237 | 하나 끝나야 다음 시작 |
| 대량 일괄 로드 (Size=1000) | OrderList.razor.cs L145 | 서버 응답 + DOM 렌더링 느림 |
| 가상화/페이지네이션 없음 | OrderList.razor | 1000건 전부 HTML 렌더링 |
개선 방안
| # | 개선 사항 | 예상 효과 | 난이도 |
|---|---|---|---|
| 1 | API 호출 병렬화 (Task.WhenAll) | 로딩 50~70% 감소 | 하 |
| 2 | 초기 로드 건수 축소 (1000→50~100) | 응답+렌더링 감소 | 하 |
| 3 | 페이지네이션 UI 추가 | 필요한 만큼만 로드 | 중 |
| 4 | Blazor <Virtualize> 적용 | DOM 최적화 | 중 |
| 5 | 드롭다운 데이터 캐싱 | 페이지 이동 시 재호출 방지 | 하 |
우선 적용 (난이도 하)
// 현재 (순차)
await GetOrderList();
await GetAllShipCompanies();
await GetAllPorts();
...
// 개선 (병렬)
await Task.WhenAll(
GetOrderList(),
GetAllShipCompanies(),
GetAllPorts(),
GetAllOils(),
GetPayTypes(),
GetSaleCompanies(),
GetAgentCompanies(),
GetBargeCompanies(),
GetAdminUsers()
);
배경/목적
현재 "최근 업데이트" 시간(ModDate)만 표시, 누가 수정했는지 알 수 없음. 기존 OrderLog 테이블은 있지만 수정자 이름 조회 불가 구조.
현재 상태
| 항목 | 현재 | 문제점 |
|---|---|---|
OrderInfo.ModDate | 수정 시간 저장 ✓ | 수정자 정보 없음 |
OrderInfo.RegUserId | 등록자 ID ✓ | 수정자와 다를 수 있음 |
OrderLog.LogModifier | 수정자 ID (long) | UserAccount FK 없음 → 이름 조회 불가 |
UI (OrderList.razor L417) | ModDate만 표시 | 수정자명 미표시 |
상세 요구사항
OrderInfo에ModUserId필드 추가 — 마지막 수정자 UserId 저장OrderLog.LogModifier→UserAccountFK 연결 — 수정자 이름 조회 가능- PUT API에서 JWT 기반 현재 사용자 →
ModUserId저장 - UI에 수정자명 + 시간 표시: "하지혁 2026.03.26 17:30"
- (선택) 수정 이력 팝업 — 주문별 수정 로그 목록
참고 패턴 (OrderMemo)
// OrderMemo.cs — 이 패턴을 OrderInfo에도 동일 적용
public long ModUserId { get; set; }
[ForeignKey("ModUserId")]
public virtual UserAccount ModUser { get; set; }
주요 수정 파일
| 파일 | 변경 내용 |
|---|---|
OrderInfo.cs | ModUserId 필드 + FK 추가 |
OrderLog.cs L45 | LogModifier → UserAccount FK 추가 |
OrderController.cs L1115 | PUT에서 ModUserId 저장 |
RespSearchOrderDetail.cs | ModUserName 필드 추가 |
OrderList.razor L417 | 수정자명 + 시간 표시 |
배경/목적
신규 홈페이지(SaaS)에 문의가 들어오는데 관리자 인트라넷에서 볼 수 없음. SaaS라 API 없으므로 Playwright로 브라우저 자동화하여 크롤링.
현재 → 개선
| 현재 | 개선 후 |
|---|---|
| 직원이 홈페이지 직접 접속해서 문의 확인 (수동) | Playwright가 5~30분마다 자동 크롤링 → 인트라넷에서 조회 |
상세 요구사항
- 홈페이지 관리자 로그인 → 문의 목록 → 신규 문의 파싱 → DB 저장
- BackgroundService로 주기적 실행 (5~30분)
- 관리자 UI: 문의 목록 + 상세 + 미처리/처리중/완료 상태
- 중복 방지 + 문의→주문 전환 (NEW-007 연동)
사전 확인: 홈페이지 관리자 로그인 URL, 로그인 방식(ID/PW? SSO?), 문의 목록 HTML 구조
주의: SaaS UI 변경 시 크롤러 수정 필요 / 서버 메모리 200~300MB 추가 / 과도한 크롤링 시 차단 가능
주요 파일 (예상)
| 파일 | 설명 |
|---|---|
Services/Crawler/InquiryCrawlerService.cs | Playwright 크롤러 (BackgroundService) |
Models/DB/WebInquiry.cs | 크롤링된 문의 DB 모델 |
Controllers/WebInquiryController.cs | 문의 조회 API |
Pages/WebInquiryList.razor | 문의 관리 UI |
배경/목적
운영 중인 서버/서비스가 여러 개인데 상태를 한눈에 확인할 수 없음. 네이버웍스 Service Health 대시보드를 벤치마킹하여 자체 모니터링 구축.
벤치마킹: 네이버웍스 Service Health
- 서비스별 정상/점검중/장애 상태 표시
- 카테고리 분류 (공통, 코어 서비스, 드라이브 등)
- 심플한 UI — 서비스명 + 상태 아이콘
모니터링 대상 (확인 필요)
| 서비스 | 헬스체크 방식 |
|---|---|
| 인트라넷 프론트 | HTTP GET → 200 OK |
| 백엔드 API | HTTP GET /health → 200 OK |
| 홈페이지 | HTTP GET → 200 OK |
| DB (SQL Server) | TCP 연결 테스트 |
| DDD 공휴일 API (Nager) | HTTP GET → 200 OK |
상세 요구사항
- 헬스체크 서비스 — BackgroundService, 30초~1분 간격, 타임아웃 5초
- 대시보드 UI — 서비스명 + 상태 아이콘 (정상 ✅ / 장애 ❌) + 응답 시간(ms)
- 이력 저장 — 상태 변경 이력 DB 저장 (장애 발생/복구 시점)
- (선택) 알림 — 장애 감지 시 이메일 또는 네이버웍스 봇 알림
구현 단계
| 단계 | 내용 | 난이도 |
|---|---|---|
| 1단계 | HTTP 헬스체크 + 대시보드 UI | 중 |
| 2단계 | 알림 + 이력 그래프 | 상 |
ASP.NET Core 내장 헬스체크 활용
builder.Services.AddHealthChecks()
.AddSqlServer(connectionString)
.AddUrlGroup(new Uri("https://..."), "Homepage");
배경/목적
BDR, 인보이스 등 서류 PDF/이미지에서 특정 내용(업체명, 선박명 등)을 지워서 수정본을 만들어야 하는 경우가 있음. 외부 편집 도구 없이 인트라넷 내에서 바로 편집 가능해야 함.
상세 요구사항
- 서류관리 → 서류보기에서 각 파일 옆에 편집 아이콘 표시
- 클릭 시 이미지 편집 모달 열림
- 파일 로드: 📂 파일 선택 버튼으로 로컬 PDF/이미지 직접 로드
- 멀티페이지 PDF 지원: 전체 페이지를 세로로 이어붙여 표시, 페이지 구분선 + 번호
- 두 가지 편집 모드:
• 브러시 모드: 원형 브러시로 자유롭게 칠하기 (크기 조절 슬라이더)
• 사각형 모드: 드래그로 사각형 영역 선택 - 편집 중에는 빨간 하이라이트로 표시만 — 실제 지우기는 저장 시 적용
- 지우기 알고리즘: 각 마스크 픽셀마다 주변 배경 픽셀 30개 평균으로 채움 (글자 건너뜀, 워터마크 상쇄)
- 색상지정 모드: 이미지에서 클릭하여 3×3 평균색 추출 → 해당 색으로 고정 채움
- 되돌리기/초기화: 스트로크 단위 Undo (최대 30회), 전체 초기화
- 저장: 원본 유지 + 편집본을 새 파일로 추가 (PDF→PDF, 이미지→이미지)
- 파일명 규칙: 원본명 + E (예:
_BDR_20260326A9PPE.pdf), 중복 시 E2, E3... - 바이어 관리(OrderList), 셀러 관리(SalesOrderList) 양쪽 모두 적용
영향 범위
- 바이어 관리 — OrderList.razor 편집 모달/로직 추가
- 셀러 관리 — SalesOrderList.razor 동일 적용
- 정산 관리
- 이메일 시스템
- 업체 관리
- 회원 관리
- 견적(RFQ)
- 문서/파일 — 편집된 파일 저장, ImageLink 신규 생성
- DB 스키마 변경
- 신규 API 필요 — ImageEditController (Save)
- 기존 API 수정
API 설계
| Method | Endpoint | 설명 | 신규/수정 |
|---|---|---|---|
| POST | /api/ImageEdit/Save | 편집된 페이지 이미지 배열을 PDF로 변환하여 저장 | 신규 |
신규 파일
| 파일 | 설명 |
|---|---|
wwwroot/js/bdr-editor.js | Canvas 기반 이미지 편집기 (브러시/사각형/멀티페이지/색상지정) |
Controllers/ImageEditController.cs | 편집 저장 API (멀티페이지 → PDF 변환) |
수정 파일
| 파일 | 변경 내용 |
|---|---|
SngDocUploadPanel.razor | OnEditImage 콜백 + 편집 아이콘 추가 |
OrderList.razor | 편집 모달 UI |
OrderList.razor.cs | 편집 모달 로직 (파일 로드, 저장, 모드 전환, 색상지정) |
SalesOrderList.razor | 편집 모달 UI (동일) |
SalesOrderList.razor.cs | 편집 모달 로직 (동일) |
index.html | pdf.js CDN + bdr-editor.js 스크립트 등록 |
사용 라이브러리
| 라이브러리 | 용도 | 위치 |
|---|---|---|
| pdf.js (CDN) | 브라우저에서 PDF → Canvas 렌더링 | 프론트 |
| PuppeteerSharp (기존) | 편집된 이미지 → PDF 변환 | 백엔드 |
참고사항
PDF 렌더링은 클라이언트(pdf.js)에서 처리, 서버 의존 없음. 편집은 Canvas 위 마스크 방식 (원본 변경 없이 표시만). 저장 시에만 마스크 영역을 배경색으로 채워서 최종 이미지 생성. 고정색 모드는 워터마크가 심한 문서에서 유용.
배경/목적
현재 입금 확인을 수동으로 하고 있음. 은행 API로 입금 여부를 자동 확인하고, 지급기한일 기준으로 바이어에게 페이먼트 리마인더 메일을 자동 발송하여 업무 효율화.
Part A — 입금 자동 확인
- 은행 API 연동하여 입금 내역 자동 조회 (은행사 추후 결정)
- 입금 확인되면 해당 주문의
PayCompleteDate(지급완료일) 자동 업데이트 - 주문 상태를 "정산완료"로 자동 변경
- 변경 이력에 "입금 확인 (자동)" 로그 기록
- 백그라운드 서비스로 주기적 조회 (예: 30분마다)
Part B — 페이먼트 리마인더 (바이어 대상)
- 지급기한일 2일 전 — 첫 번째 리마인더 메일 발송
- 지급기한일 1일 전 — 두 번째 리마인더 메일 발송
- 지급기한일 당일 — 세 번째 리마인더 메일 발송
- 지급기한일 지남 (미입금) — 연체 알림 메일 발송
- 이미 입금 완료된 주문은 리마인더 발송 안 함
- 리마인더 발송 이력을 OrderLog에 기록
- 기존 OutboundMailer 사용
- 백그라운드 서비스로 매일 오전 9시 자동 체크
영향 범위
- 바이어 관리 — 입금 상태 자동 업데이트
- 셀러 관리
- 정산 관리 — 지급완료일 자동 세팅
- 이메일 시스템 — 리마인더 메일 템플릿 신규
- 업체 관리
- 회원 관리
- DB 스키마 변경
- 신규 API 필요 — 은행 연동 + 리마인더 상태 조회
- 기존 API 수정 — OrderSettlement 자동 업데이트
API 설계
| Method | Endpoint | 설명 | 신규/수정 |
|---|---|---|---|
| POST | /api/Payment/CheckDeposit | 수동 입금 확인 트리거 | 신규 |
| GET | /api/Payment/ReminderStatus | 리마인더 발송 현황 조회 | 신규 |
| - | BackgroundService | 자동 입금 확인 (30분) + 리마인더 (매일 9시) | 신규 |
메일 템플릿
| 시점 | 제목 | 내용 |
|---|---|---|
| D-2 | [Seanergy] Payment Reminder - Invoice {주문번호} | 지급기한 2일 전 안내 |
| D-1 | [Seanergy] Payment Reminder - Invoice {주문번호} | 지급기한 1일 전 안내 |
| D-day | [Seanergy] Payment Due Today - Invoice {주문번호} | 오늘 지급기한 안내 |
| 연체 | [Seanergy] Payment Overdue - Invoice {주문번호} | 지급기한 초과 안내 |
참고사항
은행사 결정 후 API 연동 방식 확정. Part B(리마인더)는 은행 API 없이도 독립 구현 가능 — 지급기한일과 PayCompleteDate만 비교하면 됨.
배경/목적
바이어 관리 화면에서 라디오버튼 정렬 미흡, IMO 미표시, 필수 입력 필드 구분 부재 등으로 휴먼에러 발생 가능성 높음. UI/UX 차원에서 체계적으로 개선.
상세 요구사항
| # | 항목 | 현재 | 개선 | 적용 범위 |
|---|---|---|---|---|
| 1 | 라디오버튼 정렬 | DELIVERED / EX-WHARF, SPOT / TERM, FLOATING / FIXED 가 정렬 안 됨 | EX-WHARF 쪽으로 하단 정렬 (라디오그룹 세로 정렬 통일) | 바이어 관리 |
| 2 | IMO 필드 추가 | 헤더에 선박명만 표시, IMO 없음 | 선박명 옆에 IMO 표시 + 검색 헤더에도 IMO 컬럼 추가 | 바이어 관리 |
| 3 | 필수필드 빨간 테두리 | BDR 발행자, 바지선명, 공급완료일 필드가 일반 필드와 구분 안 됨 | 해당 3개 필드 테두리를 빨간색으로 표시하여 필수 입력 강조 | 바이어 관리만 (셀러 관리 제외) |
필수필드 빨간 테두리 대상 (바이어 관리만)
| 필드 | 위치 | 이유 |
|---|---|---|
| BDR 발행자 | 유종 정보 영역 | BDR 발행 시 누락 방지 |
| 바지선명 | 배선 정보 영역 | 정산 시 바지선 매칭 필수 |
| 공급완료일 | 공급일 영역 | DDD 계산 및 정산 기준일 |
영향 범위
- 바이어 관리 — OrderList.razor (라디오 정렬 + IMO + 빨간 테두리)
- 셀러 관리 — 적용 안 함
- 백엔드 변경 없음
주요 수정 파일
| 파일 | 변경 내용 |
|---|---|
OrderList.razor | 라디오버튼 정렬 CSS + IMO 컬럼 추가 + 필수필드 빨간 테두리 스타일 |
OrderList.razor.cs | IMO 데이터 바인딩 (기존 Ship.Imo 활용) |
배경/목적
톤당 수수료(브로커리지) 거래를 일반 거래와 구분해서 관리해야 함. 태그로 시각 구분 + 필터로 브로커리지 건만 조회 가능해야 함.
상세 요구사항
- 브로커리지 체크박스 추가: BUNKER ONLY 체크박스 바로 아래에 "BROKERAGE" 체크박스 추가
- 태그 표시: 기존 태그 영역(M/B, B/O, N/I)에 B/K : Brokerage 추가
- 태그 디자인: 다른 태그와 차별화된 독특한 스타일 (그라데이션 or 골드 계열)
- 브로커리지 필터 버튼: 태그 헤더 위에 "브로커리지" 필터 버튼 추가
- 클릭 시 브로커리지 태그가 있는 주문만 필터링
- 다시 클릭 시 필터 해제 (토글)
- 태그 정보 팝오버 업데이트: 기존 M/B, B/O, N/I에 B/K 설명 추가
태그 디자인
| 태그 | 약어 | 스타일 | 예시 |
|---|---|---|---|
| Multi barge | M/B | 기존 스타일 | M/B |
| Bunker only | B/O | 기존 스타일 | B/O |
| Not internal | N/I | 기존 스타일 | N/I |
| Brokerage | B/K | 골드 그라데이션 + 볼드 | B/K |
영향 범위
- 바이어 관리 — OrderList.razor (체크박스 + 태그 + 필터 버튼)
- 셀러 관리 — SalesOrderList.razor (태그 표시 + 필터)
- 기존 API 수정 — 브로커리지 플래그 저장/조회
- DB — OrderInfo에 IsBrokerage 필드 추가 (bit)
DB 변경
| 테이블 | 컬럼 | 타입 | 설명 |
|---|---|---|---|
| OrderInfo | IsBrokerage | bit (default 0) | 브로커리지 거래 여부 |
주요 수정 파일
| 파일 | 변경 내용 |
|---|---|
OrderList.razor | BROKERAGE 체크박스 + B/K 태그 + 필터 버튼 UI |
OrderList.razor.cs | IsBrokerage 바인딩 + 필터 로직 |
SalesOrderList.razor | B/K 태그 표시 |
SalesOrderList.razor.cs | IsBrokerage 바인딩 |
OrdersController.cs | IsBrokerage 필터 쿼리 파라미터 |
OrderInfo.cs | IsBrokerage 프로퍼티 추가 |
참고
브로커리지 태그는 골드 그라데이션으로 다른 태그와 시각적 차별화. 필터 버튼은 활성화 시 골드 배경으로 토글 표시.
배경/목적
구매관리 주문 상세의 추가비용(AddPay) 항목에 현재 BARGE CHARGE만 있음. 실무에서 자주 사용하는 DEMURRAGE(정박료)와 AGENT FEE(에이전트 수수료)를 추가비용 드롭다운에 추가해야 함.
상세 요구사항
- 추가비용 항목(Item) 드롭다운에 DEMURRAGE 옵션 추가
- 추가비용 항목(Item) 드롭다운에 AGENT FEE 옵션 추가
- 기존 BARGE CHARGE, ETC 와 동일한 구조로 매출/매입 구분 가능
- 인쿼리 파싱 및 외부 API에서도 해당 항목 지원
영향 범위
- 바이어 관리 — 추가비용 등록 UI
- 셀러 관리
- 정산 관리
- 이메일 시스템
- DB 스키마 변경
- 신규 API 필요
- 기존 API 수정 — AddPay 항목 유효성
현재 추가비용 항목
| 항목 | 상태 |
|---|---|
| BARGE CHARGE | 기존 |
| ETC | 기존 |
| DEMURRAGE | 신규 추가 |
| AGENT FEE | 신규 추가 |
배경/목적
현재 메일 발송 시 "송신 요청 성공"만 표시되고, 실제로 메일이 도착했는지/실패했는지 확인할 수 없음. 본서버에서 메일이 안 보내지는 경우가 종종 발생. 메일 발송 상태를 콜백으로 받아서 UI에 반영해야 함.
현재 문제점
- 메일 발송 API 호출 → "송신 요청 성공" 표시 → 실제 발송 여부 불명
- 발송 실패 시 사용자에게 알림 없음
- 발송 중/성공/실패 상태를 구분할 수 없음
- 본서버에서 간헐적으로 메일 발송 실패 발생
상세 요구사항
- 발송 상태 콜백: 메일 서비스(NAVER Outbound Mail)에서 발송 결과 콜백 수신
- 상태 구분:
PENDING— 발송 요청됨 (대기 중)SENDING— 발송 중SUCCESS— 발송 완료 (수신자 도달)FAILED— 발송 실패BOUNCED— 반송됨 (주소 오류 등)
- UI 반영: 발송 이력 목록에 상태 컬럼 추가 (색상 뱃지로 표시)
- 실패 알림: 발송 실패 시 관리자에게 알림 (웹 알림 또는 재발송 버튼)
- 재발송: 실패한 메일을 1클릭으로 재발송할 수 있는 기능
- 자동 재시도: 발송 실패 시 자동 재시도 (최대 3회, 5분 간격)
영향 범위
- 바이어 관리 — 발송 이력 UI
- 셀러 관리 — 발송 이력 UI
- 정산 관리
- 이메일 시스템 — 콜백 수신 + 상태 저장 + 재시도
- 업체 관리
- DB 스키마 변경 — OutboundMail 상태 필드 확장
- 신규 API 필요 — 콜백 수신 엔드포인트
- 기존 API 수정 — 발송 이력 조회에 상태 포함
발송 상태 UI 예시
| 상태 | 색상 | 표시 |
|---|---|---|
| PENDING | 회색 | 대기 중 |
| SENDING | 파란색 | 발송 중 |
| SUCCESS | 초록색 | 발송 완료 |
| FAILED | 빨간색 | 발송 실패 [재발송] |
| BOUNCED | 주황색 | 반송됨 |
참고사항
NAVER Outbound Mail API에서 콜백 지원 여부 확인 필요. 콜백 미지원 시 폴링(주기적 상태 조회) 방식으로 대체 가능.
배경/목적
동림 인보이스 등에서 환율을 수동으로 확인하고 있음. 기존 환율 API가 이미 연동되어 있으므로, 날짜를 선택하면 해당 날짜의 환율을 바로 조회할 수 있는 UI를 추가. 로그아웃 버튼 왼쪽에 배치.
상세 요구사항
- 상단 네비게이션 바의 로그아웃 버튼 왼쪽에 환율 조회 영역 배치
- 날짜 선택 (DatePicker) + 환율 표시 영역
- 날짜 선택 시 해당 날짜의 USD/KRW 환율을 API에서 자동 조회
- 기본값: 오늘 날짜의 환율
- 기존
ExchangeRateService/ExchangeRateController활용 - 환율 데이터가 없는 날짜 (주말/공휴일)는 가장 가까운 영업일 환율 표시
UI 배치
[바이어 관리] [셀러 관리] [정산관리] [업체관리] [회원관리] 📅 2026-04-10 💰 1,382.50원/USD [로그아웃]
영향 범위
- 바이어 관리 — 상단 네비게이션
- 셀러 관리 — 상단 네비게이션 (공통 레이아웃)
- 정산 관리
- 이메일 시스템
- 업체 관리
- DB 스키마 변경
- 신규 API 필요
- 기존 API 수정
기존 환율 인프라
| 항목 | 현재 상태 |
|---|---|
| 환율 API | ExchangeRateController — 이미 존재 |
| 환율 서비스 | ExchangeRateService — 이미 존재 |
| 환율 DB | ExchangeRate 테이블 — 이미 존재 |
| 외부 API | ExchangeRate API Key — GlobalDatas에 이미 로드 |
참고사항
환율 표시 UI는 공통 레이아웃(SngNavMenu.razor)에 추가하면 모든 페이지에서 보임. 동림 인보이스 발행 시 해당 환율을 자동으로 적용하는 기능은 추후 확장 가능.
배경/목적
외부 시스템에서 인쿼리 텍스트를 API로 전송하면, 웹에서 인쿼리 파싱 → 주문 등록하는 것과 동일하게 바이어 주문 + 셀러 주문이 자동 생성됩니다. 수작업 없이 시스템 간 자동 연동이 가능합니다.
상세 요구사항
- API Key 인증: 헤더
X-Api-Key로 인증. DBExternalApi테이블에서 키 관리 - 인쿼리 텍스트 파싱: 기존
InquiryTextParser와 동일한 파싱 엔진 사용 - 자동 주문 생성: 웹 인쿼리 파싱과 동일한 4단계 흐름 (POST 생성 → GET 조회 → PUT 셀러 정보 수정 → 셀러 지급기한 업데이트)
- 동시 호출 제어: SemaphoreSlim(1,1)로 동시 1개만 처리 → 주문번호 정합성 보장
- 자동 생성 항목: 바이어 주문, 셀러 주문, 유종/가격, 추가비용(BARGE CHARGE), 지급기한(DDD+공휴일), 에이전트, 선박(미등록 시 자동 생성)
API 설계
| Method | Endpoint | 설명 | 신규/수정 |
|---|---|---|---|
| POST | /api/ExternalInquiry/Create | 인쿼리 텍스트 → 자동 주문 생성 | 신규 |
인증 방식
| 항목 | 설명 |
|---|---|
| 인증 헤더 | X-Api-Key: 발급받은키 |
| 키 저장 | DB ExternalApi 테이블 (ApiName = 'EXTERNAL_INQUIRY') |
| 키 발급 | DB INSERT 1줄 (업체별 별도 키 발급 가능) |
| 키 폐기 | 해당 행 DELETE |
신규 파일
| 파일 | 설명 |
|---|---|
Authentication/ApiKeyAuthAttribute.cs | API Key 인증 필터 |
Controllers/ExternalInquiryController.cs | 외부 인쿼리 API 엔드포인트 |
수정 파일
| 파일 | 변경 내용 |
|---|---|
Program.cs | AddControllersAsServices() 추가 |
OrderController.cs | FullDetailsOfOrderForAdmin AllowAnonymous + RegUserId 지원 |
DB 변경
| 테이블 | 변경 |
|---|---|
ExternalApi | EXTERNAL_INQUIRY 행 INSERT (API Key + 서비스 유저 GUID) |
요청/응답 예시
// 요청
POST /api/ExternalInquiry/Create
X-Api-Key: sk-sngy-8f2a4c6e9b1d3f5a7c0e2b4d
{"text":"VESSEL NAME: DIAMOND SEA\nIMO : 9112233\n..."}
// 응답 (성공)
{
"code": 200,
"message": "주문 생성 완료",
"data": {
"orderId": "20260331WMQG",
"vesselName": "DIAMOND SEA",
"buyerCompany": "홍콩이마린",
"sellerCompany": "동림",
"buyerPayDueDate": "2026-04-13",
"sellerPayDueDate": "2026-03-23"
}
}