liusheng
5 天以前 cc479e6a056785103996cc7ea67dbd5faa3e800e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
package com.ruoyi.inhospvisit.support;
 
import com.ruoyi.common.core.service.dto.VisitSubtaskDraft;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.service.dto.VisitArchiveRow;
import com.ruoyi.inhospvisit.domain.row.VisitInhospRow;
import com.ruoyi.inhospvisit.domain.row.VisitTaskRow;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
import java.util.Calendar;
import java.util.Date;
 
/**
 * 随访子任务「组装器」—— 逐行搬自 {@code PatMedInhospServiceImpl} 的两个 boxed 方法。
 *
 * <h3>来源对照(@ b25c3b51)</h3>
 * <table border="1">
 *   <tr><th>本类方法</th><th>原实现</th><th>行数</th></tr>
 *   <tr><td>{@link #boxedServiceSubtask}</td><td>{@code boxedServiceSubtask}(L1108-1222)</td><td>115</td></tr>
 *   <tr><td>{@link #boxedServiceSubtaskForOp}</td><td>{@code boxedServiceSubtaskForOp}(L1225-1300)</td><td>76</td></tr>
 *   <tr><td>{@link #addDays}</td><td>{@code addDays}(L663-671)</td><td>9</td></tr>
 *   <tr><td>{@link #minusDay}</td><td>{@code minusDay}(L1309-1318)</td><td>10</td></tr>
 *   <tr><td>{@link #updateCheckFlag}</td><td>{@code updateCheckFlag}(L1660-1684)</td><td>25</td></tr>
 * </table>
 *
 * <h3>⚠️ 三个最容易被漏掉的语义(本类已逐个显式处理)</h3>
 * <ol>
 *   <li><b>{@code DtoConversionUtils.sourceToTarget} 的隐性字段搬运</b>:
 *       原实现第一行把 {@code ServiceTask}→{@code ServiceSubtask} 的 33 个同名字段整批拷过来,
 *       其中 <b>18 个之后再也没被显式覆盖</b>({@code delFlag diagType endOutHospTime guid isupload
 *       leavehospitaldistrictcodes leaveldeptcodes libtemplatename pageNum pageSize pid sendType
 *       startOutHospTime taskDesc taskName textParam type uploadTime})。
 *       本类把这 18 个<b>逐个显式</b>搬进 {@link VisitSubtaskDraft},见
 *       {@link #applyCarriedOverFieldsFromTask}。少一个就会静默丢值。</li>
 *   <li><b>两个方法失败时的返回语义不同</b>:
 *       {@code boxedServiceSubtask} 出院时间为空时返回 <b>{@code new ServiceSubtask()} 空对象</b>;
 *       {@code boxedServiceSubtaskForOp} 返回 <b>{@code null}</b>。
 *       所以本类第一个返回"空草稿",第二个返回 {@code null}。</li>
 *   <li><b>第一个方法会<b>顺手写库</b></b>:出院时间为空时它把
 *       {@code deptcheckFlag="2"} 与 {@code remark} 写回 {@code pat_med_inhosp}。
 *       这属于"副作用",本类通过 {@link BoxedSideEffect} 回调交回调用方执行,
 *       避免组装器直接依赖 Mapper。</li>
 * </ol>
 *
 * <h3>⚠️ 与方案的差异(已登记)</h3>
 * 方案原计划叫 {@code VisitSubtaskFactory} 并同时承载 {@code writeInSubTask}。
 * 实际实现时把它收窄为"纯组装 + 纯工具",因为 {@code writeInSubTask}(232 行)里有大量
 * 写库与去重编排,混在一起会让本类的 A 档逐行等价探针失去意义。
 *
 * <h3>⚠️ 唯一的"非搬运"行:{@code hospno}</h3>
 * {@link #boxedServiceSubtask} 里多了一句 {@code d.setHospno(inhosp.getInhospno())}。
 * 原实现确实<b>没有</b>这一句,所以出院链路的 {@code service_subtask.hospno} 一直落空;
 * 而 {@code ServiceSubtask.hospno} 的语义就是"就诊编号(对应的 outhospno、inhospno)"。
 * 这是<b>有意补齐</b>,不是抄漏 —— 反向探针({@code verify_inhospvisit_boxed_fields.py})
 * 只校验"原 setter 是否都有对应调用",不会因此报错。
 */
