Add multi-office seat maps and dev/prod DB sync protocol

This commit is contained in:
hyunho
2026-03-27 16:34:43 +09:00
parent 1d15cf9b9b
commit d66614123e
16 changed files with 3427 additions and 146 deletions

View File

@@ -421,6 +421,7 @@ const App = () => {
if (parts.length < 3) return clean;
return `${parts[0]}-${parts[1].padStart(2, '0')}-${parts[2].padStart(2, '0')}`;
};
const getExpenseDate = (row) => norm(getVal(row, ['발행일', '청구일', '발행 일자', '청구 일자'], 2));
const processedData = useMemo(() => {
if (!dataLoaded.expense || !dataLoaded.work) return null;
@@ -484,7 +485,8 @@ const App = () => {
const workerName = norm(getVal(row, ['이름']));
const position = norm(getVal(row, ['직책', '직급']));
const userState = norm(getVal(row, ['user_state', 'User State', 'user state', 'userstate', 'User_State']));
const isWeekend = userState.includes('주말');
const weekendFlag = norm(getVal(row, ['주말/지각']));
const isWeekend = userState.includes('주말') || weekendFlag.includes('주말');
const isMhSchema = hasNamedHeader(row, '메인업무 프로젝트명') || hasNamedHeader(row, '연장근무 프로젝트명') || hasNamedHeader(row, '연장근무 시간(가공)');
const importedLabor = pnum(getVal(row, ['산정금액', '인건비']));
const overtimeHoursFromRow = isMhSchema
@@ -623,7 +625,7 @@ const App = () => {
return raw.length >= 7 ? raw.slice(0, 7) : raw;
};
const excludedWorkers = new Set(['정태원', '양병홍', '장계석', '장종찬', '김원식', '김형준']);
const excludedWorkers = new Set(['정태원', '양병홍', '장종찬', '김형준']);
const selectedProjectKey = normalizeProjectKey(selectedProject === '전체' ? '' : selectedProject);
const projectSearchKey = normalizeProjectKey(projectSearch);
@@ -648,7 +650,7 @@ const App = () => {
const d1FromRow = normalizeD1Category(getVal(item, ['D1', '매출/비매출', '매출비매출'], 14));
if (!d1FromRow) return false; // D1(K) empty rows are ignored.
const info = projectToDepth[pKey] || unknownDepth;
const issueDate = getVal(item, ['발행일'], 2);
const issueDate = getExpenseDate(item);
return (selectedRev === '전체' || info.d1 === selectedRev) &&
(selectedD1 === '전체' || info.d2 === selectedD1) &&
(selectedD2 === '전체' || info.d3 === selectedD2) &&
@@ -667,7 +669,7 @@ const App = () => {
const baseAllExp = expenseRaw.filter(item => {
const d1FromRow = normalizeD1Category(getVal(item, ['D1', '매출/비매출', '매출비매출'], 14));
if (!d1FromRow) return false;
const issueDate = getVal(item, ['발행일'], 2);
const issueDate = getExpenseDate(item);
return isWithinRange(issueDate);
});
@@ -996,7 +998,7 @@ const App = () => {
else if (div === '외주비' || div === '외주') category = '외주비';
else if (div === '제외') return;
const issueDate = norm(getVal(e, ['발행일', '발행 일자', '일자'], 2));
const issueDate = getExpenseDate(e);
expenseDetailByCategory[category].push({
issueMonth: toIssueMonth(issueDate),
issueDate,