modules_schedule_check-scdul.js
/**
* @typedef CheckScdulOptions
* @property {string | string[]} [scdulCd]
* 체크할 일정 코드 (기본으로 메뉴의 것을 사용)<br>
* 여러 일정을 인수로 넘길 시 다음과 같이 처리합니다.<br>
* 일정 중 단 하나라도 일치하는 것이 있으면 통과<br>
* 모두 불일치 일 시 다음 우선순위로 일정 메시지와 정보를 반환합니다.<br>
* 1. 현재 일자보다 시작일자가 늦는 일정<br>
* 2. 현재 일자와 시작일자가 가까운 일정
* @property {string} [sno]
* 학번<br>
* 해당 인수의 학번을 바탕으로 일정의 대학 코드를 찾습니다.
* 학생의 경우 세션의 ID를 사용합니다.
* @property {string} [univCd]
* 대학 코드<br>
* 해당 인수를 생략 시 업무 코드를 기반 등의 조건을 거쳐 찾습니다.
* @property {boolean} [enableDefaultAction=true]
* 기본 액션 실행 여부<br>
* 해당 값을 false로 설정 시 일정 결과에 따른 기본 작업을 수행하지 않습니다.
* @property {nexacro.Component[]|function(boolean)} [targetComponent]
* 일정의 대상이 되는 컴포넌트 혹은 boolean 타입의 매개변수를 받는 함수<br>
* nexacro.Component 객체의 경우 set_enable을 실행하여 컴포넌트 활성/비활성화를 진행합니다.<br>
* function 객체의 경우 일정 여부를 받는 매개변수를 받는 함수입니다.
* @property {boolean} [targetComponentOverride=true]
* 일정의 대상이 되는 컴포넌트의 기본을 유지하고자 하는 경우 false.
* @property {boolean} [isCloseOutOfScdul=false]
* 일정 범위가 아닐 시 화면을 닫을 지 여부<br>
* 이 기능은 enableDefaultAction 값이 true여야 동작합니다.<br>
* 즉시 종료하므로 리턴받은 Promise에서 후처리 동작이 불가합니다.
*/
/**
* @typedef CheckSchdReturn
* @property {boolean} scdulYn 일정 체크 결과
* @property {string} scdulMsg 일정 메시지
* @property {boolean} chkSuccessYn 일정 검사 수행의 성공 여부. 인수 부족, 일정 해당 없는 메뉴의 경우
* true로 반환
* @property {string} scdulCd 일정 검사 후 해당되는 일정 코드
* @property {string} beginDtm 일정 시작 일시
* @property {string} endDtm 일정 종료 일시
*/
/**
* @param {string} scdulCd 일정 코드
* @returns {Promise}
* @access private
*/
const _fetchCheckScdul = async function (formObj, scdulCd, options) {
const args = {
SCDUL_CD: scdulCd,
SNO: options.sno || "",
UNIV_CD: options.univCd || "",
};
const datasetName = "_ds_schdChk_" + scdulCd;
const dataset = new Dataset();
if (!formObj._ds_schdChk) {
formObj.addChild(datasetName, dataset);
}
try {
await formObj.tx.search({
action: "basic",
svcId: "f_chkScdul_" + scdulCd,
sqlId: "@f_chkScdul.s01",
param: args,
outDs: datasetName,
cursorNm: "OUT_CURSOR",
});
const scdulYn = dataset.getColumn(0, "SCDUL_YN") === "1";
const scdulMsg = dataset.getColumn(0, "SCDUL_MSG");
return {
scdulYn: scdulYn,
scdulMsg: scdulMsg,
scdulCd: scdulCd,
beginDtm: dataset.getColumn(0, "BEGIN_DTM"),
endDtm: dataset.getColumn(0, "END_DTM"),
};
} finally {
formObj.removeChild(datasetName);
}
};
/**
* @typedef ParsedScdulMsg
* @property {string} [scdulNm] 일정명
* @property {string} [beginDtm] 시작 일자
* @property {string} [endDtm] 종료 일자
* @access private
*/
/**
* @param {string} scdulMsg 일정 메시지
* @returns {ParsedScdulMsg} 파싱된 데이터
* @access private
*/
const _parseScdulMsg = function (scdulMsg) {
if (!scdulMsg) {
return {};
}
const matched = scdulMsg.match(
/^(.+?) : ([0-9]+)년 ([0-9]+)월 ([0-9]+)일 \(([0-9]+):([0-9]+)\) 부터 ([0-9]+)년 ([0-9]+)월 ([0-9]+)일 \(([0-9]+):([0-9]+)\) 까지 입니다/,
);
if (!matched) {
return {};
}
return {
scdulNm: matched[1],
beginDtm:
matched[2] + matched[3] + matched[4] + matched[5] + matched[6],
endDtm:
matched[7] + matched[8] + matched[9] + matched[10] + matched[11],
};
};
/**
* 화면을 닫는 함수
*
* @access private
*/
const _closeCurrentMenu = function () {
const menuId = $comp.getWorkInfoDataset(this).getColumn(0, "MENU_ID");
nexacro.$workManager.close(menuId);
};
/**
* 일정을 검사하여 관련한 처리를 수행합니다.
*
* 이 함수는 this를 form으로 간주합니다. 다른 스코프에서 실행 시 call을 통해서 this를 form으로 바인딩하여야 합니다.
*
* @function checkScdul
* @param {nexacro.Form} formObj 넥사크로 폼 오브젝트
* @param {CheckScdulOptions} [options={}] 일정 검사 옵션
* @returns {Promise<CheckSchdResultCode>} 일정 체크의 결과
* @memberof $f
* @example
* // 기본 처리
* // 메뉴에 등록된 일정을 가지고 검사를 수행하며, 각 일정에 대하여 다음과 같이 동작합니다.
* //
* // 일정 범위 내:
* // 메인 타이틀바의 모든 버튼을 활성화 합니다.
* //
* // 일정 범위 밖:
* // 메인 타이틀바의 조회, 초기화 버튼을 제외한 버튼을 모두 비활성화 합니다.
* $f.fn_checkScdul(this);
*
* // 모든 인수
* $f.checkScdul(this, {
* scdulCd: "ABC1234", // 일정 코드 (기본 값: 메뉴의 일정 코드)
* sno: this.div_search.form.sch_sno.value, // 학번
* enableDefaultAction: true, // 기본 컴포넌트 활성/비활성, 화면 종료 등의 액션을 사용합니다. false시 아무런 작업도 하지 않습니다.
* targetComponent: [
* this.getMainTitle().getButton("SEARCH"), // 대상 컴포넌트를 지정하면 set_enable 함수로 동작합니다.
* (b) => formObj.getMainTitle().set_useSaveButton(b), // 일정 여부를 인수로 갖는 함수입니다. 일정 여부에 따라 다른 옵션을 지정하는데 사용합니다.
* ], // 대상 컴포넌트 및 함수
* targetComponentOverride: false, // true로 설정 시 targetComponent를 기본 대상 컴포넌트로 덮어쓰며, false로 설정 시 병합합니다.
* isCloseOutOfScdul: false, // true로 설정 시 일정 검사 후 화면을 닫습니다.
* });
*
* // 리턴값 활용 예제
* // 1. 일정 범위 내인 경우만 처리
* const result = await $f.checkScdul(this);
* if (result.scdulYn) {
* // 일정 범위 내 처리
* this.saveData();
* }
*
* // 2. 시작/종료 일시 활용
* const result = await $f.checkScdul(this);
* if (result.scdulYn) {
* this.div_info.form.sta_period.set_text(
* `${result.beginDtm} ~ ${result.endDtm}`,
* );
* }
*
* // 3. 메시지 직접 처리
* const result = await $f.checkScdul(this, {
* enableDefaultAction: false,
* });
* if (!result.scdulYn) {
* await this.pop.warn(result.scdulMsg);
* }
*
*/
export async function checkScdul(formObj, options) {
options = options || {};
// 인수로 들어온 옵션이 없을 경우
if (!options.scdulCd) {
options.scdulCd = [
$comp.getWorkInfoDataset(formObj).getColumn(0, "SCDUL_CD"),
];
}
if (options.enableDefaultAction === undefined) {
options.enableDefaultAction = true;
}
if (options.targetComponentOverride === undefined) {
options.targetComponentOverride = true;
}
if (!(options.scdulCd instanceof Array)) {
options.scdulCd = [options.scdulCd];
}
if (options.isCloseOutOfScdul === undefined) {
options.isCloseOutOfScdul = false;
}
const defaultTargetComponent = [
formObj?.getMainTitle?.()?.getButton?.("SAVE"),
formObj?.getMainTitle?.()?.getButton?.("ADD"),
formObj?.getMainTitle?.()?.getButton?.("REMOVE"),
formObj?.getMainTitle?.()?.getButton?.("PRINT"),
formObj?.getMainTitle?.()?.getButton?.("EXCEL"),
].filter((e) => e);
if (!options.targetComponent) {
options.targetComponent = [...defaultTargetComponent];
} else if (options.targetComponent && !options.targetComponentOverride) {
options.targetComponent = [
...options.targetComponent,
...defaultTargetComponent,
];
}
// 해당 화면에 일정 코드 부여가 안된 경우
if (options.scdulCd.length <= 0) {
return Promise.resolve({
scdulYn: true,
scdulMsg: "",
chkSuccessYn: false,
});
}
const proms = options.scdulCd.map((scdulCd) => {
return _fetchCheckScdul(formObj, scdulCd, options);
});
const promsList = await Promise.allSettled(proms);
let scdulYn = false;
let scdulMsg = "";
let scdulCd = "";
let beginDtm = "";
let endDtm = "";
const dataList = promsList.map((v) => {
const { scdulCd, scdulMsg, scdulYn, beginDtm, endDtm } = v.value;
return {
scdulYn,
scdulMsg,
beginDtm,
endDtm,
scdulCd,
};
});
//수강신청같이 여러일정이 동시에 걸쳐있는경우 오늘날짜가 일정안에 포함되면 1순위 이미지났으면 2순위 아예 안걸려있으면 3순위로 정렬후 0번째 가져감
// 이미 일정안에 포함되면 해당스케쥴은 열린거로 봐야하기때문
const today = $util.todayTime.substring(0, 12); // 현재 시간 (YYYYMMDDHHMI)
dataList.sort((a, b) => {
// 1. 진행 중인 일정인지 확인 (SCDUL_YN이 1이고 현재 기간 내)
const aIsRunning =
a.scdulYn && today >= a.beginDtm && today <= a.endDtm ? 0 : 1;
const bIsRunning =
b.scdulYn && today >= b.beginDtm && today <= b.endDtm ? 0 : 1;
// 2. 진행 중인 게 있다면 최우선 (0이 1보다 앞으로)
if (aIsRunning !== bIsRunning) return aIsRunning - bIsRunning;
// 3. 둘 다 진행 중이거나 둘 다 아닌 경우, SCDUL_YN이 1인 것을 우선
const aYn = a.scdulYn ? 0 : 1;
const bYn = b.scdulYn ? 0 : 1;
if (aYn !== bYn) return aYn - bYn;
// 4. 그 외에는 시작일자가 가까운 순으로 정렬
return b.beginDtm - a.beginDtm;
});
let schduledDataList = dataList.filter((e) => e.scdulYn);
if (schduledDataList.length > 0) {
// 일정 통과
scdulYn = true;
scdulCd = schduledDataList[0].scdulCd;
beginDtm = schduledDataList[0]?.beginDtm;
endDtm = schduledDataList[0]?.endDtm;
} else {
const today = $util.todayTime.substring(0, 12);
const displayScdul = dataList
.filter((a) => a.beginDtm)
.sort(
(a, b) =>
Math.abs(today - a.beginDtm) +
(today - a.beginDtm > 0 ? 1000000000 : 0) -
(Math.abs(today - b.beginDtm) +
(today - b.beginDtm > 0 ? 1000000000 : 0)),
)[0];
scdulYn = false;
scdulMsg = displayScdul && displayScdul.scdulMsg;
scdulCd = displayScdul && displayScdul.scdulCd;
beginDtm = displayScdul?.beginDtm;
endDtm = displayScdul?.endDtm;
}
// 후처리
if (options.enableDefaultAction) {
if (!scdulYn) {
await formObj.pop.alert(scdulMsg || "일정이 존재하지 않습니다.");
options.isCloseOutOfScdul && _closeCurrentMenu.call(formObj);
}
// 대상 컴포넌트 활성화, 비활성화
options.targetComponent.forEach((comp) => {
if (typeof comp === "function") {
comp && comp(scdulYn);
} else {
comp && comp.set_enable && comp.set_enable(scdulYn);
}
});
}
// 결과 반환
return {
scdulYn: scdulYn,
scdulMsg: scdulMsg,
chkSuccessYn: true,
scdulCd: scdulCd,
beginDtm: beginDtm,
endDtm: endDtm,
};
}