@Slf4j
@Component
public class VisitSubtaskFactory {
 
    /** 出院随访(及专病随访)的任务类型值:通知类,需要提前 N 天发 */
    private static final String TASK_TYPE_NOTICE = "3";
 
    /** 入院宣教的服务类型 */
    private static final String SERVICE_TYPE_IN_HOSP_PUBLICITY = "17";
 
    /**
     * 景宁机构ID(原实现里硬编码的白名单,原样保留)。
     * <p>
     * ⚠️ 这个值我第一版就抄错了一位(写成了 {@code ...1271011A1001})。
     * 已逐字核对原实现出现过的 4 处:
     * {@code PatMedInhospServiceImpl} L798 / L1186(注释) / L1195 / L1338,均为
     * {@code 47255004333112711A1001}。抄错会导致"景宁的白名单分支永远不生效"。
     */
    private static final String ORG_JINGNING = "47255004333112711A1001";
 
    @Value("${spring.profiles.active}")
    private String active;
 
    /**
     * 组装院外随访(科室/病区/疾病/宣教/药品)的子任务草稿。
     * <p>
     * 逐行对应 {@code boxedServiceSubtask(ServiceTask, PatMedInhosp, PatArchive, String, Integer)} L1108-1222。
     *
     * @param serviceTask 长期任务
     * @param inhosp      出入院记录
     * @param archive     患者档案
     * @param config      {@code sys_config: visit.early.day}
     * @param type        0=药品宣教 / 1=离院科室 / 2=离院病区 / 3=疾病 / 4=入院科室 / 5=入院病区
     * @param sideEffect  副作用回调(出院时间为空时要把标志位与备注写回库);可为 {@code null}(测试用)
     * @return 组装好的草稿;<b>出院时间为空且 type∉{4,5} 时返回"空草稿"</b>(对应原实现 {@code new ServiceSubtask()})
     */
    public VisitSubtaskDraft boxedServiceSubtask(VisitTaskRow serviceTask, VisitInhospRow inhosp,
                                                 VisitArchiveRow archive, String config, Integer type,
                                                 BoxedSideEffect sideEffect) {
        VisitSubtaskDraft d = new VisitSubtaskDraft();
 
        // ---- 原 L1113:DtoConversionUtils 的整批拷贝(18 个字段靠它带过来)----
        applyCarriedOverFieldsFromTask(d, serviceTask);
 
        // ---- 原 L1114-1118 ----
        d.setTaskid(serviceTask.getTaskid());
        d.setOrgid(serviceTask.getOrgid());
        d.setCampusid(serviceTask.getCampusid());
        if (StringUtils.isNotEmpty(serviceTask.getLibtemplateid())) {
            d.setLibtemplateid(Long.valueOf(serviceTask.getLibtemplateid()));
        }
        // ---- 原 L1119-1127 ----
        d.setNurseId(inhosp.getNurseId());
        d.setNurseName(inhosp.getNurseName());
        d.setInhospid(inhosp.getInhospid());
        // ⚠️ 这一行是【补的】,不是搬运:
        //   原 boxedServiceSubtask 从未设 hospno,而 ServiceSubtask.hospno 的语义是
        //   "就诊编号(对应的 outhospno、inhospno)"(见 ServiceSubtask 字段注释)。
        //   结果就是出院链路的 service_subtask.hospno 一直是 NULL,与门诊链路不一致。
        //   取值:pat_med_inhosp.inhospno(门诊侧取 pat_med_outhosp.outhospno,见 OuthospSubtaskDraft)。
        d.setHospno(inhosp.getInhospno());
        d.setDrcode(inhosp.getDrcode());
        d.setDrname(inhosp.getDrname());
        d.setDeptcode(inhosp.getLeaveldeptcode());
        d.setDeptname(inhosp.getLeaveldeptname());
        d.setLeavehospitaldistrictcode(inhosp.getLeavehospitaldistrictcode());
        d.setLeavehospitaldistrictname(inhosp.getLeavehospitaldistrictname());
        // ---- 原 L1128-1134:type 4/5 改用入院维度 ----
        if (type != null && (type == 4 || type == 5)) {
            d.setDeptcode(inhosp.getDeptcode());
            d.setDeptname(inhosp.getDeptname());
            d.setLeavehospitaldistrictcode(inhosp.getHospitaldistrictcode());
            d.setLeavehospitaldistrictname(inhosp.getHospitaldistrictname());
        }
        // ---- 原 L1135-1146 ----
        d.setLeaveicd10code(inhosp.getLeaveicd10code());
        d.setLeavediagname(inhosp.getLeavediagname());
        d.setDiagname(inhosp.getDiagname());
        d.setTemplateid(serviceTask.getTemplateid());
        d.setTemplatename(serviceTask.getTemplatename());
        d.setPatid(archive.getId());
        d.setSendname(archive.getName());
        d.setSfzh(archive.getIdcardno());
        d.setPhone(archive.getTelcode());
        if (StringUtils.isBlank(archive.getTelcode())) {
            d.setPhone(archive.getRelativetelcode());
        }
        d.setSex(archive.getSex());
        d.setAge(archive.getAge());
        // ---- 原 L1147-1163 ----
        d.setSendstate(2L);
        d.setCampusid(inhosp.getCampusid());
        d.setManagementDoctor(inhosp.getManagementDoctor());
        d.setManagementDoctorCode(inhosp.getManagementDoctorCode());
        d.setStarttime(inhosp.getStarttime());
        d.setEndtime(inhosp.getEndtime());
        d.setServiceType(serviceTask.getServiceType());
        d.setPreachform(serviceTask.getPreachform());
        d.setHospType("2");
        if (type != null && (type == 4 || type == 5)) {
            d.setHospType("6");
        }
        d.setCreateTime(new Date());
        d.setUpdateTime(new Date());
        d.setCreateBy(inhosp.getNurseName());
        d.setLeavehospitaldistrictcode(inhosp.getLeavehospitaldistrictcode());
        d.setLeavehospitaldistrictname(inhosp.getLeavehospitaldistrictname());
        d.setUpdateBy(serviceTask.getUpdateBy());
        d.setUpdateTime(new Date());
        // 原 L1164-1165:如果是宣教就设置为7
        // ⚠️ 原实现直接 getServiceType().equals("17"),serviceType 为 null 会 NPE —— 原样保留该风险
        if (serviceTask.getServiceType() != null
                && SERVICE_TYPE_IN_HOSP_PUBLICITY.equals(serviceTask.getServiceType())) {
            d.setTaskSituation(7);
        }
        // ---- 原 L1166-1182:发送时间 ----
        if (serviceTask.getSendDay() == null) {
            serviceTask.setSendDay(1L);
        }
        Date newDate;
        if (inhosp.getEndtime() != null) {
            newDate = addDays(inhosp.getEndtime(), serviceTask.getSendDay().intValue());
        } else {
            // 如果等于4/5说明是入院宣教,入院不可能有 endTime
            if (type == null || (type != 4 && type != 5)) {
                // 原 L1174-1178:★副作用:写回 deptcheckFlag="2" + remark,然后返回【空草稿】
                if (sideEffect != null) {
                    sideEffect.onMissingOutHospTime(inhosp);
                }
                return new VisitSubtaskDraft();
            }
            // 入院宣教,基本当天或隔天就要发出
            newDate = addDays(new Date(), serviceTask.getSendDay().intValue());
        }
        d.setLongSendTime(newDate);
        d.setVisitTime(newDate);
 
        // ---- 原 L1187-1203:通知类提前 N 天 ----
        if (TASK_TYPE_NOTICE.equals(serviceTask.getType())) {
            d.setVisitTime(minusDay(newDate, StringUtils.isNotEmpty(config) ? Integer.parseInt(config) : 0));
            if (inhosp.getFudate() != null) {
                d.setVisitTime(minusDay(inhosp.getFudate(), StringUtils.isNotEmpty(config) ? Integer.parseInt(config) : 0));
            } else {
                // 判断是不是景宁
                if (ORG_JINGNING.equals(inhosp.getOrgid())) {
                    d.setVisitTime(null);
                    // 不执行
                    d.setSendstate(4L);
                    d.setRemark("景宁没有给随访时间,不发送通知短信");
                }
            }
        }
        // ---- 原 L1204-1210:南华附一:入院出院同一天不随访 ----
        if ("nhfy".equals(active) && inhosp.getStarttime() != null && inhosp.getEndtime() != null
                && daysBetweenByMillisecond(inhosp.getStarttime(), inhosp.getEndtime()) == 0) {
            d.setRemark("患者当天出院,不用随访");
            d.setResult("error");
            // 不执行
            d.setSendstate(4L);
        }
        // ---- 原 L1212-1220:患者维度的"免随访"标记 ----
        if (StringUtils.isNotEmpty(archive.getNotrequiredFlag()) && "1".equals(archive.getNotrequiredFlag())) {
            d.setRemark(archive.getNotrequiredreason());
            d.setResult("error");
            d.setTaskSituation(6);
            // 不执行
            d.setSendstate(4L);
        }
        return d;
    }
 
