furniture-3d-system-Wu/furniture-3d-system/src/lib/supply-chain-owner-employment.ts

108 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 负责人在职状态与离职接管链的共享口径(2026-10-12 用户确认)。
//
// 全量档案的运营负责人/设计师在职状态是「显示层」能力:库里永远存原始花名,
// 页面与导出按人员信息表(SupplyChainManagementOwnerConfig)实时判断:
// - 负责人在职 → 原样显示;
// - 负责人离职 → `花名(已离职)→ 接管人`,接管人依次取其部门现任负责人、
// 运营总负责人;接管人也离职时仅追加(已离职)标注,不再上抛;
// - 花名匹配不到人员信息 → 原样显示,状态「未匹配」(需要补人员信息或改档案);
// - 档案无负责人 → 状态「未设置」。
// 设计师离职只加(已离职)标注,不做接管(设计师无层级链)。
export type OwnerEmploymentStatus = '在职' | '已离职' | '未匹配' | '未设置'
export const OWNER_EMPLOYMENT_STATUS_VALUES = [
'在职',
'已离职',
'未匹配',
'未设置',
] as const satisfies readonly OwnerEmploymentStatus[]
export type OwnerEmploymentEntry = {
/** 人员信息表花名(原样,不清洗) */
name: string
/** 在职状态;无员工目录关联的行按在职处理(不标注) */
sourceStatus: 'EMPLOYED' | 'DIMISSIONED'
departmentOwner: string
generalOwner: string
}
/** 与档案侧身份清洗同口径:去零宽字符 + 去首尾空白 + 大写。 */
export function normalizeOwnerName(value: string) {
return value.replace(/[\u200B-\u200D\uFEFF]/g, '').trim().toUpperCase()
}
export type OwnerEmploymentIndex = Map<string, OwnerEmploymentEntry>
/** 同名花名取第一条(调用方按 operationOwner asc、updatedAt desc 排序传入)。 */
export function buildOwnerEmploymentIndex(entries: OwnerEmploymentEntry[]): OwnerEmploymentIndex {
const index: OwnerEmploymentIndex = new Map()
for (const entry of entries) {
const key = normalizeOwnerName(entry.name)
if (key && !index.has(key)) index.set(key, entry)
}
return index
}
export type OwnerTakeoverView = {
status: OwnerEmploymentStatus
/** 展示值:在职/未匹配/未设置为原样(未设置为空串),离职为 `花名(已离职)→ 接管人` */
display: string
/** 接管人花名(仅离职且有接管人时有值) */
successor: string
/** 接管人本身也已离职 */
successorDeparted: boolean
}
export function resolveOwnerTakeover(
opsHead: string | null | undefined,
index: OwnerEmploymentIndex,
): OwnerTakeoverView {
const raw = (opsHead ?? '').trim()
if (!raw) return { status: '未设置', display: '', successor: '', successorDeparted: false }
const entry = index.get(normalizeOwnerName(raw))
if (!entry) return { status: '未匹配', display: raw, successor: '', successorDeparted: false }
if (entry.sourceStatus !== 'DIMISSIONED') {
return { status: '在职', display: raw, successor: '', successorDeparted: false }
}
const ownerKey = normalizeOwnerName(raw)
const pickSuccessor = (name: string | null | undefined) => {
const trimmed = (name ?? '').trim()
if (!trimmed || normalizeOwnerName(trimmed) === ownerKey) return null
return {
name: trimmed,
departed: index.get(normalizeOwnerName(trimmed))?.sourceStatus === 'DIMISSIONED',
}
}
const departmentOwner = pickSuccessor(entry.departmentOwner)
const generalOwner = pickSuccessor(entry.generalOwner)
// 接管链:部门现任负责人在职 → 直接接管;否则交给总负责人(总负责人也离职则
// 带标注展示,不再上抛);两者都缺位时退回展示部门负责人(如有)。
const successor = (departmentOwner && !departmentOwner.departed ? departmentOwner : null)
?? generalOwner
?? departmentOwner
if (!successor) {
return { status: '已离职', display: `${raw}(已离职)`, successor: '', successorDeparted: false }
}
return {
status: '已离职',
display: `${raw}(已离职)→ ${successor.name}${successor.departed ? '(已离职)' : ''}`,
successor: successor.name,
successorDeparted: successor.departed,
}
}
export function resolveDesignerView(
designer: string | null | undefined,
index: OwnerEmploymentIndex,
): { display: string; departed: boolean } {
const raw = (designer ?? '').trim()
if (!raw) return { display: '', departed: false }
const entry = index.get(normalizeOwnerName(raw))
if (entry?.sourceStatus === 'DIMISSIONED') {
return { display: `${raw}(已离职)`, departed: true }
}
return { display: raw, departed: false }
}