刚接触T100二次开发时,面对复杂的业务逻辑和陌生的代码结构,很多实习生都会经历一段"踩坑-排查-成长"的循环。本文将还原五个典型场景的完整解决路径,这些案例都来自真实项目中的报错和功能异常,覆盖了数据状态流转、参数传递、事务控制等核心痛点。
在T100系统中,单据状态管理就像交通信号灯,必须严格遵循"N(未审核)→Y(已审核)→X(失效)"的单向流转规则。新手最常犯的错误是忽略状态间的制约关系,导致出现"已审核单据直接失效"这类违反业务规则的异常操作。
典型报错场景:当用户试图将已审核(Y)状态单据直接设为失效(X)时,系统抛出业务规则冲突错误。核心问题在于未在UI层做状态流转限制。
解决方案需要修改statechange模块的状态控制逻辑:
4gl复制CASE g_xmaguc_m.xmagucstus
WHEN "X"
CALL cl_set_act_visible("valid",FALSE) # 失效状态隐藏审核按钮
WHEN "Y"
CALL cl_set_act_visible("void",FALSE) # 审核状态隐藏失效按钮
END CASE
注意:状态控制代码应放在
statechange.before_menu节点,确保在菜单渲染前完成权限过滤
实际项目中还需要考虑并发场景下的状态校验。例如在提交失效请求前,应再次查询数据库确认当前状态:
sql复制SELECT oozyucstus INTO l_current_status
FROM oozyuc_t
WHERE oozyuc001 = g_oozyuc_m.oozyuc001
FOR UPDATE -- 加锁防止并发修改
单据编号开窗是T100的高频操作,但参数传递方式却让很多新人困惑。以采购单开窗为例,必须传递参照表编号和程序编号两个关键参数,缺一不可。
参数获取的最佳路径:
ooef_t表查询,条件为企业编号+据点编号g_prog优化后的参数传递代码应放在ON ACTION controlp事件中:
4gl复制DEFINE l_ooef004 LIKE ooef_t.ooef004
SELECT ooef004 INTO l_ooef004
FROM ooef_t
WHERE ooefent = g_enterprise
AND ooef001 = g_site
LET g_qryparam.arg1 = l_ooef004 # 参照表编号
LET g_qryparam.arg2 = g_prog # 程序编号
对于高频使用的开窗参数,建议升级为全局变量。在MAIN函数头部声明:
4gl复制DEFINE g_ooef004 LIKE ooef_t.ooef004 # 单据别参照表号
DEFINE g_ooef024 LIKE ooef_t.ooef024 # 供应商编号
并在画面初始化时预加载:
4gl复制SELECT ooef004, ooef024 INTO g_ooef004, g_ooef024
FROM ooef_t
WHERE ooefent = g_enterprise
AND ooef001 = g_site
修改已审核单据是高风险操作,必须遵循"事务开启→数据锁定→修改校验→结果提交"的完整流程。下面是通过身份证号修改案例演示的标准做法:
4gl复制ON ACTION modify_sfz
IF g_oozyuc_m.oozyucstus = 'Y' THEN
CALL s_transaction_begin() # 开启事务
# 锁定要修改的记录
OPEN cooi666_cl FOR UPDATE
USING g_enterprise, g_oozyuc_m.oozyuc001
# 执行修改(示例片段)
UPDATE oozyuc_t
SET oozyuc005 = g_oozyuc_m.oozyuc005
WHERE oozyucent = g_enterprise
AND oozyuc001 = g_oozyuc_m.oozyuc001
IF SQLCA.SQLCODE THEN # 检查SQL执行状态
CALL s_transaction_end('N', '0') # 回滚
ELSE
CALL s_transaction_end('Y', '0') # 提交
END IF
ELSE
# 非审核状态报错处理
INITIALIZE g_errparam TO NULL
LET g_errparam.code = "coo-00667"
CALL cl_err()
END IF
关键注意事项:
FOR UPDATE明确锁定记录,避免脏写transaction_begin必须有对应的transaction_end业务规则校验不能依赖前端控制,必须在服务端构建多重防御。以下是年龄限制(18-60岁)的完整校验方案:
校验点分布表:
| 校验阶段 | 位置 | 技术实现 | 特点 |
|---|---|---|---|
| 前端输入 | BEFORE FIELD | INPUT BY NAME校验 | 即时反馈 |
| 字段离开 | AFTER FIELD | 正则表达式匹配 | 格式检查 |
| 提交保存 | BEFORE UPDATE | SQL条件校验 | 最终防线 |
核心校验代码示例:
4gl复制# AFTER FIELD校验
AFTER FIELD oozyuc004
IF NOT cl_null(g_oozyuc_m.oozyuc004) THEN
IF g_oozyuc_m.oozyuc004 < 18 OR g_oozyuc_m.oozyuc004 > 60 THEN
INITIALIZE g_errparam TO NULL
LET g_errparam.code = "coo-00666"
CALL cl_err()
NEXT FIELD oozyuc004 # 焦点停留在原字段
END IF
END IF
# BEFORE UPDATE校验
BEFORE UPDATE
SELECT COUNT(1) INTO l_count FROM oozyuc_t
WHERE oozyuc004 NOT BETWEEN 18 AND 60
AND oozyuc001 = g_oozyuc_m.oozyuc001
IF l_count > 0 THEN
CALL s_transaction_end('N', '0')
RAISE ERROR '年龄校验失败'
END IF
单据明细的项次(auto-increment)处理需要考虑多用户并发场景。常见错误是直接使用MAX()+1的实现方式,这在高并发时会导致项次重复。
安全增量方案对比:
| 方案类型 | 实现方式 | 并发安全性 | 适用场景 |
|---|---|---|---|
| 序列对象 | CREATE SEQUENCE | 高 | 高频新增单据 |
| 表锁方案 | SELECT FOR UPDATE | 中 | 低频复杂单据 |
| 应用锁 | LOCK TABLE | 低 | 历史程序兼容 |
推荐使用序列对象方案:
sql复制-- 创建序列
CREATE SEQUENCE seq_xmdc START WITH 1 INCREMENT BY 1;
在4GL中调用:
4gl复制BEFORE INSERT
LET g_xmdc_d[l_ac].xmdcseq =
(SELECT NEXT VALUE FOR seq_xmdc FROM systables WHERE tabid=1)
对于需要保持原有MAX()+1逻辑的程序,至少应该添加表锁:
4gl复制SELECT MAX(xmdcseq)+1 INTO g_xmdc_d[l_ac].xmdcseq
FROM xmdc_t
WHERE xmdcent = g_enterprise
AND xmdcdocno = g_xmda_m.xmdadocno
FOR UPDATE -- 关键锁机制
IF cl_null(g_xmdc_d[l_ac].xmdcseq) THEN
LET g_xmdc_d[l_ac].xmdcseq = 1
END IF
在三个月内处理过的生产问题中,有62%的项次异常都源于缺少并发控制。一个实用的调试技巧是在测试环境开启SQL日志,观察项次生成时的SQL执行顺序。