// ============================================================
// 영업관리 > 판매관리 + 매출관리 (wace_plm 도메인 이식)
// 메인 테이블: sales_registration (판매 헤더, 프로젝트당 1건)
//              + shipment_log (분할출하/매출 라인)
// 라인 표시는 contract_item + (sales_registration | shipment_log) JOIN
// ============================================================

import { getPool } from "../database/db";
import { logger } from "../utils/logger";

export interface SaleListFilter {
  orderType?: string;          // contract_mgmt.category_cd
  poNo?: string;               // contract_mgmt.po_no
  customer_objid?: string;
  search_partObjId?: string;
  search_partName?: string;
  serialNo?: string;
  shippingStatus?: string;
  orderDateFrom?: string;
  orderDateTo?: string;
  shippingDateFrom?: string;
  shippingDateTo?: string;
  salesStatus?: string;        // 판매상태 (registered/cancelled 등)
  productType?: string;        // project_mgmt.product (제품구분)
  nation?: string;             // project_mgmt.area_cd (국내/해외)
  // 매출관리 전용
  revenueMode?: string;
  salesDeadlineFrom?: string;
  salesDeadlineTo?: string;
}

export interface SaleRegisterBody {
  project_no: string;                 // = contract_mgmt.objid 또는 별도 식별자
  shipping_order_status?: string;
  serial_no?: string;
  sales_quantity?: number;
  sales_unit_price?: number;
  sales_supply_price?: number;
  sales_vat?: number;
  sales_total_amount?: number;
  sales_currency?: string;
  sales_exchange_rate?: number;
  shipping_date?: string;
  shipping_method?: string;
  manager_user_id?: string;
  incoterms?: string;
  has_split_shipment?: boolean;
}

export interface DeadlineInfoBody {
  log_id?: number;                    // shipment_log.log_id (기존이면 update)
  parent_sale_no: number;             // sales_registration.sale_no
  target_objid: string;               // contract_mgmt.objid
  sales_deadline_date?: string;
  tax_type?: string;
  tax_invoice_date?: string;
  export_decl_no?: string;
  loading_date?: string;
  sales_slip_date?: string;
  sales_slip_menu_sq?: number;
  remark?: string;
}

// ─── 판매 그리드 (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[] = ["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(`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 = 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(`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); }

  const where = `WHERE ${conditions.join(" AND ")}`;

  const sql = `
    SELECT
       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
      ,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
      ,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(
         COALESCE(CAST(NULLIF(REPLACE(T.quantity, ',', ''), '') AS NUMERIC), 0)
           - COALESCE(SR.sales_quantity, 0),
         0
       )                                AS remaining_quantity
      ,SR.sale_no
      ,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
      ,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
      ,CM.exchange_rate                 AS exchange_rate
      ,SR.sales_exchange_rate
      ,(SELECT CODE_NAME(CI2.return_reason) FROM contract_item CI2
         WHERE CI2.contract_objid = T.contract_objid AND CI2.part_objid = T.part_objid
           AND CI2.status='ACTIVE' ORDER BY CI2.objid DESC LIMIT 1) AS return_reason_name
      ,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
      /* 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
      /* 주문서첨부 카운트 — contract_mgmt.objid 기반 (wace 동일) */
      ,COALESCE(AF.cu01_cnt, 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.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
    /* 주문서첨부 카운트 (contract_mgmt.objid 기반, doc_type FTC_ORDER/ORDER — wace 동일) */
    LEFT JOIN (
      SELECT target_objid,
             COUNT(*) FILTER (WHERE doc_type IN ('FTC_ORDER','ORDER')) AS cu01_cnt
      FROM attach_file_info
      WHERE UPPER(status) = 'ACTIVE'
      GROUP BY target_objid
    ) AF ON AF.target_objid = T.contract_objid
    ${where}
    ORDER BY T.regdate DESC NULLS LAST, T.project_no DESC
  `;

  const res = await pool.query(sql, params);
  logger.info("판매 목록 조회", { count: res.rowCount, filter });
  return res.rows;
}

// ─── 매출 그리드 (shipment_log 기반) ─────────────────────────

export async function getRevenueList(filter: SaleListFilter) {
  const pool = getPool();
  const conditions: string[] = [];
  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.productType) { conditions.push(`T.product = $${idx++}`); params.push(filter.productType); }
  if (filter.nation) { conditions.push(`T.area_cd = $${idx++}`); params.push(filter.nation); }
  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 = 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(`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.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); }

  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_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
      ,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
      ,CM.exchange_rate                 AS exchange_rate
      ,SR.sales_currency
      ,CC_CUR_S.code_name               AS sales_currency_name
      ,SR.sales_exchange_rate
      ,(SELECT CODE_NAME(CI2.return_reason) FROM contract_item CI2
         WHERE CI2.contract_objid = T.contract_objid AND CI2.part_objid = T.part_objid
           AND CI2.status='ACTIVE' ORDER BY CI2.objid DESC LIMIT 1) AS return_reason_name
      ,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
      /* V1 컬럼 보강 (wace 일치) — 접수일/유무상/요청납기/고객사요청사항/수주상태/주문서첨부/담당자/인도조건 */
      ,CM.receipt_date                                                                     AS receipt_date
      ,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
      ,COALESCE(NULLIF(CI.due_date, ''), NULLIF(T.due_date, ''), NULLIF(CM.due_date, ''))   AS request_date
      ,CM.customer_request                                                                 AS customer_request
      ,T.contract_result                                                                   AS order_status
      ,CC_RES.code_name                                                                    AS order_status_name
      ,U_MGR.user_name                                                                     AS manager_name
      ,SR.incoterms                                                                        AS incoterms
      ,COALESCE(AF.cu01_cnt, 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.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'
    /* 주문서첨부 카운트 — contract_mgmt.objid 기반 (wace 동일) */
    LEFT JOIN (
      SELECT target_objid,
             COUNT(*) FILTER (WHERE doc_type IN ('FTC_ORDER','ORDER')) AS cu01_cnt
      FROM attach_file_info WHERE UPPER(status)='ACTIVE'
      GROUP BY target_objid
    ) AF ON AF.target_objid = T.contract_objid
    ${where}
    ORDER BY T.regdate DESC NULLS LAST, T.project_no DESC
  `;

  const res = await pool.query(sql, params);
  logger.info("매출 목록 조회", { count: res.rowCount, filter });
  return res.rows;
}

