From 52bfd3578a458ac6f7615404e916997cabed2062 Mon Sep 17 00:00:00 2001 From: zhangrui Date: Tue, 29 Sep 2026 15:35:03 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BE=85=E7=A1=AE=E8=AE=A4=E4=BA=BA=E5=91=98?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E6=95=B4=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dependency-reduced-pom.xml | 2 +- .../com/budwk/app/sys/models/Sys_user.java | 5 ++ .../member/constant/MemberChangeType.java | 26 ++++---- .../service/impl/MemberManageServiceImpl.java | 56 +++++++++++++++++ .../PendingConfirmUserBatchChangeParam.java | 12 ++++ .../impl/PendingConfirmUserServiceImpl.java | 62 ++++++++++++++++++- .../db/sys_user_add_welfare_stop_date.sql | 19 ++++++ .../member/change/common/memberChange.js | 21 ++++++- .../sourcechange/pending/index.html | 37 +++++++++++ .../sourcechange/pending/memberChange.html | 1 + .../sourcechange/pending/memberChange.js | 19 +++++- 11 files changed, 240 insertions(+), 20 deletions(-) create mode 100644 src/main/resources/db/sys_user_add_welfare_stop_date.sql diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml index ea8ea841..5d8c5636 100644 --- a/dependency-reduced-pom.xml +++ b/dependency-reduced-pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.budwk - zhgh_cug_v4 + mini 5.6.0-plus diff --git a/src/main/java/com/budwk/app/sys/models/Sys_user.java b/src/main/java/com/budwk/app/sys/models/Sys_user.java index 37753d57..edc832a7 100644 --- a/src/main/java/com/budwk/app/sys/models/Sys_user.java +++ b/src/main/java/com/budwk/app/sys/models/Sys_user.java @@ -237,6 +237,11 @@ public class Sys_user extends BaseModel implements Serializable { @ColDefine(type = ColType.DATE) private Date retireDate; + @Column + @Comment("福利享受截止时间") + @ColDefine(type = ColType.DATE) + private Date welfareStopDate; + @Column @Comment("博士后进站时间") @ColDefine(type = ColType.DATE) diff --git a/src/main/java/com/budwk/app/zhgh/staffmanage/member/constant/MemberChangeType.java b/src/main/java/com/budwk/app/zhgh/staffmanage/member/constant/MemberChangeType.java index 48e80c5f..8244d6a8 100644 --- a/src/main/java/com/budwk/app/zhgh/staffmanage/member/constant/MemberChangeType.java +++ b/src/main/java/com/budwk/app/zhgh/staffmanage/member/constant/MemberChangeType.java @@ -18,10 +18,21 @@ public enum MemberChangeType { //变更类型 NEW("NEW", "新入职"), + + BASIC_CHANGE("BASIC_CHANGE", "基本信息异动"), + UNIT_CHANGE("UNIT_CHANGE", "单位异动"), + UNION_CHANGE("UNION_CHANGE", "工会关系异动"), + + ONLINE("ONLINE", "在岗"), + OFFLINE("OFFLINE", "不在岗"), + RESTORE("RESTORE", "入会"), WITHDRAWAL("WITHDRAWAL", "退会"), - ONLINE("ONLINE", "在岗"), + EXTEND("EXTEND", "延聘"), + REHIRE_SCH("REHIRE_SCH", "学校返聘"), + REHIRE_DEPT("REHIRE_DEPT", "部门返聘"), + FLEX("FLEX", "柔性"), SECONDED("SECONDED", "借调挂职"), REMOTE_POST("REMOTE_POST", "异地任职"), @@ -40,18 +51,7 @@ public enum MemberChangeType { STUDY_DOMESTIC("STUDY_DOMESTIC", "国内进修"), STUDY_ABROAD("STUDY_ABROAD", "境外留学"), OFF_JOB_START("OFF_JOB_START", "离岗创业"), - INNER_RETIRE("INNER_RETIRE", "内部退养"), - - EXTEND("EXTEND", "延聘"), - REHIRE_SCH("REHIRE_SCH", "学校返聘"), - REHIRE_DEPT("REHIRE_DEPT", "部门返聘"), - - OFFLINE("OFFLINE", "不在岗"), - - UNIT_CHANGE("UNIT_CHANGE", "单位异动"), - UNION_CHANGE("UNION_CHANGE", "工会关系异动"), - BASIC_CHANGE("BASIC_CHANGE", "基本信息异动"); - + INNER_RETIRE("INNER_RETIRE", "内部退养"); private final String code; private final String changeTypeName; diff --git a/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/impl/MemberManageServiceImpl.java b/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/impl/MemberManageServiceImpl.java index 6bc1cbbd..6ce6734a 100644 --- a/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/impl/MemberManageServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/staffmanage/member/service/impl/MemberManageServiceImpl.java @@ -262,6 +262,15 @@ public class MemberManageServiceImpl extends BaseServiceImpl implement allowChangeFieldNames.add("threeUnitId"); dictMap.put("threeUnitId", "三级单位"); } + // 日期字段由具体变更表单明确传入时才参与比较,避免旧变更记录被误判为清空日期。 + if (record.getRetireDate() != null) { + allowChangeFieldNames.add("retireDate"); + dictMap.put("retireDate", "退休日期"); + } + if (record.getWelfareStopDate() != null) { + allowChangeFieldNames.add("welfareStopDate"); + dictMap.put("welfareStopDate", "福利享受截止时间"); + } // 新数据 NutMap newMap = Lang.obj2nutmap(record); @@ -305,14 +314,18 @@ public class MemberManageServiceImpl extends BaseServiceImpl implement @Override + @Aop(TransAop.READ_COMMITTED) public void compareChangeInfoAndUpdateMember(String recordId) { MemberChangeRecord record = dao().fetch(MemberChangeRecord.class, recordId); compareChangeInfoAndUpdateMember(record); } @Override + @Aop(TransAop.READ_COMMITTED) public void compareChangeInfoAndUpdateMember(MemberChangeRecord record) { + applyRetiredPersonType(record); normalizeArrivalAtSchoolDate(record); + normalizeWelfareStopDate(record); List changeList = getChangeInfos(record); // 如果有工会关系人员,这个user用来存储数据 @@ -382,6 +395,10 @@ public class MemberManageServiceImpl extends BaseServiceImpl implement // 到此,表明审核通过,需要存储历史数据,并修改用户数据 BeanUtil.copyProperties(record, info); + // 变更记录使用字符串接收页面日期,人员表使用 Date,保存前显式转换避免类型转换差异。 + if (StrUtil.isNotBlank(record.getWelfareStopDate())) { + info.setWelfareStopDate(DateUtil.parseDate(record.getWelfareStopDate())); + } info.setId(userId); // 如果有单位变更,则需要记录单位关系 @@ -518,7 +535,9 @@ public class MemberManageServiceImpl extends BaseServiceImpl implement @Override public void validateChangeAndSetBasicData(MemberChangeRecord record, MemberChangeOrigin origin) { + applyRetiredPersonType(record); normalizeArrivalAtSchoolDate(record); + normalizeWelfareStopDate(record); // 校验有没有发生变更 List changeInfos = getChangeInfos(record); if (Lang.isEmpty(changeInfos)) { @@ -536,6 +555,43 @@ public class MemberManageServiceImpl extends BaseServiceImpl implement record.setApplyDateTime(DateUtil.date()); } + /** + * 在职状态调整为退休时,按人员类型字典名称自动设置“离退休人员”编码。 + * 字典中不存在该名称时保留原人员类型,不阻断其他变更。 + * + * @param record 人员变更内容,userState 为“退休”时才执行自动设置 + */ + private void applyRetiredPersonType(MemberChangeRecord record) { + if (record == null || !StrUtil.equals("退休", record.getUserState())) { + return; + } + List personTypeOptions = sysDictService.getSubListByCode("USER_PERSON_TYPE"); + if (Lang.isEmpty(personTypeOptions)) { + return; + } + personTypeOptions.stream() + .filter(option -> StrUtil.equals("离退休人员", option.getName())) + .findFirst() + .ifPresent(option -> record.setPersonType(option.getCode())); + } + + /** + * 校验并规范福利享受截止时间,统一使用 yyyy-MM-dd 参与比较及保存。 + * + * @param record 人员变更内容,空日期保持为空 + */ + private void normalizeWelfareStopDate(MemberChangeRecord record) { + if (record == null || StrUtil.isBlank(record.getWelfareStopDate())) { + return; + } + try { + Date parsedDate = parseArrivalAtSchoolDate(StrUtil.trim(record.getWelfareStopDate())); + record.setWelfareStopDate(DateUtil.format(parsedDate, "yyyy-MM-dd")); + } catch (Exception e) { + throw new BaseException("福利享受截止时间格式不正确,请使用yyyy-MM-dd格式"); + } + } + /** * 统一规范变更记录中的到校日期,避免 Date 对象复制后以英文字符串形式参与比较或直接落库。 diff --git a/src/main/java/com/budwk/app/zhgh/staffmanage/sourcechange/param/PendingConfirmUserBatchChangeParam.java b/src/main/java/com/budwk/app/zhgh/staffmanage/sourcechange/param/PendingConfirmUserBatchChangeParam.java index 2f87d02a..7e7b0180 100644 --- a/src/main/java/com/budwk/app/zhgh/staffmanage/sourcechange/param/PendingConfirmUserBatchChangeParam.java +++ b/src/main/java/com/budwk/app/zhgh/staffmanage/sourcechange/param/PendingConfirmUserBatchChangeParam.java @@ -2,6 +2,8 @@ package com.budwk.app.zhgh.staffmanage.sourcechange.param; import lombok.Data; +import java.util.Date; + /** * 待确认人员批量变更参数。 */ @@ -23,6 +25,16 @@ public class PendingConfirmUserBatchChangeParam { */ private String userState; + /** + * 批量设置的退休时间,仅在目标在职状态为退休且传入日期时参与变更。 + */ + private Date retireDate; + + /** + * 批量设置的福利享受截止时间,仅在目标在职状态为退休且传入日期时参与变更。 + */ + private Date welfareStopDate; + /** * 批量设置的人员属性,空值不参与批量变更。 */ diff --git a/src/main/java/com/budwk/app/zhgh/staffmanage/sourcechange/service/impl/PendingConfirmUserServiceImpl.java b/src/main/java/com/budwk/app/zhgh/staffmanage/sourcechange/service/impl/PendingConfirmUserServiceImpl.java index a81cae8d..298796e8 100644 --- a/src/main/java/com/budwk/app/zhgh/staffmanage/sourcechange/service/impl/PendingConfirmUserServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/staffmanage/sourcechange/service/impl/PendingConfirmUserServiceImpl.java @@ -120,6 +120,7 @@ public class PendingConfirmUserServiceImpl extends BaseServiceImpl imp if (record == null || StrUtil.isBlank(record.getUserId())) { throw new BaseException("未获取到待确认人员数据"); } + applyRetiredPersonType(record); if (StrUtil.isBlank(record.getPersonType())) { throw new BaseException("请选择人员类型"); } @@ -143,6 +144,7 @@ public class PendingConfirmUserServiceImpl extends BaseServiceImpl imp if (!hasBatchChangeField(changeParam)) { throw new BaseException("请选择需要变更的内容"); } + applyRetiredPersonType(changeParam); if (StrUtil.isNotBlank(changeParam.getPersonType())) { validatePersonType(changeParam.getPersonType()); } @@ -173,6 +175,13 @@ public class PendingConfirmUserServiceImpl extends BaseServiceImpl imp if (StrUtil.isNotBlank(changeParam.getUserState())) { record.setUserState(changeParam.getUserState()); } + // 退休时间只随“退休”状态批量写入,其他状态忽略该参数,避免请求篡改隐藏字段。 + if (StrUtil.equals("退休", changeParam.getUserState()) && changeParam.getRetireDate() != null) { + record.setRetireDate(changeParam.getRetireDate()); + } + if (StrUtil.equals("退休", changeParam.getUserState()) && changeParam.getWelfareStopDate() != null) { + record.setWelfareStopDate(DateUtil.formatDate(changeParam.getWelfareStopDate())); + } if (StrUtil.isNotBlank(changeParam.getUserAttribute())) { record.setUserAttribute(changeParam.getUserAttribute()); } @@ -239,13 +248,15 @@ public class PendingConfirmUserServiceImpl extends BaseServiceImpl imp safeRecord.setUserId(userId); safeRecord.setPersonType(record.getPersonType() == null ? userView.getPersonType() : StrUtil.trim(record.getPersonType())); safeRecord.setUserState(record.getUserState() == null ? userView.getUserState() : record.getUserState()); + safeRecord.setRetireDate(record.getRetireDate() == null ? userView.getRetireDate() : record.getRetireDate()); safeRecord.setUserAttribute(record.getUserAttribute() == null ? userView.getUserAttribute() : record.getUserAttribute()); safeRecord.setMember(record.getMember() == null ? Integer.valueOf(1).equals(userView.getMember()) : record.getMember()); safeRecord.setWelfareMember(record.getWelfareMember() == null ? Integer.valueOf(1).equals(userView.getWelfareMember()) : record.getWelfareMember()); safeRecord.setArrivalAtSchoolDate(userView.getArrivalAtSchoolDate() == null ? null : DateUtil.formatDate(userView.getArrivalAtSchoolDate())); - safeRecord.setWelfareStopDate(userView.getWelfareStopDate() == null - ? null : DateUtil.formatDate(userView.getWelfareStopDate())); + safeRecord.setWelfareStopDate(record.getWelfareStopDate() == null + ? (userView.getWelfareStopDate() == null ? null : DateUtil.formatDate(userView.getWelfareStopDate())) + : record.getWelfareStopDate()); try { // 复用公共校验和历史记录生成口径,确保单人、批量变更结果保持一致。 @@ -312,6 +323,53 @@ public class PendingConfirmUserServiceImpl extends BaseServiceImpl imp } } + /** + * 单人待确认变更选择退休时,存在“离退休人员”字典项才自动设置人员类型编码。 + * + * @param record 单人待确认变更内容 + */ + private void applyRetiredPersonType(MemberChangeRecord record) { + if (record == null || !StrUtil.equals("退休", record.getUserState())) { + return; + } + String retiredPersonTypeCode = resolveRetiredPersonTypeCode(); + if (StrUtil.isNotBlank(retiredPersonTypeCode)) { + record.setPersonType(retiredPersonTypeCode); + } + } + + /** + * 批量待确认变更选择退休时,存在“离退休人员”字典项才自动设置人员类型编码。 + * + * @param changeParam 批量待确认变更参数 + */ + private void applyRetiredPersonType(PendingConfirmUserBatchChangeParam changeParam) { + if (changeParam == null || !StrUtil.equals("退休", changeParam.getUserState())) { + return; + } + String retiredPersonTypeCode = resolveRetiredPersonTypeCode(); + if (StrUtil.isNotBlank(retiredPersonTypeCode)) { + changeParam.setPersonType(retiredPersonTypeCode); + } + } + + /** + * 根据人员类型字典名称获取“离退休人员”编码,未配置时返回空字符串。 + * + * @return 离退休人员字典编码,未配置时为空字符串 + */ + private String resolveRetiredPersonTypeCode() { + List personTypeDicts = sysDictService.getSubListByCode("USER_PERSON_TYPE"); + if (personTypeDicts == null) { + return ""; + } + return personTypeDicts.stream() + .filter(dict -> StrUtil.equals("离退休人员", dict.getName())) + .map(Sys_dict::getCode) + .findFirst() + .orElse(""); + } + /** * 变更提交成功后仅把待确认人员更新为已确认,重复调用不会改变其他状态。 * diff --git a/src/main/resources/db/sys_user_add_welfare_stop_date.sql b/src/main/resources/db/sys_user_add_welfare_stop_date.sql new file mode 100644 index 00000000..9100c444 --- /dev/null +++ b/src/main/resources/db/sys_user_add_welfare_stop_date.sql @@ -0,0 +1,19 @@ +-- 系统用户增加福利享受截止时间,字段保持可空以兼容历史人员数据。 +-- 脚本支持重复执行,字段已存在时不再修改表结构。 +SET @welfare_stop_date_column_count = ( + SELECT COUNT(*) + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'sys_user' + AND COLUMN_NAME = 'welfareStopDate' +); + +SET @welfare_stop_date_ddl = IF( + @welfare_stop_date_column_count = 0, + 'ALTER TABLE sys_user ADD COLUMN welfareStopDate DATE NULL COMMENT ''福利享受截止时间'' AFTER retireDate', + 'SELECT 1' +); + +PREPARE welfare_stop_date_stmt FROM @welfare_stop_date_ddl; +EXECUTE welfare_stop_date_stmt; +DEALLOCATE PREPARE welfare_stop_date_stmt; diff --git a/src/main/resources/views/platform/zhgh/staffmanage/member/change/common/memberChange.js b/src/main/resources/views/platform/zhgh/staffmanage/member/change/common/memberChange.js index fae0f786..fe4f5b23 100644 --- a/src/main/resources/views/platform/zhgh/staffmanage/member/change/common/memberChange.js +++ b/src/main/resources/views/platform/zhgh/staffmanage/member/change/common/memberChange.js @@ -140,6 +140,7 @@ const MEMBER_CHANGE = { @@ -177,7 +178,6 @@ const MEMBER_CHANGE = { v.code).includes(prop) }, + /** 在职状态选择退休时,存在“离退休人员”字典项才自动设置对应人员类型编码。 */ + handleRetiredStateChange(userState) { + if (userState !== "退休") { + return + } + this.$businessTool.getDictOptions("USER_PERSON_TYPE").then((options) => { + const retiredOption = (options || []).find((item) => item.name === "离退休人员") + if (retiredOption) { + this.$set(this.formData, "personType", retiredOption.code) + } + }) + }, // 管理页仅查询当前被编辑人员所属二级单位的直属三级单位,避免越级选择其他单位数据。 initThreeUnits() { this.threeUnits = [] @@ -500,6 +512,8 @@ const MEMBER_CHANGE = { campus, threeUnitId, userState, + retireDate, + welfareStopDate, personType, preparedBy, userAttribute, @@ -528,6 +542,11 @@ const MEMBER_CHANGE = { this.$set(this.formData, "campus", campus) this.$set(this.formData, "threeUnitId", threeUnitId) this.$set(this.formData, "userState", userState) + // 日期选择器使用 yyyy-MM-dd 字符串,避免接口日期值无法在组件中正确回显。 + this.$set(this.formData, "retireDate", retireDate + ? this.$moment(retireDate).format("YYYY-MM-DD") : "") + this.$set(this.formData, "welfareStopDate", welfareStopDate + ? this.$moment(welfareStopDate).format("YYYY-MM-DD") : "") this.$set(this.formData, "personType", personType) this.$set(this.formData, "preparedBy", preparedBy) this.$set(this.formData, "userAttribute", userAttribute) diff --git a/src/main/resources/views/platform/zhgh/staffmanage/sourcechange/pending/index.html b/src/main/resources/views/platform/zhgh/staffmanage/sourcechange/pending/index.html index 687dc634..fb70ff5d 100644 --- a/src/main/resources/views/platform/zhgh/staffmanage/sourcechange/pending/index.html +++ b/src/main/resources/views/platform/zhgh/staffmanage/sourcechange/pending/index.html @@ -200,8 +200,23 @@ layout("/layouts/platform.html"){ + + + + + + @@ -254,6 +269,8 @@ layout("/layouts/platform.html"){ batchChangeForm: { personType: "", userState: "", + retireDate: "", + welfareStopDate: "", userAttribute: "", member: null, welfareMember: null @@ -343,6 +360,18 @@ layout("/layouts/platform.html"){ || this.batchChangeForm.member !== null && this.batchChangeForm.member !== undefined && this.batchChangeForm.member !== "" || this.batchChangeForm.welfareMember !== null && this.batchChangeForm.welfareMember !== undefined && this.batchChangeForm.welfareMember !== "" }, + /** 退休状态自动匹配人员类型;非退休状态清理隐藏日期,避免沿用上次结果。 */ + handleBatchUserStateChange(userState) { + if (userState === "退休") { + const retiredOption = this.pendingPersonTypeOptions.find((item) => item.name === "离退休人员") + if (retiredOption) { + this.$set(this.batchChangeForm, "personType", retiredOption.code) + } + return + } + this.$set(this.batchChangeForm, "retireDate", "") + this.$set(this.batchChangeForm, "welfareStopDate", "") + }, /** 二次确认批量操作,防止误改多名人员。 */ confirmBatchChange() { if (!this.hasBatchChangeField()) { @@ -373,6 +402,12 @@ layout("/layouts/platform.html"){ if (this.batchChangeForm.userState) { params.userState = this.batchChangeForm.userState } + if (this.batchChangeForm.userState === "退休" && this.batchChangeForm.retireDate) { + params.retireDate = this.batchChangeForm.retireDate + } + if (this.batchChangeForm.userState === "退休" && this.batchChangeForm.welfareStopDate) { + params.welfareStopDate = this.batchChangeForm.welfareStopDate + } if (this.batchChangeForm.userAttribute) { params.userAttribute = this.batchChangeForm.userAttribute } @@ -401,6 +436,8 @@ layout("/layouts/platform.html"){ resetBatchChangeForm() { this.batchChangeForm.personType = "" this.batchChangeForm.userState = "" + this.$set(this.batchChangeForm, "retireDate", "") + this.$set(this.batchChangeForm, "welfareStopDate", "") this.batchChangeForm.userAttribute = "" this.batchChangeForm.member = null this.batchChangeForm.welfareMember = null diff --git a/src/main/resources/views/platform/zhgh/staffmanage/sourcechange/pending/memberChange.html b/src/main/resources/views/platform/zhgh/staffmanage/sourcechange/pending/memberChange.html index 0fb8eaa1..e5e5bf4d 100644 --- a/src/main/resources/views/platform/zhgh/staffmanage/sourcechange/pending/memberChange.html +++ b/src/main/resources/views/platform/zhgh/staffmanage/sourcechange/pending/memberChange.html @@ -103,6 +103,7 @@ diff --git a/src/main/resources/views/platform/zhgh/staffmanage/sourcechange/pending/memberChange.js b/src/main/resources/views/platform/zhgh/staffmanage/sourcechange/pending/memberChange.js index 8542772a..a0b3b677 100644 --- a/src/main/resources/views/platform/zhgh/staffmanage/sourcechange/pending/memberChange.js +++ b/src/main/resources/views/platform/zhgh/staffmanage/sourcechange/pending/memberChange.js @@ -23,10 +23,22 @@ const PENDING_MEMBER_CHANGE = { methods: { /** 编辑模式仅允许修改待确认业务确认所需字段;查看模式下所有字段均为只读。 */ pendingAllowFields(prop) { - const editableFields = ["personType", "userState", "userAttribute", "member", "welfareMember"] + const editableFields = ["personType", "userState", "userAttribute", "member", "welfareMember", + "retireDate", "welfareStopDate"] return this.pendingChangeReadonly || !editableFields.includes(prop) }, + /** 在职状态选择退休时,存在“离退休人员”字典项才自动设置对应人员类型编码。 */ + handlePendingRetiredStateChange(userState) { + if (userState !== "退休") { + return + } + const retiredOption = this.pendingPersonTypeOptions.find((item) => item.name === "离退休人员") + if (retiredOption) { + this.$set(this.pendingChangeForm, "personType", retiredOption.code) + } + }, + /** 从人员类型字典中仅保留待确认业务允许变更的两种类型。 */ loadPendingPersonTypeOptions() { return this.$businessTool.getDictOptions("USER_PERSON_TYPE").then((options) => { @@ -92,8 +104,9 @@ const PENDING_MEMBER_CHANGE = { preparedBy: user.preparedBy, userAttribute: user.userAttribute, aidFundMemberUserType: user.aidFundMemberUserType, - retireDate: user.retireDate, - welfareStopDate: user.welfareStopDate, + retireDate: user.retireDate ? this.$moment(user.retireDate).format("YYYY-MM-DD") : "", + welfareStopDate: user.welfareStopDate + ? this.$moment(user.welfareStopDate).format("YYYY-MM-DD") : "", arrivalAtSchoolDate: user.arrivalAtSchoolDate, families: user.families || [], personalData: user.personalData