Files
superport_v2/doc/stock_approval_system_spec_v4.md

34 KiB

간단 입·출고 + 결재 시스템 설계서 (최종 v4)

버전: 2025-09-18 16:22:30Z (UTC)
요약: 벤더 ↔ 창고 ↔ 고객사 간 물품 이동(입고/출고)을 관리하는 최소구성 시스템.

  • 트랜잭션당 1개의 결재(1:1), 승인자 순서 기반의 순차 결재 지원.
  • 다음 승인자로 넘어가면 안 되는 상태approval_statuses.is_blocking_next로 제어.
  • 모든 테이블(타입/코드 테이블 포함)에 공통 컬럼 적용: is_active, is_deleted, created_at, updated_at.
  • 벤더는 트랜잭션 헤더에 연결하지 않음(벤더는 제품을 통해서만 추적).
  • customer_roles 제거: 트랜잭션-고객은 역할 없이 다수 연결만 허용.
  • 타입값은 별도 테이블로 분리하며 *_code/정렬순서 미사용, ID 기반 참조.
  • 메뉴 접근은 groupsgroup_menu_permissions를 통해 제어되며, 모든 직원은 정확히 하나의 그룹에 속함.

0) 핵심 비즈니스 규칙

  • 제품 1개는 반드시 1개의 벤더에 소속 (products.vendor_id 필수).
  • 트랜잭션 1건당 결재 1건(1:1, 소프트삭제 제외).
  • 결재는 승인자 순서(approval_steps.step_order)대로만 진행.
  • 각 단계 상태가 blocking이면 다음 단계로 이동 불가.
  • 트랜잭션에는 여러 고객사를 연결할 수 있음(역할 없음).
  • 모든 직원은 그룹에 속하며(employees.group_id), 그룹-메뉴 권한(group_menu_permissions)으로 메뉴별 CRUD 가능 여부가 결정됨.
  • 고객사는 유형is_partner/is_general 플래그로 구분하며 둘 중 하나 이상이 true여야 함(기본: 일반 true, 파트너 false).
  • 고객사는 담당자 이름(contact_name)을 별도 관리하며 고객 응답에 항상 포함.
  • 반복되는 결재 라인은 결재 템플릿으로 저장 후 호출하여 재사용 가능.
  • 모든 삭제는 소프트 삭제(is_deleted=true)이며, 삭제 시 is_active=false로 내림.

1) 개념 ERD