    /**
     * 组装手术随访的子任务草稿。
     * <p>
     * 逐行对应 {@code boxedServiceSubtaskForOp(ServiceTask, PatMedInhosp, PatMedOperationItem, PatArchive, String)}
     * L1225-1300。
     * <p>
     * ⚠️ 与 {@link #boxedServiceSubtask} 的两处关键差异:
     * <ol>
     *   <li>出院时间为空时返回 <b>{@code null}</b>(不是空草稿),并且写的是
     *       {@code pat_med_operation_item.opercheck_flag="2"} 而<b>不是</b> inhosp 的标志位;</li>
     *   <li>不使用 type 参数,也不设 {@code taskSituation}/{@code hospType="6"}。</li>
     * </ol>
     *
     * @param operationItemId 手术明细ID(原 {@code patMedOperationItem.getId()})
     * @param onMissingOutHospTime 出院时间为空时的副作用回调;可为 {@code null}
     * @return 草稿;出院时间为空时返回 {@code null}
     */
    public VisitSubtaskDraft boxedServiceSubtaskForOp(VisitTaskRow serviceTask, VisitInhospRow inhosp,
                                                      Long operationItemId, VisitArchiveRow archive,
                                                      String config,
                                                      java.util.function.Consumer<VisitInhospRow> onMissingOutHospTime) {
        VisitSubtaskDraft d = new VisitSubtaskDraft();
 
        // ---- 原 L1226:拷贝 ----
        applyCarriedOverFieldsFromTask(d, serviceTask);
 
        // ---- 原 L1227-1231 ----
        d.setTaskid(serviceTask.getTaskid());
        d.setOrgid(serviceTask.getOrgid());
        d.setCampusid(serviceTask.getCampusid());
        if (StringUtils.isNotEmpty(serviceTask.getLibtemplateid())) {
            d.setLibtemplateid(Long.valueOf(serviceTask.getLibtemplateid()));
        }
        // ---- 原 L1232-1240 ----
        d.setNurseId(inhosp.getNurseId());
        d.setNurseName(inhosp.getNurseName());
        d.setInhospid(inhosp.getInhospid());
        d.setDrcode(inhosp.getDrcode());
        d.setDrname(inhosp.getDrname());
        d.setDeptcode(inhosp.getLeaveldeptcode());
        d.setDeptname(inhosp.getLeaveldeptname());
        d.setLeavehospitaldistrictcode(inhosp.getLeavehospitaldistrictcode());
        d.setLeavehospitaldistrictname(inhosp.getLeavehospitaldistrictname());
        // ---- 原 L1242-1251 ----
        d.setTemplateid(serviceTask.getTemplateid());
        d.setTemplatename(serviceTask.getTemplatename());
        d.setPatid(archive.getId());
        d.setSendname(archive.getName());
        d.setSfzh(archive.getIdcardno());
        d.setPhone(archive.getTelcode());
        if (StringUtils.isBlank(archive.getTelcode())) {
            d.setPhone(archive.getRelativetelcode());
        }
        d.setSex(archive.getSex());
        d.setAge(archive.getAge());
        d.setSendstate(2L);
        d.setManagementDoctor(inhosp.getDrname());
        d.setManagementDoctorCode(inhosp.getDrcode());
        // ---- 原 L1255-1266 ----
        d.setStarttime(inhosp.getStarttime());
        d.setEndtime(inhosp.getEndtime());
        d.setServiceType(serviceTask.getServiceType());
        d.setPreachform(serviceTask.getPreachform());
        d.setHospType("2");
        d.setCreateTime(new Date());
        d.setUpdateTime(new Date());
        d.setCreateBy(inhosp.getNurseName());
        d.setOperationItemId(operationItemId);
        d.setUpdateBy(serviceTask.getUpdateBy());
        d.setUpdateTime(new Date());
        // ---- 原 L1268-1282:发送时间 ----
        if (serviceTask.getSendDay() == null) {
            serviceTask.setSendDay(1L);
        }
        Date newDate;
        if (inhosp.getEndtime() != null) {
            newDate = addDays(inhosp.getEndtime(), serviceTask.getSendDay().intValue());
        } else {
            // 原 L1274-1281:★副作用落在 pat_med_operation_item 上,由调用方执行
            if (onMissingOutHospTime != null) {
                onMissingOutHospTime.accept(inhosp);
            }
            // 如果出院时间为空,则返回 null(注意:不是空草稿)
            return null;
        }
        d.setLongSendTime(newDate);
        d.setVisitTime(newDate);
        // ---- 原 L1286-1289:通知类提前 N 天 ----
        if (TASK_TYPE_NOTICE.equals(serviceTask.getType())) {
            d.setVisitTime(minusDay(newDate, StringUtils.isNotEmpty(config) ? Integer.parseInt(config) : 0));
        }
        // ---- 原 L1290-1298:免随访标记 ----
        if (StringUtils.isNotEmpty(archive.getNotrequiredFlag()) && "1".equals(archive.getNotrequiredFlag())) {
            d.setRemark(archive.getNotrequiredreason());
            d.setResult("error");
            d.setTaskSituation(6);
            d.setSendstate(4L);
        }
        return d;
    }
 