// ─── 판매 등록/수정 (sales_registration upsert) ─────────────

export async function registerSale(userId: string, body: SaleRegisterBody) {
  const pool = getPool();
  const sql = `
    INSERT INTO sales_registration (
      project_no, shipping_order_status, serial_no,
      sales_quantity, sales_unit_price, sales_supply_price, sales_vat, sales_total_amount,
      sales_currency, sales_exchange_rate,
      shipping_date, shipping_method, manager_user_id, incoterms,
      has_split_shipment, reg_date, reg_user_id
    ) VALUES (
      $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,NOW(),$16
    )
    ON CONFLICT (project_no) DO UPDATE SET
      shipping_order_status=EXCLUDED.shipping_order_status,
      serial_no=EXCLUDED.serial_no,
      sales_quantity=EXCLUDED.sales_quantity,
      sales_unit_price=EXCLUDED.sales_unit_price,
      sales_supply_price=EXCLUDED.sales_supply_price,
      sales_vat=EXCLUDED.sales_vat,
      sales_total_amount=EXCLUDED.sales_total_amount,
      sales_currency=EXCLUDED.sales_currency,
      sales_exchange_rate=EXCLUDED.sales_exchange_rate,
      shipping_date=EXCLUDED.shipping_date,
      shipping_method=EXCLUDED.shipping_method,
      manager_user_id=EXCLUDED.manager_user_id,
      incoterms=EXCLUDED.incoterms,
      has_split_shipment=EXCLUDED.has_split_shipment,
      upd_date=NOW(), upd_user_id=$16
    RETURNING sale_no
  `;
  const params = [
    body.project_no, body.shipping_order_status || null, body.serial_no || null,
    body.sales_quantity ?? null, body.sales_unit_price ?? null,
    body.sales_supply_price ?? null, body.sales_vat ?? null, body.sales_total_amount ?? null,
    body.sales_currency || null, body.sales_exchange_rate ?? null,
    body.shipping_date || null, body.shipping_method || null,
    body.manager_user_id || null, body.incoterms || null,
    body.has_split_shipment ?? false, userId,
  ];
  const res = await pool.query(sql, params);
  logger.info("판매 등록/수정", { project_no: body.project_no, sale_no: res.rows[0]?.sale_no });
  return res.rows[0];
}

export async function deleteSale(projectNo: string) {
  const pool = getPool();
  await pool.query(`DELETE FROM sales_registration WHERE project_no = $1`, [projectNo]);
  logger.info("판매 삭제", { projectNo });
}

// ─── 매출 마감정보 입력/저장 (shipment_log upsert) ───────────

export async function saveDeadlineInfo(userId: string, body: DeadlineInfoBody) {
  const pool = getPool();
  if (body.log_id) {
    // 수정
    await pool.query(
      `UPDATE shipment_log SET
         sales_deadline_date=$2, tax_type=$3, tax_invoice_date=$4,
         export_decl_no=$5, loading_date=$6,
         sales_slip_date=$7, sales_slip_menu_sq=$8,
         remark=$9
       WHERE log_id=$1`,
      [
        body.log_id, body.sales_deadline_date || null, body.tax_type || null,
        body.tax_invoice_date || null, body.export_decl_no || null, body.loading_date || null,
        body.sales_slip_date || null, body.sales_slip_menu_sq ?? null,
        body.remark || null,
      ],
    );
    return { log_id: body.log_id };
  }
  // 신규
  const res = await pool.query(
    `INSERT INTO shipment_log (
       target_objid, log_type, parent_sale_no, is_split_record,
       sales_deadline_date, tax_type, tax_invoice_date,
       export_decl_no, loading_date,
       sales_slip_date, sales_slip_menu_sq, remark,
       reg_date, reg_user_id
     ) VALUES ($1, 'DEADLINE_INFO', $2, false,
               $3,$4,$5,$6,$7,$8,$9,$10, NOW(), $11)
     RETURNING log_id`,
    [
      body.target_objid, body.parent_sale_no,
      body.sales_deadline_date || null, body.tax_type || null, body.tax_invoice_date || null,
      body.export_decl_no || null, body.loading_date || null,
      body.sales_slip_date || null, body.sales_slip_menu_sq ?? null, body.remark || null,
      userId,
    ],
  );
  logger.info("매출 마감정보 등록", { log_id: res.rows[0]?.log_id, target: body.target_objid });
  return res.rows[0];
}

export async function confirmSalesDeadline(_userId: string, logIds: number[]) {
  const pool = getPool();
  // 마감 확정 = sales_deadline_date NOT NULL 로직만 적용. 잠금 컬럼이 별도 없어
  // 추후 별도 컬럼 추가하거나 정책 결정 후 강화. 지금은 단순 Touch.
  await pool.query(
    `UPDATE shipment_log SET log_message=COALESCE(log_message,'') || ' [DEADLINE_CONFIRMED ' || NOW()::text || ']'
     WHERE log_id = ANY($1::int[])`,
    [logIds],
  );
  logger.info("매출 마감 확정", { count: logIds.length });
}
