영업관리 wace 컬럼 정합성 강화 + project_mgmt 도입

- project_mgmt 테이블 신규(75컬럼/89건) — wace 운영 동일 schema 추출. 판매·매출관리 메인 테이블
- 판매·매출 SQL을 contract_item 기반 → project_mgmt 메인 + sales_registration LEFT JOIN으로 전면 재작성
  * 요청납기 COALESCE(CI.due_date, T.due_date, CM.due_date) 패턴
  * 환종/serial_no는 sales_registration 우선, contract_item_serial 집계 fallback
  * 판매상태 wace 로직 한글 라벨(미판매/완판/분할판매) 동적 계산
  * 매출관리는 shippingDateRequired EXISTS 필터로 출하 등록된 프로젝트만 표시
- 주문서: order_date 매핑 CONTRACT_DATE → ORDER_DATE 정정, 원화총액·고객사요청사항 SQL 추가
- 견적/주문/판매 page.tsx에서 wace 블록 주석 컬럼 제거(제품구분/국내·해외/접수일/반납사유 등 14개)
- 환종 raw 코드 → contract_currency_name 한글명 바인딩으로 정정

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hjjeong
2026-05-07 16:50:45 +09:00
parent 73bd4f2ba7
commit 4175ae77c1
7 changed files with 335 additions and 105 deletions
@@ -92,8 +92,8 @@ export async function getList(filter: OrderListFilter) {
if (filter.paid_type) { conditions.push(`T.PAID_TYPE = $${idx++}`); params.push(filter.paid_type); }
if (filter.product) { conditions.push(`T.PRODUCT = $${idx++}`); params.push(filter.product); }
if (filter.contract_currency) { conditions.push(`T.CONTRACT_CURRENCY = $${idx++}`); params.push(filter.contract_currency); }
if (filter.order_start_date) { conditions.push(`T.CONTRACT_DATE >= $${idx++}`); params.push(filter.order_start_date); }
if (filter.order_end_date) { conditions.push(`T.CONTRACT_DATE <= $${idx++}`); params.push(filter.order_end_date); }
if (filter.order_start_date) { conditions.push(`T.ORDER_DATE >= $${idx++}`); params.push(filter.order_start_date); }
if (filter.order_end_date) { conditions.push(`T.ORDER_DATE <= $${idx++}`); params.push(filter.order_end_date); }
if (filter.search_partObjId) {
conditions.push(`EXISTS (SELECT 1 FROM contract_item CI WHERE CI.contract_objid = T.OBJID AND CI.status='ACTIVE' AND CI.part_objid = $${idx++})`);
params.push(filter.search_partObjId);
@@ -136,7 +136,7 @@ export async function getList(filter: OrderListFilter) {
,CC_CUR.code_name AS CONTRACT_CURRENCY_NAME
,T.EXCHANGE_RATE
,T.PO_NO
,T.CONTRACT_DATE AS ORDER_DATE
,T.ORDER_DATE AS ORDER_DATE
,T.RECEIPT_DATE
,T.REQ_DEL_DATE
,T.CONTRACT_RESULT
@@ -144,6 +144,16 @@ export async function getList(filter: OrderListFilter) {
,T.ORDER_SUPPLY_PRICE AS ORDER_SUPPLY_PRICE_SUM
,T.ORDER_VAT AS ORDER_VAT_SUM
,T.ORDER_TOTAL_AMOUNT AS ORDER_TOTAL_AMOUNT_SUM
,CASE
WHEN T.EXCHANGE_RATE IS NOT NULL AND T.EXCHANGE_RATE != ''
AND REPLACE(T.EXCHANGE_RATE, ',', '') ~ '^[0-9]+\\.{0,1}[0-9]*$'
AND CAST(REPLACE(T.EXCHANGE_RATE, ',', '') AS NUMERIC) != 0
THEN ROUND(
CAST(REPLACE(COALESCE(T.ORDER_TOTAL_AMOUNT, '0'), ',', '') AS NUMERIC)
* CAST(REPLACE(T.EXCHANGE_RATE, ',', '') AS NUMERIC), 2)
ELSE CAST(REPLACE(COALESCE(T.ORDER_TOTAL_AMOUNT, '0'), ',', '') AS NUMERIC)
END AS ORDER_TOTAL_AMOUNT_KRW
,T.CUSTOMER_REQUEST AS CUSTOMER_REQUEST
,T.WRITER
,U_WR.user_name AS WRITER_NAME
,T.PM_USER_ID
+171 -80
View File
@@ -59,88 +59,149 @@ export interface DeadlineInfoBody {
remark?: string;
}
// ─── 판매 그리드 (라인 단위) ──────────────────────────────────
// 수주된 contract_item 라인을 기준으로 sales_registration 매칭 표시.
// ─── 판매 그리드 (project_mgmt 라인 단위) ─────────────────────
// project_mgmt가 메인 (수주 확정된 라인 1건당 1행). sales_registration은 LEFT JOIN.
// wace mapper salesNcollectMgmt.xml getSalesMgmtGridList(line 815~) 패턴.
export async function getSaleList(filter: SaleListFilter) {
const pool = getPool();
const conditions: string[] = ["CI.status = 'ACTIVE'"];
const conditions: string[] = ["T.project_no IS NOT NULL", "T.project_no <> ''"];
const params: any[] = [];
let idx = 1;
if (filter.orderType) { conditions.push(`T.category_cd = $${idx++}`); params.push(filter.orderType); }
if (filter.poNo) { conditions.push(`T.po_no ILIKE $${idx++}`); params.push(`%${filter.poNo}%`); }
if (filter.customer_objid) { conditions.push(`T.customer_objid = $${idx++}`); params.push(filter.customer_objid); }
if (filter.search_partObjId) { conditions.push(`CI.part_objid = $${idx++}`); params.push(filter.search_partObjId); }
if (filter.search_partObjId) { conditions.push(`T.part_objid = $${idx++}`); params.push(filter.search_partObjId); }
if (filter.serialNo) {
conditions.push(`EXISTS (SELECT 1 FROM contract_item_serial CIS
WHERE CIS.item_objid = CI.objid AND CIS.status='ACTIVE'
WHERE CIS.item_objid = T.contract_item_objid AND CIS.status='ACTIVE'
AND UPPER(CIS.serial_no) LIKE UPPER($${idx++}))`);
params.push(`%${filter.serialNo}%`);
}
if (filter.orderDateFrom) { conditions.push(`T.contract_date >= $${idx++}`); params.push(filter.orderDateFrom); }
if (filter.orderDateTo) { conditions.push(`T.contract_date <= $${idx++}`); params.push(filter.orderDateTo); }
if (filter.orderDateFrom) { conditions.push(`COALESCE(T.contract_date, CM.order_date) >= $${idx++}`); params.push(filter.orderDateFrom); }
if (filter.orderDateTo) { conditions.push(`COALESCE(T.contract_date, CM.order_date) <= $${idx++}`); params.push(filter.orderDateTo); }
if (filter.shippingDateFrom) { conditions.push(`SR.shipping_date >= $${idx++}`); params.push(filter.shippingDateFrom); }
if (filter.shippingDateTo) { conditions.push(`SR.shipping_date <= $${idx++}`); params.push(filter.shippingDateTo); }
if (filter.shippingStatus) { conditions.push(`SR.shipping_order_status = $${idx++}`); params.push(filter.shippingStatus); }
if (filter.shippingDateTo) { conditions.push(`SR.shipping_date <= $${idx++}`); params.push(filter.shippingDateTo); }
if (filter.shippingStatus) { conditions.push(`SR.shipping_order_status = $${idx++}`); params.push(filter.shippingStatus); }
const where = `WHERE ${conditions.join(" AND ")}`;
const sql = `
SELECT
T.objid AS project_no
,T.contract_no AS contract_no
,T.category_cd AS order_type
,CC_CAT.code_name AS order_type_name
,T.contract_date AS order_date
T.objid AS project_objid
,T.project_no AS project_no
,T.contract_objid
,T.contract_item_objid
,T.contract_no AS contract_no
,T.category_cd AS order_type
,CC_CAT.code_name AS order_type_name
,COALESCE(T.contract_date, CM.order_date) AS order_date
,T.po_no
,T.req_del_date AS request_date
,COALESCE(NULLIF(CI.due_date, ''), NULLIF(T.due_date, ''), NULLIF(CM.due_date, '')) AS request_date
,T.due_date
,T.customer_objid
,C.customer_name AS customer
,T.product AS product_type
,CI.objid AS contract_item_objid
,CI.seq
,CI.part_objid
,CI.part_no AS product_no
,CI.part_name AS product_name
,CI.order_quantity
,COALESCE(SR.sales_quantity, 0) AS sales_quantity
,C.customer_name AS customer
,T.product AS product_type
,CC_PRD.code_name AS product_type_name
,T.area_cd AS nation
,CC_AREA.code_name AS nation_name
,T.part_objid
,T.part_no AS product_no
,T.part_name AS product_name
,T.quantity AS order_quantity
,COALESCE(SR.sales_quantity, 0) AS sales_quantity
,GREATEST(
CAST(REPLACE(NULLIF(CI.order_quantity,''), ',', '') AS NUMERIC) - COALESCE(SR.sales_quantity, 0),
COALESCE(CAST(NULLIF(REPLACE(T.quantity, ',', ''), '') AS NUMERIC), 0)
- COALESCE(SR.sales_quantity, 0),
0
) AS remaining_quantity
) AS remaining_quantity
,SR.sale_no
,SR.sales_unit_price
,SR.sales_supply_price
,SR.sales_vat
,SR.sales_total_amount
,SR.sales_currency
,CASE
WHEN SR.sales_exchange_rate IS NOT NULL AND SR.sales_exchange_rate <> 0
THEN ROUND(COALESCE(SR.sales_total_amount, 0) * SR.sales_exchange_rate, 2)
ELSE COALESCE(SR.sales_total_amount, 0)
END AS sales_total_amount_krw
,CASE
WHEN SR.sales_exchange_rate IS NOT NULL AND SR.sales_exchange_rate <> 0
THEN ROUND(
GREATEST(
COALESCE(CAST(NULLIF(REPLACE(T.quantity, ',', ''), '') AS NUMERIC), 0)
- COALESCE(SR.sales_quantity, 0),
0
) * COALESCE(SR.sales_unit_price, 0) * SR.sales_exchange_rate, 2)
ELSE 0
END AS remaining_amount_krw
/* 환종: sales_registration 우선, 없으면 project_mgmt.contract_currency (wace 패턴) */
,COALESCE(NULLIF(SR.sales_currency, ''), T.contract_currency) AS sales_currency
,COALESCE(CC_CUR_S.code_name, CC_CUR.code_name) AS sales_currency_name
,T.contract_currency
,CC_CUR.code_name AS contract_currency_name
,SR.sales_exchange_rate
,SR.shipping_date
,SR.shipping_method
,SR.shipping_order_status
,SR.manager_user_id
,U_MGR.user_name AS manager_name
,SR.incoterms
,SR.has_split_shipment
,T.contract_result AS order_status
,CC_RES.code_name AS order_status_name
,CASE WHEN SR.sale_no IS NULL THEN 'NOT_REGISTERED' ELSE 'REGISTERED' END AS sales_status
,T.production_status
,T.paid_type AS payment_type
,CASE WHEN T.paid_type='paid' THEN '유상' WHEN T.paid_type='free' THEN '무상' ELSE T.paid_type END AS payment_type_name
,T.area_cd AS nation
,CC_AREA.code_name AS nation_name
,SR.serial_no
FROM contract_item CI
JOIN contract_mgmt T ON T.objid = CI.contract_objid
/* S/N: contract_item_serial 집계 우선, 없으면 sales_registration 텍스트 fallback (wace 패턴) */
,COALESCE(NULLIF(CIS_AGG.serial_list, ''), SR.serial_no) AS serial_no
,T.contract_result AS order_status
,CC_RES.code_name AS order_status_name
/* 판매상태 — wace 로직: 합계가 수주량 이상이면 완판, 일부만 판매면 분할판매, 0이면 미판매 */
,CASE
WHEN COALESCE(SR_AGG.sales_qty_sum, 0) = 0 THEN '미판매'
WHEN COALESCE(SR_AGG.sales_qty_sum, 0) >= COALESCE(CAST(NULLIF(REPLACE(T.quantity, ',', ''), '') AS NUMERIC), 0) THEN '완판'
WHEN COALESCE(SR_AGG.sales_qty_sum, 0) > 0 THEN '분할판매'
ELSE ''
END AS sales_status
,T.sales_status AS sales_status_raw
,CM.paid_type AS payment_type
,CASE WHEN CM.paid_type='paid' THEN '유상' WHEN CM.paid_type='free' THEN '무상' ELSE CM.paid_type END AS payment_type_name
,CM.receipt_date
,CM.customer_request
,T.sales_deadline_date
,T.regdate
/* 출하지시상태/생산상태/분할S/N/거래명세서/주문서첨부 — 1차 placeholder */
,NULL::text AS production_status
,NULL::text AS split_serial_no
,'N'::text AS has_transaction_statement
,0 AS cu01_cnt
FROM project_mgmt T
LEFT JOIN contract_mgmt CM ON CM.objid = T.contract_objid
LEFT JOIN contract_item CI ON CI.objid = T.contract_item_objid AND CI.status = 'ACTIVE'
LEFT JOIN customer_mng C
ON C.customer_code = CASE WHEN T.customer_objid LIKE 'C_%' THEN substring(T.customer_objid, 3) ELSE T.customer_objid END
LEFT JOIN sales_registration SR ON SR.project_no = T.objid
LEFT JOIN comm_code CC_CAT ON CC_CAT.code_id = T.category_cd AND CC_CAT.status='active'
LEFT JOIN comm_code CC_AREA ON CC_AREA.code_id = T.area_cd AND CC_AREA.status='active'
LEFT JOIN comm_code CC_RES ON CC_RES.code_id = T.contract_result AND CC_RES.status='active'
LEFT JOIN sales_registration SR ON SR.project_no = T.project_no
LEFT JOIN user_info U_MGR ON U_MGR.user_id = SR.manager_user_id
LEFT JOIN comm_code CC_CAT ON CC_CAT.code_id = T.category_cd AND CC_CAT.status='active'
LEFT JOIN comm_code CC_AREA ON CC_AREA.code_id = T.area_cd AND CC_AREA.status='active'
LEFT JOIN comm_code CC_PRD ON CC_PRD.code_id = T.product AND CC_PRD.status='active'
LEFT JOIN comm_code CC_RES ON CC_RES.code_id = T.contract_result AND CC_RES.status='active'
LEFT JOIN comm_code CC_CUR ON CC_CUR.code_id = T.contract_currency AND CC_CUR.status='active'
LEFT JOIN comm_code CC_CUR_S ON CC_CUR_S.code_id = SR.sales_currency AND CC_CUR_S.status='active'
/* 판매수량 합계 (분할 판매 LIKE 패턴 — wace 동일) */
LEFT JOIN (
SELECT SR2.project_no, SUM(SR2.sales_quantity) AS sales_qty_sum
FROM sales_registration SR2
GROUP BY SR2.project_no
) SR_AGG ON SR_AGG.project_no LIKE T.project_no || '%'
/* contract_item_serial 집계 (S/N 표시) */
LEFT JOIN (
SELECT CIS.item_objid,
STRING_AGG(CIS.serial_no, ',' ORDER BY CIS.seq) AS serial_list
FROM contract_item_serial CIS
WHERE UPPER(CIS.status) = 'ACTIVE'
AND CIS.serial_no IS NOT NULL AND CIS.serial_no != ''
GROUP BY CIS.item_objid
) CIS_AGG ON CIS_AGG.item_objid = T.contract_item_objid
${where}
ORDER BY T.regdate DESC, CI.seq ASC
ORDER BY T.regdate DESC NULLS LAST, T.project_no DESC
`;
const res = await pool.query(sql, params);
@@ -159,52 +220,82 @@ export async function getRevenueList(filter: SaleListFilter) {
if (filter.orderType) { conditions.push(`T.category_cd = $${idx++}`); params.push(filter.orderType); }
if (filter.poNo) { conditions.push(`T.po_no ILIKE $${idx++}`); params.push(`%${filter.poNo}%`); }
if (filter.customer_objid) { conditions.push(`T.customer_objid = $${idx++}`); params.push(filter.customer_objid); }
if (filter.salesDeadlineFrom) { conditions.push(`SL.sales_deadline_date >= $${idx++}`); params.push(filter.salesDeadlineFrom); }
if (filter.salesDeadlineTo) { conditions.push(`SL.sales_deadline_date <= $${idx++}`); params.push(filter.salesDeadlineTo); }
if (filter.shippingDateFrom) { conditions.push(`SL.shipping_date >= $${idx++}`); params.push(filter.shippingDateFrom); }
if (filter.shippingDateTo) { conditions.push(`SL.shipping_date <= $${idx++}`); params.push(filter.shippingDateTo); }
if (filter.salesDeadlineFrom) { conditions.push(`T.sales_deadline_date >= $${idx++}`); params.push(filter.salesDeadlineFrom); }
if (filter.salesDeadlineTo) { conditions.push(`T.sales_deadline_date <= $${idx++}`); params.push(filter.salesDeadlineTo); }
if (filter.shippingDateFrom) { conditions.push(`SR.shipping_date >= $${idx++}`); params.push(filter.shippingDateFrom); }
if (filter.shippingDateTo) { conditions.push(`SR.shipping_date <= $${idx++}`); params.push(filter.shippingDateTo); }
const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
conditions.push("T.project_no IS NOT NULL");
conditions.push("T.project_no <> ''");
/* wace mapper: shippingDateRequired='Y' — 매출관리는 출하 등록된 프로젝트만 */
conditions.push(`EXISTS (
SELECT 1 FROM sales_registration SR_X
WHERE SR_X.project_no = T.project_no
AND SR_X.shipping_date IS NOT NULL
)`);
const where = `WHERE ${conditions.join(" AND ")}`;
const sql = `
SELECT
T.objid AS project_no
,T.contract_no
,T.category_cd AS order_type
,T.contract_date AS order_date
T.objid AS project_objid
,T.project_no AS project_no
,T.contract_objid
,T.contract_no AS contract_no
,T.category_cd AS order_type
,CC_CAT.code_name AS order_type_name
,COALESCE(T.contract_date, CM.order_date) AS order_date
,T.po_no
,T.customer_objid
,C.customer_name AS customer
,T.product AS product_type
,T.area_cd AS nation
,SL.log_id
,SL.target_objid
,SL.parent_sale_no
,SL.split_quantity AS sales_quantity
,SL.sales_unit_price
,SL.sales_supply_price
,SL.sales_vat
,SL.sales_total_amount
,SL.sales_currency
,SL.sales_exchange_rate
,SL.shipping_date
,SL.shipping_method
,SL.serial_no
,SL.split_serial_no
,SL.sales_deadline_date
,SL.tax_type
,SL.tax_invoice_date
,SL.export_decl_no
,SL.loading_date
,SL.sales_slip_date
,SL.sales_slip_menu_sq
,SL.remark
FROM shipment_log SL
JOIN contract_mgmt T ON T.objid = SL.target_objid
,C.customer_name AS customer
,T.product AS product_type
,CC_PRD.code_name AS product_type_name
,T.area_cd AS nation
,CC_AREA.code_name AS nation_name
,T.part_no AS product_no
,T.part_name AS product_name
,T.quantity AS sales_quantity_raw
,COALESCE(SR.sales_quantity,
CAST(NULLIF(REPLACE(T.quantity, ',', ''), '') AS NUMERIC), 0) AS sales_quantity
,SR.sales_unit_price
,SR.sales_supply_price
,SR.sales_vat
,SR.sales_total_amount
,CASE
WHEN SR.sales_exchange_rate IS NOT NULL AND SR.sales_exchange_rate <> 0
THEN ROUND(COALESCE(SR.sales_total_amount, 0) * SR.sales_exchange_rate, 2)
ELSE COALESCE(SR.sales_total_amount, 0)
END AS sales_total_amount_krw
,T.contract_currency
,CC_CUR.code_name AS contract_currency_name
,SR.sales_currency
,CC_CUR_S.code_name AS sales_currency_name
,SR.sales_exchange_rate
,SR.shipping_date
,SR.shipping_method
,SR.serial_no
,T.sales_deadline_date
,T.tax_type
,T.tax_invoice_date
,T.export_decl_no
,T.loading_date
,T.sales_slip_date
,T.sales_slip_menu_sq
,T.sales_status
/* 분할S/N · 거래명세서 — 1차 placeholder */
,NULL::text AS split_serial_no
,'N'::text AS has_transaction_statement
FROM project_mgmt T
LEFT JOIN contract_mgmt CM ON CM.objid = T.contract_objid
LEFT JOIN customer_mng C
ON C.customer_code = CASE WHEN T.customer_objid LIKE 'C_%' THEN substring(T.customer_objid, 3) ELSE T.customer_objid END
LEFT JOIN sales_registration SR ON SR.project_no = T.project_no
LEFT JOIN comm_code CC_CAT ON CC_CAT.code_id = T.category_cd AND CC_CAT.status='active'
LEFT JOIN comm_code CC_AREA ON CC_AREA.code_id = T.area_cd AND CC_AREA.status='active'
LEFT JOIN comm_code CC_PRD ON CC_PRD.code_id = T.product AND CC_PRD.status='active'
LEFT JOIN comm_code CC_CUR ON CC_CUR.code_id = T.contract_currency AND CC_CUR.status='active'
LEFT JOIN comm_code CC_CUR_S ON CC_CUR_S.code_id = SR.sales_currency AND CC_CUR_S.status='active'
${where}
ORDER BY SL.reg_date DESC
ORDER BY T.regdate DESC NULLS LAST, T.project_no DESC
`;
const res = await pool.query(sql, params);
@@ -0,0 +1,102 @@
-- ============================================================
-- project_mgmt — 판매·매출관리 메인 테이블
-- ----------------------------------------------------------------
-- 출처: wace_plm 운영 DB (211.115.91.141:11133/waceplm), 75 컬럼 / 89건
-- 추출: information_schema.columns 직접 조회 (2026-05-07)
-- 역할: contract_item이 수주 확정되면 라인당 1행이 project_mgmt에 생성됨
-- PROJECT_NO 형식: [R/S/T 등]-[CT/BS/AC/DS/...]-[YYMMDD]-[NNN]
-- ============================================================
CREATE TABLE IF NOT EXISTS project_mgmt (
objid VARCHAR NOT NULL,
contract_objid VARCHAR NOT NULL,
category_cd VARCHAR,
customer_objid VARCHAR,
product VARCHAR,
customer_project_name VARCHAR,
status_cd VARCHAR,
due_date VARCHAR,
location VARCHAR,
setup VARCHAR,
facility VARCHAR,
facility_qty VARCHAR,
facility_type VARCHAR,
facility_depth VARCHAR,
production_no VARCHAR,
bus_cal_cd VARCHAR,
category1_cd VARCHAR,
chg_user_id VARCHAR,
plan_date VARCHAR,
complete_date VARCHAR,
result_cd VARCHAR,
project_no VARCHAR,
pm_user_id VARCHAR,
contract_price VARCHAR,
regdate TIMESTAMP WITHOUT TIME ZONE,
writer VARCHAR,
contract_no VARCHAR,
customer_equip_name VARCHAR,
req_del_date VARCHAR,
contract_del_date VARCHAR,
contract_company VARCHAR,
contract_date VARCHAR,
po_no VARCHAR,
manufacture_plant VARCHAR,
contract_result VARCHAR,
project_name VARCHAR,
spec_user_id VARCHAR,
spec_plan_date VARCHAR,
spec_comp_date VARCHAR,
spec_result_cd VARCHAR,
est_plan_date VARCHAR,
est_user_id VARCHAR,
est_comp_date VARCHAR,
est_result_cd VARCHAR,
area_cd VARCHAR,
contract_price_currency VARCHAR,
contract_currency VARCHAR,
mechanical_type VARCHAR,
is_temp VARCHAR,
overhaul_order VARCHAR,
part_objid VARCHAR,
part_no VARCHAR(100),
part_name VARCHAR(200),
quantity VARCHAR(50),
ebom_status VARCHAR(50),
mbom_status VARCHAR(50),
receiving_rate VARCHAR(10),
production_team_12 VARCHAR(50),
production_team_3 VARCHAR(50),
sales_status VARCHAR,
bom_report_objid VARCHAR(100),
mbom_version INTEGER DEFAULT 0,
mbom_regdate TIMESTAMP WITHOUT TIME ZONE,
mbom_writer VARCHAR(100),
sales_deadline_date VARCHAR(10),
source_bom_type VARCHAR(20),
source_ebom_objid VARCHAR(64),
source_mbom_objid VARCHAR(64),
tax_type VARCHAR(20),
tax_invoice_date VARCHAR(10),
export_decl_no VARCHAR(100),
loading_date VARCHAR(10),
sales_slip_date VARCHAR(8),
sales_slip_menu_sq INTEGER,
contract_item_objid VARCHAR(50),
CONSTRAINT project_mgmt_pkey PRIMARY KEY (objid)
);
CREATE INDEX IF NOT EXISTS idx_project_mgmt_contract_objid ON project_mgmt(contract_objid);
CREATE INDEX IF NOT EXISTS idx_project_mgmt_contract_part ON project_mgmt(contract_objid, part_objid);
CREATE INDEX IF NOT EXISTS idx_project_mgmt_project_no ON project_mgmt(project_no);
CREATE INDEX IF NOT EXISTS idx_project_mgmt_customer ON project_mgmt(customer_objid);
CREATE INDEX IF NOT EXISTS idx_project_mgmt_regdate ON project_mgmt(regdate DESC);
COMMENT ON TABLE project_mgmt IS '프로젝트(수주 라인 단위) 헤더 — 판매·매출관리 메인';
COMMENT ON COLUMN project_mgmt.objid IS 'PK (wace 동일)';
COMMENT ON COLUMN project_mgmt.project_no IS '프로젝트번호 (예: R-CT-260323-001) — 화면 표시 식별자';
COMMENT ON COLUMN project_mgmt.contract_objid IS 'contract_mgmt.objid 참조';
COMMENT ON COLUMN project_mgmt.contract_item_objid IS 'contract_item.objid 참조';
COMMENT ON COLUMN project_mgmt.part_objid IS 'item_info.id 참조';
COMMENT ON COLUMN project_mgmt.sales_status IS '판매상태 (NULL/등록/완료 등)';
COMMENT ON COLUMN project_mgmt.sales_deadline_date IS '매출마감일 (YYYY-MM-DD)';
@@ -36,7 +36,7 @@ const GRID_COLUMNS: DataGridColumn[] = [
{ key: "add_est_cnt", label: "추가견적", width: "w-[80px]", align: "center" },
{ key: "appr_status", label: "결재상태", width: "w-[90px]", align: "center" },
{ key: "mail_send_status_label", label: "메일발송", width: "w-[110px]", align: "center" },
{ key: "contract_currency", label: "환종", width: "w-[70px]", align: "center" },
{ key: "contract_currency_name", label: "환종", width: "w-[70px]", align: "center" },
{ key: "exchange_rate", label: "환율", width: "w-[80px]", formatMoney: true },
{ key: "serial_no", label: "S/N", width: "w-[140px]" },
{ key: "part_no", label: "품번", width: "w-[120px]" },
@@ -18,10 +18,12 @@ import { useConfirmDialog } from "@/components/common/ConfirmDialog";
import { DataGrid, DataGridColumn } from "@/components/common/DataGrid";
import { salesOrderMgmtApi, OrderRow, OrderBody, OrderItem } from "@/lib/api/salesOrderMgmt";
// wace_plm orderMgmtList.jsp 컬럼 순서/라벨에 맞춤
const GRID_COLUMNS: DataGridColumn[] = [
{ key: "contract_no", label: "영업번호", width: "w-[120px]" },
{ key: "po_no", label: "발주번호", width: "w-[110px]" },
{ key: "category_name", label: "주문유형", width: "w-[90px]", align: "center" },
{ key: "order_date", label: "발주일", width: "w-[100px]", align: "center" },
{ key: "po_no", label: "발주번호", width: "w-[110px]" },
{ key: "earliest_due_date_label", label: "요청납기", width: "w-[110px]", align: "center" },
{ key: "customer_name", label: "고객사", width: "w-[160px]" },
{ key: "item_summary", label: "품명", width: "w-[200px]" },
@@ -32,8 +34,15 @@ const GRID_COLUMNS: DataGridColumn[] = [
{ key: "order_supply_price_sum", label: "공급가액", width: "w-[120px]", formatMoney: true },
{ key: "order_vat_sum", label: "부가세", width: "w-[100px]", formatMoney: true },
{ key: "order_total_amount_sum", label: "총액", width: "w-[120px]", formatMoney: true },
{ key: "order_total_amount_krw", label: "원화총액", width: "w-[120px]", formatMoney: true },
{ key: "cu01_cnt", label: "주문서첨부", width: "w-[90px]", align: "center", formatNumber: true },
{ key: "has_order_data", label: "주문서", width: "w-[80px]", align: "center", formatNumber: true },
{ key: "customer_request", label: "고객사요청사항", width: "w-[180px]" },
{ key: "order_appr_status", label: "결재상태", width: "w-[90px]", align: "center" },
{ key: "contract_currency_name", label: "환종", width: "w-[70px]", align: "center" },
{ key: "exchange_rate", label: "환율", width: "w-[80px]", formatMoney: true },
{ key: "contract_currency", label: "환종", width: "w-[70px]", align: "center" },
{ key: "serial_no", label: "S/N", width: "w-[140px]" },
{ key: "part_no", label: "품번", width: "w-[120px]" },
{ key: "writer_name", label: "작성자", width: "w-[110px]" },
];
@@ -18,25 +18,34 @@ import { useConfirmDialog } from "@/components/common/ConfirmDialog";
import { DataGrid, DataGridColumn } from "@/components/common/DataGrid";
import { salesSaleApi, RevenueListRow, DeadlineInfoBody } from "@/lib/api/salesSale";
// wace_plm revenueMgmtList.jsp 컬럼 순서/라벨에 맞춤
const GRID_COLUMNS: DataGridColumn[] = [
{ key: "contract_no", label: "영업번호", width: "w-[120px]" },
{ key: "po_no", label: "발주번호", width: "w-[110px]" },
{ key: "project_no", label: "프로젝트번호", width: "w-[140px]" },
{ key: "order_type_name", label: "주문유형", width: "w-[90px]", align: "center" },
{ key: "sales_deadline_date", label: "매출마감", width: "w-[110px]", align: "center" },
{ key: "order_date", label: "발주일", width: "w-[100px]", align: "center" },
{ key: "shipping_date", label: "출하일", width: "w-[100px]", align: "center" },
{ key: "sales_deadline_date", label: "매출마감일", width: "w-[110px]", align: "center" },
{ key: "po_no", label: "발주번호", width: "w-[110px]" },
{ key: "customer", label: "고객사", width: "w-[160px]" },
{ key: "product_type", label: "제품구분", width: "w-[90px]", align: "center" },
{ key: "sales_quantity", label: "수량", width: "w-[80px]", formatNumber: true },
{ key: "product_type_name", label: "제품구분", width: "w-[90px]", align: "center" },
{ key: "product_name", label: "품명", width: "w-[180px]" },
{ key: "sales_quantity", label: "수량", width: "w-[80px]", align: "right", formatNumber: true },
{ key: "sales_unit_price", label: "단가", width: "w-[110px]", formatMoney: true },
{ key: "sales_supply_price", label: "공급가액", width: "w-[120px]", formatMoney: true },
{ key: "sales_vat", label: "부가세", width: "w-[100px]", formatMoney: true },
{ key: "sales_total_amount", label: "총액", width: "w-[120px]", formatMoney: true },
{ key: "sales_currency", label: "환종", width: "w-[70px]", align: "center" },
{ key: "tax_type", label: "과세구분", width: "w-[100px]", align: "center" },
{ key: "tax_invoice_date", label: "세금계산서일", width: "w-[120px]", align: "center" },
{ key: "export_decl_no", label: "수출신고필증", width: "w-[140px]" },
{ key: "loading_date", label: "선적일자", width: "w-[100px]", align: "center" },
{ key: "sales_total_amount_krw", label: "원화총액", width: "w-[120px]", formatMoney: true },
{ key: "shipping_date", label: "출하일", width: "w-[100px]", align: "center" },
{ key: "nation_name", label: "국내/해외", width: "w-[90px]", align: "center" },
{ key: "sales_currency_name", label: "환종", width: "w-[70px]", align: "center" },
{ key: "sales_exchange_rate", label: "환율", width: "w-[80px]", formatMoney: true },
{ key: "serial_no", label: "S/N", width: "w-[140px]" },
{ key: "split_serial_no", label: "분할S/N", width: "w-[140px]" },
{ key: "product_no", label: "품번", width: "w-[120px]" },
{ key: "tax_type", label: "과세구분", width: "w-[100px]", align: "center" },
{ key: "tax_invoice_date", label: "세금계산서발행일", width: "w-[140px]", align: "center" },
{ key: "export_decl_no", label: "수출신고필증신고번호", width: "w-[160px]" },
{ key: "loading_date", label: "선적일자", width: "w-[100px]", align: "center" },
{ key: "has_transaction_statement", label: "거래명세서", width: "w-[100px]", align: "center" },
];
export default function SalesRevenuePage() {
@@ -16,27 +16,36 @@ import { useAuth } from "@/hooks/useAuth";
import { DataGrid, DataGridColumn } from "@/components/common/DataGrid";
import { salesSaleApi, SaleListRow, SaleRegisterBody } from "@/lib/api/salesSale";
// wace_plm salesMgmtList.jsp 컬럼 순서/라벨에 맞춤
const GRID_COLUMNS: DataGridColumn[] = [
{ key: "contract_no", label: "영업번호", width: "w-[120px]" },
{ key: "po_no", label: "발주번호", width: "w-[110px]" },
{ key: "project_no", label: "프로젝트번호", width: "w-[140px]" },
{ key: "order_type_name", label: "주문유형", width: "w-[90px]", align: "center" },
{ key: "order_date", label: "발주일", width: "w-[100px]", align: "center" },
{ key: "po_no", label: "발주번호", width: "w-[110px]" },
{ key: "request_date", label: "요청납기", width: "w-[100px]", align: "center" },
{ key: "shipping_date", label: "출하일", width: "w-[100px]", align: "center" },
{ key: "customer", label: "고객사", width: "w-[160px]" },
{ key: "product_name", label: "품명", width: "w-[180px]" },
{ key: "product_no", label: "품번", width: "w-[120px]" },
{ key: "order_quantity", label: "수주수량", width: "w-[90px]", align: "right", formatNumber: true },
{ key: "sales_quantity", label: "판매수량", width: "w-[90px]", align: "right", formatNumber: true },
{ key: "remaining_quantity", label: "잔량", width: "w-[80px]", align: "right", formatNumber: true },
{ key: "sales_unit_price", label: "판매단가", width: "w-[110px]", formatMoney: true },
{ key: "sales_supply_price", label: "공급가액", width: "w-[120px]", formatMoney: true },
{ key: "sales_supply_price", label: "판매공급가액", width: "w-[130px]", formatMoney: true },
{ key: "sales_vat", label: "부가세", width: "w-[100px]", formatMoney: true },
{ key: "sales_total_amount", label: "총액", width: "w-[120px]", formatMoney: true },
{ key: "sales_currency", label: "환종", width: "w-[70px]", align: "center" },
{ key: "order_status", label: "수주상태", width: "w-[90px]", align: "center" },
{ key: "sales_total_amount", label: "판매총액", width: "w-[120px]", formatMoney: true },
{ key: "sales_total_amount_krw", label: "판매원화총액", width: "w-[130px]", formatMoney: true },
{ key: "remaining_amount_krw", label: "잔량원화총액", width: "w-[130px]", formatMoney: true },
{ key: "order_status_name", label: "수주상태", width: "w-[90px]", align: "center" },
{ key: "sales_status", label: "판매상태", width: "w-[100px]", align: "center" },
{ key: "shipping_method", label: "출하방법", width: "w-[100px]" },
{ key: "production_status", label: "생산상태", width: "w-[100px]", align: "center" },
{ key: "shipping_order_status", label: "출하지시상태", width: "w-[110px]", align: "center" },
{ key: "payment_type_name", label: "유/무상", width: "w-[80px]", align: "center" },
{ key: "sales_currency_name", label: "환종", width: "w-[70px]", align: "center" },
{ key: "sales_exchange_rate", label: "환율", width: "w-[80px]", formatMoney: true },
{ key: "serial_no", label: "S/N", width: "w-[140px]" },
{ key: "split_serial_no", label: "분할S/N", width: "w-[140px]" },
{ key: "product_no", label: "품번", width: "w-[120px]" },
{ key: "has_transaction_statement", label: "거래명세서", width: "w-[100px]", align: "center" },
];
export default function SalesSalePage() {