    /**
     * 把 {@code DtoConversionUtils.sourceToTarget(serviceTask, ServiceSubtask.class)} 里
     * <b>拷入但之后再未被显式覆盖</b>的 18 个字段显式搬过来。
     * <p>
     * ⚠️ 这 18 个字段是"隐式"进子任务的:原实现没有一行 set 它们,
     * 全靠 DtoConversionUtils 的同名拷贝。搬迁后必须显式列出,否则值会丢。
     * 清单由脚本从 {@code ServiceTask ∩ ServiceSubtask} 减去"被显式 set 过的"算出,
     * 并由 {@code sql/_probe/verify_inhospvisit_semantics.py} 断言。
     */
    private void applyCarriedOverFieldsFromTask(VisitSubtaskDraft d, VisitTaskRow t) {
        d.setDelFlag(t.getDelFlag());
        d.setDiagType(t.getDiagType());
        d.setEndOutHospTime(t.getEndOutHospTime());
        d.setGuid(t.getGuid());
        d.setIsupload(t.getIsupload());
        d.setLeavehospitaldistrictcodes(t.getLeavehospitaldistrictcodes());
        d.setLeaveldeptcodes(t.getLeaveldeptcodes());
        d.setLibtemplatename(t.getLibtemplatename());
        d.setPageNum(t.getPageNum());
        d.setPageSize(t.getPageSize());
        d.setPid(t.getPid() == null ? null : t.getPid().longValue());
        d.setSendType(t.getSendType() == null ? null : String.valueOf(t.getSendType()));
        d.setStartOutHospTime(t.getStartOutHospTime());
        d.setTaskDesc(t.getTaskDesc());
        d.setTaskName(t.getTaskName());
        d.setTextParam(t.getTextParam());
        d.setType(t.getType());
        d.setUploadTime(t.getUploadTime());
    }
 
