SuiteCRM 二次开发:基于 logic hook 实现合同回款率的计算

更新日期: 2024-03-27 阅读次数: 369 字数: 1488 分类: 管理

SuiteCRM 的这套 hook 机制还是挺灵活的。就是文档有的少,大部分靠摸索。

部署时,只能在工作日晚上,或者周末没人使用时才能部署。异常痛苦。

关联记录存储时相关的 hook

这个属于 Module Hooks,即模块级的 Hook。

  • after_relationship_add
  • after_relationship_delete

除了关系的添加和删除,实际上应该还有收款记录的值修改需要监听。

  • after_save

看起来,在合同模块添加 Hook,不如在收款记录模块添加 Hook 更方便管理。因为统计函数能够复用。

后续补充:

这里还是想简单了。关系的添加和收款金额变更这两种情况可以加到收款记录模块的 logic hook 中。

但是,关系的删除,只能加到合同模块的 logic hook 中,因为关系删除的同时,通常也把收款记录删除了。所以不能放到收款记录模块中。

记录展示时相关的 hook

上面是通过更新增加的自定义统计字段实现的回款率计算,还有一种方式是在展示合同时,才查询的做法:

  • process_record:Fired when a record is processed ready to be displayed in list views or dashlets. 及在列表展示时触发
  • after_retrieve:Fired after a record is retrieved from the DB. 需要注意,这个可能会触发多次。相当于每次从数据库去读取时,都会触发。实际测试,在 Account 详情页,刷新一次,被触发三次这个钩子。

这种做法,是平时自己写程序时的标准做法。

但是,SuiteCRM 由于钩子机制不可控。

我觉得用来做简单的数据格式化,没问题。要是用来触发关联记录查询,就得防止造成数据库的查询压力。

如何确认关联关系的名称

即 relationship name.

比如,合同模块关联了收款模块,一条合同记录会关联多条收款记录。

那么就需要在 logic hook 中知道这个关系的名称是什么,否则无法获取相关的数据。

查看方法一:

使用管理员账号,在系统管理中,工作室 - 相关模块 - 关联关系。

这里就能看到对应的关联名称,即对应模块。

例如,合同与收款记录的关联关系名为:

aos_contracts_skgl_shoukuanguanli_1

查看方法二:

进入 SuiteCRM 项目代码根目录,查看代码文件:

cache/modules/skgl_shoukuanguanli/skgl_shoukuanguanlivardefs.php

这个非内置模块,估计是基于中文名字的拼音自动生成的。

在代码中搜索 relationship 关键词,就能找到跟合同 Contract 的相关信息。

'aos_contracts_skgl_shoukuanguanli_1' =>
    array (
      'name' => 'aos_contracts_skgl_shoukuanguanli_1',
      'type' => 'link',
      'relationship' => 'aos_contracts_skgl_shoukuanguanli_1',
      'source' => 'non-db',
      'module' => 'AOS_Contracts',
      'bean_name' => 'AOS_Contracts',
      'vname' => 'LBL_AOS_CONTRACTS_SKGL_SHOUKUANGUANLI_1_FROM_AOS_CONTRACTS_TITLE',
      'id_name' => 'aos_contracts_skgl_shoukuanguanli_1aos_contracts_ida',
    ),

对应的,在 cache/modules/AOS_Contracts/AOS_Contractsvardefs.php 中看到的关系信息是:

'aos_contracts_skgl_shoukuanguanli_1' =>
    array (
      'name' => 'aos_contracts_skgl_shoukuanguanli_1',
      'type' => 'link',
      'relationship' => 'aos_contracts_skgl_shoukuanguanli_1',
      'source' => 'non-db',
      'module' => 'skgl_shoukuanguanli',
      'bean_name' => 'skgl_shoukuanguanli',
      'side' => 'right',
      'vname' => 'LBL_AOS_CONTRACTS_SKGL_SHOUKUANGUANLI_1_FROM_SKGL_SHOUKUANGUANLI_TITLE',
    ),

小知识: AOS 前缀

从上面的关系信息中可以看到,合同模块的名字为 AOS_Contracts。而合同和联系人分别是 Account 和 Contact。为何有的带 AOS 前缀,有的不带呢?suitecrm AOS prefix 代表什么?

在 SuiteCRM 中,AOS(Advanced OpenSales)是一个模块,用于管理销售、报价和发票等相关业务。AOS prefix 是指 AOS 模块中用于标识相关数据库表的前缀。在 SuiteCRM 中,数据库表名通常使用特定的前缀来标识所属的模块。AOS prefix 是 AOS 模块相关表的前缀,它通常为 "aos_"。例如,AOS 模块中的报价表的完整数据库表名可能是 "aos_quotes",其中 "aos_" 就是 AOS prefix。

通过使用前缀,SuiteCRM 可以确保不同模块的数据库表之间不会发生冲突,并提供更好的模块化和数据结构管理。

如何判断具体是哪个 relationship 发生了变化

以 after_relationship_add 触发为例,单单判断了事件名是否是 after_relationship_add 是不够的。还需要判断具体是新增了哪种关联关系。

例如,合同模块就好多个关联关系,不能因为一个不相关的关系建立,就每次都把收款记录都重新统计一遍。

参考 ChatGPT 给出的建议:

在 after_relationship_add 触发器中,$arguments 参数包含以下键值对:

  • module: 触发关联操作的模块名。
  • related_module: 被关联的模块名。
  • related_id: 被关联记录的 ID。
  • relationship: 关联关系的名称。
  • related_bean: 被关联记录的 Bean 对象。

而通过 github 上的参考代码,结合实际,发现用 related_module 来判断非常合适。以联系人关联的客户为例:

public function addPayment($bean, $event, $arguments)
{
    $GLOBALS['log']->fatal('event: ' . $event);
    $GLOBALS['log']->fatal('related module: ' . $arguments['related_module']);

    if ($arguments['related_module'] == 'Accounts') {
        // do something 
    }
}

自定义字段

使用自定义字段前,必须显示拉取一次么?

$project->custom_fields->retrieve();

调试

网上查到的代码真是千差万别,只能本地搭建一套开发环境,一点点打印日志调试了。 我觉得直接在服务器上重新部署一个测试版本的更简单方便。

调试前的备份

  • 备份数据库
  • 备份项目目录

after_save 失败

应该是 $argument 参数,不包含 related_module 这个 key。

果然是这个问题,将这行代码删除即可。

after_relationship_delete 失败

现象是,读取不到相关联的合同。

这里不单是关联关系删除了,同时回款记录也删除了。

所以读取不到关联关系也很合理。

正确的做法应该是,去合同模块那里去实现一个 after_relationship_delete 的钩子。

参考

tags: CRM

关于作者 🌱

我是来自山东烟台的一名开发者,有敢兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式