OpenERP几个小机制介绍:Request, Sequence and Scheduler
-
本帖先说Request。什么是Request?简单的说,OpenERP的Request就是在线消息。举例来说,你通过OpenERP提交了一张请假单,你希望主管快点审批。这时,你可以使用OE给主管发送一个Request,Request里写明你的请求。如果请假模块是你自己开发的,当有人提交请假单时,你也可以在模块中自动发送Request给主管,并且以附件的形式添加请假单到Request中。当主管登录OE时,他能立即看见Request,主管可以回复Request,或者关闭Request。OE会自动记录Request的发送、回复、关闭等操作历史。
Request的操作。在Web Client的右上角,或者GUI Client的“用户”菜单下,可以发送、接收、查看Request。
[attach]532[/attach]
点击Request,进入Request的列表画面,继续点击,进入Request的表单画面。
[attach]533[/attach][attach]534[/attach]
Request的字段介绍,From:发送人;To:接收人,接收人登录OE时,会看到消息提示(注意,消息提示不是即时显示,而是在登录系统时显示);Subject:消息主题;Request:消息内容;Trigger Date:系统当前未使用,仅供显示,没其它特别含义;References:相当于附件,可以引用其它对象的数据(资源);Status:有('draft','draft'),('waiting','waiting'),('active','active'),('closed','closed') 几个状态。
Request对象的源代码参考serverbinaddonsbaseresres_request.py。[[i] 本帖最后由 NewZN 于 2010-4-19 08:23 编辑 [/i]]
-
Request的代码中(res_request.py),两个Reference字段的定义如下。
[code]
'ref_doc1':fields.reference('Document Ref 1', selection=_links_get, size=128, states={'closed':[('readonly',True)]}),
'ref_doc2':fields.reference('Document Ref 2', selection=_links_get, size=128, states={'closed':[('readonly',True)]}),
[/code]References字段,表示引用别的对象的资源(或者说,引用别的数据表的一条记录),数据库中的存储形式是(对象名,ID),如(product.product,3)表示引用对象product.product(数据表product_product)中id=3的数据。References字段在界面上的显示通常是,一个下拉框,用于选择对象名,一个查找框,用于选择该对象的具体记录。记录在界面上显示的是name,不是ID。如(product.product,3),id=3,但显示的是“[PC1] Basic PC”,这是id=3的product的name。
上述定义中的“selection=_links_get”表示,通过函数_links_get取得一序列列表,该序列列表是诸如这样的东西:[(product.product,Product),(crm.case,Case),(account.invoice,Invoice)],即(对象名,对象显示名)的列表,该列表的内容即是界面上的下拉框中显示的内容。该列表的内容来自对象"res.request.link"(数据表 res_request_link),对象res.request.link的数据来自各个模块,如(product.product,Product),在product模块的文件product_data.xml中,有如下数据导入代码。
[code]
<record id="req_link_product" model="res.request.link">
<field name="name">Product</field>
<field name="object">product.product</field>
</record>
[/code]
该代码导入(name=Product, object=product.product)的记录到数据表res_request_link中,这样,Request中便可以引用Product对象的数据。如果希望Request中可以引用请假单对象,则请假模块中必须将请假单对象导入到res_request_link中。
代码中states={'closed':[('readonly',True)]} 的意思是,Request的状态为closed时,本字段只读。[[i] 本帖最后由 NewZN 于 2010-4-18 18:05 编辑 [/i]]
-
OpenERP的Sequence,是序号生成器,类似于数据库中常见的id生成器。例如,发票和会计凭证,它们都需要序号,而且,这个序号通常不是简单的数字,而是按一定规则组合的字母和数字。在软件开发中,需要序号的情况很多,OpenERP的Sequence就是满足这种需要的东西。
Sequence的配置。OpenERP中,可以添加任意多的序号生成器,每一序号生成器可以配置前缀、后缀、序号间隔等。即序号的一般格式是(前缀 序号 后缀),其中,序号为按一定间隔递增的数字。Sequence配置画面是,Administrator --> Configuration --> Sequences --> Sequences ,如下。
[attach]535[/attach]本例中,Account Journal的序列生成器生成的序列格式形如:1004-001,即前缀为年月(%(y)s%(month)s-),序列长度为8位(Number padding=8),序列间隔为1(Increment Number)。
Fiscal Years是account模块添加的。安装account模块后,OpenERP的序列生成器可以按会计年度定义分别采用不同的序列生成器,即不同会计年度,按不同规则采集序列。序列生成器的代码参见:serverbinaddonsbaseirir_sequence.py,account 模块对Sequence的修改参见:serverbinaddonsaccountsequence.py, 对Sequence配置画面的修改参见:serverbinaddonsaccountsequence_view.xml 。
在sequence.py 中,继承对象ir.sequence,在原对象中增加字段fiscal_ids。该字段为不同的会计年度定义不同的序列生成器。而后,覆盖原对象的序列采集函数get_id,根据参数给定的会计年度,调用该年度的序列生成器采集序列。如果参数未指定会计年度,或者指定的会计年度没有定义生成器,则仍调用原来的序列生成器。
在sequence_view.xml 中,继承原来的配置Form,添加了Fiscal Years的Page,用于配置各会计年度的生成器。
按中国的会计制度,凭证号必须按月编号。一个可取的实现办法是,修改sequence.py和sequence_view.xml,将按年定义生成器改成按月定义生成器,这样就能让凭证的序号生成器每个月都启用新的编号器。
生成新的序列号的代码示例如下:
[code]
c = {'fiscalyear_id': move.period_id.fiscalyear_id.id}
new_name = self.pool.get('ir.sequence').get_id(cr, uid, journal.sequence_id.id, context=c)
[/code]本例中,取得凭证的会计年度,从对象池中取得对象ir.sequence,指定序号生成器id(journal.sequence_id.id,本例即为Account Journal的序列生成器),调用gei_id方法取得一个新序号。[[i] 本帖最后由 NewZN 于 2010-4-18 19:05 编辑 [/i]]
-
OpenERP的Scheduler。软件系统中,经常有些需要定期自动运行的任务。在unix中,支持这种需求的是cron命令,在Windows中,是附件中的任务计划程序。在OpenERP中,例如,如果设置了最小库存规则,系统需要每天自动检查产品库存,如果低于设定的最小库存值,则自动产生该产品的供应单。又如,系统每天自动检查收付款条件,到了收付款日,或收付款前后多少天,系统自动产生Request或EMail提醒相关人。诸如这样的任务,需要一个任务调度器,即Scheduler。OpenERP的Scheduler就是满足此类需求的东西。
OpenERP的Scheduler的配置画面,Administrator --> Configuration --> Scheduler --> Scheduled Actions,画面如下。
[attach]536[/attach][attach]537[/attach]
该配置画面上,可以配置任务每隔多少时间调用一次(Interval Number和Interval Unit),下次调用时间(Next Call Date,每次调用任务后,当前时间加上间隔时间,算出下次调用时间),调用多少次后停止调用(Number of Calls),是否重复过去的调用(Repeat Missed,例如,有一段时间服务器未启动,当启动后,是否重复补充未启动期间的作业),自动调用的函数(Technical Data)。本例中,系统每天调用对象mrp.procurement的方法run_scheduler,参数是(False,)。注意,OE中方法的前三个参数总是(self, cr, uid),因此,本例的实际调用形式是run_scheduler(cr,uid,False)。方法run_scheduler在serverbin addonsmrpmrp.py中。
OpenERP的Scheduler的代码参见:serverbinaddonsbaseir ir_cron.py。其中,函数_poolJobs 实现任务的定期调用,关键代码行是:
self.setAlarm(self._poolJobs, int(time.time()) + next_call, db_name, db_name)
ir_cron继承自 netsvc.Agent,setAlarm定义在netsvc.Agent中。netsvc.Agent的代码参见serverbin netsvc.py,其中定义了一个timer:
[code]
timer = threading.Timer(wait, fn, args, kwargs)
timer.start()
[/code]
上述代码相当于启动了一个线程,该线程会等待wait时间后调用fn函数,这里的fn是ir_cron的_poolJobs 方法。关于threading.Timer的详细解释,参考Python的threading模块说明。
综上分析,ir_cron的实现原理是,每当新建、修改一个定期Job的时候,系统第一次调用_poolJobs,_poolJobs启动一个新线程。该线程等待Job中设定的间隔时间后激活,激活后再次调用_poolJobs,_poolJobs再次设定下次激活时间,如此反复,即实现了作业的定期调用。[[i] 本帖最后由 NewZN 于 2010-4-19 08:20 编辑 [/i]]