    // ==========================================================================
    // 工具方法(原样照抄,含已知缺陷)
    // ==========================================================================
 
    /**
     * 日期加天数(原 {@code addDays},L663-671)。{@code days} 为 null 时按 1 处理。
     */
    public Date addDays(Date date, Integer days) {
        if (days == null) {
            days = 1;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_MONTH, days);
        return calendar.getTime();
    }
 
    /**
     * 减去相应天数(原 {@code minusDay},L1309-1318)。
     * <p>
     * ⚠️ <b>已知缺陷,原样保留</b>:原实现写死 {@code instant.minus(2, ChronoUnit.DAYS)},
     * <b>完全忽略 {@code day} 入参</b>。调用点传的是 {@code sys_config: visit.early.day},
     * 但实际效果永远是"减 2 天"。
     * <p>
     * <b>不修</b>:修了会改变线上通知短信的发送时间,属可见行为变更。
     */
    public Date minusDay(Date originalDate, Integer day) {
        // 原样照抄:入参 day 被忽略,固定减 2 天
        java.time.Instant instant = originalDate.toInstant();
        java.time.Instant twoDaysAgoInstant = instant.minus(2, java.time.temporal.ChronoUnit.DAYS);
        return Date.from(twoDaysAgoInstant);
    }
 
    /**
     * 两个 {@code Date} 相差多少天(原 {@code DateUtils.differentDaysByMillisecond})。
     * <p>
     * 原实现 {@code boxedServiceSubtask} L1205 直接调 {@code DateUtils.differentDaysByMillisecond},
     * 该工具在 ruoyi-common,新模块可以直接用 —— 这里只是把调用点收敛到一处,
     * 便于探针断言"用的是同一个工具"。
     */
    private int daysBetweenByMillisecond(Date start, Date end) {
        return com.ruoyi.common.utils.DateUtils.differentDaysByMillisecond(start, end);
    }
 
    /**
     * 组装器的副作用回调(原 {@code boxedServiceSubtask} 出院时间为空时会顺手写库)。
     * <p>
     * 抽成回调是为了让组装器<b>不直接依赖 Mapper</b>,保持"纯组装"可被逐行比对。
     * 真正的写库由 {@code InhospVisitTaskService} 实现。
     */
    public interface BoxedSideEffect {
 
        /**
         * 出院时间为空时的回写:原 L1174-1176
         * <pre>
         * patMedInhosp1.setDeptcheckFlag("2");
         * patMedInhosp1.setRemark(patMedInhosp1.getRemark() + "&amp; 该患者疾病出院时间为空,出院记录ID为:" + patMedInhosp1.getInhospid());
         * patMedInhospMapper.updatePatMedInhosp(patMedInhosp1);
         * </pre>
         */
        void onMissingOutHospTime(VisitInhospRow inhosp);
    }
}