xTuple扩展开发部分介绍
基于xTuple框架的扩展是在尽可能不修改xTuple核心代码的前提下进行ui和脚本开发的。
以下为开发步骤,将在以后空闲时间里逐步进行细节描述。
一、需求分析
略。
二、数据库设计
序列
对自动增加的唯一编号可以通过序列来实现,这样可以通过postgresql提供的nextval获取
CREATE SEQUENCE house.payrecord_id_seq
INCREMENT 1
MINVALUE 1
MAXVALUE 2147483647
START 13
CACHE 1;
ALTER TABLE house.payrecord_id_seq OWNER TO postgres;
INCREMENT 1
MINVALUE 1
MAXVALUE 2147483647
START 13
CACHE 1;
ALTER TABLE house.payrecord_id_seq OWNER TO postgres;
三、ui设计
xTuple custom widget 之 xcombobox
对于xcombobox控件,可以增加其他我们需要的类型
1 在xcombobox.h里的XComboBoxTypes枚举里增加我们要加的类型(如HousePayWays)
2 在xcombobox.cpp里的XComboBox::init函数里增加代码:
insertEditor(HousePayWays, "housepayways", "MaintainPayWay");
第二个参数为ui名称:在xcombobox控件下拉里点击编辑的时候要连接到的ui;
第三个参数为权限名称:需要在数据库的数据表priv里存在,可以通过升级包增加此权限,也可以直接在数据库里增加
3 在xcombobox.cpp里的XComboBox::setType函数里增加switch分支:
case HousePayWays:
query.exec("select payway_id, (payway_name || '-' || payway_description), payway_name "
"from house.payway "
"order by payway_name");
break;
query.exec("select payway_id, (payway_name || '-' || payway_description), payway_name "
"from house.payway "
"order by payway_name");
break;
用来获取数据库信息数据。
4 编译xTuple,(不需要重新编译)。
5 重新打开Qt Designer,即可在xTuple Custom Controls里的xcombobox控件里找到我们之前加好的枚举类型。
xTuple custom widget 之cluster控件
cluster控件是用户自己定义的可以实现输入下拉的一个控件,可以在一个输入框后面出现搜素标志,并可以进行列表查找、增加等连接操作。
1 创建一个.h和一个.cpp文件,根据需要自行决定是否创建ui文件(如housecluster.h, housecluster.cpp)。里面的代码请自己增加,如有需要可参考已存在的itemcluster控件代码。
2 创建一个houseclusterplugin.h头文件。(请注意命名:以housecluster+plugin),并填充代码。
3 在widgets.pro里增加这3个文件。(请手动添加,如果自动添加,有可能出现将代码添加到dll.pro中。)
4 修改widgets.cpp里增加头文件引用#include "plugins/houseclusterplugin.h",并在构造函数里增加代码:
m_plugins.append(new HouseClusterPlugin(this));
5 编译xTuple。(不需要全部重新编译)
6 重新打开Qt Designer,即可使用此新加的控件。
四、脚本开发
display设计
display页面在3.8以上的版本中可以完全用脚本实现,以下描述仅限3.8以上的版本
1 创建housepays-detail.mql,用来取出数据库的数据和dispay里的filter、查询等功能:
-- Group: housepays
-- Name: detail
-- Notes:
select house.payrecord.*, house.item.item_number, house.custinfo.cust_name
from house.payrecord, house.item, house.custinfo
where house.payrecord.item_id = house.item.item_id
and house.payrecord.cust_id = house.custinfo.cust_id
-- This clause handles filtering if the user wants to search the list
-- The ~* uses PostgreSQL built in support for Regular Expressions
<? if exists("search_pattern") ?>
AND house.item.item_number || house.custinfo.cust_name || house.payrecord.pay_price ~* <? value("search_pattern") ?>
<? endif ?>
-- Name: detail
-- Notes:
select house.payrecord.*, house.item.item_number, house.custinfo.cust_name
from house.payrecord, house.item, house.custinfo
where house.payrecord.item_id = house.item.item_id
and house.payrecord.cust_id = house.custinfo.cust_id
-- This clause handles filtering if the user wants to search the list
-- The ~* uses PostgreSQL built in support for Regular Expressions
<? if exists("search_pattern") ?>
AND house.item.item_number || house.custinfo.cust_name || house.payrecord.pay_price ~* <? value("search_pattern") ?>
<? endif ?>
2 在连接到此页面的脚本里增加连接代码(initMenu.js):
houseExtension.housepaylistDisplay = function()
{
toolbox.newDisplay("housepays");
}
{
toolbox.newDisplay("housepays");
}
3 xTuple客户端设计里加载脚本initMenu.js和housepays-detail.mql。
4 重启xTuple客户端。运行即可。
参考资料:
www.xtuple.org/node/4596
菜单设计
简单示例:
var productsMenu = mainwindow.findChild("menu.prod");
housecustomerMenu = toolbox.menuAddMenu(productsMenu, qsTr("Customer"));
newhousecustomerAction = toolbox.menuAddAction(housecustomerMenu, qsTr("New Customer"));
newhousecustomerAction.triggered.connect(houseExtension.newhousecustomer);
housecustomersAction = toolbox.menuAddAction(housecustomerMenu, qsTr("Customer List"));
housecustomersAction.triggered.connect(houseExtension.housecustomersDisplay);
var tmpMenu = mainwindow.findChild("menu.prod.reports");
toolbox.menuRemove(productsMenu, tmpMenu);
tmpMenu = mainwindow.findChild("menu.prod.items");
toolbox.menuRemove(productsMenu, tmpMenu);
housecustomerMenu = toolbox.menuAddMenu(productsMenu, qsTr("Customer"));
newhousecustomerAction = toolbox.menuAddAction(housecustomerMenu, qsTr("New Customer"));
newhousecustomerAction.triggered.connect(houseExtension.newhousecustomer);
housecustomersAction = toolbox.menuAddAction(housecustomerMenu, qsTr("Customer List"));
housecustomersAction.triggered.connect(houseExtension.housecustomersDisplay);
var tmpMenu = mainwindow.findChild("menu.prod.reports");
toolbox.menuRemove(productsMenu, tmpMenu);
tmpMenu = mainwindow.findChild("menu.prod.items");
toolbox.menuRemove(productsMenu, tmpMenu);
请注意menuAddMenu和menuAddAction方法的使用。
参考资料:http://www.xtuple.org/node/337
五、其他
脚本语言翻译
1 创建test.pro工程文件
2 加入.ui,.h,.cpp文件;加入.js文件
3 降.js文件的类型改为SCOURCES
4 增加TRANSLATIONS = test_zh.ts
5 执行lupdate test.pro,生成test_zh.ts文件
6 用Qt Linguist对ts文件进行翻译操作
7 翻译结束后执行Qt Linguist的发布操作
8 与原来的xTuple.zh_CN.qm整合:执行lrelease test_zh.qm xTuple.zh_CN.qm -qm xTuple.zh_CN.qm
9 成功。将新的qm文件放入执行目录即可。
六、更新包创建与发布
升级包package.xml示例:
<package id = "house"
name = "housepackage"
version = "1.0alpha"
developer = "seahouse"
descrip = "house rent system"
updater = "2.2.4">
<pkgnotes>
This package creates a house rent system though scripting.
</pkgnotes>
<prerequisite type = "license" name = "houseLicense">
<message>
<This file is created by seahouse. Copyright @seahouse. seahouse QQ: 260822310, seahouse email: [email protected].
</message>
</prerequisite>
<loadappui file = "uiforms/house.ui" />
<loadappui file = "uiforms/houseClass.ui.ui" />
<loadappui file = "uiforms/houseClasses.ui" />
<loadappui file = "uiforms/housecustomer.ui" />
<loadappui file = "uiforms/housepay.ui" />
<loadappui file = "uiforms/housepayway.ui" />
<loadappui file = "uiforms/housepayways.ui" />
<loadappui file = "uiforms/houserent.ui" />
<loadappscript file = "scripts/house.js" />
<loadappscript file = "scripts/houseClass.js" />
<loadappscript file = "scripts/houseClasses.js" />
<loadappscript file = "scripts/housecustomer.js" />
<loadappscript file = "scripts/housecustomers.js" />
<loadappscript file = "scripts/houseitems.js" />
<loadappscript file = "scripts/housepay.js" />
<loadappscript file = "scripts/housepays.js" />
<loadappscript file = "scripts/housepayway.js" />
<loadappscript file = "scripts/housepayways.js" />
<loadappscript file = "scripts/houserent.js" />
<loadappscript file = "scripts/houserents.js" />
<loadappscript file = "scripts/initMenu.js" />
<loadappscript file = "scripts/user.js" />
<loadmetasql file = "metasql/houseitems-detail.mql" />
<loadmetasql file = "metasql/housepays-detail.mql" />
<loadmetasql file = "metasql/houserents-detail.mql" />
<loadpriv name = "MaintainHouseClasses" module = "Products">
Allawed to maintain house classes.
</loadpriv>
<loadpriv name = "MaintainHouseItem" module = "Products">
Allawed to maintain house item.
</loadpriv>
<loadpriv name = "MaintainPayWay" module = "Products">
Allawed to maintain house payway.
</loadpriv>
</package>
name = "housepackage"
version = "1.0alpha"
developer = "seahouse"
descrip = "house rent system"
updater = "2.2.4">
<pkgnotes>
This package creates a house rent system though scripting.
</pkgnotes>
<prerequisite type = "license" name = "houseLicense">
<message>
<This file is created by seahouse. Copyright @seahouse. seahouse QQ: 260822310, seahouse email: [email protected].
</message>
</prerequisite>
<loadappui file = "uiforms/house.ui" />
<loadappui file = "uiforms/houseClass.ui.ui" />
<loadappui file = "uiforms/houseClasses.ui" />
<loadappui file = "uiforms/housecustomer.ui" />
<loadappui file = "uiforms/housepay.ui" />
<loadappui file = "uiforms/housepayway.ui" />
<loadappui file = "uiforms/housepayways.ui" />
<loadappui file = "uiforms/houserent.ui" />
<loadappscript file = "scripts/house.js" />
<loadappscript file = "scripts/houseClass.js" />
<loadappscript file = "scripts/houseClasses.js" />
<loadappscript file = "scripts/housecustomer.js" />
<loadappscript file = "scripts/housecustomers.js" />
<loadappscript file = "scripts/houseitems.js" />
<loadappscript file = "scripts/housepay.js" />
<loadappscript file = "scripts/housepays.js" />
<loadappscript file = "scripts/housepayway.js" />
<loadappscript file = "scripts/housepayways.js" />
<loadappscript file = "scripts/houserent.js" />
<loadappscript file = "scripts/houserents.js" />
<loadappscript file = "scripts/initMenu.js" />
<loadappscript file = "scripts/user.js" />
<loadmetasql file = "metasql/houseitems-detail.mql" />
<loadmetasql file = "metasql/housepays-detail.mql" />
<loadmetasql file = "metasql/houserents-detail.mql" />
<loadpriv name = "MaintainHouseClasses" module = "Products">
Allawed to maintain house classes.
</loadpriv>
<loadpriv name = "MaintainHouseItem" module = "Products">
Allawed to maintain house item.
</loadpriv>
<loadpriv name = "MaintainPayWay" module = "Products">
Allawed to maintain house payway.
</loadpriv>
</package>
其他数据库操作(在package节点下):
<loadreport file="client/reports/DisplayItemLocations.xml" >sample_scripts package</loadreport>
<createfunction file="database/functions/cashregadjust.sql" name="cashregadjust" />
<createtable file="database/tables/createTerminal.sql" name="terminal" />
<finalscript file="database/tables/createSalenumberseq.sql" />
<createtrigger file="database/triggers/rtlsitestatuscheck.sql" name="rtlsitestatuscheck" />
<createview file="database/views/api_cashregister.sql" name="api_cashregister" />
<createfunction file="database/functions/cashregadjust.sql" name="cashregadjust" />
<createtable file="database/tables/createTerminal.sql" name="terminal" />
<finalscript file="database/tables/createSalenumberseq.sql" />
<createtrigger file="database/triggers/rtlsitestatuscheck.sql" name="rtlsitestatuscheck" />
<createview file="database/views/api_cashregister.sql" name="api_cashregister" />
参考:http://www.xtuple.org/CreateUpdaterPackages