2026-03-20 16:09:39 +09:00
/ * *
* 입 고 관 리 컨 트 롤 러
*
* 입 고 유 형 별 소 스 테 이 블 :
Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers
- Update admin pages (company management, disk usage)
- Improve sales/logistics pages (order, shipping, outbound, receiving)
- Enhance V2 components (file-upload, split-panel-layout, table-list)
- Add SmartSelect common component
- Update DataGrid, FullscreenDialog common components
- Add gitignore rules for personal pipeline tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:52:03 +09:00
* - 구 매 입 고 → purchase_order_mng ( 발 주 헤 더 ) + purchase_detail ( 발 주 디 테 일 )
2026-03-20 16:09:39 +09:00
* - 반 품 입 고 → shipment_instruction + shipment_instruction_detail ( 출 하 )
* - 기 타 입 고 → item_info ( 품 목 )
* /
import { Response } from "express" ;
import { AuthenticatedRequest } from "../types/auth" ;
import { getPool } from "../database/db" ;
import { logger } from "../utils/logger" ;
// 입고 목록 조회
export async function getList ( req : AuthenticatedRequest , res : Response ) {
try {
const companyCode = req . user ! . companyCode ;
const {
inbound_type ,
inbound_status ,
search_keyword ,
date_from ,
date_to ,
} = req . query ;
const conditions : string [ ] = [ ] ;
const params : any [ ] = [ ] ;
let paramIdx = 1 ;
if ( companyCode === "*" ) {
// 최고 관리자: 전체 조회
} else {
conditions . push ( ` im.company_code = $ ${ paramIdx } ` ) ;
params . push ( companyCode ) ;
paramIdx ++ ;
}
if ( inbound_type && inbound_type !== "all" ) {
conditions . push ( ` im.inbound_type = $ ${ paramIdx } ` ) ;
params . push ( inbound_type ) ;
paramIdx ++ ;
}
if ( inbound_status && inbound_status !== "all" ) {
conditions . push ( ` im.inbound_status = $ ${ paramIdx } ` ) ;
params . push ( inbound_status ) ;
paramIdx ++ ;
}
if ( search_keyword ) {
conditions . push (
` (im.inbound_number ILIKE $ ${ paramIdx } OR im.item_name ILIKE $ ${ paramIdx } OR im.item_number ILIKE $ ${ paramIdx } OR im.supplier_name ILIKE $ ${ paramIdx } OR im.reference_number ILIKE $ ${ paramIdx } ) `
) ;
params . push ( ` % ${ search_keyword } % ` ) ;
paramIdx ++ ;
}
if ( date_from ) {
conditions . push ( ` im.inbound_date >= $ ${ paramIdx } ::date ` ) ;
params . push ( date_from ) ;
paramIdx ++ ;
}
if ( date_to ) {
conditions . push ( ` im.inbound_date <= $ ${ paramIdx } ::date ` ) ;
params . push ( date_to ) ;
paramIdx ++ ;
}
const whereClause =
conditions . length > 0 ? ` WHERE ${ conditions . join ( " AND " ) } ` : "" ;
const query = `
SELECT
im . * ,
wh . warehouse_name
FROM inbound_mng im
LEFT JOIN warehouse_info wh
ON im . warehouse_code = wh . warehouse_code
AND im . company_code = wh . company_code
$ { whereClause }
ORDER BY im . created_date DESC
` ;
const pool = getPool ( ) ;
const result = await pool . query ( query , params ) ;
logger . info ( "입고 목록 조회" , {
companyCode ,
rowCount : result.rowCount ,
} ) ;
return res . json ( { success : true , data : result.rows } ) ;
} catch ( error : any ) {
logger . error ( "입고 목록 조회 실패" , { error : error.message } ) ;
return res . status ( 500 ) . json ( { success : false , message : error.message } ) ;
}
}
// 입고 등록 (다건)
export async function create ( req : AuthenticatedRequest , res : Response ) {
const pool = getPool ( ) ;
const client = await pool . connect ( ) ;
try {
const companyCode = req . user ! . companyCode ;
const userId = req . user ! . userId ;
const { items , inbound_number , inbound_date , warehouse_code , location_code , inspector , manager , memo } = req . body ;
if ( ! items || ! Array . isArray ( items ) || items . length === 0 ) {
return res . status ( 400 ) . json ( { success : false , message : "입고 품목이 없습니다." } ) ;
}
await client . query ( "BEGIN" ) ;
const insertedRows : any [ ] = [ ] ;
for ( const item of items ) {
const result = await client . query (
` INSERT INTO inbound_mng (
company_code , inbound_number , inbound_type , inbound_date ,
reference_number , supplier_code , supplier_name ,
item_number , item_name , spec , material , unit ,
inbound_qty , unit_price , total_amount ,
lot_number , warehouse_code , location_code ,
inbound_status , inspection_status ,
inspector , manager , memo ,
source_table , source_id ,
created_date , created_by , writer , status
) VALUES (
$1 , $2 , $3 , $4 : : date ,
$5 , $6 , $7 ,
$8 , $9 , $10 , $11 , $12 ,
$13 , $14 , $15 ,
$16 , $17 , $18 ,
$19 , $20 ,
$21 , $22 , $23 ,
$24 , $25 ,
NOW ( ) , $26 , $26 , '입고'
) RETURNING * ` ,
[
companyCode ,
inbound_number || item . inbound_number ,
item . inbound_type ,
inbound_date || item . inbound_date ,
item . reference_number || null ,
item . supplier_code || null ,
item . supplier_name || null ,
item . item_number || null ,
item . item_name || null ,
item . spec || null ,
item . material || null ,
item . unit || "EA" ,
item . inbound_qty || 0 ,
item . unit_price || 0 ,
item . total_amount || 0 ,
item . lot_number || null ,
warehouse_code || item . warehouse_code || null ,
location_code || item . location_code || null ,
item . inbound_status || "대기" ,
item . inspection_status || "대기" ,
inspector || item . inspector || null ,
manager || item . manager || null ,
memo || item . memo || null ,
item . source_table || null ,
item . source_id || null ,
userId ,
]
) ;
insertedRows . push ( result . rows [ 0 ] ) ;
2026-03-25 10:48:47 +09:00
// 재고 업데이트 (inventory_stock): 입고 수량 증가
const itemCode = item . item_number || null ;
const whCode = warehouse_code || item . warehouse_code || null ;
const locCode = location_code || item . location_code || null ;
const inQty = Number ( item . inbound_qty ) || 0 ;
if ( itemCode && inQty > 0 ) {
const existingStock = await client . query (
` SELECT id FROM inventory_stock
WHERE company_code = $1 AND item_code = $2
AND COALESCE ( warehouse_code , '' ) = COALESCE ( $3 , '' )
AND COALESCE ( location_code , '' ) = COALESCE ( $4 , '' )
LIMIT 1 ` ,
[ companyCode , itemCode , whCode || '' , locCode || '' ]
) ;
if ( existingStock . rows . length > 0 ) {
await client . query (
` UPDATE inventory_stock
SET current_qty = CAST ( COALESCE ( CAST ( NULLIF ( current_qty , '' ) AS numeric ) , 0 ) + $1 AS text ) ,
last_in_date = NOW ( ) ,
updated_date = NOW ( )
WHERE id = $2 ` ,
[ inQty , existingStock . rows [ 0 ] . id ]
) ;
} else {
await client . query (
` INSERT INTO inventory_stock (
company_code , item_code , warehouse_code , location_code ,
current_qty , safety_qty , last_in_date ,
created_date , updated_date , writer
) VALUES ( $1 , $2 , $3 , $4 , $5 , '0' , NOW ( ) , NOW ( ) , NOW ( ) , $6 ) ` ,
[ companyCode , itemCode , whCode , locCode , String ( inQty ) , userId ]
) ;
}
}
2026-03-20 16:09:39 +09:00
// 구매입고인 경우 발주의 received_qty 업데이트
if ( item . inbound_type === "구매입고" && item . source_id && item . source_table === "purchase_order_mng" ) {
await client . query (
` UPDATE purchase_order_mng
SET received_qty = CAST (
COALESCE ( CAST ( NULLIF ( received_qty , '' ) AS numeric ) , 0 ) + $1 AS text
) ,
remain_qty = CAST (
GREATEST ( COALESCE ( CAST ( NULLIF ( order_qty , '' ) AS numeric ) , 0 )
- COALESCE ( CAST ( NULLIF ( received_qty , '' ) AS numeric ) , 0 ) - $1 , 0 ) AS text
) ,
status = CASE
WHEN COALESCE ( CAST ( NULLIF ( received_qty , '' ) AS numeric ) , 0 ) + $1
>= COALESCE ( CAST ( NULLIF ( order_qty , '' ) AS numeric ) , 0 )
THEN '입고완료'
ELSE '부분입고'
END ,
updated_date = NOW ( )
WHERE id = $2 AND company_code = $3 ` ,
[ item . inbound_qty || 0 , item . source_id , companyCode ]
) ;
}
Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers
- Update admin pages (company management, disk usage)
- Improve sales/logistics pages (order, shipping, outbound, receiving)
- Enhance V2 components (file-upload, split-panel-layout, table-list)
- Add SmartSelect common component
- Update DataGrid, FullscreenDialog common components
- Add gitignore rules for personal pipeline tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:52:03 +09:00
// 구매입고인 경우 purchase_detail 기반 발주의 헤더 상태 업데이트
if ( item . inbound_type === "구매입고" && item . source_id && item . source_table === "purchase_detail" ) {
// 해당 디테일의 발주번호 조회
const detailInfo = await client . query (
` SELECT purchase_no FROM purchase_detail WHERE id = $ 1 AND company_code = $ 2 ` ,
[ item . source_id , companyCode ]
) ;
if ( detailInfo . rows . length > 0 ) {
const purchaseNo = detailInfo . rows [ 0 ] . purchase_no ;
// 해당 발주의 모든 디테일 잔량 확인
const unreceived = await client . query (
` SELECT pd.id
FROM purchase_detail pd
LEFT JOIN (
SELECT source_id , SUM ( COALESCE ( inbound_qty , 0 ) ) AS total_received
FROM inbound_mng
WHERE source_table = 'purchase_detail' AND company_code = $1
GROUP BY source_id
) r ON r . source_id = pd . id
WHERE pd . purchase_no = $2 AND pd . company_code = $1
AND COALESCE ( CAST ( NULLIF ( pd . order_qty , '' ) AS numeric ) , 0 ) - COALESCE ( r . total_received , 0 ) > 0
LIMIT 1 ` ,
[ companyCode , purchaseNo ]
) ;
const newStatus = unreceived . rows . length === 0 ? '입고완료' : '부분입고' ;
await client . query (
` UPDATE purchase_order_mng SET status = $ 1, updated_date = NOW()
WHERE purchase_no = $2 AND company_code = $3 ` ,
[ newStatus , purchaseNo , companyCode ]
) ;
}
}
2026-03-20 16:09:39 +09:00
}
await client . query ( "COMMIT" ) ;
logger . info ( "입고 등록 완료" , {
companyCode ,
userId ,
count : insertedRows.length ,
inbound_number ,
} ) ;
return res . json ( {
success : true ,
data : insertedRows ,
message : ` ${ insertedRows . length } 건 입고 등록 완료 ` ,
} ) ;
} catch ( error : any ) {
await client . query ( "ROLLBACK" ) ;
logger . error ( "입고 등록 실패" , { error : error.message } ) ;
return res . status ( 500 ) . json ( { success : false , message : error.message } ) ;
} finally {
client . release ( ) ;
}
}
// 입고 수정
export async function update ( req : AuthenticatedRequest , res : Response ) {
try {
const companyCode = req . user ! . companyCode ;
const userId = req . user ! . userId ;
const { id } = req . params ;
const {
inbound_date , inbound_qty , unit_price , total_amount ,
lot_number , warehouse_code , location_code ,
inbound_status , inspection_status ,
inspector , manager : mgr , memo ,
} = req . body ;
const pool = getPool ( ) ;
const result = await pool . query (
` UPDATE inbound_mng SET
inbound_date = COALESCE ( $1 : : date , inbound_date ) ,
inbound_qty = COALESCE ( $2 , inbound_qty ) ,
unit_price = COALESCE ( $3 , unit_price ) ,
total_amount = COALESCE ( $4 , total_amount ) ,
lot_number = COALESCE ( $5 , lot_number ) ,
warehouse_code = COALESCE ( $6 , warehouse_code ) ,
location_code = COALESCE ( $7 , location_code ) ,
inbound_status = COALESCE ( $8 , inbound_status ) ,
inspection_status = COALESCE ( $9 , inspection_status ) ,
inspector = COALESCE ( $10 , inspector ) ,
manager = COALESCE ( $11 , manager ) ,
memo = COALESCE ( $12 , memo ) ,
updated_date = NOW ( ) ,
updated_by = $13
WHERE id = $14 AND company_code = $15
RETURNING * ` ,
[
inbound_date , inbound_qty , unit_price , total_amount ,
lot_number , warehouse_code , location_code ,
inbound_status , inspection_status ,
inspector , mgr , memo ,
userId , id , companyCode ,
]
) ;
if ( result . rowCount === 0 ) {
return res . status ( 404 ) . json ( { success : false , message : "입고 데이터를 찾을 수 없습니다." } ) ;
}
logger . info ( "입고 수정" , { companyCode , userId , id } ) ;
return res . json ( { success : true , data : result.rows [ 0 ] } ) ;
} catch ( error : any ) {
logger . error ( "입고 수정 실패" , { error : error.message } ) ;
return res . status ( 500 ) . json ( { success : false , message : error.message } ) ;
}
}
// 입고 삭제
export async function deleteReceiving ( req : AuthenticatedRequest , res : Response ) {
try {
const companyCode = req . user ! . companyCode ;
const { id } = req . params ;
const pool = getPool ( ) ;
const result = await pool . query (
` DELETE FROM inbound_mng WHERE id = $ 1 AND company_code = $ 2 RETURNING id ` ,
[ id , companyCode ]
) ;
if ( result . rowCount === 0 ) {
return res . status ( 404 ) . json ( { success : false , message : "데이터를 찾을 수 없습니다." } ) ;
}
logger . info ( "입고 삭제" , { companyCode , id } ) ;
return res . json ( { success : true , message : "삭제 완료" } ) ;
} catch ( error : any ) {
logger . error ( "입고 삭제 실패" , { error : error.message } ) ;
return res . status ( 500 ) . json ( { success : false , message : error.message } ) ;
}
}
Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers
- Update admin pages (company management, disk usage)
- Improve sales/logistics pages (order, shipping, outbound, receiving)
- Enhance V2 components (file-upload, split-panel-layout, table-list)
- Add SmartSelect common component
- Update DataGrid, FullscreenDialog common components
- Add gitignore rules for personal pipeline tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:52:03 +09:00
// 구매입고용: 발주 데이터 조회 (미입고분) - 신규 헤더-디테일 구조 + 레거시 단일 테이블 UNION ALL
2026-03-20 16:09:39 +09:00
export async function getPurchaseOrders ( req : AuthenticatedRequest , res : Response ) {
try {
const companyCode = req . user ! . companyCode ;
Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers
- Update admin pages (company management, disk usage)
- Improve sales/logistics pages (order, shipping, outbound, receiving)
- Enhance V2 components (file-upload, split-panel-layout, table-list)
- Add SmartSelect common component
- Update DataGrid, FullscreenDialog common components
- Add gitignore rules for personal pipeline tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:52:03 +09:00
const { keyword , page , pageSize } = req . query ;
const currentPage = Math . max ( 1 , Number ( page ) || 1 ) ;
const limit = Math . min ( 500 , Math . max ( 1 , Number ( pageSize ) || 20 ) ) ;
const offset = ( currentPage - 1 ) * limit ;
2026-03-20 16:09:39 +09:00
const params : any [ ] = [ companyCode ] ;
let paramIdx = 2 ;
Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers
- Update admin pages (company management, disk usage)
- Improve sales/logistics pages (order, shipping, outbound, receiving)
- Enhance V2 components (file-upload, split-panel-layout, table-list)
- Add SmartSelect common component
- Update DataGrid, FullscreenDialog common components
- Add gitignore rules for personal pipeline tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:52:03 +09:00
let keywordConditionDetail = "" ;
let keywordConditionLegacy = "" ;
2026-03-20 16:09:39 +09:00
if ( keyword ) {
Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers
- Update admin pages (company management, disk usage)
- Improve sales/logistics pages (order, shipping, outbound, receiving)
- Enhance V2 components (file-upload, split-panel-layout, table-list)
- Add SmartSelect common component
- Update DataGrid, FullscreenDialog common components
- Add gitignore rules for personal pipeline tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:52:03 +09:00
keywordConditionDetail = ` AND (pd.purchase_no ILIKE $ ${ paramIdx } OR COALESCE(NULLIF(pd.item_name, ''), ii.item_name) ILIKE $ ${ paramIdx } OR COALESCE(NULLIF(pd.item_code, ''), ii.item_number) ILIKE $ ${ paramIdx } OR COALESCE(pd.supplier_name, po.supplier_name) ILIKE $ ${ paramIdx } ) ` ;
keywordConditionLegacy = ` AND (po.purchase_no ILIKE $ ${ paramIdx } OR po.item_name ILIKE $ ${ paramIdx } OR po.item_code ILIKE $ ${ paramIdx } OR po.supplier_name ILIKE $ ${ paramIdx } ) ` ;
2026-03-20 16:09:39 +09:00
params . push ( ` % ${ keyword } % ` ) ;
paramIdx ++ ;
}
Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers
- Update admin pages (company management, disk usage)
- Improve sales/logistics pages (order, shipping, outbound, receiving)
- Enhance V2 components (file-upload, split-panel-layout, table-list)
- Add SmartSelect common component
- Update DataGrid, FullscreenDialog common components
- Add gitignore rules for personal pipeline tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:52:03 +09:00
const baseQuery = `
WITH detail_received AS (
SELECT source_id , SUM ( COALESCE ( inbound_qty , 0 ) ) AS total_received
FROM inbound_mng
WHERE source_table = 'purchase_detail' AND company_code = $1
GROUP BY source_id
) ,
combined AS (
-- 디 테 일 기 반 발 주 데 이 터 ( 신 규 헤 더 - 디 테 일 구 조 , 헤 더 없 는 디 테 일 도 포 함 )
SELECT
pd . id ,
COALESCE ( po . purchase_no , pd . purchase_no ) AS purchase_no ,
po . order_date ,
COALESCE ( pd . supplier_code , po . supplier_code ) AS supplier_code ,
COALESCE ( pd . supplier_name , po . supplier_name ) AS supplier_name ,
COALESCE ( NULLIF ( pd . item_code , '' ) , ii . item_number ) AS item_code ,
COALESCE ( NULLIF ( pd . item_name , '' ) , ii . item_name ) AS item_name ,
COALESCE ( NULLIF ( pd . spec , '' ) , ii . size ) AS spec ,
COALESCE ( NULLIF ( pd . material , '' ) , ii . material ) AS material ,
COALESCE ( CAST ( NULLIF ( pd . order_qty , '' ) AS numeric ) , 0 ) AS order_qty ,
COALESCE ( dr . total_received , 0 ) AS received_qty ,
COALESCE ( CAST ( NULLIF ( pd . order_qty , '' ) AS numeric ) , 0 ) - COALESCE ( dr . total_received , 0 ) AS remain_qty ,
COALESCE ( CAST ( NULLIF ( pd . unit_price , '' ) AS numeric ) , 0 ) AS unit_price ,
COALESCE ( po . status , '' ) AS status ,
COALESCE ( pd . due_date , po . due_date ) AS due_date ,
'purchase_detail' AS source_table
FROM purchase_detail pd
LEFT JOIN purchase_order_mng po
ON pd . purchase_no = po . purchase_no AND pd . company_code = po . company_code
LEFT JOIN item_info ii ON pd . item_id = ii . id
LEFT JOIN detail_received dr ON dr . source_id = pd . id
WHERE pd . company_code = $1
AND COALESCE ( CAST ( NULLIF ( pd . order_qty , '' ) AS numeric ) , 0 ) - COALESCE ( dr . total_received , 0 ) > 0
AND COALESCE ( pd . approval_status , '' ) NOT IN ( '반려' )
AND COALESCE ( po . status , '' ) NOT IN ( '입고완료' , '취소' )
$ { keywordConditionDetail }
UNION ALL
-- 레 거 시 단 일 테 이 블 데 이 터 ( purchase_detail에 없 는 발 주 )
SELECT
po . id ,
po . purchase_no ,
po . order_date ,
po . supplier_code ,
po . supplier_name ,
po . item_code ,
po . item_name ,
po . spec ,
po . material ,
COALESCE ( CAST ( NULLIF ( po . order_qty , '' ) AS numeric ) , 0 ) AS order_qty ,
COALESCE ( CAST ( NULLIF ( po . received_qty , '' ) AS numeric ) , 0 ) AS received_qty ,
COALESCE ( CAST ( NULLIF ( po . remain_qty , '' ) AS numeric ) ,
COALESCE ( CAST ( NULLIF ( po . order_qty , '' ) AS numeric ) , 0 )
- COALESCE ( CAST ( NULLIF ( po . received_qty , '' ) AS numeric ) , 0 )
) AS remain_qty ,
COALESCE ( CAST ( NULLIF ( po . unit_price , '' ) AS numeric ) , 0 ) AS unit_price ,
po . status ,
po . due_date ,
'purchase_order_mng' AS source_table
FROM purchase_order_mng po
WHERE po . company_code = $1
AND NOT EXISTS (
SELECT 1 FROM purchase_detail pd
WHERE pd . purchase_no = po . purchase_no AND pd . company_code = po . company_code
)
AND COALESCE ( CAST ( NULLIF ( po . remain_qty , '' ) AS numeric ) ,
COALESCE ( CAST ( NULLIF ( po . order_qty , '' ) AS numeric ) , 0 )
- COALESCE ( CAST ( NULLIF ( po . received_qty , '' ) AS numeric ) , 0 )
) > 0
AND po . status NOT IN ( '입고완료' , '취소' )
$ { keywordConditionLegacy }
) ` ;
2026-03-20 16:09:39 +09:00
const pool = getPool ( ) ;
Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers
- Update admin pages (company management, disk usage)
- Improve sales/logistics pages (order, shipping, outbound, receiving)
- Enhance V2 components (file-upload, split-panel-layout, table-list)
- Add SmartSelect common component
- Update DataGrid, FullscreenDialog common components
- Add gitignore rules for personal pipeline tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:52:03 +09:00
const countResult = await pool . query (
` ${ baseQuery } SELECT COUNT(*) AS total FROM combined ` ,
2026-03-20 16:09:39 +09:00
params
) ;
Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers
- Update admin pages (company management, disk usage)
- Improve sales/logistics pages (order, shipping, outbound, receiving)
- Enhance V2 components (file-upload, split-panel-layout, table-list)
- Add SmartSelect common component
- Update DataGrid, FullscreenDialog common components
- Add gitignore rules for personal pipeline tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:52:03 +09:00
const totalCount = parseInt ( countResult . rows [ 0 ] . total , 10 ) ;
2026-03-20 16:09:39 +09:00
Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers
- Update admin pages (company management, disk usage)
- Improve sales/logistics pages (order, shipping, outbound, receiving)
- Enhance V2 components (file-upload, split-panel-layout, table-list)
- Add SmartSelect common component
- Update DataGrid, FullscreenDialog common components
- Add gitignore rules for personal pipeline tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:52:03 +09:00
const dataResult = await pool . query (
` ${ baseQuery } SELECT * FROM combined ORDER BY order_date DESC, purchase_no LIMIT ${ limit } OFFSET ${ offset } ` ,
params
) ;
return res . json ( { success : true , data : dataResult.rows , totalCount } ) ;
2026-03-20 16:09:39 +09:00
} catch ( error : any ) {
logger . error ( "발주 데이터 조회 실패" , { error : error.message } ) ;
return res . status ( 500 ) . json ( { success : false , message : error.message } ) ;
}
}
// 반품입고용: 출하 데이터 조회
export async function getShipments ( req : AuthenticatedRequest , res : Response ) {
try {
const companyCode = req . user ! . companyCode ;
Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers
- Update admin pages (company management, disk usage)
- Improve sales/logistics pages (order, shipping, outbound, receiving)
- Enhance V2 components (file-upload, split-panel-layout, table-list)
- Add SmartSelect common component
- Update DataGrid, FullscreenDialog common components
- Add gitignore rules for personal pipeline tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:52:03 +09:00
const { keyword , page , pageSize } = req . query ;
const currentPage = Math . max ( 1 , Number ( page ) || 1 ) ;
const limit = Math . min ( 500 , Math . max ( 1 , Number ( pageSize ) || 20 ) ) ;
const offset = ( currentPage - 1 ) * limit ;
2026-03-20 16:09:39 +09:00
const conditions : string [ ] = [ "si.company_code = $1" ] ;
const params : any [ ] = [ companyCode ] ;
let paramIdx = 2 ;
if ( keyword ) {
conditions . push (
` (si.instruction_no ILIKE $ ${ paramIdx } OR sid.item_name ILIKE $ ${ paramIdx } OR sid.item_code ILIKE $ ${ paramIdx } ) `
) ;
params . push ( ` % ${ keyword } % ` ) ;
paramIdx ++ ;
}
Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers
- Update admin pages (company management, disk usage)
- Improve sales/logistics pages (order, shipping, outbound, receiving)
- Enhance V2 components (file-upload, split-panel-layout, table-list)
- Add SmartSelect common component
- Update DataGrid, FullscreenDialog common components
- Add gitignore rules for personal pipeline tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:52:03 +09:00
const whereClause = conditions . join ( " AND " ) ;
2026-03-20 16:09:39 +09:00
const pool = getPool ( ) ;
Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers
- Update admin pages (company management, disk usage)
- Improve sales/logistics pages (order, shipping, outbound, receiving)
- Enhance V2 components (file-upload, split-panel-layout, table-list)
- Add SmartSelect common component
- Update DataGrid, FullscreenDialog common components
- Add gitignore rules for personal pipeline tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:52:03 +09:00
const countResult = await pool . query (
` SELECT COUNT(*) AS total
FROM shipment_instruction si
JOIN shipment_instruction_detail sid
ON si . id = sid . instruction_id AND si . company_code = sid . company_code
WHERE $ { whereClause } ` ,
params
) ;
const totalCount = parseInt ( countResult . rows [ 0 ] . total , 10 ) ;
const dataResult = await pool . query (
2026-03-20 16:09:39 +09:00
` SELECT
sid . id AS detail_id ,
si . id AS instruction_id ,
si . instruction_no ,
si . instruction_date ,
si . partner_id ,
si . status AS instruction_status ,
sid . item_code ,
sid . item_name ,
sid . spec ,
sid . material ,
COALESCE ( sid . ship_qty , 0 ) AS ship_qty ,
COALESCE ( sid . order_qty , 0 ) AS order_qty ,
sid . source_type
FROM shipment_instruction si
JOIN shipment_instruction_detail sid
ON si . id = sid . instruction_id
AND si . company_code = sid . company_code
Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers
- Update admin pages (company management, disk usage)
- Improve sales/logistics pages (order, shipping, outbound, receiving)
- Enhance V2 components (file-upload, split-panel-layout, table-list)
- Add SmartSelect common component
- Update DataGrid, FullscreenDialog common components
- Add gitignore rules for personal pipeline tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:52:03 +09:00
WHERE $ { whereClause }
ORDER BY si . instruction_date DESC , si . instruction_no
LIMIT $ { limit } OFFSET $ { offset } ` ,
2026-03-20 16:09:39 +09:00
params
) ;
Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers
- Update admin pages (company management, disk usage)
- Improve sales/logistics pages (order, shipping, outbound, receiving)
- Enhance V2 components (file-upload, split-panel-layout, table-list)
- Add SmartSelect common component
- Update DataGrid, FullscreenDialog common components
- Add gitignore rules for personal pipeline tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:52:03 +09:00
return res . json ( { success : true , data : dataResult.rows , totalCount } ) ;
2026-03-20 16:09:39 +09:00
} catch ( error : any ) {
logger . error ( "출하 데이터 조회 실패" , { error : error.message } ) ;
return res . status ( 500 ) . json ( { success : false , message : error.message } ) ;
}
}
// 기타입고용: 품목 데이터 조회
export async function getItems ( req : AuthenticatedRequest , res : Response ) {
try {
const companyCode = req . user ! . companyCode ;
Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers
- Update admin pages (company management, disk usage)
- Improve sales/logistics pages (order, shipping, outbound, receiving)
- Enhance V2 components (file-upload, split-panel-layout, table-list)
- Add SmartSelect common component
- Update DataGrid, FullscreenDialog common components
- Add gitignore rules for personal pipeline tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:52:03 +09:00
const { keyword , page , pageSize } = req . query ;
const currentPage = Math . max ( 1 , Number ( page ) || 1 ) ;
const limit = Math . min ( 500 , Math . max ( 1 , Number ( pageSize ) || 20 ) ) ;
const offset = ( currentPage - 1 ) * limit ;
2026-03-20 16:09:39 +09:00
const conditions : string [ ] = [ "company_code = $1" ] ;
const params : any [ ] = [ companyCode ] ;
let paramIdx = 2 ;
if ( keyword ) {
conditions . push (
` (item_number ILIKE $ ${ paramIdx } OR item_name ILIKE $ ${ paramIdx } ) `
) ;
params . push ( ` % ${ keyword } % ` ) ;
paramIdx ++ ;
}
Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers
- Update admin pages (company management, disk usage)
- Improve sales/logistics pages (order, shipping, outbound, receiving)
- Enhance V2 components (file-upload, split-panel-layout, table-list)
- Add SmartSelect common component
- Update DataGrid, FullscreenDialog common components
- Add gitignore rules for personal pipeline tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:52:03 +09:00
const whereClause = conditions . join ( " AND " ) ;
2026-03-20 16:09:39 +09:00
const pool = getPool ( ) ;
Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers
- Update admin pages (company management, disk usage)
- Improve sales/logistics pages (order, shipping, outbound, receiving)
- Enhance V2 components (file-upload, split-panel-layout, table-list)
- Add SmartSelect common component
- Update DataGrid, FullscreenDialog common components
- Add gitignore rules for personal pipeline tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:52:03 +09:00
const countResult = await pool . query (
` SELECT COUNT(*) AS total FROM item_info WHERE ${ whereClause } ` ,
params
) ;
const totalCount = parseInt ( countResult . rows [ 0 ] . total , 10 ) ;
const dataResult = await pool . query (
2026-03-20 16:09:39 +09:00
` SELECT
id , item_number , item_name , size AS spec , material , unit ,
COALESCE ( CAST ( NULLIF ( standard_price , '' ) AS numeric ) , 0 ) AS standard_price
FROM item_info
Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers
- Update admin pages (company management, disk usage)
- Improve sales/logistics pages (order, shipping, outbound, receiving)
- Enhance V2 components (file-upload, split-panel-layout, table-list)
- Add SmartSelect common component
- Update DataGrid, FullscreenDialog common components
- Add gitignore rules for personal pipeline tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:52:03 +09:00
WHERE $ { whereClause }
ORDER BY item_name
LIMIT $ { limit } OFFSET $ { offset } ` ,
2026-03-20 16:09:39 +09:00
params
) ;
Enhance backend controllers, frontend pages, and V2 components
- Fix department, receiving, shippingOrder, shippingPlan controllers
- Update admin pages (company management, disk usage)
- Improve sales/logistics pages (order, shipping, outbound, receiving)
- Enhance V2 components (file-upload, split-panel-layout, table-list)
- Add SmartSelect common component
- Update DataGrid, FullscreenDialog common components
- Add gitignore rules for personal pipeline tools
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:52:03 +09:00
return res . json ( { success : true , data : dataResult.rows , totalCount } ) ;
2026-03-20 16:09:39 +09:00
} catch ( error : any ) {
logger . error ( "품목 데이터 조회 실패" , { error : error.message } ) ;
return res . status ( 500 ) . json ( { success : false , message : error.message } ) ;
}
}
// 입고번호 자동생성
export async function generateNumber ( req : AuthenticatedRequest , res : Response ) {
try {
const companyCode = req . user ! . companyCode ;
const pool = getPool ( ) ;
const today = new Date ( ) ;
const yyyy = today . getFullYear ( ) ;
const prefix = ` RCV- ${ yyyy } - ` ;
const result = await pool . query (
` SELECT inbound_number FROM inbound_mng
WHERE company_code = $1 AND inbound_number LIKE $2
ORDER BY inbound_number DESC LIMIT 1 ` ,
[ companyCode , ` ${ prefix } % ` ]
) ;
let seq = 1 ;
if ( result . rows . length > 0 ) {
const lastNo = result . rows [ 0 ] . inbound_number ;
const lastSeq = parseInt ( lastNo . replace ( prefix , "" ) , 10 ) ;
if ( ! isNaN ( lastSeq ) ) seq = lastSeq + 1 ;
}
const newNumber = ` ${ prefix } ${ String ( seq ) . padStart ( 4 , "0" ) } ` ;
return res . json ( { success : true , data : newNumber } ) ;
} catch ( error : any ) {
logger . error ( "입고번호 생성 실패" , { error : error.message } ) ;
return res . status ( 500 ) . json ( { success : false , message : error.message } ) ;
}
}
// 창고 목록 조회
export async function getWarehouses ( req : AuthenticatedRequest , res : Response ) {
try {
const companyCode = req . user ! . companyCode ;
const pool = getPool ( ) ;
const result = await pool . query (
` SELECT warehouse_code, warehouse_name, warehouse_type
FROM warehouse_info
WHERE company_code = $1 AND status != '삭제'
ORDER BY warehouse_name ` ,
[ companyCode ]
) ;
return res . json ( { success : true , data : result.rows } ) ;
} catch ( error : any ) {
logger . error ( "창고 목록 조회 실패" , { error : error.message } ) ;
return res . status ( 500 ) . json ( { success : false , message : error.message } ) ;
}
}