erDiagram
vendors ||--o{ products : supplies
uoms ||--o{ products : measured_in

warehouses ||--o{ stock_transactions : occurs_in
transaction_types ||--o{ stock_transactions : typed_as
transaction_statuses ||--o{ stock_transactions : has_status

stock_transactions ||--o{ transaction_lines : has
products ||--o{ transaction_lines : item

stock_transactions ||--o{ transaction_customers : serves
customers ||--o{ transaction_customers : party

stock_transactions ||--|| approvals : has_one
approval_statuses ||--o{ approvals : overall_status
approvals ||--o{ approval_steps : has_sequence
approval_statuses ||--o{ approval_steps : step_status
approval_steps ||--o{ approval_histories : logs
approval_actions ||--o{ approval_histories : acted_as

approval_templates ||--o{ approval_template_steps : has_sequence

employees ||--o{ approvals : requested_by
employees ||--o{ approval_steps : assigned_to
employees ||--o{ approval_histories : actor
employees ||--o{ stock_transactions : created_by
employees ||--o{ approval_templates : authored
employees ||--o{ approval_template_steps : template_approver
groups ||--o{ employees : members
groups ||--o{ group_menu_permissions : controls
menus ||--o{ group_menu_permissions : target
zipcodes ||--o{ warehouses : located
zipcodes ||--o{ customers : addressed

2) 공통 컬럼 (모든 테이블 공통 적용)

영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
note 비고 text - - N N N -
is_active 사용여부 boolean - true Y N N -
is_deleted 삭제여부(소프트) boolean - false Y N N -
created_at 생성일시 timestamp - now() Y N N -
updated_at 변경일시 timestamp - now() Y N N -

모든 테이블(타입/코드 테이블 포함)에 위 4개 컬럼을 명시적으로 포함.
note는 테이블별 메모/추가 설명을 저장하는 자유 텍스트 필드.
updated_at은 UPDATE 시 자동 갱신(트리거/생성 컬럼 권장). 삭제 시 is_deleted=true, is_active=false 처리.


3) 테이블 정의

3.1 vendors (벤더)

영문테이블명 한글테이블명
vendors 벤더
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 벤더ID bigint - identity Y Y Y -
vendor_code 벤더코드 varchar 30 - Y (부분유니크: is_deleted=false) N -
vendor_name 벤더명 varchar 100 - Y
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

zipcodes 테이블이 우편번호 마스터를 보유하며, 참조 측 테이블은 zipcode_idzipcodes.id FK만 저장한다. 응답 시에는 FK를 통해 조회한 우편번호 요약 정보를 포함한다.


3.2 warehouses (창고)

영문테이블명 한글테이블명
warehouses 창고
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 창고ID bigint - identity Y Y Y -
warehouse_code 창고코드 varchar 30 - Y (부분유니크: is_deleted=false) N -
warehouse_name 창고명 varchar 100 - Y
zipcode_id 우편번호ID bigint - - N zipcodes.id
address_detail 상세주소 varchar 200 - N -
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

3.3 customers (고객사)

영문테이블명 한글테이블명
customers 고객사
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 고객사ID bigint - identity Y Y Y -
customer_code 고객사코드 varchar 30 - Y (부분유니크: is_deleted=false) N -
customer_name 고객사명 varchar 100 - Y
contact_name 담당자명 varchar 100 - N -
is_partner 파트너여부 boolean - false Y -
is_general 일반여부 boolean - true Y -
email 이메일 varchar 100 - N -
mobile_no 모바일번호 varchar 20 - N -
zipcode_id 우편번호ID bigint - - N zipcodes.id
address_detail 상세주소 varchar 200 - N -
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

contact_name은 고객사의 대표 연락 창구로 사용하는 담당자 실명. 필수는 아니며 미입력 시 null 저장.

고객/창고 모두 zipcode_id를 통해 zipcodes.id와 연결하며, API 응답은 FK가 가리키는 zipcodes 행에서 필요한 우편번호 메타 정보를 추출해 제공한다.


3.4 employees (사원)

영문테이블명 한글테이블명
employees 사원
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 사원ID bigint - identity Y Y Y -
employee_no 사번 varchar 30 - Y (부분유니크: is_deleted=false) N -
employee_name 성명 varchar 100 - Y
email 이메일 varchar 100 - N Y
mobile_no 모바일번호 varchar 20 - N
group_id 그룹ID bigint - - Y groups.id
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

3.5 zipcodes (우편번호)

영문테이블명 한글테이블명
zipcodes 우편번호
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 우편번호ID bigint - identity Y Y Y -
zipcode 우편번호 varchar 5 - Y N -
sido 시도 varchar 50 - Y -
sido_eng 시도영문 varchar 100 - N -
sigungu 시군구 varchar 100 - Y -
sigungu_eng 시군구영문 varchar 100 - N -
eupmyeon 읍면 varchar 100 - N -
eupmyeon_eng 읍면영문 varchar 100 - N -
road_code 도로명코드 varchar 12 - Y -
road_name 도로명 varchar 200 - Y -
road_name_eng 도로명영문 varchar 200 - N -
underground_flag 지하여부 varchar 1 'N' Y -
building_main_no 건물번호본번 integer - 0 Y -
building_sub_no 건물번호부번 integer - 0 N -
building_mgmt_no 건물관리번호 varchar 25 - Y -
bulk_receiver 다량배달처명 varchar 200 - N -
sigungu_building_name 시군구용건물명 varchar 200 - N -
legal_dong_code 법정동코드 varchar 10 - Y -
legal_dong_name 법정동명 varchar 100 - Y -
ri_name 리명 varchar 100 - N -
admin_dong_name 행정동명 varchar 100 - N -
mountain_flag 산여부 varchar 1 'N' Y -
land_main_no 지번본번 integer - 0 Y -
town_serial_no 읍면동일련번호 integer - 0 N -
land_sub_no 지번부번 integer - 0 N -
old_zipcode 구우편번호 varchar 6 - N -
zipcode_serial_no 우편번호일련번호 integer - 0 Y -
search_text 검색텍스트 text - - N -
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

도로명 주소 데이터와 매핑되는 5자리 우편번호 기준. id는 내부용 서러겟 PK이며, zipcode는 동일 코드가 여러 주소 행에 등장할 수 있다.


3.6 menus (메뉴)

영문테이블명 한글테이블명
menus 메뉴
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 메뉴ID bigint - identity Y Y Y -
menu_code 메뉴코드 varchar 50 - Y (부분유니크: is_deleted=false) N -
menu_name 메뉴명 varchar 100 - Y
parent_menu_id 상위메뉴ID bigint - - N menus.id
route_path 경로 varchar 255 - N -
display_order 표시순서 integer - 0 Y -
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

메뉴는 계층 구조를 지원하며 parent_menu_id가 NULL이면 1차 메뉴로 간주.


3.8 groups (그룹)

영문테이블명 한글테이블명
groups 그룹
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 그룹ID bigint - identity Y Y Y -
group_name 그룹명 varchar 100 - Y (부분유니크: is_deleted=false) N -
group_description 그룹설명 varchar 255 - N -
is_default 기본그룹여부 boolean - false Y
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

group_menu_permissions를 통해 각 그룹별 메뉴 CRUD 권한을 정의하며, 사원은 employees.group_id로 그룹에 연결됨.


3.9 group_menu_permissions (그룹_메뉴_권한)

영문테이블명 한글테이블명
group_menu_permissions 그룹_메뉴_권한
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 메뉴그룹권한ID bigint - identity Y Y Y -
group_id 그룹ID bigint - - Y (복합유니크: group_id, menu_id, is_deleted) N groups.id
menu_id 메뉴ID bigint - - Y (복합유니크: group_id, menu_id, is_deleted) N menus.id
can_create 생성권한 boolean - false Y -
can_read 조회권한 boolean - true Y -
can_update 수정권한 boolean - false Y -
can_delete 삭제권한 boolean - false Y -
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

각 메뉴에 대한 CRUD 권한을 그룹 단위로 정의하며, 권한 미설정 시 기본적으로 조회만 허용.


3.10 uoms (단위) — 타입 테이블

영문테이블명 한글테이블명
uoms 단위
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 단위ID bigint - identity Y Y Y -
uom_name 단위명 varchar 100 - Y (선택) 부분유니크 N -
is_default 기본여부 boolean - false Y
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

예시 값: EA(기본 단위), BOX, KG, LITER 등.


3.11 products (제품)

영문테이블명 한글테이블명
products 제품
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 제품ID bigint - identity Y Y Y -
product_code 제품코드 varchar 30 - Y (부분유니크: is_deleted=false) N -
product_name 제품명 varchar 100 - Y
vendor_id 벤더ID bigint - - Y vendors.id
uom_id 단위ID bigint - - Y uoms.id
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

3.12 transaction_types (입출고_유형) — 타입 테이블

영문테이블명 한글테이블명
transaction_types 입출고_유형
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 유형ID bigint - identity Y Y Y -
type_name 유형명 varchar 100 - Y (선택) 부분유니크 N -
is_default 기본여부 boolean - false Y
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

예시 값: 입고(is_default=true), 출고.


3.13 transaction_statuses (트랜잭션_상태) — 타입 테이블

영문테이블명 한글테이블명
transaction_statuses 트랜잭션_상태
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 상태ID bigint - identity Y Y Y -
status_name 상태명 varchar 100 - Y (선택) 부분유니크 N -
is_default 기본여부 boolean - false Y
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

예시 값: 정상(is_default), 반품, 폐기.


3.14 stock_transactions (입출고_트랜잭션)

영문테이블명 한글테이블명
stock_transactions 입출고_트랜잭션
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 트랜잭션ID bigint - identity Y Y Y -
transaction_no 트랜잭션번호 varchar 30 - Y (부분유니크: is_deleted=false) N -
transaction_type_id 입출고유형ID bigint - - Y transaction_types.id
transaction_status_id 트랜잭션상태ID bigint - - Y transaction_statuses.id
warehouse_id 창고ID bigint - - Y warehouses.id
transaction_date 처리일자 date - current_date Y -
created_by_id 작성자ID bigint - - N employees.id
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

주의: 벤더ID 없음. 벤더 정보는 라인의 product_id가 가리키는 products.vendor_id로 파생. 목록 조회는 customer_id 쿼리 파라미터를 지원해 특정 고객이 연결된 트랜잭션만 필터링할 수 있다. (2024-10 갱신)


3.15 transaction_lines (트랜잭션_라인)

영문테이블명 한글테이블명
transaction_lines 트랜잭션_라인
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 라인ID bigint - identity Y Y Y -
transaction_id 트랜잭션ID bigint - - Y stock_transactions.id
line_no 라인번호 integer - 1 Y (복합유니크: transaction_id, line_no, is_deleted) N -
product_id 제품ID bigint - - Y products.id
quantity 수량 numeric 20,6 0 Y -
unit_price 단가 numeric 20,6 0 N -
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

3.16 transaction_customers (트랜잭션_고객사)

영문테이블명 한글테이블명
transaction_customers 트랜잭션_고객사
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id bigint - identity Y Y Y -
transaction_id 트랜잭션ID bigint - - Y stock_transactions.id
customer_id 고객사ID bigint - - Y (복합유니크: transaction_id, customer_id, is_deleted) N customers.id
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

3.17 approval_statuses (결재_상태) — 타입 테이블

영문테이블명 한글테이블명
approval_statuses 결재_상태
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 상태ID bigint - identity Y Y Y -
status_name 상태명 varchar 100 - Y (선택) 부분유니크 N -
is_default 기본여부 boolean - false Y
is_blocking_next 차기이동차단 boolean - true Y
is_terminal 종결여부 boolean - false Y
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

예시 값:

  • 대기(is_default=true, is_blocking_next=true, is_terminal=false)
  • 진행중(is_blocking_next=true, is_terminal=false)
  • 보류(is_blocking_next=true, is_terminal=false)
  • 승인(is_blocking_next=false, is_terminal=false)
  • 반려(is_blocking_next=true, is_terminal=true).

3.18 approval_actions (결재_행위) — 타입 테이블

영문테이블명 한글테이블명
approval_actions 결재_행위
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 행위ID bigint - identity Y Y Y -
action_name 행위명 varchar 100 - Y (선택) 부분유니크 N -
is_default 기본여부 boolean - false Y
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

예시 값: approve(승인), reject(반려), comment(코멘트).


3.19 approvals (결재)

영문테이블명 한글테이블명
approvals 결재
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 결재ID bigint - identity Y Y Y -
transaction_id 트랜잭션ID bigint - - Y (부분유니크: is_deleted=false) N stock_transactions.id
approval_no 결재번호 varchar 30 - Y (부분유니크: is_deleted=false) N -
approval_status_id 전체결재상태ID bigint - - Y approval_statuses.id
current_step_id 현재단계ID bigint - - N approval_steps.id
requested_by_id 상신자ID bigint - - Y employees.id
requested_at 상신일시 timestamp - now() Y -
decided_at 최종결정일시 timestamp - - N -
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

3.20 approval_steps (결재_단계)

영문테이블명 한글테이블명
approval_steps 결재_단계
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 단계ID bigint - identity Y Y Y -
approval_id 결재ID bigint - - Y approvals.id
step_order 단계순서 integer - 1 Y (복합유니크: approval_id, step_order, is_deleted) N -
approver_id 승인자ID bigint - - Y employees.id
step_status_id 단계상태ID bigint - - Y approval_statuses.id
assigned_at 배정일시 timestamp - now() Y -
decided_at 결정일시 timestamp - - N -
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

3.21 approval_histories (결재_승인이력)

영문테이블명 한글테이블명
approval_histories 결재_승인이력
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 이력ID bigint - identity Y Y Y -
approval_id 결재ID bigint - - Y approvals.id
approval_step_id 결재단계ID bigint - - Y approval_steps.id
approver_id 승인자ID bigint - - Y employees.id
approval_action_id 결재행위ID bigint - - Y approval_actions.id
from_status_id 변경전상태ID bigint - - N approval_statuses.id
to_status_id 변경후상태ID bigint - - Y approval_statuses.id
action_at 작업일시 timestamp - now() Y -
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

3.22 approval_templates (결재_템플릿)

영문테이블명 한글테이블명
approval_templates 결재_템플릿
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 템플릿ID bigint - identity Y Y Y -
template_code 템플릿코드 varchar 30 - Y (부분유니크: is_deleted=false) N -
template_name 템플릿명 varchar 100 - Y
description 설명 varchar 255 - N -
created_by_id 작성자ID bigint - - Y employees.id
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

3.23 approval_template_steps (결재_템플릿_단계)

영문테이블명 한글테이블명
approval_template_steps 결재_템플릿_단계
영문필드명 한글필드명 타입 길이 기본값 NOT NULL UNIQUE PK FK
id 템플릿단계ID bigint - identity Y Y Y -
template_id 템플릿ID bigint - - Y (복합유니크: template_id, step_order, is_deleted) N approval_templates.id
step_order 단계순서 integer - 1 Y (복합유니크: template_id, step_order, is_deleted) N -
approver_id 승인자ID bigint - - Y employees.id
note 비고 text - - N -
is_active 사용여부 boolean - true Y
is_deleted 삭제여부 boolean - false Y
created_at 생성일시 timestamp - now() Y
updated_at 변경일시 timestamp - now() Y

템플릿 단계는 실제 결재 단계 생성 시 그대로 복제되며, step_order 순서대로 승인자가 배치됨.


4) FK 관계 (source → target)

  • menus.parent_menu_idmenus.id
  • employees.group_idgroups.id
  • group_menu_permissions.group_idgroups.id
  • group_menu_permissions.menu_idmenus.id
  • products.vendor_idvendors.id
  • products.uom_iduoms.id
  • stock_transactions.warehouse_idwarehouses.id
  • stock_transactions.created_by_idemployees.id
  • stock_transactions.transaction_type_idtransaction_types.id
  • stock_transactions.transaction_status_idtransaction_statuses.id
  • transaction_lines.transaction_idstock_transactions.id
  • transaction_lines.product_idproducts.id
  • transaction_customers.transaction_idstock_transactions.id
  • transaction_customers.customer_idcustomers.id
  • approvals.transaction_idstock_transactions.id
  • approvals.approval_status_idapproval_statuses.id
  • approvals.current_step_idapproval_steps.id
  • approvals.requested_by_idemployees.id
  • approval_steps.approval_idapprovals.id
  • approval_steps.approver_idemployees.id
  • approval_steps.step_status_idapproval_statuses.id
  • approval_histories.approval_idapprovals.id
  • approval_histories.approval_step_idapproval_steps.id
  • approval_histories.approver_idemployees.id
  • approval_histories.approval_action_idapproval_actions.id
  • approval_histories.from_status_idapproval_statuses.id
  • approval_histories.to_status_idapproval_statuses.id
  • approval_templates.created_by_idemployees.id
  • approval_template_steps.template_idapproval_templates.id
  • approval_template_steps.approver_idemployees.id

5) 비즈니스/검증 규칙

  • 제품 등록 시 vendor_id 필수.
  • 입고(transaction_type_id=입고) 트랜잭션의 공급자 정보는 라인 제품의 벤더로만 해석. (헤더에 벤더 금지)
  • 출고 트랜잭션은 transaction_customers 최소 1건 필요.
  • 결재는 트랜잭션당 1건(미삭제 기준)만 허용.
  • 단계 전이는 현재 단계에서만 수행 가능. blocking 상태에서는 차기 이동 불가.
  • 수량/단가 음수 금지(CHECK).
  • 그룹이 비활성(is_active=false) 또는 삭제되면 해당 그룹 권한/구성원은 즉시 무효 처리.
  • 사원의 소속 그룹(employees.group_id)에서 해당 메뉴에 대한 can_create|can_update|can_delete 중 하나라도 true이면 그 동작을 수행할 수 있음.

6) 인덱스/유니크 권장

  • 부분 유니크(또는 복합 유니크)로 소프트 삭제와 공존:
    • vendors(vendor_code), warehouses(warehouse_code), customers(customer_code), employees(employee_no), menus(menu_code), groups(group_name), products(product_code), stock_transactions(transaction_no), approvals(approval_no)
    • group_menu_permissions(group_id, menu_id, is_deleted)
    • approvals(transaction_id) — 미삭제 조건에서 1:1 보장
    • transaction_lines(transaction_id, line_no, is_deleted)
    • transaction_customers(transaction_id, customer_id, is_deleted)
  • FK 및 조회 인덱스: 모든 *_id, updated_at, is_deleted, is_active.

7) 에러 규격(예시)

  • 400 BAD_REQUEST — 필수 필드 누락, 형식 오류
  • 409 CONFLICT — 유니크 충돌(코드/번호/조합), 현재 단계 아님
  • 422 UNPROCESSABLE_ENTITY — 비즈니스 규칙 위반(출고인데 고객 없음, blocking 상태에서 이동 등)
  • 404 NOT_FOUND — 리소스 없음 또는 삭제됨(deleted=false 기본 필터로 미노출)

8) 마이그레이션 가이드(요약)

  1. stock_transactions에서 vendor_id 드롭.
  2. customer_roles 테이블 및 관련 컬럼 드롭.
  3. 모든 타입/코드 테이블에 공통 컬럼 4종 추가(미존재 시).
  4. 부분 유니크 인덱스(WHERE is_deleted=false) 또는 (컬럼, is_deleted) 복합 유니크 구성.
  5. 기존 결재 이력은 approval_step_id 매핑(없으면 1단계로 귀속).
  6. approval_statusesis_blocking_next, is_terminal 값 시드.
  7. menus, groups, group_menu_permissions 신규 생성 및 기존 관리자 권한/사원-그룹 매핑을 employees.group_id로 이관.
  8. zipcodes 테이블 생성 및 도로명 주소 기준 데이터 적재.
  9. 모든 테이블에 note(text) 컬럼 추가 및 필요한 경우 기본값 NULL 유지.

9) 초기 시드 값(예시)

  • transaction_types: [입고, 출고] (is_default: 입고)
  • transaction_statuses: [초안, 상신, 승인, 반려, 완료] (is_default: 초안)
  • approval_statuses: [대기(pending, default, blocking), 진행중(in_progress, blocking), 보류(on_hold, blocking), 승인(approved, !blocking), 반려(rejected, blocking+terminal)]
  • approval_actions: [승인(approve), 반려(reject), 코멘트(comment)]
  • uoms: [EA(기본), BOX, KG ...]
  • menus: [대시보드, 입출고 관리, 결재 관리, 레포트 등] — 상위/하위 메뉴 구조 포함
  • groups: [전사 관리자(기본), 창고 관리자, 결재 담당자]
  • group_menu_permissions: 기본 그룹별 메뉴 권한(CRUD 플래그); 전사 관리자는 모든 메뉴 can_*=true, 역할별로 세분화 설정
  • zipcodes: 행정안전부 도로명 주소 DB(5자리) 최신본을 기준으로 일괄 적재

10) 구현 팁

  • updated_at 자동 갱신 트리거, 소프트 삭제 처리 트리거 권장.
  • 낙관적 잠금(선택): version(int) + ETag.
  • 병렬 결재 확장(선택): approval_stepsgroup_no, approval_mode(all|any) 도입.
  • /health 응답의 build_versionconfig/default.toml[app].build_version을 사용하며, script/deploy_remote.sh가 배포 아카이브 파일명에서 버전을 추출해 값을 주입한다.