<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Jeff&#39;s Blog</title>
  
  <subtitle>Jianhua Zhang&#39;s blog</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://www.jeffzhang.cn/"/>
  <updated>2021-05-09T03:37:32.600Z</updated>
  <id>http://www.jeffzhang.cn/</id>
  
  <author>
    <name>Jianhua Zhang</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>为什么网络分析法的计算结果中有些要素权重是零？</title>
    <link href="http://www.jeffzhang.cn/anp_zero_weight/"/>
    <id>http://www.jeffzhang.cn/anp_zero_weight/</id>
    <published>2021-04-14T16:00:00.000Z</published>
    <updated>2021-05-09T03:37:32.600Z</updated>
    
    <content type="html"><![CDATA[<a id="more"></a><p><strong><em>如果ANP计算结果里，存在要素权重为零的情况，那么在模型中，这个要素对应的节点一般是以下两种情况中的一种：</em></strong></p><ul><li><strong><em>不是任何连接的终点；</em></strong></li><li><strong><em>终点是此节点的所有连接，起点要素权重全部为零。</em></strong></li></ul><p><strong><em>如果不存在这两种情况，但还是存在权重为0的要素，可能存在比较复杂的结构，比如环状的关联关系，在网络结构图上不好理清楚。对于这种情况，可以查看计算结果中的超矩阵数据，检查要素关系，找出原因。</em></strong></p><h2 id="如何查看模型中节点图形的连接情况"><a href="#如何查看模型中节点图形的连接情况" class="headerlink" title="如何查看模型中节点图形的连接情况"></a>如何查看模型中节点图形的连接情况</h2><p>在yaanp中，为了确定一个节点的连接情况，可以使用连接检视模式功能。在连接检视模式下，可以查看以某个节点为终点或起点的连接线。</p><p>点击网络编辑区域顶部工具栏中的“连接检视模式”按钮，如下图红框中所示，进入连接检视模式。</p><p><img src="../image/anp_zero_weight/review_button.png" alt="连接检视模式切换按钮"></p><p>进入连接检视模式后，标题栏变成灰色，然后点击右边的“终点连接”按钮(红框所示)，进入检视模式的终点连接模式。</p><p><img src="../image/anp_zero_weight/end_button.png" alt="检视模式的终点连接模式按钮"></p><p>在检视模式的终点连接模式下，点击模型中任意一个节点图形，将会显示所有以选中节点图形为终点的连接。 例如下图中选中了节点“12”，显示这个节点是三条连接的终点，蓝色的连接线表示该连接是双向连接。</p><p><img src="../image/anp_zero_weight/end_12.png" alt="以12为终点的连接"></p><p>选中节点“11”，不显示任何连接线，说明没有任何连接以它为终点。</p><p><img src="../image/anp_zero_weight/end_11.png" alt="以11为终点的连接"></p><h2 id="不是任何连接的终点"><a href="#不是任何连接的终点" class="headerlink" title="不是任何连接的终点"></a>不是任何连接的终点</h2><p>上图展示的单网络模型中，因为“11”不是任何连接的终点，所以不会分配到任何权重，最终结果中它的权重一定是零。<br>下图是该模型对应的计算结果，无论判断矩阵中的两两比较数据如何填写，“11”的权重一定是零。</p><p><img src="../image/anp_zero_weight/result_zero_1.png" alt="计算结果"></p><h2 id="终点是此节点的所有连接的起点要素权重全部为零"><a href="#终点是此节点的所有连接的起点要素权重全部为零" class="headerlink" title="终点是此节点的所有连接的起点要素权重全部为零"></a>终点是此节点的所有连接的起点要素权重全部为零</h2><p>如果一个节点是一些连接的终点，但是这些连接的起点要素权重全部为零，那么这个节点对应的要素权重也是零。例如，在之前的模型中增加一个簇“3“，簇内添加两个节点“31”和“32”，并连接到簇“1”中的“11”和“12”节点，如下图所示。</p><p><img src="../image/anp_zero_weight/model_3.png" alt="另一个模型"></p><p>可以看出，因为“31“和“32“不是任何连接的终点，所以“31”和“32”的权重为零；而“31“和“32“连接到了”11“，”11“现在是两条连接的终点，所以“11”的权重也是零。</p><p><img src="../image/anp_zero_weight/result_zero_2.png" alt="计算结果"></p><h2 id="权重为零是什么意思"><a href="#权重为零是什么意思" class="headerlink" title="权重为零是什么意思?"></a>权重为零是什么意思?</h2><p>模型中某个节点的权重为零意味着什么？想一下层次模型里的决策目标(下图中的”过河收益“)，它是不是就是模型里唯一一个不是任何连接终点的要素？</p><p>层次模型里除决策目标外，其他所有要素的权重可能都会用到，唯独决策目标的权重绝对不会用到， 也就是没有任何作用。在yaanp里，层次模型的权重分布图上决策目标的权重被标记为“1.0”， 只是从权重分配的角度考虑<font size="2" color="Red"><b> 直接设定成了1.0</b></font>，这样更容易理解。</p><p><img src="../image/anp_zero_weight/ahp_weight_distro.png" alt="AHP权重分布图"></p><p>但如果用单网络模型构造等价的模型(<em>关于层次模型的不同构建方式，可以参考<a href="http://blog.metadecsn.com/yaanp_ahp/" target="_blank" rel="noopener">这篇文章</a></em>)， 权重分布图中决策目标的权重就显示为“0.0”了，这是因为网络模型里不像层次模型一样可以有确定的决策目标，可以做特殊的、设定为1.0的处理， 而是直接根据极限矩阵里的数据显示权重，也就是0.0，如下图所示。</p><p><img src="../image/anp_zero_weight/anp_weight_distro.png" alt="ANP权重分布图"></p><p>所以，如果某个要素权重为0，但是按实际业务分析确实应该有权重， 说明模型抽象还是存在问题，需要调整模型。</p>]]></content>
    
    <summary type="html">
    
      本文对网络分析法计算结果中某些要素权重为零的原因进行说明。
    
    </summary>
    
      <category term="ANP" scheme="http://www.jeffzhang.cn/categories/ANP/"/>
    
    
      <category term="yaanp" scheme="http://www.jeffzhang.cn/tags/yaanp/"/>
    
  </entry>
  
  <entry>
    <title>AHP群决策集结数据及其不适合作为论文中数据依据的说明</title>
    <link href="http://www.jeffzhang.cn/group-data-in-article/"/>
    <id>http://www.jeffzhang.cn/group-data-in-article/</id>
    <published>2019-01-18T16:00:00.000Z</published>
    <updated>2019-01-19T02:13:26.000Z</updated>
    
    <content type="html"><![CDATA[<p><img src="../image/group-data-in-article/all.png" alt="AHP模型"><br><a id="more"></a></p><h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>群决策数据集结是将多位专家给出的决策数据综合起来，并以此为基础进行计算得到群体决策结论。<br>yaahp中群决策专家数据集结方式有四种，这四种又可以分为两类：</p><ul><li><p>计算结果集结</p><blockquote><p>判断矩阵排序权重加权几何平均；<br>判断矩阵排序权重加权算术平均；</p></blockquote></li><li><p>判断矩阵集结</p><blockquote><p>判断矩阵数据等级平均；<br>判断矩阵数据数值平均；</p></blockquote></li></ul><h2 id="计算结果集结"><a href="#计算结果集结" class="headerlink" title="计算结果集结"></a>计算结果集结</h2><h3 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h3><p>首先对每个专家的判断矩阵进行计算，得到各个判断矩阵的排序权重，然后再对所有专家的排序权重（计算结果）利用算术/集合平均完成数据集结，最后利用集结后的判断矩阵排序权重计算总排序权重。</p><h3 id="yaahp中的设定"><a href="#yaahp中的设定" class="headerlink" title="yaahp中的设定"></a>yaahp中的设定</h3><p>计算结果集结是yaahp的默认群决策数据集结算法，群决策面板设定如下图所示。</p><p><img src="../image/group-data-in-article/gpanel1.png" width="300" alt="计算结果集结设定" title="计算结果集结设定"></p><h3 id="示例说明"><a href="#示例说明" class="headerlink" title="示例说明"></a>示例说明</h3><p>下面结合计算数据，对计算结果集结具体说明。</p><p>假设两位专家Tom和White参与群决策计算，他们的某个判断矩阵(过河收益)数据如下面两个图所示。</p><p><img src="../image/group-data-in-article/result-tom.png" width="550" alt="专家Tom的数据" title="专家Tom的数据"></p><p><img src="../image/group-data-in-article/result-white.png" width="550" alt="专家White的数据" title="专家White的数据"></p><p>对于某个确定的判断矩阵，计算结果集结首先计算得到各个专家此判断矩阵的排序权重<em>Wi</em>，如上图中红框中两位专家的<em>Wi</em>列数据。</p><p>将对应的<em>Wi</em>加权平均（两位专家权重设定为平均分配，即均为0.5），得到两位专家集结后的<em>Wi</em>，如下表所示。</p><table><thead><tr><th style="text-align:center">要素</th><th style="text-align:center"><em>Wi</em></th></tr></thead><tbody><tr><td style="text-align:center">经济收益</td><td style="text-align:center">0.5065</td></tr><tr><td style="text-align:center">社会收益</td><td style="text-align:center">0.1250</td></tr><tr><td style="text-align:center">环境收益</td><td style="text-align:center">0.3684</td></tr></tbody></table><blockquote><p>注：<em>Wi</em>之和不等于1.0是由于浮点数显示只保留小数点后四位的原因。</p></blockquote><p>计算得到<em>Wi</em>后，再计算出判断矩阵中上三角元素的值，就可以得到yaahp集结后的判断矩阵数据，如下图所示。</p><p><img src="../image/group-data-in-article/result-agg.png" width="550" alt="集结数据" title="集结数据"></p><p>对各个专家的所有判断矩阵用这种方法进行集结，就可以得到一组集结后的所有判断矩阵的<em>Wi</em>，最后再根据层次模型结构就可以计算出总排序权重。</p><h3 id="计算结果集结后的判断矩阵完全一致"><a href="#计算结果集结后的判断矩阵完全一致" class="headerlink" title="计算结果集结后的判断矩阵完全一致"></a>计算结果集结后的判断矩阵完全一致</h3><p>因为判断矩阵中各元素的值是根据<em>Wi</em>计算出来的，所以一致性比例一定是0.0，完全一致。但这个一致性比例是<font color="red">没有实际意义的</font>，也即<font color="red">不能用它来说明决策数据的一致性</font>。</p><h2 id="判断矩阵集结"><a href="#判断矩阵集结" class="headerlink" title="判断矩阵集结"></a>判断矩阵集结</h2><h3 id="简介-1"><a href="#简介-1" class="headerlink" title="简介"></a>简介</h3><p>首先对每个专家的判断矩阵中的对应项进行等级/数值平均，得到一组集结后的判断矩阵，然后计算各个矩阵的排序权重<em>Wi</em>，最后在计算总排序权重。</p><p>等级平均和数值平均的差别在于对小于1的两两对比等级的处理。等级平均认为小于1的两两对比值之间的差距也是“1”，而数值平均小于1的等级差距也小于1。例如等级平均认为1/2和1/4之间相差与2和4之间的差距相同，而使用数值平均时，1/2与1/4之间的差距为1/4，而2与4之间的差距为2。</p><h3 id="yaahp中的设定-1"><a href="#yaahp中的设定-1" class="headerlink" title="yaahp中的设定"></a>yaahp中的设定</h3><p>计算结果集结是yaahp的默认群决策数据集结算法，群决策面板设定如下图所示。</p><p><img src="../image/group-data-in-article/gpanel2.png" width="300" alt="判断矩阵集结设定" title="判断矩阵集结设定"></p><h3 id="示例说明-1"><a href="#示例说明-1" class="headerlink" title="示例说明"></a>示例说明</h3><p>下面结合计算数据，对计算结果集结具体说明。</p><p>假设两位专家Tom和White参与群决策计算，他们的某个判断矩阵(过河收益)数据如下面两个图所示。</p><p><img src="../image/group-data-in-article/matrix-tom.png" width="550" alt="专家Tom的数据" title="专家Tom的数据"></p><p><img src="../image/group-data-in-article/matrix-white.png" width="550" alt="专家White的数据" title="专家White的数据"></p><p>两两比较项“经济收益 vs 环境收益”数据使用红框圈了起来，用这项数据来说明等级平均和数值平均的不同。</p><ul><li>等级平均</li></ul><p>等级平均的集结结果如下图所示。</p><p><img src="../image/group-data-in-article/matrix-levelavg.png" width="550" alt="等级平均" title="等级平均"></p><p>对于两两比较项“经济收益 vs 环境收益”，Tom给出的决策数据为6，而White给出的决策数据为1/3。等级平均后的值为5/2。</p><ul><li>数值平均</li></ul><p>数值平均的集结结果如下图所示。</p><p><img src="../image/group-data-in-article/matrix-numavg.png" width="550" alt="数值平均" title="数值平均"></p><p>对于两两比较项“经济收益 vs 环境收益”，Tom给出的决策数据为6，而White给出的决策数据为1/3。数值平均后的值为19/6。</p><h3 id="集结后的判断矩阵的一致性比例"><a href="#集结后的判断矩阵的一致性比例" class="headerlink" title="集结后的判断矩阵的一致性比例"></a>集结后的判断矩阵的一致性比例</h3><p>一致性简单地说就是决策者在给出多个相关联的决策是否前后逻辑相符、一致，只有针对<font color="red">一个具体的决策者</font>才有意义。</p><p>判断矩阵集结通过一些计算把各个专家的数据汇总在一起，只是为了计算群决策的总排序权重而做的数据处理，这些集结后的判断矩阵数据已经<font color="red">没有前后逻辑是否相符或一致的意义</font>了。 所以集结后的数据是<font color="red">没有实际意义的</font>，只具有计算上的意义，<font color="red">不能用它来说明决策数据的一致性</font>。</p><p>例如两个专家的某个判断矩阵均满足一致性要求，也就是按这两位专家各自的思考给出的决策都是一致的，但由于这两位专家给出的各项决策数据有一定差异（例如之前数据中某项数据Tom给出6，White给出1/3），最终判断矩阵集结后得到了一个不一致的判断矩阵，这种情况可以认为两位专家这个判断矩阵数据不合格吗？或者反之两位专家数据都不满足一致性比例，但集结后的判断矩阵满足一致性比例，能认为两位专家的数据合格吗？</p><h2 id="论文中如何说明群决策所依据的数据是有效的？"><a href="#论文中如何说明群决策所依据的数据是有效的？" class="headerlink" title="论文中如何说明群决策所依据的数据是有效的？"></a>论文中如何说明群决策所依据的数据是有效的？</h2><p>了解了集结后的判断矩阵数据及其一致性比例数值只在计算时有作用，并没有实际意义，就可以得出<font color="red">集结后的判断矩阵数据和一致性比例无法为论文提供有效的数据依据</font>的结论，所以不应该把这些数据放在论文中。</p><p>那要如何在论文中说明群决策所依据的数据是有效的呢？应该使用<font color="red">所有的原始数据，</font>也就是所有参与决策的专家判断矩阵及其一致性比例数据。只有所有专家的所有判断矩阵都满足一致性要求，才能认为群决策所依据的数据是有效的，也就是在论文正文中说明所有判断矩阵均满足一致性比例（或修正后满足一致性比例），如果必须提供完整数据，可以在附录中列出所有的判断矩阵数据。</p><p>要求所有专家的所欲判断矩阵都满足一致性要求一般无法做到，但可以利用yaahp提供的一致性自动修正功能修正不一致判断矩阵。如果使用yaahp的<a href="http://ahpman.metadecsn.com/manual/matrix/#49" target="_blank" rel="noopener">判断矩阵检查</a>功能给出的建议是可以自动修正（绿色图标），一般可以让yaahp在计算时自动修正一致性不需要用户额外的处理。 关于自动修正的基本思路、自动修正是否合理、自动修正对数据有怎样的要求，请参考文章<a href="http://www.jeffzhang.cn/yaahp-matrix-check/">判断矩阵数据检查</a>。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>在论文中列出集结后的判断矩阵及其一致性比例数据作为合理群决策的数据依据是不合适的，应该将所有参与群决策的专家的所有判断矩阵及其一致性比例数据作为数据依据。</p><p>群决策所有专家的判断矩阵全部写在论文正文中会占用太大篇幅，一般没法这么做。只需要在论文正文中说明所有判断矩阵均满足/修正后满足一致性比例，如果必须提供完整数据，将判断矩阵数据作为附录部分提供。</p><h2 id="Copyright"><a href="#Copyright" class="headerlink" title="Copyright"></a>Copyright</h2><p>本文版权归博主张建华(Jianhua Zhang、两招)所有，转载或引用请注明出处。</p>]]></content>
    
    <summary type="html">
    
      本文对yaahp中的群决策专家数据集结方法进行介绍，并针对用户经常问到的论文中如何给出群决策数据的问题进行说明。
    
    </summary>
    
      <category term="AHP" scheme="http://www.jeffzhang.cn/categories/AHP/"/>
    
    
      <category term="yaahp" scheme="http://www.jeffzhang.cn/tags/yaahp/"/>
    
  </entry>
  
  <entry>
    <title>关于yaahp与www.yaahp.com关系的说明</title>
    <link href="http://www.jeffzhang.cn/yaahp-announce/"/>
    <id>http://www.jeffzhang.cn/yaahp-announce/</id>
    <published>2017-05-12T07:05:55.000Z</published>
    <updated>2017-05-12T11:11:33.000Z</updated>
    
    <content type="html"><![CDATA[<p>www.yaahp.com网站现在由<a href="http://www.metadecsn.com" target="_blank" rel="noopener">山西元决策软件科技有限公司</a>维护管理.</p><p>在<a href="http://www.foreology.com" target="_blank" rel="noopener">北京欣晟允软件技术有限公司</a>购买旧版本yaahp的客户可以联系<a href="http://www.metadecsn.com" target="_blank" rel="noopener">山西元决策软件科技有限公司</a>进行软件升级.</p><p><br></p><p><div align="left">2017年5月10日</div></p><p><br></p><p><del>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最近很多朋友询问此处(www.jeffzhang.cn)发布的yaahp软件与www.yaahp.com以及www.foreology.com的关系, 在此做统一回答:</del></p><p><del><font color="blue">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yaahp是我自2006年至今在业余时间独立开发的软件，自2011年起与北京欣晟允软件技术有限公司合作进行商业化，但已于2013年停止合作关系。</font></del><br><del>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 该公司网站（www.foreology.com）以及该公司注册的产品网站（www.yaahp.com）上仍然在销售yaahp软件的较低版本(7.X)，但我不再为该公司的客户提供软件升级和技术支持服务。</del></p><p><del>2015年4月25日</del></p>]]></content>
    
    <summary type="html">
    
      www.yaahp.com网站现在由山西元决策软件科技有限公司维护.
    
    </summary>
    
      <category term="OR" scheme="http://www.jeffzhang.cn/categories/OR/"/>
    
    
      <category term="yaahp" scheme="http://www.jeffzhang.cn/tags/yaahp/"/>
    
  </entry>
  
  <entry>
    <title>yaahp V7.5 版本相关问题</title>
    <link href="http://www.jeffzhang.cn/yaahp-version/"/>
    <id>http://www.jeffzhang.cn/yaahp-version/</id>
    <published>2017-05-11T21:05:55.000Z</published>
    <updated>2018-02-15T15:33:47.000Z</updated>
    
    <content type="html"><![CDATA[<p>在<a href="http://www.foreology.com" target="_blank" rel="noopener">北京欣晟允软件技术有限公司</a>购买旧版本yaahp的客户可以联系<a href="http://www.metadecsn.com" target="_blank" rel="noopener">山西元决策软件科技有限公司</a>进行软件升级.</p><p><br><br><br></p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最近有不少朋友询问yaahp版本的问题，例如：V10.5与V7.5是否相同开发者开发的吗，怎么界面看起来不同？yaahp V10.X版本都只是个人发布吗？在此对这些问题统一答复：</p><h2 id="1-V10-5与V7-5是否相同开发者开发的吗，怎么界面看起来不同？"><a href="#1-V10-5与V7-5是否相同开发者开发的吗，怎么界面看起来不同？" class="headerlink" title="1. V10.5与V7.5是否相同开发者开发的吗，怎么界面看起来不同？"></a><font color="blue">1. V10.5与V7.5是否相同开发者开发的吗，怎么界面看起来不同？</font></h2><font color="blue">A</font>：是相同的开发者，从V0.1到V10.5都是我(两招、张建华、Jianhua Zhang)。<br>yaahp软件界面风格的演变经过三个阶段：<br><br>    V0.1 ~ V0.5.3：传统的菜单工具栏界面；<br>    V6.0 ~ V7.5：Ribbon风格界面；<br>    V9.0 ~ 现在：简洁的Tab整合界面风格；<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;现在Tab整合界面风格是在V7.5的Ribbon风格基础上，进一步优化用户体验的结果，可以进一步提高软件可用性，方便用户的操作。<br><br><br>##  <font color="blue">2. 我购买的北京欣晟允软件技术有限公司的yaahp V7.5使用授权文件可以用在最新版的yaahp上吗？</font><p><font color="blue">A</font>：在<a href="http://www.foreology.com" target="_blank" rel="noopener">北京欣晟允软件技术有限公司</a>购买旧版本yaahp的客户可以联系<a href="http://www.metadecsn.com" target="_blank" rel="noopener">山西元决策软件科技有限公司</a>进行软件升级.</p><p><del><font color="blue">A</font>：很抱歉，不能。</del><br><del>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最新版V10.5于2016年9月发布，而以北京欣晟允软件技术有限公司发布的V7.5于2013年10月发布。虽然yaahp所有版本都是我(两招、张建华、Jianhua Zhang)开发的，但是与北京欣晟允软件技术有限公司解除合作关系时，该公司并未与我就后续的升级和维护签订任何合同。</del><br><del>该公司并未与我就后续的升级和维护签订任何合同&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;因为<font color="blue">该公司并没有yaahp源代码</font>(源码所有人是我，未授权给该公司使用)，所以<font color="blue">无法针对V7.5版本进行bug修复或功能升级</font>，但是仍然在销售网站上列出了“享受3年内免费升级，3年后半价升级的服务”的条目误导用户购买。</del></p><h2 id="3-yaahp-V10-X版本都只是个人发布吗？"><a href="#3-yaahp-V10-X版本都只是个人发布吗？" class="headerlink" title="3. yaahp V10.X版本都只是个人发布吗？"></a><font color="blue">3. yaahp V10.X版本都只是个人发布吗？</font></h2><p><font color="blue">A</font>：yaahp V9.0到现在的V10.5都只是以我个人名义发布的，所以有些客户可能会担心产品交付以及后续服务能力。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为此，已经联合管理咨询及计算机软件行业几位经验丰富的人士组建了一个专业团队，并成立一个专门的公司处理yaahp软件及服务等相关业务。下一个版本(V11.0)的yaahp将会以该公司的名义发布。</p>]]></content>
    
    <summary type="html">
    
      最近有不少朋友询问yaahp版本的问题，在此对这些问题统一答复.
    
    </summary>
    
      <category term="OR" scheme="http://www.jeffzhang.cn/categories/OR/"/>
    
    
      <category term="yaahp" scheme="http://www.jeffzhang.cn/tags/yaahp/"/>
    
  </entry>
  
  <entry>
    <title>yaahp V11.0 发布声明及下载</title>
    <link href="http://www.jeffzhang.cn/yaahp11.0-release-note/"/>
    <id>http://www.jeffzhang.cn/yaahp11.0-release-note/</id>
    <published>2017-04-30T16:00:00.000Z</published>
    <updated>2018-02-15T16:04:32.000Z</updated>
    
    <content type="html"><![CDATA[<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yaahp官方网站已<a href="http://www.jeffzhang.cn/yaahp-metadecsn/">变更</a>, yaahp V11.0安装程序在<a href="http://www.metadecsn.com" target="_blank" rel="noopener">新的官方网站</a>下载.</p><h3 id="下载"><a href="#下载" class="headerlink" title="下载"></a>下载</h3><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://www.metadecsn.com/download/" target="_blank" rel="noopener">下载yaahpV11.0</a></p><h3 id="Changelogs"><a href="#Changelogs" class="headerlink" title="Changelogs"></a>Changelogs</h3><h4 id="Version-11-0"><a href="#Version-11-0" class="headerlink" title="Version 11.0"></a>Version 11.0</h4><font color="blue">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yaahp V11.0改进排序权重计算结果展示、灵敏度分析, 修正多处bug.</font><br><font color="blue">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 自V10.5以来的变化如下:</font><ol><li>新增: 计算结果页面使用数据展示效果更好的Chart重做；</li><li>新增：灵敏度分析增加各备选方案受各中间层要素权重变化的影响程度（横向分析）；</li><li>新增：灵敏度分析增加定量的灵敏度指标，用于标识要素的灵敏度。该值越大说明总排序权重对该要素的变化越敏感；反之该值越小总排序权重对其权重的变化越不敏感，也即总排序对此要素变化越稳定；</li><li>修改：VC运行时升级为2017;</li><li>修改：升级.Net Reactor至5.0.0，处理可能导致安全软件误报病毒/木马的固定资源签名问题；</li><li>修改：层次模型中，只有一个影响因素要素结构也是合法的；</li><li>修改：灵敏度分析窗口中，“分析结果”页面标题改为“纵向分析”。今后版本会加入横向分析，以得到备选方案权重受各中间层要素权重变化的影响程度；</li><li>修改：如果层次模型图中没有或只有一个备选方案，双击错误信息显示提示信息：“如果没有或无法确定备选方案，可以尝试把当前下方没有连接的中间层要素改为备选方案。”，并列出建议修改的中间层要素；</li><li>修改：灵敏度分析中静态分析图表X轴坐标文字增加”权重”；</li><li>修改：获取使用授权之前提示需填写订单号及相应的电子邮件；</li><li>修改：AHP调查表中显示UUID，方便导入数据失败时检查调查问卷版本；</li><li>修改：由于判断矩阵中存在0元素，群决策无法计算提示信息中增加0元素所属专家和判断矩阵的说明信息；</li><li>修改：存盘文件升级，增强载入效率；</li><li>修正：群决策数据检查特定情况下等级差均值计算错误的bug；</li><li>修改：免费版使用授权参数变更，增加10AHP专家群决策的免费使用版（试用截止2017/08/01）；</li><li>修正：群决策专家数据集结方式设定为判断矩阵集结时，特定情况下计算失败的bug；</li><li>修正：特定情况下，计算结果页面条状图非法alpha值引起的软件崩溃；</li><li>修正：特定的排序权重结果赋值引起的软件崩溃；</li><li>修正：导出ahp调查表，特定情况下表头表格高度计算不准确的bug；</li><li>修正：由于Excel的Sheet名长度限制导致的导出FCE报告失败的bug；</li><li>修正：灵敏度分析panel显示时内容部分错误地向下方移动的bug；</li><li>修正：在判断矩阵页面，导入专家数据，如果专家数大于License数量，提示错误并切换到模型编辑页面，但顶部的MainTab还在判断矩阵页面没有相应地切换回模型编辑页面的bug；</li><li>修正：灵敏度分析窗口上方特定情况下存在空白的bug；</li><li>修正：模糊综合评价主窗口中，保存按钮文本错误；</li><li>修正：英文版excel、pdf调查表，矩阵头部说明文本错误；</li><li>修正：模型编辑上下文菜单中的语言相关文本错误；</li><li>修正：英文版FCE示例文件中要素说明文本错误；</li><li>修正：系统DPI设定较高时，创建FCE问卷窗口无法显示确定和取消按钮的bug；</li><li>修正：特定情况下启动时custom文件数据问题的bug；</li><li>修正：灵敏度分析中，某些情况下错误地将权重0.0或1.0位置计算为变化点的bug；</li><li>修正：判断矩阵自动修正使用最大方向改进算法时，如果修正范围达到最大并且正好修正成功，被修正要素对角线要素没有正确地赋值为其倒数的bug；</li><li>修正：判断矩阵输入页面下方表格高度较高时，底部横向滚动条被部分遮挡的bug；</li><li>修正：专家调查软件启动时异常退出的bug。</li></ol>]]></content>
    
    <summary type="html">
    
      层次分析法软件yaahp Version 11.0发布声明及下载链接。
    
    </summary>
    
      <category term="OR" scheme="http://www.jeffzhang.cn/categories/OR/"/>
    
    
      <category term="yaahp" scheme="http://www.jeffzhang.cn/tags/yaahp/"/>
    
  </entry>
  
  <entry>
    <title>yaahp官方网站变更说明</title>
    <link href="http://www.jeffzhang.cn/yaahp-metadecsn/"/>
    <id>http://www.jeffzhang.cn/yaahp-metadecsn/</id>
    <published>2017-04-30T16:00:00.000Z</published>
    <updated>2017-05-02T10:21:29.000Z</updated>
    
    <content type="html"><![CDATA[<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;yaahp V9.0到V10.5都是以我个人名义发布的，随着yaahp用户数量的快速增长, 只是我个人来完成软件的技术支持和二次开发服务很吃力. 为了提高用户对产品交付和后续服务能力的信心, 几位经验丰富的管理咨询和计算机软件行业人士与我一起组建了一个专业团队，并成立一个专门的公司(<a href="http://www.metadecsn.com" target="_blank" rel="noopener">山西元决策软件科技有限公司</a>)来处理yaahp软件及服务等相关业务.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;未来我们还会推出更多的决策分析服务及相应软件工具, 敬请期待!</p>]]></content>
    
    <summary type="html">
    
      yaahp官方网站从之前的ahp.jeffzhang.cn移至www.metadecsn.com
    
    </summary>
    
      <category term="Other" scheme="http://www.jeffzhang.cn/categories/Other/"/>
    
    
      <category term="yaahp" scheme="http://www.jeffzhang.cn/tags/yaahp/"/>
    
  </entry>
  
  <entry>
    <title>yaahp V10.5 发布声明及下载</title>
    <link href="http://www.jeffzhang.cn/yaahp10.5-release-note/"/>
    <id>http://www.jeffzhang.cn/yaahp10.5-release-note/</id>
    <published>2016-09-17T16:00:00.000Z</published>
    <updated>2016-09-18T05:17:04.000Z</updated>
    
    <content type="html"><![CDATA[<p>###下载</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://ahp.jeffzhang.cn/get/" target="_blank" rel="noopener">下载yaahpV10.5</a></p><p>###Changelogs</p><p>####Version 10.5</p><font color="blue">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yaahp V10.5改用Dotnet Framework 4.0, 修正了几处软件bug, 提高软件稳定性.</font><br><font color="blue">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 自V10.4以来的变化如下:</font><ol><li>新增: 增加英文语言支持;</li><li>新增: 英文版使用Paddle(www.paddle.com)支付;</li><li>修改: Dotnet Framework 改为 4.0, 如需安装, Setup将可以自动启动安装;</li><li>修改: Windows高DPI设定时的UI布局;</li><li>修正: 资源管理器中直接双击ahpx文件打开时, 某些特定情况下使用授权未生效的bug;</li><li>修正: FCE详细数据计算AHP高层要素得分时, 特定情况下发生除零错误的bug;</li><li>修正: 特定情况下生成的FCE评测表中提示文字不能显示完全的bug;</li><li>修正: 配置文件中的某个无效设定引起的软件启动崩溃的bug.</li></ol>]]></content>
    
    <summary type="html">
    
      层次分析法软件yaahp Version 10.5发布声明及下载链接。
    
    </summary>
    
      <category term="OR" scheme="http://www.jeffzhang.cn/categories/OR/"/>
    
    
      <category term="yaahp" scheme="http://www.jeffzhang.cn/tags/yaahp/"/>
    
  </entry>
  
  <entry>
    <title>yaahp V10 本站下载数量过10000</title>
    <link href="http://www.jeffzhang.cn/yaahp-dl10000/"/>
    <id>http://www.jeffzhang.cn/yaahp-dl10000/</id>
    <published>2016-05-14T16:00:00.000Z</published>
    <updated>2016-05-15T14:28:22.000Z</updated>
    
    <content type="html"><![CDATA[<p>自2015年3月25日发布yaahp V10 以来, 从本站(jeffzhang.cn)下载的yaahp安装程序数量超过一万了.</p><p>yaahp安装程序除了<a href="http://ahp.jeffzhang.cn/get/" target="_blank" rel="noopener">本站下载</a>, 还可以在<a href="http://xiazai.sogou.com/detail/34/6/1642995063362212212.html?uID=1C4BB26E1930980A0000000057385A4F&amp;euid=1947" target="_blank" rel="noopener">腾讯软件中心下载</a>.</p><p>以上两种方式(本站或腾讯软件中心)下载到的是yaahp官方发布的软件安装包, 如果是从其他途径获得的yaahp安装程序, 请一定要检查安装程序的md5, 确保安装程序没有被篡改.</p><p>yaahp提供了免费版本供大家使用, 所以欢迎大家分享yaahp安装包, 目前在<a href="http://bbs.pinggu.org" target="_blank" rel="noopener">人大经济论坛</a>、<a href="http://download.csdn.net" target="_blank" rel="noopener">CSDN下载</a>等网站有不少朋友发帖分享yaahp下载, 谢谢大家,  但分享时为了安全建议提供原始的安装包.</p><p><img src="../image/dlstat1.png" alt="yaahp-download-statistics" title="Download Statistics"></p>]]></content>
    
    <summary type="html">
    
      自2015年3月25日发布yaahp V10 以来, 从本站(jeffzhang.cn)下载的yaahp安装程序数量超过一万了.
    
    </summary>
    
      <category term="OR" scheme="http://www.jeffzhang.cn/categories/OR/"/>
    
    
      <category term="yaahp" scheme="http://www.jeffzhang.cn/tags/yaahp/"/>
    
  </entry>
  
  <entry>
    <title>关于学生申请免费多专家使用授权的说明</title>
    <link href="http://www.jeffzhang.cn/edu-request/"/>
    <id>http://www.jeffzhang.cn/edu-request/</id>
    <published>2016-03-10T16:00:00.000Z</published>
    <updated>2016-04-24T00:28:11.000Z</updated>
    
    <content type="html"><![CDATA[<p>由于种种原因，<font color="red">yaahp免费多专家授权不再分发</font>, 非常抱歉!</p><p>全功能的yaahp免费版(可以使用软件所有功能, 能够处理5专家群决策、10FCE问卷), <a href="http://ahp.jeffzhang.cn/get/" target="_blank" rel="noopener">可以在此处下载</a>.</p><p><br><br><br></p><p>====================原文====================</p><p><del>#请确认免费版不能处理你的问题!</del></p><p><del><font color="red">yaahp 10.3免费版可以使用软件的所有功能</font>, 只是在模型规模和群决策专家数量上有所限制, 一般的问题够用了, <a href="http://ahp.jeffzhang.cn/get/#_3" target="_blank" rel="noopener">点击查看具体技术参数</a>.<br>申请前<font color="red">请确认免费版确实不足以处理你的问题</font>, 再进行申请.</del></p><p><del>#何时可以申请?</del></p><p><del>每年过了春节的第二个学期开学后,  向学生提供限时的(有效期至该年的7月1日)免费多专家使用授权.</del></p><p><del>#这种免费使用授权的分发对象是谁?</del></p><p><del>这种使用授权的对象是<font color="red">做毕业设计/论文的本专科学生</font>，并且原则上<font color="red">不向任何有经费支持</font>的课题提供.</del></p><p><del>#这种免费使用授权可以处理的问题规模如何?</del></p><p><del>最大AHP中间层数量: 99</del><br><del>每个AHP中间层最大要素数量: 65535</del><br><del>AHP群决策最大专家数量: 30</del><br><del>模糊综合评价最大问卷数量: 50</del></p><p><del>#如何申请?</del></p><p><del>请使用yaahp的技术支持功能发送, <font color="red">注明学校名称及毕业设计/论文题目</font>, 并将<font color="red">学生证照片文件作为附件一同发送</font>, 下图是一个示例.</del></p><p><del>收到申请后会尽快处理, 处理完成将会发送使用授权至<font color="red">申请时填写的Email邮箱</font>中.</del></p>]]></content>
    
    <summary type="html">
    
      近期收到大量免费多专家使用授权申请, 但并不是所有申请可以通过, 此文对申请这种免费使用授权相关的问题统一进行回复.
    
    </summary>
    
      <category term="OR" scheme="http://www.jeffzhang.cn/categories/OR/"/>
    
    
      <category term="yaahp" scheme="http://www.jeffzhang.cn/tags/yaahp/"/>
    
  </entry>
  
  <entry>
    <title>yaahp执行计算时异常退出的解决方法</title>
    <link href="http://www.jeffzhang.cn/yaahp-calc-crash/"/>
    <id>http://www.jeffzhang.cn/yaahp-calc-crash/</id>
    <published>2016-03-09T16:00:00.000Z</published>
    <updated>2018-02-15T15:30:23.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h1><p>有些用户反映在使用yaahp过程中, 计算排序权重时会出现软件异常退出的问题, 这个问题一般都是因为Visual C++ 2015运行时未正常安装引起的.</p><h1 id="yaahp为什么需要Visual-C-运行时"><a href="#yaahp为什么需要Visual-C-运行时" class="headerlink" title="yaahp为什么需要Visual C++运行时?"></a>yaahp为什么需要Visual C++运行时?</h1><p>为了提高计算效率，yaahp中一些耗时的算法使用C/C++编写(例如判断矩阵自动修正、自动补全等), 这部分代码执行需要微软的Visual C++ 2015运行时正确安装.</p><p>yaahp的安装程序会在安装时自动安装所需的Visual C++ 运行时，但是也有可能由于不满足安装条件,  或与系统中已安装的软件冲突而安装失败.</p><p>如果Visual C++运行时安装失败, 那么执行这些C/C++编写的算法将会导致yaahp异常退出.</p><h1 id="yaahp-10-3-初期版本"><a href="#yaahp-10-3-初期版本" class="headerlink" title="yaahp 10.3 初期版本"></a>yaahp 10.3 初期版本</h1><p>yaahp的10.3初期版本安装程序会显示安装Visual C++运行时的过程, 已有用户反馈: “以为这个安装过程是垃圾的附加软件, 自己点击取消了”, 这种情况也会导致Visual C++运行时安装失败.</p><p>最新的10.3版本安装程序(10.3.5848.27866)不再单独显示这个过程, 可以在<a href="http://ahp.jeffzhang.cn/get/" target="_blank" rel="noopener">这里</a>下载.</p><h1 id="手动安装Visual-C-2015运行时解决此问题"><a href="#手动安装Visual-C-2015运行时解决此问题" class="headerlink" title="手动安装Visual C++ 2015运行时解决此问题"></a>手动安装Visual C++ 2015运行时解决此问题</h1><p>首先<a href="https://www.microsoft.com/zh-cn/download/details.aspx?id=48145" target="_blank" rel="noopener">下载Visual C++ Redistributable for Visual Studio 2015</a>.<br>下载<font color="red">vc_redist.x86.exe</font>.</p><p>执行下载的安装程序, 如果安装成功(如下图), 该问题解决.</p><p><img src="../image/calc-failed/installok.png" alt="VCRuntime install OK" title="VCRuntime install OK" width="300"></p><p>如果安装失败(如下图), 请参考下一节内容.</p><p><img src="../image/calc-failed/installfailed.png" alt="VCRuntime install faile" title="VCRuntime install failed" width="300"></p><h1 id="Windows版本"><a href="#Windows版本" class="headerlink" title="Windows版本"></a>Windows版本</h1><p>Visual C++ 2015运行时支持的操作系统如下:</p><pre><code>Windows 10 , Windows 7 Service Pack 1, Windows 8, Windows 8.1Windows Server 2003 Service Pack 2, Windows Server 2008 R2 SP1Windows Server 2008 Service Pack 2, Windows Server 2012Windows Vista Service Pack 2, Windows XP Service Pack 3</code></pre><p>更详细信息请点击<a href="https://www.microsoft.com/zh-cn/download/details.aspx?id=48145" target="_blank" rel="noopener">这里</a>.</p><p>所以, 如果电脑上的Windows版本(并打相应的补丁包)不在这个列表中, 将无法成功安装Visual C++ 2015运行时, 导致yaahp执行计算时失败.</p><h2 id="以Windows-7为例说明解决办法"><a href="#以Windows-7为例说明解决办法" class="headerlink" title="以Windows 7为例说明解决办法"></a>以Windows 7为例说明解决办法</h2><p>查看系统信息, 发现系统为未打Service Pack1的Windows 7, 如下图所示:</p><p><img src="../image/calc-failed/win7.png" alt="win7" title="Win7"></p><p><a href="https://www.microsoft.com/zh-CN/download/details.aspx?id=5842" target="_blank" rel="noopener">下载Windows 7 Service Pack1</a>, 如果是32位系统, 下载<font color="red">windows6.1-KB976932-X86.exe</font>; 如果是64位系统, 下载<font color="red">windows6.1-KB976932-X64.exe</font>.</p><p>安装后查看系统信息, 如下图.</p><p><img src="../image/calc-failed/win7sp1.png" alt="win7sp1" title="Win7 SP1"></p><p>然后再次安装Visual C++ 2015运行时, 安装成功, 如下图.</p><p><img src="../image/calc-failed/installok.png" alt="VCRuntime install OK" title="VCRuntime install OK" width="300"></p><p>启动yaahp, 如果没有其他问题, 既可以正常进行计算了.</p>]]></content>
    
    <summary type="html">
    
      有些用户反映yaahp在执行计算操作时异常退出, 这个错误是由于Visual C++运行时安装失败引起的, 本文对这个问题进行说明并给出解决方法.
    
    </summary>
    
      <category term="OR" scheme="http://www.jeffzhang.cn/categories/OR/"/>
    
    
      <category term="yaahp" scheme="http://www.jeffzhang.cn/tags/yaahp/"/>
    
  </entry>
  
  <entry>
    <title>使用yaahp进行灵敏度分析</title>
    <link href="http://www.jeffzhang.cn/yaahp-sensitivity-analysis-intro/"/>
    <id>http://www.jeffzhang.cn/yaahp-sensitivity-analysis-intro/</id>
    <published>2015-11-25T16:00:00.000Z</published>
    <updated>2018-02-15T14:38:12.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>AHP是一种经典的多属性决策方法, 已经广泛地应用于众多领域. 但是, 由于AHP很大程度上依赖于决策者主观判断, 而这些主观判断本身是一个模糊值, 不能准确地反映客观情况, 并且客观情况也可能随着环境/时间的改变而变化, 所以在很多情况下决策者<strong><em>不但要求得到决策结果</em></strong>, 也即备选方案排序结果, <strong><em>还需要了解决策结果的稳定性</em></strong>, 也就是层次模型中某些要素的权重变化将会对决策结果产生什么样的影响.</p><p>这种备选方案排序结果随某属性权重变化而变化的程度就是灵敏度. 如果某属性权重发生较小的变化后, 备选方案的排序结果就会发生变化, 说明决策结果对该属性的灵敏度较高, 也就是说决策结果不稳定.</p><h2 id="作用"><a href="#作用" class="headerlink" title="作用"></a>作用</h2><p>通过灵敏度分析, 决策者可以掌握属性权重变化对决策结果的影响及影响的程度, 帮助决策者做出正确的判断.</p><h3 id="通过灵敏度分析获得的信息"><a href="#通过灵敏度分析获得的信息" class="headerlink" title="通过灵敏度分析获得的信息"></a>通过灵敏度分析获得的信息</h3><p>1) 基本信息</p><p>使用yaahp的灵敏度分析功能, 可以获得以下基本信息:</p><ul><li>备选方案权重值随某个属性权重变化的变化情况;</li><li>备选方案排序结果随某个属性权重变化的变化情况.</li></ul><p>2) 统计信息</p><p>根据基本信息, 经过统计还可以得到以下信息:</p><ul><li>各个属性对决策结果的影响程度;</li><li>保证备选方案排序结果不变条件下, 各属性权重的变化范围.</li></ul><h3 id="利用权重变化图判断决策结果的稳定性"><a href="#利用权重变化图判断决策结果的稳定性" class="headerlink" title="利用权重变化图判断决策结果的稳定性"></a>利用权重变化图判断决策结果的稳定性</h3><p>本节利用yaahp灵敏度分析功能提供的权重变化图说明决策结果的稳定性.图1是yaahp的灵敏度分析窗口, 右上方的权重变化图中显示出各个备选方案随某属性权重变化而变化的曲线.</p><div align="center"><br><img src="../image/sensitivity/sensitivity-full.png" alt="yaahp灵敏度分析" title="yaahp灵敏度分析" width="500"><br><font size="2"><b>图1 灵敏度分析</b></font><br></div><p>图2是四种不同稳定性的权重变化图对比, 各曲线的说明如下:</p><ul><li>黑色竖线与x轴交点, 表示得到实际决策结果时属性的权重;</li><li>蓝色竖线与x轴交点是变化点, 表示属性权重值超过这个点后, 决策结果会发生变化;</li><li>其他颜色的斜线是各个备选方案随属性权重变化而变化的曲线.</li></ul><div align="center"><br><img src="../image/sensitivity/sensitivity-curve.png" alt="权重变化曲线图" title="权重变化图" width="900"><br><font size="2"><b>图2 权重变化图</b></font><br></div><p>决策结果的稳定性可以从权重变化图上看出:</p><ul><li><p>如果图中没有蓝色竖线, 说明各备选方案的权重排序(顺序)不会随着这个属性权重的变化而变化, 是稳定的;</p></li><li><p>如果存在蓝色竖线, 可以根据黑线左右两侧最近的蓝线确定一个范围, 在这个范围内属性权重变化不会影响决策结果变化, 根据这个范围的大小确定稳定性.</p></li></ul><p>根据以上的两点, 图2中四张权重变化图所对应的稳定性如下:</p><ul><li>图2左上图中没有蓝线, 无论该属性权重如何变化都不会影响决策结果, 是稳定的;</li><li>图2右上图中, 在黑线只有左侧一条蓝线, 所以这个属性的不影响决策结果的权重变化范围就是从蓝线(约0.3)起到最右侧(1.0);</li><li>图2左下图, 其中有两条蓝线, 所以这个属性的不影响决策结果的权重变化范围就是从左侧蓝线(约0.41)起到右侧蓝线(约0.98);</li><li>图2右下图中, 这个属性的不影响决策结果的权重变化范围就是从左侧蓝线(约0.58)起到右侧蓝线(约0.68), 四个图中它的稳定性最差.</li></ul><h2 id="更多"><a href="#更多" class="headerlink" title="更多"></a>更多</h2><p>更多的新版灵敏度分析功能资料请参考<a href="http://ahp.jeffzhang.cn/manual/sensitivity/" target="_blank" rel="noopener">yaahp手册”灵敏度分析”</a>和 <a href="http://ahp.jeffzhang.cn/videos/sensitive/" target="_blank" rel="noopener">yaahp视频演示”灵敏度分析”</a>.</p>]]></content>
    
    <summary type="html">
    
      yaahp V10.3对灵敏度分析进行了大幅改进, 并新增了群决策的灵敏度分析功能以及生成灵敏度分析报告.  本文结合V10.3新功能对灵敏度分析进行介绍.
    
    </summary>
    
      <category term="AHP" scheme="http://www.jeffzhang.cn/categories/AHP/"/>
    
    
      <category term="yaahp" scheme="http://www.jeffzhang.cn/tags/yaahp/"/>
    
  </entry>
  
  <entry>
    <title>yaahp V10.3 发布声明及下载</title>
    <link href="http://www.jeffzhang.cn/yaahp10.X-release-note/"/>
    <id>http://www.jeffzhang.cn/yaahp10.X-release-note/</id>
    <published>2015-11-25T16:00:00.000Z</published>
    <updated>2015-11-26T02:16:21.000Z</updated>
    
    <content type="html"><![CDATA[<p>###下载</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://ahp.jeffzhang.cn/get/" target="_blank" rel="noopener">下载yaahpV10.3</a></p><p>###Changelogs</p><p>####Version 10.3<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="blue">yaahp V10.3对灵敏度分析进行了大幅改进, 并新增了群决策的灵敏度分析功能以及生成灵敏度分析报告, 可用性大大提高.</font></p><font color="blue">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 自V10.2以来的变化如下:</font><ol><li>新增: 群决策的灵敏度分析;</li><li>新增: Excel格式的灵敏度分析报告, 报告中包括了备选方案排序权重随要素权重的变化情况图, 权重变化情况数据和权重排序变化情况数据;</li><li>新增: 灵敏度分析中对权重变化情况数据进行计算并显示, 该数据展示某个要素权重从0变化到1过程中, 各个备选方案权重值的变化情况;</li><li>新增: 灵敏度分析中对排序变化情况数据进行计算并显示, 该数据展示某个要素权重从0变化到1过程中, 备选方案权重排序发生变化情况;</li><li>新增: 配套最新版软件的使用教程视频;</li><li>修改: 灵敏度分析改为在已产生的计算结果基础上进行计算, 之前是独立计算并且不能进行一致性自动修正和残缺补全;</li><li>修改: 群决策功能按钮现在在计算结果页面的工具栏上显示;</li><li>修改: 灵敏度分析的决策目标改为决策总目标, 而不是之前的各个子目标;</li><li>修改: 灵敏度分析要素选择方式由之前的列表改为树形结构;</li><li>修改: 灵敏度分析中将交互分析功能单独放至一个TabUI中;</li><li>修改: 几处操作成功通知消息框为不影响用户操作的背景提示窗口;</li><li>修改: 软件图标修改, 之前的图标透明部分较多, 在深色背景上效果较差;</li><li>修改: 修正判断矩阵页面错误信息提示窗口标题乱码;</li><li>修改: VC runtime改为VC2015, setup自动安装;</li><li>修改: 全面更新软件文档;</li><li>修正: 生成AHP调查表时, 非第一次生成时字体异常的Bug;</li><li>修正: 设定不检查基本一致性, 判断矩阵计算前仍然进行修正的Bug;</li><li>修正: 导入Excel格式的AHP专家数据时, 存盘后无法追加导入专家数据的Bug;</li><li>修正: 灵敏度分析窗口计算线程工作时关闭引发异常的Bug;</li><li>修正: 特定情况下, 不存在残缺可接受判断矩阵但是却错误地提示由于它们的存在不能标记自动修正的Bug;</li><li>修正: 残缺可接受判断矩阵满足拟一致性不需要修正, 但是在标记修正不一致判断矩阵时提示它们不能标记的Bug;</li><li>修正: 根法计算残缺可接受判断矩阵一致性比例时, 除零导致的一致性无穷大Bug;</li><li>修正: 群决策输入专家姓名时, 如果专家姓名还未输入就直接新建文件操作, 主程序崩溃的Bug;</li><li>修正: 群决策添加第一个专家, 如果在提示清空非群决策数据确认时选择否, 主程序崩溃的Bug.</li></ol>]]></content>
    
    <summary type="html">
    
      层次分析法软件yaahp Version 10.3发布声明及下载链接。
    
    </summary>
    
      <category term="OR" scheme="http://www.jeffzhang.cn/categories/OR/"/>
    
    
      <category term="yaahp" scheme="http://www.jeffzhang.cn/tags/yaahp/"/>
    
  </entry>
  
  <entry>
    <title>Odoo8.0模块开发全流程</title>
    <link href="http://www.jeffzhang.cn/Odoo-Official-Doc-Notes/"/>
    <id>http://www.jeffzhang.cn/Odoo-Official-Doc-Notes/</id>
    <published>2015-09-19T16:00:00.000Z</published>
    <updated>2015-11-03T08:54:39.000Z</updated>
    
    <content type="html"><![CDATA[<p>参考资料点击<a href="https://www.odoo.com/documentation/8.0/howtos/backend.html" target="_blank" rel="noopener">这里</a>.</p><p>#构建Odoo模块</p><p>##模块组成</p><ul><li><p>业务对象</p><blockquote><p>业务对象声明为Python类, 由Odoo自动载入.</p></blockquote></li><li><p>数据文件</p><blockquote><p>XML或CSV文件格式, 在其中声明了元数据(视图或工作流)、配置数据(模块参数)、演示数据等.</p></blockquote></li><li><p>Web控制器</p><blockquote><p>处理Web浏览器发来的requests.</p></blockquote></li><li><p>静态web数据</p><blockquote><p>Web用到的图像, CSS或JavaScript文件.</p></blockquote></li></ul><p>##模块结构<br>一个Odoo模块也是一个Python模块, 存放在一个目录中, 包含一个__init__.py文件, 用于导入其他Python模块.</p><pre><code>from . import mymodule</code></pre><p>odoo.py提供了一个子命令scaffold可以方便地创建一个空的模块.</p><pre><code>$ odoo.py scaffold &lt;module name&gt; &lt;where to put it&gt;</code></pre><p>命令执行后, 将会创建一个子目录并且其中包括了Odoo模块所需的一些基本文件.</p><p>###练习 #1</p><p>执行 ./odoo.py scaffold openacademy addons, 在addons目录下创建一个名为openacademy的模块, 生成的目录文件结构如下.</p><pre><code>openacademy├── __init__.py├── __openerp__.py├── controllers.py├── demo.xml├── models.py├── security│   └── ir.model.access.csv└── templates.xml</code></pre><p>各文件内容请查看文件或查看<a href="https://www.odoo.com/documentation/8.0/howtos/backend.html" target="_blank" rel="noopener">原文</a>, 然后对__openerp__.py中的几种标识文本进行修改.</p><p>##对象关系映射</p><p>ORM层是Odoo的一个关键组件, 它可以避免大部分的SQL语句编写从而提高扩展性和安全性.</p><p>业务对象用派生自Model的Python类(模型)来编写, 该类的_name属性定义了模型在Odoo系统中的名称.</p><pre><code>from openerp import modelsclass MinimalModel(models.Model):    _name = &apos;test.model&apos;</code></pre><p>##字段</p><p>字段定义模型能够存储什么以及在哪里存储, 字段在模型类中用属性来定义.</p><pre><code>from openerp import models, fieldsclass LessMinimalModel(models.Model):    _name = &apos;test.model2&apos;    name = fields.Char()</code></pre><p>###通用属性</p><p>与模型类似, 字段也可以通过参数传递对其进行设定:</p><pre><code>name = field.Char(required=True)</code></pre><p>字段的常用属性有:</p><ul><li><p>string (unicode, default: field’s name)</p><blockquote><p>The label of the field in UI (visible by users).</p></blockquote></li><li><p>required (bool, default: False)</p><blockquote><p>If True, the field can not be empty, it must either have a default value or always be given a value when creating a record.</p></blockquote></li><li><p>help (unicode, default: ‘’)</p><blockquote><p>Long-form, provides a help tooltip to users in the UI.</p></blockquote></li><li><p>index (bool, default: False)</p><blockquote><p>Requests that Odoo create a database index on the column</p></blockquote></li></ul><p>###简单字段</p><p>字段可以分为两类: 简单字段和关系字段. 前者为原子值, 直接保存在模型对应的数据库表中; 后者连接到其他的记录上(可以是相同的模型也可以是不同的模型).</p><p>Boolean, Date, Char这些都是简单字段.</p><p>###保留字段</p><p>Odoo在模型中自动创建并维护一些字段, 这些字段就是保留字段, 这些字段数据不需要也不应该手动去修改.</p><ul><li><p>id (Id)</p><blockquote><p>the unique identifier for a record in its model</p></blockquote></li><li><p>create_date (Datetime)</p><blockquote><p>creation date of the record</p></blockquote></li><li><p>create_uid (Many2one)</p><blockquote><p>user who created the record</p></blockquote></li><li><p>write_date (Datetime)</p><blockquote><p>last modification date of the record</p></blockquote></li><li><p>write_uid (Many2one)</p><blockquote><p>user who last modified the record</p></blockquote></li></ul><p>###特殊字段</p><p>默认情况下, Odoo要求模型中有一个<em>name</em>字段, 用于显示和搜索, 通过设置_rec_name也可以达到这样的目的.</p><p>###练习 #2</p><p>在openacademy模块中定义一个新的模型<em>Course</em>, openacademy/models.py内容如下:</p><pre><code># -*- coding: utf-8 -*-from openerp import models, fields, apiclass Course(models.Model):    _name = &apos;openacademy.course&apos;    name = fields.Char(string=&quot;Title&quot;, required=True)    description = fields.Text()</code></pre><p>##数据文件</p><p>Odoo是一个高度数据驱动的系统, 虽然使用Python代码来定制模块行为, 但很多模块数据是在其载入时setup的, 并且有些模块仅仅为Odoo添加数据.</p><p>通过数据文件来定义模块数据, 例如可以使用XML文件中的&lt;record&gt;元素定义数据, 每一个&lt;record&gt;元素创建或者更新数据库中的一条记录, 形式如下:</p><pre><code>&lt;openerp&gt;    &lt;data&gt;        &lt;record model=&quot;{model name}&quot; id=&quot;{record identifier}&quot;&gt;            &lt;field name=&quot;{a field name}&quot;&gt;{a value}&lt;/field&gt;        &lt;/record&gt;    &lt;/data&gt;&lt;openerp&gt;</code></pre><ul><li><p>model</p><blockquote><p>Odoo模型名.</p></blockquote></li><li><p>id</p><blockquote><p>外部ID(External Identifier), 通过它可以引用到记录(并且不需要知道记录所在的数据库ID).</p></blockquote></li><li><p><field>元素</field></p><blockquote><p>name属性用于确定字段名称(例如description), 该元素的body给出字段的值.</p></blockquote></li></ul><p>数据文件必须在模块载入清单文件列表中, 也就是__openerp__.py的’data’列表(全部载入)或’demo’列表(只有设定为载入演示数据才会载入)中.</p><p>###练习 #3</p><p>创建一个数据文件来向Course中添加数据, 编辑openacademy/demo.xml, 并确认__openerp__.py的’demo’列表中有该文件.</p><pre><code>&lt;openerp&gt;    &lt;data&gt;        &lt;record model=&quot;openacademy.course&quot; id=&quot;course0&quot;&gt;            &lt;field name=&quot;name&quot;&gt;Course 0&lt;/field&gt;            &lt;field name=&quot;description&quot;&gt;Course 0&apos;s descriptionCan have multiple lines            &lt;/field&gt;        &lt;/record&gt;        &lt;record model=&quot;openacademy.course&quot; id=&quot;course1&quot;&gt;            &lt;field name=&quot;name&quot;&gt;Course 1&lt;/field&gt;            &lt;!-- no description for this one --&gt;        &lt;/record&gt;        &lt;record model=&quot;openacademy.course&quot; id=&quot;course2&quot;&gt;            &lt;field name=&quot;name&quot;&gt;Course 2&lt;/field&gt;            &lt;field name=&quot;description&quot;&gt;Course 2&apos;s description&lt;/field&gt;        &lt;/record&gt;    &lt;/data&gt;&lt;/openerp&gt;</code></pre><p>##动作和菜单<br>在Odoo中, 动作和菜单都是定义在数据库中的数据记录, 一般通过数据文件来定义.</p><p>动作可以由三种方式触发:</p><ul><li>点击菜单项(菜单项链接到特定动作)</li><li>点击视图上的按钮(如果按钮连接到动作)</li><li>作为对象的上下文动作</li></ul><p>使用&lt;menuitem&gt;声明一个ir.ui.menu并将其连接到一个action, 可以用下面的形式的代码.</p><pre><code>&lt;record model=&quot;ir.actions.act_window&quot; id=&quot;action_list_ideas&quot;&gt;    &lt;field name=&quot;name&quot;&gt;Ideas&lt;/field&gt;    &lt;field name=&quot;res_model&quot;&gt;idea.idea&lt;/field&gt;    &lt;field name=&quot;view_mode&quot;&gt;tree,form&lt;/field&gt;&lt;/record&gt;&lt;menuitem id=&quot;menu_ideas&quot; parent=&quot;menu_root&quot; name=&quot;Ideas&quot; sequence=&quot;10&quot;          action=&quot;action_list_ideas&quot;/&gt;</code></pre><blockquote><p>注意: action必须先于menu的连接使用定义, 数据文件在载入时顺序地执行, 所以动作的ID必须首先存在于数据库中才能使用.</p></blockquote><p>###练习 #4</p><p>定义一个新的菜单项访问OpenAcademy课程.</p><p>创建openacademy/views/openacademy.xml文件, 并在其中添加动作和菜单.</p><pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;openerp&gt;    &lt;data&gt;        &lt;!-- window action --&gt;        &lt;!--            The following tag is an action definition for a &quot;window action&quot;,            that is an action opening a view or a set of views        --&gt;        &lt;record model=&quot;ir.actions.act_window&quot; id=&quot;course_list_action&quot;&gt;            &lt;field name=&quot;name&quot;&gt;Courses&lt;/field&gt;            &lt;field name=&quot;res_model&quot;&gt;openacademy.course&lt;/field&gt;            &lt;field name=&quot;view_type&quot;&gt;form&lt;/field&gt;            &lt;field name=&quot;view_mode&quot;&gt;tree,form&lt;/field&gt;            &lt;field name=&quot;help&quot; type=&quot;html&quot;&gt;                &lt;p class=&quot;oe_view_nocontent_create&quot;&gt;Create the first course                &lt;/p&gt;            &lt;/field&gt;        &lt;/record&gt;        &lt;!-- top level menu: no parent --&gt;        &lt;menuitem id=&quot;main_openacademy_menu&quot; name=&quot;Open Academy&quot;/&gt;        &lt;!-- A first level in the left side menu is needed             before using action= attribute --&gt;        &lt;menuitem id=&quot;openacademy_menu&quot; name=&quot;Open Academy&quot;                  parent=&quot;main_openacademy_menu&quot;/&gt;        &lt;!-- the following menuitem should appear *after*             its parent openacademy_menu and *after* its             action course_list_action --&gt;        &lt;menuitem id=&quot;courses_menu&quot; name=&quot;Courses&quot; parent=&quot;openacademy_menu&quot;                  action=&quot;course_list_action&quot;/&gt;        &lt;!-- Full id location:             action=&quot;openacademy.course_list_action&quot;             It is not required when it is the same module --&gt;    &lt;/data&gt;&lt;/openerp&gt;</code></pre><p>在__openerp__.py中添加这个数据文件名到’data’.</p><pre><code>&apos;data&apos;: [        # &apos;security/ir.model.access.csv&apos;,        &apos;templates.xml&apos;,        &apos;views/openacademy.xml&apos;,    ],</code></pre><p>更新模块后可以看到菜单, 操作看看效果.</p><p>#基本视图</p><p>视图定义了模型数据如何显示, 每种类型的视图代表一种数据可视化模式.</p><p>##基本的视图定义</p><p>一个视图是以一条ir.ui.view模型数据的形式定义的.</p><pre><code>&lt;record model=&quot;ir.ui.view&quot; id=&quot;view_id&quot;&gt;    &lt;field name=&quot;name&quot;&gt;view.name&lt;/field&gt;    &lt;field name=&quot;model&quot;&gt;object_name&lt;/field&gt;    &lt;field name=&quot;priority&quot; eval=&quot;16&quot;/&gt;    &lt;field name=&quot;arch&quot; type=&quot;xml&quot;&gt;        &lt;!-- view content: &lt;form&gt;, &lt;tree&gt;, &lt;graph&gt;, ... --&gt;    &lt;/field&gt;&lt;/record&gt;</code></pre><p>##Tree views</p><p>Tree view也被称为list views, 在一个表格中显示记录. 根元素是&lt;tree&gt;, 最简形式的tree view只是简单地列出每条记录的多个字段, 每个字段为一列.</p><pre><code>&lt;tree string=&quot;Idea list&quot;&gt;    &lt;field name=&quot;name&quot;/&gt;    &lt;field name=&quot;inventor_id&quot;/&gt;&lt;/tree&gt;</code></pre><p>##Form views</p><p>Form用于创建或编辑单条记录, 根元素是&lt;form&gt;, 可以在form中组合各种高层结构元素(如groups, notebooks)以及交互元素(如buttons, fields).</p><pre><code>&lt;form string=&quot;Idea form&quot;&gt;    &lt;group colspan=&quot;4&quot;&gt;        &lt;group colspan=&quot;2&quot; col=&quot;2&quot;&gt;            &lt;separator string=&quot;General stuff&quot; colspan=&quot;2&quot;/&gt;            &lt;field name=&quot;name&quot;/&gt;            &lt;field name=&quot;inventor_id&quot;/&gt;        &lt;/group&gt;        &lt;group colspan=&quot;2&quot; col=&quot;2&quot;&gt;            &lt;separator string=&quot;Dates&quot; colspan=&quot;2&quot;/&gt;            &lt;field name=&quot;active&quot;/&gt;            &lt;field name=&quot;invent_date&quot; readonly=&quot;1&quot;/&gt;        &lt;/group&gt;        &lt;notebook colspan=&quot;4&quot;&gt;            &lt;page string=&quot;Description&quot;&gt;                &lt;field name=&quot;description&quot; nolabel=&quot;1&quot;/&gt;            &lt;/page&gt;        &lt;/notebook&gt;        &lt;field name=&quot;state&quot;/&gt;    &lt;/group&gt;&lt;/form&gt;</code></pre><p>###练习 #5</p><p>为openacademy创建form view, views/openacademy.xml数据文件中增加&lt;record model=”ir.ui.view”…&gt;内容.</p><pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&lt;openerp&gt;    &lt;data&gt;        &lt;record model=&quot;ir.ui.view&quot; id=&quot;course_form_view&quot;&gt;            &lt;field name=&quot;name&quot;&gt;course.form&lt;/field&gt;            &lt;field name=&quot;model&quot;&gt;openacademy.course&lt;/field&gt;            &lt;field name=&quot;arch&quot; type=&quot;xml&quot;&gt;                &lt;form string=&quot;Course Form&quot;&gt;                    &lt;sheet&gt;                        &lt;group&gt;                            &lt;field name=&quot;name&quot;/&gt;                            &lt;field name=&quot;description&quot;/&gt;                        &lt;/group&gt;                    &lt;/sheet&gt;                &lt;/form&gt;            &lt;/field&gt;        &lt;/record&gt;        &lt;!-- window action --&gt;        &lt;!--            The following tag is an action definition for a &quot;window action&quot;,</code></pre><p>更新模块, 创建一个Course, 可以看到form view变了.</p><p>###练习 #6</p><p>使用<em>notebook</em>. 在form view中, 将description字段放在一个tab中, 方便随后添加其他tabs, 对练习#5的form view数据做如下修改.</p><pre><code>        &lt;sheet&gt;            &lt;group&gt;                &lt;field name=&quot;name&quot;/&gt;            &lt;/group&gt;            &lt;notebook&gt;                &lt;page string=&quot;Description&quot;&gt;                    &lt;field name=&quot;description&quot;/&gt;                &lt;/page&gt;                &lt;page string=&quot;About&quot;&gt;                    This is an example of notebooks                &lt;/page&gt;            &lt;/notebook&gt;        &lt;/sheet&gt;    &lt;/form&gt;&lt;/field&gt;</code></pre><p>更新模块, 看效果.</p><p>###More</p><p>还可以使用HTML为form view提供更加灵活的布局, 例如下面的例子.</p><pre><code>&lt;form string=&quot;Idea Form&quot;&gt;    &lt;header&gt;        &lt;button string=&quot;Confirm&quot; type=&quot;object&quot; name=&quot;action_confirm&quot;                states=&quot;draft&quot; class=&quot;oe_highlight&quot; /&gt;        &lt;button string=&quot;Mark as done&quot; type=&quot;object&quot; name=&quot;action_done&quot;                states=&quot;confirmed&quot; class=&quot;oe_highlight&quot;/&gt;        &lt;button string=&quot;Reset to draft&quot; type=&quot;object&quot; name=&quot;action_draft&quot;                states=&quot;confirmed,done&quot; /&gt;        &lt;field name=&quot;state&quot; widget=&quot;statusbar&quot;/&gt;    &lt;/header&gt;    &lt;sheet&gt;        &lt;div class=&quot;oe_title&quot;&gt;            &lt;label for=&quot;name&quot; class=&quot;oe_edit_only&quot; string=&quot;Idea Name&quot; /&gt;            &lt;h1&gt;&lt;field name=&quot;name&quot; /&gt;&lt;/h1&gt;        &lt;/div&gt;        &lt;separator string=&quot;General&quot; colspan=&quot;2&quot; /&gt;        &lt;group colspan=&quot;2&quot; col=&quot;2&quot;&gt;            &lt;field name=&quot;description&quot; placeholder=&quot;Idea description...&quot; /&gt;        &lt;/group&gt;    &lt;/sheet&gt;&lt;/form&gt;</code></pre><p>##Search views</p><p>Search views用来自定义list views及其它统计/多条记录视图中的搜索字段. 根元素为&lt;search&gt;, 其子元素定义了在哪些字段上进行搜索.</p><pre><code>&lt;search&gt;    &lt;field name=&quot;name&quot;/&gt;    &lt;field name=&quot;inventor_id&quot;/&gt;&lt;/search&gt;</code></pre><p>如果一个模型没有定义对应的Search view, odoo自动创建一个仅搜索name字段的search view.</p><p>###练习 #7</p><p>添加title以及description搜索, 在views/openacademy.xml中定义search view.</p><pre><code>    &lt;/field&gt;&lt;/record&gt;&lt;record model=&quot;ir.ui.view&quot; id=&quot;course_search_view&quot;&gt;    &lt;field name=&quot;name&quot;&gt;course.search&lt;/field&gt;    &lt;field name=&quot;model&quot;&gt;openacademy.course&lt;/field&gt;    &lt;field name=&quot;arch&quot; type=&quot;xml&quot;&gt;        &lt;search&gt;            &lt;field name=&quot;name&quot;/&gt;            &lt;field name=&quot;description&quot;/&gt;        &lt;/search&gt;    &lt;/field&gt;&lt;/record&gt;&lt;!-- window action --&gt;&lt;!--    The following tag is an action definition for a &quot;window action&quot;,</code></pre><p>更新模块, 搜索框输入字符后可以看到下方能够选择搜索description字段.</p><p>#模型中的关联</p><p>##概述<br>一个模型中的记录可能关联到其他模型的记录, 例如销售订单记录会关联到一个包含客户信息的客户记录.</p><p>###练习 #8</p><p>为了说明数据关联, 首先增加新的模型.</p><p>Open Academy模块中, 一个session是一个在特定时间针对特定听众讲授课程的过程. 需要为session创建相应的模型.</p><p>session具有name, 开始日期, 持续时间以及座位数量等. 此外还需要添加相应的action和menuitem显示模型数据.</p><p>首先在openacademy/models.py中创建Session类.</p><pre><code>class Session(models.Model):    _name = &apos;openacademy.session&apos;    name = fields.Char(required=True)    start_date = fields.Date()    duration = fields.Float(digits=(6, 2), help=&quot;Duration in days&quot;)    seats = fields.Integer(string=&quot;Number of seats&quot;)</code></pre><p>然后在openacademy/view/openacademy.xml中添加用于访问session模型的action和menuitem定义.</p><pre><code>        &lt;!-- Full id location:             action=&quot;openacademy.course_list_action&quot;             It is not required when it is the same module --&gt;        &lt;!-- session form view --&gt;        &lt;record model=&quot;ir.ui.view&quot; id=&quot;session_form_view&quot;&gt;            &lt;field name=&quot;name&quot;&gt;session.form&lt;/field&gt;            &lt;field name=&quot;model&quot;&gt;openacademy.session&lt;/field&gt;            &lt;field name=&quot;arch&quot; type=&quot;xml&quot;&gt;                &lt;form string=&quot;Session Form&quot;&gt;                    &lt;sheet&gt;                        &lt;group&gt;                            &lt;field name=&quot;name&quot;/&gt;                            &lt;field name=&quot;start_date&quot;/&gt;                            &lt;field name=&quot;duration&quot;/&gt;                            &lt;field name=&quot;seats&quot;/&gt;                        &lt;/group&gt;                    &lt;/sheet&gt;                &lt;/form&gt;            &lt;/field&gt;        &lt;/record&gt;        &lt;record model=&quot;ir.actions.act_window&quot; id=&quot;session_list_action&quot;&gt;            &lt;field name=&quot;name&quot;&gt;Sessions&lt;/field&gt;            &lt;field name=&quot;res_model&quot;&gt;openacademy.session&lt;/field&gt;            &lt;field name=&quot;view_type&quot;&gt;form&lt;/field&gt;            &lt;field name=&quot;view_mode&quot;&gt;tree,form&lt;/field&gt;        &lt;/record&gt;        &lt;menuitem id=&quot;session_menu&quot; name=&quot;Sessions&quot;                  parent=&quot;openacademy_menu&quot;                  action=&quot;session_list_action&quot;/&gt;    &lt;/data&gt;&lt;/openerp&gt;</code></pre><blockquote><p>digits=(6,2)确定浮点数的精度, 6表示总的数字位数(不包括小数点), 2表示小数点后的位数. 所以, digits=(6,2)小数点前最多4位.</p></blockquote><p>##关联字段</p><p>未完待续!</p>]]></content>
    
    <summary type="html">
    
      根据Odoo8.0模块开发官方文档, openacademy模块开发全流程 .
    
    </summary>
    
      <category term="OpenERP" scheme="http://www.jeffzhang.cn/categories/OpenERP/"/>
    
    
      <category term="Python" scheme="http://www.jeffzhang.cn/tags/Python/"/>
    
      <category term="OpenERP" scheme="http://www.jeffzhang.cn/tags/OpenERP/"/>
    
      <category term="Odoo" scheme="http://www.jeffzhang.cn/tags/Odoo/"/>
    
  </entry>
  
  <entry>
    <title>Odoo开发--利用继承扩展已有应用</title>
    <link href="http://www.jeffzhang.cn/Odoo-Notes-2/"/>
    <id>http://www.jeffzhang.cn/Odoo-Notes-2/</id>
    <published>2015-09-17T16:00:00.000Z</published>
    <updated>2015-09-20T00:02:34.000Z</updated>
    
    <content type="html"><![CDATA[<p>Odoo开发入门课程的学生资料, 参考书点击<a href="http://www.amazon.com/Odoo-Development-Essentials-Daniel-Reis/dp/1784392790/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1439075703&amp;sr=1-1&amp;keywords=odoo+development+essentials" target="_blank" rel="noopener">这里</a>.</p><p>#概述</p><p>利用继承机制, Odoo可以在不直接修改底层对象的情况下为应用增加特性, 不需要修改已存在的模块, 而是通过创建新的模块来实现对模块的修改. 继承可以应用在所有的级别, 例如模型, 视图, 业务逻辑.</p><p>本文通过继承机制为todo_app增加社交和消息特性.</p><p>##用户共享特性路线图</p><ul><li>为Task模型增加字段, 例如任务的责任人;</li><li>修改业务逻辑, 用户只能操作自己负责的任务, 而不是所有能看到的任务;</li><li>视图添加显示的字段;</li><li>添加社交网络特性: 消息墙以及关注.</li></ul><p>##创建模块框架</p><p>创建一个名为todo_user的odoo模块, 创建__init__.py文件以及__openerp__.py, 修改__openerp__.py文件内容为:</p><pre><code>{  &apos;name&apos;: &apos;Multiuser To-Do&apos;,  &apos;description&apos;: &apos;Extend the To-Do app to multiuser.&apos;,  &apos;author&apos;: &apos;Jeff Zhang&apos;,  &apos;depends&apos;: [&apos;todo_app&apos;],}</code></pre><p>设置-&gt;更新模块列表-&gt;本地模块查找”todo_user”, 安装该模块.</p><p>#模型扩展</p><p>##扩展To-Do的模型的说明</p><p>Odoo中实体模型使用Python类来定义, 扩展模型时也是使用Python类, 但是需要使用Odoo的一个特殊机制.<br>为了扩展一个已有的模型, 需要使用一个具有_inherit属性的Python类来定义. _inherit属性指明扩展/继承哪个模型. 新的模型继承了父模型的所有特性, 在新类中定义我们期望的特性即可.</p><p>在Odoo中, 模型是独立于特定模块的, 可以通过self.env[<model name="">]获得模型的引用. 例如模型res.partner的引用可以使用self.env[‘res.partner’]获得.</model></p><p>当服务启动后模块载入时, 任何扩展的模型修改只会影响之前载入的模块, 所以载入顺序很重要需要确认模块依赖关系是正确的.</p><p>##向模型增加一个字段</p><p>扩展todo.task模型, 增加以下字段: 任务的责任人, 最后期限日期.</p><p>创建models子目录, 并在其中创建todo_task.py文件, 内容如下:</p><pre><code># -*- coding: utf-8 -*-#!/usr/bin/env pythonfrom openerp import models, fields, apiclass TodoTask(models.Model):  _inherit = &apos;todo.task&apos;  user_id = fields.Many2one(&apos;res.users&apos;, &apos;Responsible&apos;)  date_deadline = fields.Date(&apos;Deadline&apos;)</code></pre><p>注意_inherit属性, 此处其值为’todo.task’, 说明这个类继承自todo.task模型. 使用了_inherit属性, 那么_name属性不需要了.</p><p>在models下创建__init__.py文件, 内容如下:</p><pre><code># -*- coding: utf-8 -*-#!/usr/bin/env pythonimport todo_task</code></pre><p>在模块根目录下的__init__.py中增加:</p><pre><code>from models import todo_task</code></pre><p>更新模块, 在技术特性中查看模型, 可以看到模型todo.task中增加了的新字段.</p><p><img src="../image/odoo-dev/note2/inherit-model.png" alt="Inherit model" title="Inherit model" width="500"></p><p>##修改已存在的字段</p><p>除了通过继承增加新的字段, 自odoo8.0开始, 还可以修改已存在的字段的属性, 只需简单地添加相同名称的字段并设置字段属性值即可.</p><p>例如, 为了改变name字段的help tooltip, 在todo_task.py中增加一句即可:</p><pre><code>name = fields.Char(help=&quot;What needs to be done?&quot;)</code></pre><p>升级模块, 查看模型, 可以发现修改后的变化.</p><p>##修改模型的方法</p><p>继承还可以应用在业务逻辑上, 例如向模型添加新的方法, 简单地在派生类中定义相应的函数即可.</p><p>###覆盖已有的业务逻辑</p><p>如果希望扩展已存在的业务逻辑, 可以定义一个同名的方法覆盖已有模型中的方法, 并且在这个派生类的方法中还可以使用Python的super关键字来访问其覆盖的父方法.</p><p>示例: 覆盖To-Do应用模型中的do_clean_done方法, 该方法之前的业务逻辑是将所有is_done为True的记录的active字段赋值为False, 原来的代码如下:</p><pre><code>@api.multidef do_clear_done(self):  done_recs = self.search([(&apos;is_done&apos;, &apos;=&apos;, True)])  done_recs.write({&apos;active&apos;: False})  return True</code></pre><p>由于现在希望每个用户只能操作他自己的任务, 所以需要修改do_clean_done的业务逻辑, 修改后的代码如下:</p><pre><code>@api.multidef do_clear_done(self):  domain = [(&apos;is_done&apos;, &apos;=&apos;, True),    &apos;|&apos;, (&apos;user_id&apos;, &apos;=&apos;, self.env.uid),    (&apos;user_id&apos;, &apos;=&apos;, False)]  done_recs = self.search(domain)  done_recs.write({&apos;active&apos;: False})  return True</code></pre><p>发生的修改很简单, 就是将之前的记录过滤条件由仅仅考虑is_done为True改为is_done为True并且user_id为当前用户或user_id为空(False).</p><blockquote><p>odoo中过滤条件使用一个domain来描述, 一个domain用包含多个条件的列表来描述. 每个条件用一个元组来表示, 各个条件之间为’与’的关系. 如果希望表达’或’的关系, 先使用’|’, 在’|’后面的两个元组为’或’关系的两个条件表达式.</p></blockquote><p>这样, 新的方法覆盖了父方法. 更新并升级模块, 还看不出效果, 因为现在所有任务的user_id还都是False.</p><p>###改进已有的业务逻辑</p><p>除了上例中完整的替换某个业务逻辑外, 更多的情况是在不破坏已有业务逻辑并利用它的情况下扩展它, 比如增加预处理或后处理等. 下面的代码在现有的标记任务完成的业务逻辑基础上, 增加一个用户身份的判断:</p><pre><code>@api.onedef do_toggle_done(self):  if self.user_id != self.env.user:    raise Exception(&apos;Only the responsible can do this!&apos;)  else:    return super(TodoTask, self).do_toggle_done()</code></pre><p>使用Python的super()方法可以调用父类中的方法, super相关信息如下:</p><pre><code>Help on class super in module __builtin__:class super(object) |  super(type) -&gt; unbound super object |  super(type, obj) -&gt; bound super object; requires isinstance(obj, type) |  super(type, type2) -&gt; bound super object; requires issubclass(type2, type) |  Typical use to call a cooperative superclass method: |  class C(B): |      def meth(self, arg): |           super(C, self).meth(arg)</code></pre><p>#视图扩展</p><p>##基本视图扩展</p><p>在odoo中, 视图继承的形式如下:</p><pre><code>&lt;record id=&quot;view_form_todo_task_inherited&quot; model=&quot;ir.ui.view&quot;&gt; &lt;field name=&quot;name&quot;&gt;Todo Task form – User extension&lt;/field&gt; &lt;field name=&quot;model&quot;&gt;todo.task&lt;/field&gt; &lt;field name=&quot;inherit_id&quot; ref=&quot;todo_app.view_form_todo_task&quot;/&gt; &lt;field name=&quot;arch&quot; type=&quot;xml&quot;&gt;     &lt;!-- ...match and extend elements here! ... --&gt; &lt;/field&lt;/record&gt;</code></pre><p>inherit_id域指定欲扩展的视图, 它的ref属性用来指定欲扩展视图的外部ID.</p><blockquote><p>外部ID跨模块地唯一标识一个资源, 更多的信息在后续的内容中介绍.</p></blockquote><p>Form, Tree/List以及Search视图都是使用arch的XML结构来定义的, 为了修改这个XML, 需要定位XML元素. 在XML中定位元素最直接的方法是使用XPath表达式, 例如<field name="is_done">这个元素可以使用下面的表达式来定位:</field></p><pre><code>//field[@name]=&apos;is_done&apos;</code></pre><p>在此基础上, 如果希望在<em>is_done</em>之前增加<em>date_deadline</em>字段, 可以使用下面的XML实现:</p><pre><code>&lt;xpath expr=&quot;//field[@name]=&apos;is_done&apos;&quot; position=&quot;before&quot;&gt;     &lt;field name=&quot;date_deadline&quot; /&gt;&lt;/xpath&gt;</code></pre><p>除了XPath外, odoo还提供了更加方便的标记方法, 可以避免使用XPath, 上面的XML可以用简化为下面的内容:</p><pre><code>&lt;field name=&quot;is_done&quot; position=&quot;before&quot;&gt;     &lt;field name=&quot;date_deadline&quot; /&gt;&lt;/field&gt;</code></pre><p>&lt;field&gt;用于确定新字段显示位置的定位, 除此之外, &lt;sheet&gt;, &lt;group&gt;, &lt;div&gt;等都有类似特性.<br>相应地, <em>name</em>属性是经常用到的资源查找方式, 但是也可以使用其他的方式, 例如如果需要查找显示某些特定字符串的label或者CSS类名, 就可以使用<em>string</em>.</p><p><em>position</em>属性用于指定新增元素的位置, 可以有以下的选择:</p><pre><code>1.after: 在匹配元素之后, 与匹配元素具有相同的父元素;2.befor: 在匹配元素之前, 与匹配元素具有相同的父元素;3.inside(默认值): 作为匹配元素内容;4.replace: 替换匹配元素的内容, 如果值为空将会删除匹配的元素;5.attributes: 修改匹配元素的属性.</code></pre><p><em>position</em>属性的这几种可选值大部分容易理解, 只对<em>attributes</em>进行说明. 如果<em>position</em>属性设定为<em>attributes</em>, 那么需要使用&lt;attribute name=”attr-name”&gt;value&lt;/attribute&gt;设定该属性的新值. 例如下面的代码将<em>invisible</em>属性设置为1:</p><pre><code>&lt;field name=&quot;active&quot; position=&quot;attributes&quot;&gt;     &lt;attribute name=&quot;invisible&quot;&gt;1&lt;/attribute&gt;&lt;/field&gt;</code></pre><p>新建一个views文件夹, 在其中新增一个todo_view.xml文件, 将基本视图扩展代码写入其中:</p><pre><code>&lt;?xml version=&quot;1.0&quot;?&gt;&lt;openerp&gt;    &lt;data&gt;        &lt;record id=&quot;view_form_todo_task_inherited&quot; model=&quot;ir.ui.view&quot;&gt;             &lt;field name=&quot;name&quot;&gt;Todo Task form – User extension&lt;/field&gt;             &lt;field name=&quot;model&quot;&gt;todo.task&lt;/field&gt;             &lt;field name=&quot;inherit_id&quot; ref=&quot;todo_app.view_form_todo_task&quot;/&gt;             &lt;field name=&quot;arch&quot; type=&quot;xml&quot;&gt;               &lt;field name=&quot;name&quot; position=&quot;after&quot;&gt;                 &lt;field name=&quot;user_id&quot; /&gt;               &lt;/field&gt;               &lt;field name=&quot;is_done&quot; position=&quot;before&quot;&gt;                 &lt;field name=&quot;date_deadline&quot; /&gt;               &lt;/field&gt;               &lt;field name=&quot;name&quot; position=&quot;attributes&quot;&gt;                 &lt;attribute name=&quot;string&quot;&gt;I have to...&lt;/attribute&gt;               &lt;/field&gt;             &lt;/field&gt;        &lt;/record&gt;    &lt;/data&gt;&lt;/openerp&gt;</code></pre><p>最后, 在__openerp__.py中增加data描述:</p><pre><code>&apos;data&apos;: [&apos;views/todo_view.xml&apos;],</code></pre><p>更新模块, 创建新任务, 发现新增特性已经应用了, 如下图.</p><p><img src="../image/odoo-dev/note2/extern-module-result1.png" alt="Extern module result1" title="Extern module result1" width="500"></p><p>新增一个任务, 然后设定为完成, 切换到管理员角色可以看到包括已完成任务在内的所有任务, 但是现在点击清除已完成任务, 不会将非admin用户的任务的activity设置为False, 满足预期的业务逻辑要求.</p><p>但现在存在一个问题: 如果使用admin用户创建一个任务, 并将Responsible设定为另一个用户(例如aaa), 以aaa登录并不能看到这个由admin分配的任务. 这是由于之前设定了行级访问规则, 其中的下面这一句设定导致不能显示, 这个问题在本文最后一部分解决.</p><pre><code>&lt;field name=&quot;domain_force&quot;&gt;    [(&apos;create_uid&apos;,&apos;=&apos;,user.id)]&lt;/field&gt;</code></pre><p>##List/Tree视图和搜索视图扩展</p><p>向todo_view.xml中增加下面的内容扩展List/Tree视图:</p><pre><code>&lt;record id=&quot;view_tree_todo_task_inherited&quot; model=&quot;ir.ui.view&quot;&gt;    &lt;field name=&quot;name&quot;&gt;Todo Task tree – User extension&lt;/field&gt;    &lt;field name=&quot;model&quot;&gt;todo.task&lt;/field&gt;    &lt;field name=&quot;inherit_id&quot; ref=&quot;todo_app.view_tree_todo_task&quot;/&gt;    &lt;field name=&quot;arch&quot; type=&quot;xml&quot;&gt;        &lt;field name=&quot;name&quot; position=&quot;after&quot;&gt;            &lt;field name=&quot;user_id&quot; /&gt;        &lt;/field&gt;    &lt;/field&gt;&lt;/record&gt;</code></pre><p>向todo_view.xml中增加下面的内容扩展搜索视图:</p><pre><code>&lt;record id=&quot;view_filter_todo_task_inherited&quot; model=&quot;ir.ui.view&quot;&gt;    &lt;field name=&quot;name&quot;&gt;Todo Task tree – User extension&lt;/field&gt;    &lt;field name=&quot;model&quot;&gt;todo.task&lt;/field&gt;    &lt;field name=&quot;inherit_id&quot; ref=&quot;todo_app.view_filter_todo_task&quot;/&gt;    &lt;field name=&quot;arch&quot; type=&quot;xml&quot;&gt;        &lt;field name=&quot;name&quot; position=&quot;after&quot;&gt;             &lt;field name=&quot;user_id&quot; /&gt;             &lt;filter name=&quot;filter_my_tasks&quot; string=&quot;My Tasks&quot;                     domain=&quot;[(&apos;user_id&apos;,&apos;in&apos;,[uid,False])]&quot; /&gt;             &lt;filter name=&quot;filter_not_assigned&quot; string=&quot;Not Assigned&quot;                     domain=&quot;[(&apos;user_id&apos;,&apos;=&apos;,False)]&quot; /&gt;        &lt;/field&gt;    &lt;/field&gt;&lt;/record&gt;</code></pre><p>更新模块, 看效果.</p><p><img src="../image/odoo-dev/note2/extern-view-list.png" alt="Extern view list/search" title="Extern view list/search" width="500"></p><p>#关于扩展模型的更多讨论<br>前面介绍的模型继承是最基本的模型继承方式, 但是还有更强力的混合类继承和代理继承方式. 前者通过继承将某个模型的所有功能特性加入另一个模型中, 后者通过一种透明的方式访问另一个模型中的数据.</p><p>##利用原型继承实现模型特性的拷贝<br>之前使用<em>_inherit</em>属性来扩展一个模型, 定义一个继承<em>todo.task</em>模型的类, 然后向该其添加了一些特性, 这种方法中类的<em>_name</em>属性没有进行设定, 仍然是<em>todo.task</em>.</p><p>如果使用模型的<em>_name</em>属性, 可以创建混合类(mixin class).</p><pre><code>from openerp import modelsclass TodoTask(models.Model):  _name = &apos;todo.task&apos;  _inherit = &apos;mail.thread&apos;</code></pre><p>以上代码<strong>将模型<em>mail.thread</em>的特性拷贝到模型<em>todo.task</em>,</strong> 使<em>todo.task</em>具有了<em>mail.thread</em>的功能特性. <em>mail.thread</em>实现了消息和关注功能, 这个功能是可重用的, 所以可以非常方便地将该特性添加到其他模型上.</p><p>上面提到的<strong><em>拷贝</em></strong>意味着继承的新模型中可以获得被继承的所有字段和方法, 也即被继承模型中的字段也将在继承的新模型对应的数据库表中创建, 并且新模型中也包括未继承前的所有字段. 旧模型和继承后的新模型的数据被分别保存, 不会产生冲突. 下图是一个示意图.</p><p><img src="../image/odoo-dev/note2/social-network-inherit-data.png" alt="Social network messages" title="Social network messages" width="700"></p><p>上图中最上方可以看到一个名为”gogogo”的任务中, 用户aaa发送了两条消息给所有关注该任务的用户. 在数据库的todo_task表(旧模型)中, 只保存旧的字段, 而社交网络数据则保存在mail_message表(新模型中包括)中并与todo_task中的记录关联起来.</p><p>混合模型一般用于抽象模型, 例如<em>mail.thread</em>. 抽象模型与通常的模型类似, 只是抽象模型不在数据库中创建相应的数据表, 所以抽象模型更像是一个模板, 描述重用于普通模型时的字段和业务逻辑, 抽象模型中定义的字段只会在继承自它的普通模型中创建.</p><p>一般使用混合(mixin)继承时, 很少继承自普通模型, 因为这种继承机制将会复制相同的数据结构. 如果必须继承普通模型, 那么应该使用odoo提供你的代理继承机制, 这种机制能够避免数据结构的复制.</p><p>##使用代理继承嵌入模型</p><p>代理继承使用<em>_inherits</em>属性(多一个s)将继承的模型映射到字段.</p><pre><code>from openerp import models, fieldsclass User(models.Model):  _name = &apos;res.users&apos;  _inherits = {&apos;res.partner&apos;: &apos;partner_id&apos;}  partner_id = fields.Many2one(&apos;res.partner&apos;)</code></pre><p>例如以上的代码是标准的Users模型(res.users), 其中嵌入了了一个Partner模型. 当创建一个新的User时, 一个Partner将会同时创建, 并且partner_id中将会保存这个Partner的引用, 这种机制类似OO中的多态概念.</p><p>嵌入的模型(Partner)中的所有字段就像是User的字段一样, 可以以相同的形式直接使用. 例如, partner的name和address字段使用起来与使用User本身的字段一样, 只不过它们的数据实际上保存在关联的Partner模型中. 这样没有数据结构的复制.</p><pre><code>注意, 代理继承中只有字段被继承, 方法是不能继承的!</code></pre><p>#为TODO应用增加社交网络功能</p><p>##基本步骤<br>社交网络模块(技术名称<em>mail</em>)提供的消息板可以在很多页面的底部看到, 也被称作<em>Open Chatter</em>.</p><p>社交网络消息功能通过<em>mail</em>模块的<em>mail.thread</em>模型提供, 这是一个抽象模型, 可以用以下步骤将其添加到其他模型:</p><ul><li>Step 1. 模块中添加对<em>mail</em>模块的依赖;</li><li>Step 2. 模型集成于<em>mail.thread</em>模型;</li><li>Step 3. 向该模型的form视图中添加Followers和Thread widgets;</li><li>Step 4. 为followers设定行级访问规则(可选).</li></ul><p>##Step1 to Step3<br>Step1, todo_task依赖于todo_app, 而todo_app依赖于mail, 所以不需要再做;<br>Step2, 之前我们的模型已经使用<em>_inherit</em>继承了<em>todo.task</em>模型, 因为odoo的<em>_inherit</em>可以接受多个模型继承, 使用列表即可, 所以对todo_task.py进行如下修改:</p><pre><code>由:_inherit = &apos;todo.task&apos;改为:_name = &apos;todo.task&apos;_inherit = [&apos;todo.task&apos;, &apos;mail.thread&apos;]</code></pre><blockquote><p>注意: 创建抽象模型: 派生自models.AbstractModel而不是models.Model即可.</p></blockquote><p>Step3, 使用视图继承将社交网络小组件(widgets)摆放在form底部, 打开todo_view.xml, 添加下面的内容到view_form_todo_task_inherited的arch部分:</p><pre><code>&lt;sheet position=&quot;after&quot;&gt;     &lt;div class=&quot;oe_chatter&quot;&gt;       &lt;field name=&quot;message_follower_ids&quot; widget=&quot;mail_followers&quot; /&gt;       &lt;field name=&quot;message_ids&quot; widget=&quot;mail_thread&quot; /&gt;     &lt;/div&gt;&lt;/sheet&gt;</code></pre><p><em>message_follower_ids</em>和<em>message_ids</em>这两个字段在<em>mail.thread</em>模型中定义.</p><p>更新模块, 看效果. 关注一个任务, 然后登录其他用户打开这个任务, 在下方的社交组件处向关注这个任务的用户发送消息, 其他所有关注该任务的用户都可以收到这条消息了.</p><p><img src="../image/odoo-dev/note2/social-network-msg.png" alt="Social network messages" title="Social network messages" width="500"></p><p>##修改菜单项</p><p>与视图不同, 一般数据记录没有XML的arch结构, 所以不能通过XPath表达式扩展和修改. 想要修改这些数据, 需要使用另一种方式.</p><p>利用&lt;record id=”x” model=”y”&gt;可以完成模型上的插入或更新操作: 如果x不存在, 插入记录; 否则, 对其进行更新.</p><p>例如下面的代码修改菜单项, 将其添加到todo_user模块的todo_view.xml中:</p><pre><code>&lt;!-- Modify menu item --&gt;&lt;record id=&quot;todo_app.menu_todo_task&quot; model=&quot;ir.ui.menu&quot;&gt;    &lt;field name=&quot;name&quot;&gt;My To-Do&lt;/field&gt;&lt;/record&gt;&lt;!-- Action to open To-Do Task list --&gt;&lt;record model=&quot;ir.actions.act_window&quot;   id=&quot;todo_app.action_todo_task&quot;&gt;    &lt;field name=&quot;context&quot;&gt;       {&apos;search_default_filter_my_tasks&apos;: True}    &lt;/field&gt;&lt;/record&gt;</code></pre><p><img src="../image/odoo-dev/note2/social-network-menu-modified.png" alt="Social network menu" title="Social network menu" width="500"></p><p>##Step4, 修改行级访问规则</p><p>在之前的To-Do引用中, 应用了一条行级访问规则, 使得任务只对创建它的用户可见, 但是现在增建了社交网络, 用户之间可以加入协作, 任务需要对其他的用户可见, 例如被分配为任务责任人的让用户或关注任务的用户.</p><p>这里需要做的工作于上一节修改菜单项类似: 重写todo_app.todo_task_user_rule, 修改domain_force字段为新的值(现在是任务创建用户ID与当前用户相同). 但是由于todo_app中设定了&lt;data no_update=”1”&gt;, 所以不能进行写操作, 这里需要做的是: 删除之前的行级访问规则并且创建一个新的.</p><p>为了保持模块组织性, 新建一个security子目录并在其中创建一个名为todo_access_rules.xml的文件, 内容如下:</p><pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;&lt;openerp&gt;   &lt;data noupdate=&quot;1&quot;&gt;      &lt;delete model=&quot;ir.rule&quot; search=&quot;[(&apos;id&apos;, &apos;=&apos;,       ref(&apos;todo_app.todo_task_user_rule&apos;))]&quot; /&gt;      &lt;record id=&quot;todo_task_per_user_rule&quot; model=&quot;ir.rule&quot;&gt;         &lt;field name=&quot;name&quot;&gt;ToDo Tasks only for owner&lt;/field&gt;         &lt;field name=&quot;model_id&quot; ref=&quot;model_todo_task&quot;/&gt;         &lt;field name=&quot;groups&quot;           eval=&quot;[(4, ref(&apos;base.group_user&apos;))]&quot;/&gt;         &lt;field name=&quot;domain_force&quot;&gt;           [&apos;|&apos;,(&apos;user_id&apos;,&apos;in&apos;, [user.id,False]),             (&apos;message_follower_ids&apos;,&apos;in&apos;,[user.partner_id.id])]         &lt;/field&gt;      &lt;/record&gt;   &lt;/data&gt;&lt;/openerp&gt;</code></pre><p>以上代码首先删除已有的todo_task_user_rule记录, 然后创建一条新的记录.需要注意的是对于Followers的处理, 因为followers是partners, 并不是User对象, 所以需要使用user.partner_id.id而不是user.id.</p><pre><code>还可以在开发时将&lt;data noupdate=&quot;1&quot;&gt;改为&lt;data noupdate=&quot;0&quot;&gt;, 正式发布时再改回来.</code></pre><p>最后, 修改__openerp__.py中的data属性:</p><pre><code>&apos;data&apos;: [      &apos;views/todo_view.xml&apos;,      &apos;security/todo_access_rules.xml&apos;,           ],</code></pre><p>更新模块, 看效果吧, 之前提到的由于行级访问控制规则引起的现象没有了, 一切都如计划运行.</p><p><img src="../image/odoo-dev/note2/social-network-final.png" alt="Social network feature" title="Social network feature" width="500"></p>]]></content>
    
    <summary type="html">
    
      Odoo开发入门课程学生资料之二, 利用继承扩展已有应用
    
    </summary>
    
      <category term="OpenERP" scheme="http://www.jeffzhang.cn/categories/OpenERP/"/>
    
    
      <category term="Python" scheme="http://www.jeffzhang.cn/tags/Python/"/>
    
      <category term="OpenERP" scheme="http://www.jeffzhang.cn/tags/OpenERP/"/>
    
      <category term="Odoo" scheme="http://www.jeffzhang.cn/tags/Odoo/"/>
    
  </entry>
  
  <entry>
    <title>某些杀毒软件报yaahp主程序有恶意代码的说明</title>
    <link href="http://www.jeffzhang.cn/yaahp-antivirus-false-positive/"/>
    <id>http://www.jeffzhang.cn/yaahp-antivirus-false-positive/</id>
    <published>2015-08-07T16:00:00.000Z</published>
    <updated>2015-08-08T16:09:29.000Z</updated>
    
    <content type="html"><![CDATA[<p>个别用户在安装yaahp时, 遇到了杀毒软件提示yaahp主程序存在恶意代码并隔离导致yaahp不能正常使用的问题. 在此对这个问题进行说明, 并对yaahp内部的一些可能网络访问操作进行说明.</p><p>###杀毒软件报恶意代码/木马</p><p>如果报的代码是<font color="red">HEUR/QVM10.1.Malware.Gen</font>, 那么可以忽略它(这个是误报, 我已向360申诉, 并且360已经不再误报). 如果杀毒软件已经将yaahp主程序(yaahp.exe)隔离, 取消隔离即可. 当然不放心的话就等一段时间, 等杀毒软件不再误报时再使用.</p><p><img src="../image/antivirus-false.png" alt="fig3" title="yaahp主程序误报申诉"></p><p>这种误报的原因如上图所写, 因为yaahp主程序使用了.NET Reactor进行混淆加密, 但这种混淆加密使用的技术会给被加密的可执行文件加一个外壳, 这种加外壳的技术很多木马也使用, 而杀毒软件有时会将加壳的正常可执行文件误判为存在恶意代码或被病毒/木马感染.</p><p>###yaahp的网络操作</p><p>yaahp目前仅有以下几个访问网络的操作:</p><ul><li>启动后读取服务器上的一个xml文件, 判断是否有软件更新;</li><li>使用”技术支持”功能发送Email;</li><li>专家数据输入软件提交判断矩阵决策数据时发送Email;</li><li>点击主窗口、关于窗口等窗口中的链接, 访问相应的帮助等web页面或打开Email应用程序新建邮件.</li></ul><font color="blue">所以, 我保证yaahp在发布到下载服务器时没有任何恶意代码或广告代码. 但是如果杀毒软件提示病毒/木马/恶意代码也一定要仔细观察确认, 因为无法排除yaahp被其他流传途径感染的可能.</font>]]></content>
    
    <summary type="html">
    
      个别用户反映yaahp主程序被某些杀毒软件提示存在恶意代码并隔离, 本文对此进行说明.
    
    </summary>
    
      <category term="OR" scheme="http://www.jeffzhang.cn/categories/OR/"/>
    
    
      <category term="yaahp" scheme="http://www.jeffzhang.cn/tags/yaahp/"/>
    
  </entry>
  
  <entry>
    <title>yaahp V10.2 发布声明及下载</title>
    <link href="http://www.jeffzhang.cn/yaahp10.2-rellease-note/"/>
    <id>http://www.jeffzhang.cn/yaahp10.2-rellease-note/</id>
    <published>2015-08-04T16:00:00.000Z</published>
    <updated>2015-08-05T09:37:22.000Z</updated>
    
    <content type="html"><![CDATA[<p>###下载</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://d.jeffzhang.cn/index.php?ddownload=5" target="_blank" rel="noopener">点击下载yaahpV10.2安装程序</a></p><p>###Changelogs</p><p>####Version 10.2<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="blue">改进了判断矩阵一致性修正算法精度、提高了大规模层次结构模型下的群决策计算性能及用户体验、改进了灵敏度分析的可用性、 修改了多处bug, 并整合了V10.1发布以来的几个Patch.</font></p><font color="blue">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 自V10.1以来的变化如下:</font><p>1). 增加判断矩阵数据检查功能，对所有的判断矩阵数据进行检查，并给出处理建议;<br>2). 增加不一致判断矩阵修正算法自动选择方式并作为默认设置选项，该方法不需要设定阈值来选择算法，首先使用最小变化方法进行修正，如果修正不成功，再使用最大方向方法修正;<br>3). 增加一致性修正算法精度的参数设定, 分为高/中/低三个等级, 算法精度越高, 计算时间越长;<br>4). 改进增删群决策专家时的性能, 之前版本如果处理很大规模的层次模型时, 增删专家需要等待的时间较长;<br>5). 改进一致性修正的最小改变算法计算参数。最小改变算法中的最大种群规模和最大迭代次数根据判断矩阵维数计算，如果判断矩阵维数特大将会大大增加计算时间。修改为限定最大种群规模最多为500、最大迭代次数最多为1000，正常情况下当判断矩阵维数超过13时才能达到这个参数;<br>6). 修改判断矩阵最小改变一致性修正所使用的PSO算法参数：c1、c2、种群规模和最大进化代数。这些参数的新值是针对一致性修正问题所做的参数优化研究结果，提高了算法的全局收敛性能;<br>7). 修改判断矩阵默认计算方法为幂法;<br>8). 修改专家数据录入工具判断矩阵计算方法为幂法;<br>9). 修改不与AHP结合的模糊综合评价，权向量显示的小数点后位数，由2改为4;<br>10). 修改导入License后点击完成软件自动重新启动;<br>11). 修改如果没有不一致的判断矩阵, 判断矩阵检查结果显示无不一致判断矩阵, 而不是空白的窗口;<br>12). 修改灵敏度分析窗口布局, 改进要素数量较多时的操作体验;<br>13). 修改模型绘制窗口选中不同绘图工具时的鼠标光标;<br>14). 修改更新提示窗口;<br>15). 修正AHP专家调查表中，如果要素文本较多，导致该要素对应的判断矩阵表头要素说明表格中无法完全显示要素文本的Bug;<br>16). 修正直接生成模糊综合评价评测表时，如果不填写权向量说明，不会生成评测表并且没有提示信息的Bug;<br>17). 修正群决策时，如果所有专家状态均为非残缺且满足一致性比例，群决策计算无法开始的bug;<br>18). 修正AHP层次模型规模较大时，生成模糊综合评价评测问卷失败的bug;<br>19). 修正AHP层次模型规模较大时，导出Excel 2003格式的AHP专家调查问卷失败的bug;<br>20). 修正特定情况下，Excel 2003格式AHP专家调查问卷用Excel打开时提示字体数过多的bug;<br>21). 修正模型检查时某些特定情况下不检查连接数的bug;<br>22). 修正无效的专家数据检查窗口帮助按钮链接;<br>23). 修正几处功能列表相关的功能，从而重新启用了一些由于Windows XP下导致软件崩溃而禁用的视觉效果;<br>24). 修改设置判断矩阵计算方法为非幂法时，提示如果有残缺矩阵建议使用幂法;<br>25). 修正判断矩阵数据录入界面提示文本某些情况下被遮盖的Bug;<br>26). 修正计算结果详细信息中，判断矩阵修正前后的数据比较在特定情况下数据错位的Bug;<br>27). 修正判断矩阵一致性计算时，特定情况下出现除零错误的Bug;<br>28). 修正群决策计算前处理时，如果存在空的判断矩阵，统计列表中对应单元格错位的Bug;<br>29). 修正判断矩阵输入页面，决策目标的说明文字总是显示“决策目标”而不是当前判断矩阵对应要素文本的Bug;<br>30). 修正专家姓名修改重复时, 提示错误信息, 但将专家姓名清空的Bug;<br>31). 移除开始页面的留言.</p><p>####Version 10.1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="blue">V10.1提升了性能, 修正了十余处bug. 自V10.0以来的功能变化如下:</font><br>1). 改进AHP群决策数据导入性能；<br>2). 改进AHP群决策专家状态刷新性能；<br>3). 改进群决策计算预处理，改为多线程计算并显示进度条，注意：残缺且不可接受判断矩阵，残缺且设定为自动填充和空判断矩阵 这三种情况预处理很慢；<br>4). 改进模糊综合评价数据导入性能；<br>5). 增加AHP群决策专家搜索定位；<br>6). 增加判断矩阵计算方法设定选项，现在可以在根法、和法和幂法之间进行选择(之前默认判断矩阵计算方法为幂法)；<br>7). 增加多处等待时鼠标忙指针的显示；<br>8). 增加AHP调查表以及模糊综合评价评测表中增加姓名和单位必须填写的提示文字；<br>9). 修改AHP群决策增加专家后显示新增专家并将专家列表滚动到合适位置，同时修正了AHP群决策增加专家后总是选中列表中第一个专家但判断矩阵显示不完整的bug；<br>10). 恢复导出RTF格式AHP调查问卷；<br>11). 改进模糊综合评价模块信息显示， 例如导入数据显示导入总数、左右侧工具栏标题显示评测对象和评测人总数等；<br>12). 非Excel2003格式的AHP调查问卷中，判断矩阵要素说明文字中增加要素说明文本；<br>13). 修正非Excel2003格式的AHP调查问卷中示例说明默认文字；<br>14). 修正判断矩阵输入界面说明文字较长时被遮挡的bug；<br>15). 修正AHP计算结果作为FCE权向量时，由于权向量和检测计算精度太高而导致的提示权向量和不为1导出报告失败的bug；<br>16). 修正导入Excel2003格式专家调查问卷时“1/9”值不能导入的bug；<br>17). 修正模糊综合评价导入大量评测问卷时失败的Bug；<br>18). 修正Windows XP下模糊综合评价模块中，移动鼠标至评测目标和评测人列表项导致模糊综合评价模块崩溃的Bug；<br>19). 关于对话框中无需移动License的类型不再显示删除License按钮。</p><p>####Version 10.0<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="blue">V10.0主要增加了可导入的Excel 2003格式的AHP专家调查表, 以及模糊综合评价功能. 自V9.2以来的功能变化如下:</font><br>1). 增加模糊综合评价，与现有的AHP功能集成；<br>2). 增加导出Excel 2003格式(xls)专家调查表，此格式调查表专家填写后可以直接导入软件中；<br>3). 修改群决策计算结果集结方法：由原来的总排序权重集结改为先集结各判断矩阵排序权重，在此基础上计算得到群决策总排序权重；<br>4). 修改详细计算结果中的权重分布图总是显示准确值，而不是之前的群决策计算结果集结后各中间/准则层要素权重显示平均值；<br>5). 修改判断矩阵排序权重计算算法，由和法改为幂法；<br>6). 修改保存模型图的左上角坐标，由之前的(0,0)改为根据实际模型绘制情况确定，保存的模型图不再显示左上的空白。这个改动影响所有保存模型图的功能，例如文件预览、报告中的模型图等；<br>7). 修改模型编辑时粘贴操作的新要素初始位置，从之前的被拷贝要素向右下偏移10单位改为在鼠标光标位置；<br>8). 修改要素文本编辑，在层次模型页面修改要素文本时，按下回车键即可完成编辑，不能设置多行文本的要素标题，但要素备注任然可以多行文本；<br>9). 修改判断矩阵修正算法，满足一致性比例的判断矩阵，即使标记为自动修正也不对其进行修正。此项修改影响群决策全局补全残缺判断矩阵计算；<br>10). 修正残缺可接受判断矩阵排序权重及一致性比例计算的Bug，统一判断矩阵计算方法；<br>11). 修正存在可接受判断矩阵时导出Excel失败的bug；<br>12). 修正专家调查软件不可输入要素选中后不显示说明文本的bug；<br>13). 修正专家调查软件不可输入要素选中后还能拖动slider的bug；<br>14). 修正特定情况下不存在可接受残缺判断矩阵但在标记判断矩阵自动修正时仍然提示由于存在可接受残缺判断矩阵而不能标记一致性修正眨层次模型页面修改要素文本时，按下回车键即可完成编辑，不能设置多行文本的要素标题Ｃ专家ID修改重复时, 提示”此专家ID已使用，请重柩阵修正算法，满足一致性比例的判断矩阵，正模型编辑页面编辑要素文本时按下Delete键误删正在编辑要素的Bug；<br>18). 修正一些模型编辑快捷键在模型编辑页面编辑文本框激活状态时及非模型编辑页面存在异常行为的Bug；<br>19). 修正直接编辑判断矩阵数据可以输入0的Bug，判断矩阵中存在0值将导致不能完成排序权重计算；<br>20). 移除打印层次模型功能。</p>]]></content>
    
    <summary type="html">
    
      层次分析法软件yaahp Version 10.2发布声明及下载链接。
    
    </summary>
    
      <category term="OR" scheme="http://www.jeffzhang.cn/categories/OR/"/>
    
    
      <category term="yaahp" scheme="http://www.jeffzhang.cn/tags/yaahp/"/>
    
  </entry>
  
  <entry>
    <title>yaahp V10.1.5675 发布声明及下载</title>
    <link href="http://www.jeffzhang.cn/yaahp10.1.5675-release-note/"/>
    <id>http://www.jeffzhang.cn/yaahp10.1.5675-release-note/</id>
    <published>2015-07-14T16:00:00.000Z</published>
    <updated>2015-08-05T03:25:03.000Z</updated>
    
    <content type="html"><![CDATA[<p>##下载</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://d.jeffzhang.cn/index.php?ddownload=5" target="_blank" rel="noopener">点击下载yaahpV10.1.5675安装程序</a></p><p>##Changelogs</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; V10.1.5675是一个Bug修正版本, 与V10.1.5633相比变化如下：</p><ol><li>修正AHP专家调查表中，如果要素文本较多，导致该要素对应的判断矩阵表头要素说明表格中无法完全显示要素<br>文本的Bug；</li><li>修正直接生成模糊综合评价评测表时，如果不填写权向量说明，不会生成评测表并且没有提示信息的Bug；</li><li>修正判断矩阵输入页面，决策目标的说明文字总是显示“决策目标”而不是当前判断矩阵对应要素文本的Bug。</li></ol>]]></content>
    
    <summary type="html">
    
      层次分析法软件yaahp Version 10.1.5675发布声明及下载链接。
    
    </summary>
    
      <category term="OR" scheme="http://www.jeffzhang.cn/categories/OR/"/>
    
    
      <category term="yaahp" scheme="http://www.jeffzhang.cn/tags/yaahp/"/>
    
  </entry>
  
  <entry>
    <title>OmniFocus的任务执行时间统计脚本</title>
    <link href="http://www.jeffzhang.cn/omnifocus-script-Lyubishchev/"/>
    <id>http://www.jeffzhang.cn/omnifocus-script-Lyubishchev/</id>
    <published>2015-07-09T16:00:00.000Z</published>
    <updated>2015-10-06T02:35:39.000Z</updated>
    
    <content type="html"><![CDATA[<p>#问题<br>前段读了《奇特的一生》, 也尝试了一段柳比歇夫时间事件记录法, 感觉不错. 近几年来一直使用OmniFocus作为GTD工具软件, 虽然不算重度使用者, 但也觉得挺顺手离开它会不舒服. 如果能够直接在OmniFocus中记录任务的执行时间, 并统计出任务和项目的执行时间就更好了, 所以有了本文所描述的工作.</p><p>#思路<br>Ben Waldie已经写了一个统计OmniFocus中已完成任务, 并保存到Evernote中的AppleScript脚本(OmniFocus &gt; Prepare Task Completion Report Version 2.0.0), 只需要设计一套在任务中记录执行时间的机制, 然后改进该脚本, 向报告中加入任务执行时间的统计即可.</p><p>#脚本文件列表及作用</p><p><a href="https://github.com/jeffzhangfly/of-timetracking-script" target="_blank" rel="noopener">GitHub</a></p><p>共三个AppleScript脚本文件: <a href="../files/start.scpt">start.scpt</a>, <a href="../files/stop.scpt">stop.scpt</a>和<a href="../files/report.scpt">report.scpt</a>.</p><ul><li><p><a href="../files/start.scpt">start.scpt</a></p><p>  开始一个任务, 此脚本将在该任务的标题前添加”#Ongoing:”, 表示该任务正在进行, 并在该任务的备注(note)属性尾部增加一个记录时间的字符串(或修改这个记录时间的字符串, 如果该任务之前已经开始执行过).</p><p>  如果开始任务时该任务的Defer Until没有填写, 那么将其设置为当前时间, 作为报告中的任务开始时间.</p></li><li><p><a href="../files/stop.scpt">stop.scpt</a></p><p>  停止一个任务, 该脚本可以停止已经开始的任务(标题前有”#Ongoing:”文本的任务, 停止后删除任务标题中的”#Ongoing”文本), 并根据该任务备注(note)属性中的时间记录字符串计算该任务已执行时间.</p></li></ul><pre><code>注意: 任务的执行可以分为多次启动/停止, 一个任务的总执行时间是多次执行时间之和.</code></pre><ul><li><p><a href="../files/report.scpt">report.scpt</a></p><p>  该脚本统计已完成的任务及其执行时间, 并累加各个项目中所有任务的执行时间作为项目执行时间.<br>  对原脚本(OmniFocus &gt; Prepare Task Completion Report Version 2.0.0)所做的修改如下:</p><ol><li>增加一个属性用于设置保存报告的Evernote笔记本名称;</li><li>统计范围中增加Year, 并设置Today为默认统计范围;</li><li>根据start和stop脚本记录在备注(note)中的时间字符串, 在报告中为任务增加执行时间数据;</li><li>统计项目执行时间, 其值为项目下的所有任务执行时间之和;</li><li>将报告的标题改为统计的日期范围;</li><li>如果某项没有设定值, 报告中将不显示该项数据;</li></ol></li></ul><p>#安装方法</p><ul><li>将以上三个脚本文件拷贝到OmniFocus的脚本目录(可以使用OmniFocus的Help-&gt;Open Scripts Folder直接打开);</li><li>OmniFocus工具栏上右键, 在弹出的菜单上选择”Customize Toolbar…”, 将”Script: start”, “Script: stop”和”Script: report”拖到工具栏上.</li></ul><p>#使用说明</p><p>##启动任务</p><p>选中一个任务后, 点击工具栏的”start”脚本图标.</p><p><img src="../image/omnifocus/task-init-stoped2.png" alt="Select a task" title="Select a task" width="700"></p><p>任务启动后, 会弹出任务开始执行通知.</p><p><img src="../image/omnifocus/notify-started.png" alt="Start notification" title="Start notification" width="400"></p><p>点击”start”脚本后, 任务标题前将添加”#Ongoing:”字符串, 并且任务备注(note)尾部将添加时间字符串.</p><p><img src="../image/omnifocus/task-started-inbox2.png" alt="Task started" title="Task started" width="700"></p><p>##停止任务</p><p>在任务执行一段时间需要停止时, 例如被打断或暂时休息或完成了该任务, 点击工具栏的”stop”脚本图标. 停止后的任务会记录本次任务执行的时间, 可以在备注(note)尾部时间字符串中看到变化.</p><p><img src="../image/omnifocus/task-stoped2.png" alt="Task stoped" title="Task stoped" width="700"></p><p>任务停止后, 会弹出任务停止执行通知.</p><p><img src="../image/omnifocus/notify-stoped.png" alt="Stop notification" title="Stop notification" width="400"></p><p>##生成报告</p><p>任何时候点击”report”脚本图标, 将会首先弹出报告时间范围选择窗口, 如下图所示.</p><p><img src="../image/omnifocus/report-timerscope.png" alt="Report date scope" title="Report date scope" width="300"></p><p>选择一个时间范围后, 点击”OK”按钮, 将会生成统计报告并在Evernote的”Omnifocus_reports”笔记本中创建一个统计报告, 如下图. 笔记本名称”Omnifocus_reports”可以在脚本中修改.</p><p><img src="../image/omnifocus/report-content.png" alt="Report" title="Report" width="500"></p><p>报告中, 每个任务如果使用了”start”和”stop”脚本记录任务执行时间, 将会显示执行时间, 并会统计一个项目的总执行时间, 如上图中下方的蓝色文本.</p><p>#注意事项</p><ul><li>使用”start”和”stop”脚本记录任务执行时间后, 将会在该任务的备注(note)的尾部添加时间字符串, 不要删除它或手动修改它, 并且一定不要将备注文本放在时间字符串之后, 否则会影响执行时间的统计. 统计报告中的备注文本会自动忽略掉时间字符串;</li><li>任务开始后会在任务标题头部增加一个标记字符串”#Ongoing:”, 任务停止后自动删除, 不要手动修改它.</li></ul><p>#不足之处</p><p>因为没有OmniFocus额外的属性可以使用, 只好通过向任务标题和备注添加额外字符串的方式来达到统计执行时间的目的, 这种方式需要遵守一些额外的使用规则, 会影响使用体验.</p><p>如果OmniFocus能够增加记录任务执行时间的功能就好了, 不过应该是希望不大. 或者做个GTD软件开发计划, 把我想要的功能都做出来……</p>]]></content>
    
    <summary type="html">
    
      三个AppleScript脚本, 可以在OmniFocus中实现自动记录任务执行时间并统计报告到Evernote.
    
    </summary>
    
      <category term="GTD" scheme="http://www.jeffzhang.cn/categories/GTD/"/>
    
    
      <category term="OmniFocus" scheme="http://www.jeffzhang.cn/tags/OmniFocus/"/>
    
  </entry>
  
  <entry>
    <title>Odoo Workflow</title>
    <link href="http://www.jeffzhang.cn/Odoo-Workflow-Notes/"/>
    <id>http://www.jeffzhang.cn/Odoo-Workflow-Notes/</id>
    <published>2015-06-30T16:00:00.000Z</published>
    <updated>2015-08-09T03:06:34.000Z</updated>
    
    <content type="html"><![CDATA[<p>##概述</p><p>在Odoo中工作流用于管理与某个模型的记录相关的一系列”需要做的事情”. 工作流为组织各种记录相关的任务提供了一种高层次的方法.</p><p>一个工作流是一个有向图, 其中节点称为”activities(活动)”, 边叫做”transitions(转移)”:</p><ul><li>活动定义了在Odoo服务器内部完成的某工作, 例如改变某些记录的状态或者发送Email;</li><li>转移控制工作流程如何从一个活动到另一个活动.</li></ul><p>在工作流的定义中, 还可以附加条件, 信号, 触发转移的触发器, 所以工作流的行为依赖于用户的动作(例如点击按钮), 或记录值的变化, 或任意的Python代码.</p><p>Odoo的工作流系统提供了以下功能:</p><ul><li>描述记录或文档随时间的演化过程;</li><li>基于多种灵活条件的自动动作(automatic actions);</li><li>公司规则及验证步骤的管理;</li><li>对象间交互的管理;</li><li>文档流转整个生命周期的可视化表示.</li></ul><p>例如, 一个简单的订单可能的流程如下图:</p><p><img src="../image/odoo-dev/order-0.png" alt="订单开始于草稿状态, 经过用户确认, 最终发货(Closed)或取消" title="订单开始于草稿状态, 经过用户确认, 最终发货(Closed)或取消" width="300"></p><p>再看一个复杂些的例子, 有时销售员可以对订单给出折扣, 但是如果要求折扣超过15%则需要上级领导进行确认才能继续, 那么可以在Odoo中直接修改工作流而不需要对Python或XML文件进行编辑:</p><p><img src="../image/odoo-dev/order-1.png" alt="根据订单草稿中的折扣不同, 确认(Validation)这个活动会在条件满足时自动触发, 给相关的管理人员发送一个确认请求" title="根据订单草稿中的折扣不同, 确认(Validation)这个活动会在条件满足时自动触发, 给相关的上级管理人员发送一个确认请求" width="300"></p><pre><code>注意: 相应地, 上级管理人员的订单视图上需要增加一个&quot;接受折扣&quot;的按钮.</code></pre><p>##基础</p><p>可以使用数据文件直接定义工作流, 例如下面的XML代码片段定义了一个两个活动的工作流.</p><pre><code>&lt;record id=&quot;test_workflow&quot; model=&quot;workflow&quot;&gt;    &lt;field name=&quot;name&quot;&gt;test.workflow&lt;/field&gt;    &lt;field name=&quot;osv&quot;&gt;test.workflow.model&lt;/field&gt;    &lt;field name=&quot;on_create&quot;&gt;True&lt;/field&gt;&lt;/record&gt;&lt;record id=&quot;activity_a&quot; model=&quot;workflow.activity&quot;&gt;    &lt;field name=&quot;wkf_id&quot; ref=&quot;test_workflow&quot;/&gt;    &lt;field name=&quot;flow_start&quot;&gt;True&lt;/field&gt;    &lt;field name=&quot;name&quot;&gt;a&lt;/field&gt;    &lt;field name=&quot;kind&quot;&gt;function&lt;/field&gt;    &lt;field name=&quot;action&quot;&gt;print_a()&lt;/field&gt;&lt;/record&gt;&lt;record id=&quot;activity_b&quot; model=&quot;workflow.activity&quot;&gt;    &lt;field name=&quot;wkf_id&quot; ref=&quot;test_workflow&quot;/&gt;    &lt;field name=&quot;flow_stop&quot;&gt;True&lt;/field&gt;    &lt;field name=&quot;name&quot;&gt;b&lt;/field&gt;    &lt;field name=&quot;kind&quot;&gt;function&lt;/field&gt;    &lt;field name=&quot;action&quot;&gt;print_b()&lt;/field&gt;&lt;/record&gt;&lt;record id=&quot;trans_a_b&quot; model=&quot;workflow.transition&quot;&gt;    &lt;field name=&quot;act_from&quot; ref=&quot;activity_a&quot;/&gt;    &lt;field name=&quot;act_to&quot; ref=&quot;activity_b&quot;/&gt;&lt;/record&gt;</code></pre><p>工作流定义总是关于一个特定模型的, 该模型在XML配置中用osv属性指定, 活动和转移中指定的方法应该在该模型中定义, 调用时将会到这个模型中查看.</p><p>例如上面的XML中, 创建了一个名为”test_workflow”的工作流, 它由两个活动中组成, 一个名为”a”, 一个名为”b”, 还有一个转移, 从”a”到”b”.</p><p>第一个活动的”flow_start”属性为True, Odoo在实例化工作流后据此知道工作流从哪开始. 因为第一个record段中的”on_create”属性设置为True, 所以该工作流在每次创建新的记录后都会被实例化; 如果该属性为False, 工作流就不会在创建记录时被实例化, 必须用其他方式实例化, 例如从模块的Python代码中实例化.</p><p>当工作流实例化后, 它处于活动”a”, 这个活动的类型属性是”function”, 动作(action)属性为”print_a()”, 表示需要调用模型test.workflow中的”print_a()”方法.</p><p>“a”和”b”之间的转移没有任何条件, 意味着当”a”处理完成后立刻转移到活动”b”.</p><p>##活动(Activities)</p><p>转移可以看做是工作流的控制结构, 活动是从改变记录状态到发送Email等任何事情发生的地方.</p><p>有多种不同类型的活动: Dummy, Function, Subflow和Stop all, 不同类型的活动在执行过程中有不同的作用.</p><p>###Flow start and flow stop</p><p><em>flow_start</em>是一个布尔型属性, 用于指定工作流实例化时该活动是否被执行. 一个工作流中可以有多个活动的<em>flow_start</em>属性被标记为True, 这种情况下, 实例化工作流时Odoo将处理所有这些活动, 并评估它们的outgoing转移.</p><p><em>flow_stop</em>属性也是一个布尔型属性, 用于确定某个活动是否停止工作流实例. 当一个工作流的所有活动的<em>flow_stop</em>属性均为True时, 该工作流就完成了.</p><p>###Subflow</p><p>一个活动可以嵌入一个完整的工作流, 称为子工作流, 被嵌入的工作流称为父工作流. 某个活动用<em>subflow_id</em>属性来指明其对应的子工作流.</p><p>如果一个活动是子工作流类型的, 那么只有当这个子工作流完成后, 才能被认为该活动完成了(并准备评估其outgoing转移).</p><p>###从子工作流发送信号</p><p>当一个工作流中存在嵌入的子工作流时, 子工作流可以从它的活动中(例如某个函数调用)发送信号给它的父工作流, 通过在<em>signal_send</em>属性中给定一个信号名称即可. Odoo processes those activities by sending the value of signal_send prefixed by “subflow.” to the parent workflow instance.</p><p>也就是说, 可以在子工作流执行时与扶工作流进行反应和转移.</p><p>###Server actions</p><p>一个活动可以通过在<em>action_id</em>属性中指定”Server Action”的ID来运行它.</p><p>###Python action</p><p>活动可以执行一些Python代码, <em>action</em>属性中指定即可.</p><p>###Split mode</p><p>当一个活动处理完成后, Odoo评估其转移以到达工作流中的下一个活动. 但是如果一个活动有多个转移, Odoo必须决定下一/几个活动是什么.</p><p><img src="../image/odoo-dev/wf-split.png" alt="Split mode" title="Split mode" width="200"></p><p>由<em>split_mode</em>属性控制:</p><p><strong>XOR(default)</strong></p><pre><code>默认情况下, Odoo将使用第一个满足条件的转移(in sequence order), 并忽略其他所有的转移.</code></pre><p><strong>OR</strong></p><pre><code>OR模式下, 所有满足某个条件的转移将会同时发生, 无效的转移将忽略, 即使它随后变得有效也忽略.</code></pre><p><strong>AND</strong></p><pre><code>AND模式下, Odoo将会等待所有转移都满足后进行所有的转移.</code></pre><p>###Join mode</p><p>类似于outgoing转移条件可以组合使用, incoming转移也可以组合在一起使用.</p><p><img src="../image/odoo-dev/wf-join.png" alt="Join mode" title="Join mode" width="200"></p><p>由<em>join_mode</em>属性控制:</p><p><strong>XOR(default)</strong></p><pre><code>任何incoming转移enables该活动并开始其处理过程.</code></pre><p><strong>AND</strong></p><pre><code>只有所有incoming转移都到达才能enable该活动并开始其处理过程.</code></pre><p>###Kinds</p><p>活动的类型定义这个活动可以执行任务的类型, 有以下几种:</p><p><strong>Dummy(dummy, default)</strong></p><pre><code>不作任何事情, 或调用一个server action. 经常用作转移的分发或收集hubs.</code></pre><p><strong>Function(function)</strong></p><pre><code>执行一些Python代码, 执行一个server action.</code></pre><p><strong>Stop all(stopall)</strong></p><pre><code>完全停止工作流实例并标记其为完成.</code></pre><p><strong>Subflow(subflow)</strong></p><pre><code>启动另一个工作流, 一旦子工作流完成该活动即处理完成.默认情况下子工作流是与父工作流相同的记录上实例化, 但是也可以改变, 通过提供一段Python代码, 返回记录ID(但必须是相同的模型)即可.</code></pre><p>##转移</p><p>转移提供控制结构来编排工作流. 当一个活动完成后, 工作流引擎尝试利用转移离开已完成的活动, 到达下一个活动. 上一节的转移是最简单的形式, 前序活动完成后直接执行后续的活动.</p><p>除了一下完成所有活动外, 还可以在转移中等待, 并且在某些条件满足后结束等待继续流程, 这些待确认的条件可以是条件(conditions), 信号(signals)或触发器(triggers).</p><p>###条件<br>当一个活动完成后, 它的outgoing转移会检查确认该工作流是否可以继续流转到下一个活动. 如果只定义了条件(没有信号或触发器定义), 那么Odoo将会评估这个条件, 并且如果评估的结果是True, 那么工作流通过这个转移; 如果不满足条件, 那么Odoo将会在每次相关数据记录发生变化后再次评估该条件, 或者也可以通过一个明确的方法调用进行条件评估.</p><p>###信号<br>除了条件外, 一个转移也可以指定一个信号名, 这种情况下即使条件评估为True, 这个转移将不会发生, 而会保持阻塞状态直到被唤醒.</p><p>为了用某个信号名唤醒一个转移, 信号必须被发送到工作流实例. 一般的做法是按下界面上的某个按钮发送一个信号过去.</p><pre><code>注意, 在信号发送到工作流实例后, 还需要评估条件是否为True.</code></pre>]]></content>
    
    <summary type="html">
    
      Odoo官方文档工作流部分简单翻译/笔记
    
    </summary>
    
      <category term="OpenERP" scheme="http://www.jeffzhang.cn/categories/OpenERP/"/>
    
    
      <category term="Python" scheme="http://www.jeffzhang.cn/tags/Python/"/>
    
      <category term="OpenERP" scheme="http://www.jeffzhang.cn/tags/OpenERP/"/>
    
      <category term="Odoo" scheme="http://www.jeffzhang.cn/tags/Odoo/"/>
    
  </entry>
  
  <entry>
    <title>Odoo开发--Getting started with Odoo development</title>
    <link href="http://www.jeffzhang.cn/Odoo-Notes-1/"/>
    <id>http://www.jeffzhang.cn/Odoo-Notes-1/</id>
    <published>2015-06-24T16:00:00.000Z</published>
    <updated>2015-09-19T06:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>Odoo入门课程的学生资料, 参考书点击<a href="http://www.amazon.com/Odoo-Development-Essentials-Daniel-Reis/dp/1784392790/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1439075703&amp;sr=1-1&amp;keywords=odoo+development+essentials" target="_blank" rel="noopener">这里</a>.</p><p>#1. 起步</p><p>##1.1 安装</p><p>##1.2 Odoo版本号说明</p><p>Odoo各个主版本号之间数据库不兼容;<br>Odoo主版本号保证”API stable”, 即同一个主版本中model的数据结构以及view的各个要素保持不变.</p><p>##1.3 Configuration</p><ul><li>查看更多server选项:    ./odoo.py  —help</li><li>保存配置文件: ./odoo.py —save —stop-after-init</li><li>使用特定配置文件: ./odoo.py —conf=<filepath></filepath></li><li>改变服务器监听端口: ./odoo.py —xmlrpc-port=8080</li><li>log输出等级: ./odoo.py —log-level=[debug | debug_sql | debug_rpc]</li><li>log输出位置: ./odoo.py —logfile=<filepath>, 默认是stdout</filepath></li><li>debug模式: ./odoo.py —debug, 当异常发生, 将启动Python调试器(pdb)</li></ul><p>##1.4 启用技术特性和开发者模式</p><p>#2 开发一个Odoo应用</p><p>##2.1 概述</p><p>Odoo基础上的开发就是开发我们自己的模块, 本章及第三章开发一个TODO应用, 具有添加任务, 显示任务, 标记任务为完成, 清除已完成任务功能. 在此过程中可以学习到Odoo的MVC方面的知识:</p><ul><li>model: 定义数据的结构. model层使用Python对象定义, 数据保存在PostgreSQL数据库中, 数据映射由Odoo自动管理.</li><li>view: 描述用户界面. views使用XML定义, 用于web client框架根据数据生成的HTML视图.</li><li>controller: 支持商业逻辑. web客户端视图与服务器ORM交互完成数据持久化操作, 这些操作可以是简单的读写删除, 也可以是调用ORM Python对象中定义的复杂的业务逻辑.</li></ul><p>##2.2 应用(applications)和模块(modules)</p><p>一个模块可以为Odoo增加特性或修改Odoo的特性, 一个模块保存在一个包含模块描述文件(__openerp__.py)的目录中. 模块是Odoo应用的组成部分, 与模块不同, 应用提供某个功能, 应用中的某个模块提供一个核心特性, 应用中的其他模块为这个中心模块添加其他特性. 例如人力资源管理, 账号模块作为核心, 其他很多模块为其添加特性, 构成了HR应用</p><p>##2.3 修改和扩展模块<br>除了创建新的模块, 还可以在已有模块的基础上进行扩展, Odoo的”继承(inheritance)”机制允许自定义模块扩展已有的模块, 继承可以发生在几乎任何层次的数据模型, 业务逻辑以及用户界面层.</p><p>##2.4 创建一个新的模块<br>从Odoo 8.0开始, __openerp__.py的description内容可以利用一个放在模块根目录下的README.rst或README.md文件.</p><p>创建两个文件: __init__.py和__openerp__.py, 在__openerp__.py中添加以下内容:</p><pre><code>{   &apos;name&apos;: &apos;To-Do Application&apos;,   &apos;description&apos;: &quot;&quot;&quot;   This module adds TODO to mail.   &quot;&quot;&quot;,   &apos;author&apos;: &apos;Jeff Zhang&apos;,   &apos;depends&apos;: [&apos;mail&apos;],   &apos;application&apos;: True,}</code></pre><p>其中’application’为true表示该模块设定为一个应用.</p><p>##2.5 增加模块路径</p><p>为了让Odoo能够找到我们新创建模块, 需要将该模块所在的路径添加到Odoo addons path中, 除了直接编辑.openerp_sever.rc文件外, 还可以使用以下的方法:</p><pre><code>$ cd ~/odoo-dev$ odoo/odoo.py -d v8dev —addons-path=“custom-addons, odoo/addons” —save</code></pre><ul><li>-d v8dev参数指定所使用的数据库</li><li>—save说明将这次设定的模块路径保存下来, 以后只需要odoo.py启动即可, 不再需要加—addons-path参数.</li></ul><p>##2.6 安装新模块</p><p>选择setting=&gt;Local Modules, 因为TODO设定为一个应用, 所以不需要删除应用的搜索项, 直接输入todo, 就可以搜索到.</p><p><img src="../image/odoo-dev/search-todoapp.png" alt="Install new modules" title="Install new modules" width="500"></p><p> ##2.7 升级模块</p><p>###normal</p><p>模块开发是一个迭代的过程, 需要经常查看源码修改的效果. 这个工作可以通过升级来做到, 在Local Modules中找到相应的模块, 如果它已安装, 将会在该模块的详细信息中看到Upgrade按钮.</p><p>但是, 如果仅仅改变了Python代码, 那么点击Upgrade按钮可能不会有任何效果, 这种情况下, 应当重启应用服务器. <font color="blue">如果Python代码和数据文件都有了改变</font>, 应该两步都做, <font color="blue">也即更新模块并重启应用服务器</font>. (数据文件的变化说明界面, 权限, 菜单, 动作等有变化, 需要更新DB中相应的数据)</p><p>###better way</p><p>更方便的方法: 关闭应用服务器, 并在重启服务进程时通过参数指明更新数据库.</p><p>为了在启动服务进程时为jeff数据库更新todo_app模块, 使用以下命令启动:</p><pre><code>$ ./odoo.py -d jeff -u todo_app</code></pre><p>启动时, 在终端可以看到以下信息:</p><pre><code>2015-06-27 02:45:01,364 2367 INFO jeff openerp.modules.module: module todo_app: creating or updating database tables</code></pre><p>-u 选项要求-d 选项, 并且接受用逗号分隔的欲更新模块列表, 例如:</p><pre><code>-u todo_app, mail</code></pre><p>###更新模块列表和卸载模块</p><p>更新模块列表及卸载模块操作无法在命令行完成, 必须通过web界面.</p><p>##2.8 创建模型</p><p>模型用一个派生自Odoo模板类的Python类实现, 它们被转换为一个数据库对象, Odoo在安装和升级模块时自动处理这些事情.</p><font color="blue">将所有模型的Python模块放在”models”子目录中是一个好的实践.</font><p><em>Step 1</em> 创建一个models子目录, 并在该目录下添加一个__init__.py文件, 内容如下:</p><pre><code># -*- coding: utf-8 -*-#!/usr/bin/env pythonimport todo_model</code></pre><p><em>Step 2</em> 在models目录下创建一个todo_model.py文件, 内容如下:</p><pre><code># -*- coding: utf-8 -*-#!/usr/bin/env pythonfrom openerp import models, fieldsclass TodoTask(models.Model):  _name = &apos;todo.task&apos;  name = fields.Char(&apos;Description&apos;, required=True)  is_done = fields.Boolean(&apos;Done?&apos;)  active = fields.Boolean(&apos;Active?&apos;, default=True)</code></pre><p><em>Step 3</em> 在模块根目录(todo_app)下的__init__.py中, 添加以下内容:</p><pre><code>from models import todo_model</code></pre><p>模型编写完毕, Ctrl+C结束服务进程然后使用以下命令重新启动:</p><pre><code>$ ./odoo.py -d jeff -u todo_app</code></pre><p>然后在Local Modules中找到todo app, 并点击升级按钮. 在技术=&gt;数据库结构=&gt;模型中找到todo.task模型, 点击它查看详细信息.</p><p><img src="../image/odoo-dev/model-todo-task.png" alt="Model" title="Model" width="500"></p><p>从上图可以看到, 除了模型中设定的几个字段外, 还有几个Odoo自动添加的字段.</p><pre><code>注意: 其他Odoo模块用_name(例如本例的todo.task)来区分不同模型, 而不是Python类名.</code></pre><p>##2.9 添加菜单</p><p>上一节的模型用于在数据库中保存数据, 现在将它显示在用户界面上.  只需要加一个菜单, 打开todo.task即可, 其他工作Odoo自动完成.</p><p>添加菜单需要在一个XML格式的data file中完成, 与模型文件类似, <font color="blue">所有视图定义相关的文件放在”views”子目录中是一个好的实践</font>.</p><p><em>Step 1</em> 在模块根目录下新建一个views目录, 在其中新建一个名为”todo_view.xml”的文件, 内容如下:</p><pre><code>&lt;?xml version=&quot;1.0&quot;?&gt;&lt;openerp&gt;    &lt;data&gt;        &lt;!-- Action to open todo task list --&gt;        &lt;act_window id=&quot;action_todo_task&quot;                    name=&quot;To-do Task&quot;                    res_model=&quot;todo.task&quot;                    view_mode=&quot;tree,form&quot; /&gt;        &lt;!-- Menu item to open todo task list --&gt;        &lt;menuitem id=&quot;menu_todo_task&quot;                  name=&quot;To-Do Tasks&quot;                  parent=&quot;mail.mail_feeds&quot;                  sequence=&quot;20&quot;                  action=&quot;action_todo_task&quot; /&gt;    &lt;/data&gt;&lt;/openerp&gt;</code></pre><p><em>Step 2</em> 在模块根目录下的__openerp__.py中的字典中增加视图的data files:</p><pre><code>&apos;data&apos;: [&apos;views/todo_view.xml&apos;],</code></pre><p>然后, 在Local Modules中找到todo模块并升级它, 然后就可以在”消息”下看到”To-Do Tasks”菜单了.</p><p><img src="../image/odoo-dev/todo-menu1.png" alt="Menu" title="Menu" width="500"></p><p>在本节, 只是定义了菜单入口, 并没有定义如何显示数据, 但是Odoo自动给出了默认的添加编辑等视图.</p><p>###错误处理</p><p>如果升级是发生错误, 屏蔽上一次的修改, 例如本节内容如果出错, 注释掉__openerp__.py中’data’的todo_veiw部分设定, 确认错误发生位置, 然后一步步进行检查处理.</p><p>##2.10 创建视图 - form, tree及search</p><p>前一节没有定义任何视图, 这种情况下Odoo为数据自动生成基本的视图, 为了或得更合适的视图, 就需要定制模块的视图.</p><p>Odoo支持多种类型的视图, 其中三种最主要的是: list(也称为tree), form和search视图.</p><p>所有的视图保存在数据库中的<font color="blue">ir.model.view</font>模型中. 为了在模块中添加视图, 需要XML格式的在data file中定义<record>元素, 在其中给出描述视图的定义, 当模型安装时, data file中的视图定义会载入到数据库中.</record></p><p>###创建form视图</p><p>编辑todo_view.xml文件, 在<data>节内增加<record>节, 内容如下:</record></data></p><pre><code>&lt;?xml version=&quot;1.0&quot;?&gt;&lt;openerp&gt;    &lt;data&gt;      &lt;record id=&quot;view_form_todo_task&quot; model=&quot;ir.ui.view&quot;&gt;          &lt;field name=&quot;name&quot;&gt;To-do task Form&lt;/field&gt;          &lt;field name=&quot;model&quot;&gt;todo.task&lt;/field&gt;          &lt;field name=&quot;arch&quot; type=&quot;xml&quot;&gt;              &lt;form string=&quot;To-do Task&quot;&gt;                  &lt;field name=&quot;name&quot;/&gt;                  &lt;field name=&quot;is_done&quot;/&gt;                  &lt;field name=&quot;active&quot; readonly=&quot;1&quot;/&gt;              &lt;/form&gt;          &lt;/field&gt;      &lt;/record&gt;      &lt;!-- actions and menus ...... --&gt;    &lt;/data&gt;&lt;/openerp&gt;</code></pre><p>这段代码在ir.ui.view中添加一条标识为<em>view_form_todo_task</em>的记录. 这段代码定义了以下信息:</p><ul><li>这个视图对应的模型是todo.task;</li><li>这个视图被命名为<em>To-do Task Form</em>, 这个名称不需要唯一, 仅是一个信息, 但是该名称应当是从字面容易理解其功能的.</li></ul><p>最重要的属性是<font color="blue">arch</font>, 其中给出了视图的定义, 上面的一段xml代码首先说明这个视图是一个form, 包括三个字段, 并且设定<em>active</em>字段为只读.</p><p><img src="../image/odoo-dev/view-defined1.png" alt="View1" title="View1" width="500"></p><p>odoo 8.0版这样定以后, view中没有字段的说明, 但可以看到三个字段中最后一个active字段已是只读.</p><p>###格式化视图</p><p>上面创建了一个基本的视图, 与odoo自动生成的相比没什么区别.</p><p>下面一步步的开始定制, 首先增加header区域并将各个字段分组显示. 将form部分修改为以下内容:</p><pre><code>&lt;form string=&quot;To-do Task&quot;&gt;    &lt;header&gt;        &lt;!-- buttons go here --&gt;    &lt;/header&gt;    &lt;sheet&gt;        &lt;group name=&quot;group_top&quot;&gt;            &lt;group name=&quot;group_left&quot;&gt;                &lt;field name=&quot;name&quot;/&gt;            &lt;/group&gt;            &lt;group name=&quot;group_right&quot;&gt;                &lt;field name=&quot;is_done&quot;/&gt;                &lt;field name=&quot;active&quot; readonly=&quot;1&quot;/&gt;            &lt;/group&gt;        &lt;/group&gt;    &lt;/sheet&gt;&lt;/form&gt;</code></pre><p><img src="../image/odoo-dev/view-defined2.png" alt="View2" title="View2" width="500"></p><p>###添加动作按钮</p><p>form中可以定义按钮用于执行一些动作, 例如触发工作流动作, 执行窗口动作(例如打开另一个form, 运行model中的Python函数等).</p><p>按钮可以放在form中的任何地方, 但是放在<header>节中更好. 在header节中增加以下内容:</header></p><pre><code>&lt;form string=&quot;To-do Task&quot;&gt;    &lt;header&gt;        &lt;button name=&quot;do_toggle_done&quot; type=&quot;object&quot;                string=&quot;Toggle Done&quot; class=&quot;oe_highlight&quot;/&gt;        &lt;button name=&quot;do_clear_done&quot; type=&quot;object&quot;                string=&quot;Clear All Done&quot;/&gt;    &lt;/header&gt;    &lt;sheet&gt;      &lt;!-- ...... --&gt;    &lt;/sheet&gt;&lt;/form&gt;</code></pre><p>按钮基本的属性有:</p><ul><li>name: 动作的标识, 对应一个定义在model中的Python函数;</li><li>string: 按钮上的文本;</li><li>class: 制定一个CSS类, 类似HTML中应用CSS.</li></ul><p><img src="../image/odoo-dev/view-defined3.png" alt="View3" title="View3" width="500"></p><p>###完成后的form视图</p><p>完成form试图定义后的todo_view.xml如下:</p><pre><code>&lt;?xml version=&quot;1.0&quot;?&gt;&lt;openerp&gt;    &lt;data&gt;        &lt;record id=&quot;view_form_todo_task&quot; model=&quot;ir.ui.view&quot;&gt;            &lt;field name=&quot;name&quot;&gt;To-do task Form&lt;/field&gt;            &lt;field name=&quot;model&quot;&gt;todo.task&lt;/field&gt;            &lt;field name=&quot;arch&quot; type=&quot;xml&quot;&gt;                &lt;form string=&quot;To-do Task&quot;&gt;                    &lt;header&gt;                        &lt;button name=&quot;do_toggle_done&quot; type=&quot;object&quot;                                string=&quot;Toggle Done&quot; class=&quot;oe_highlight&quot;/&gt;                        &lt;button name=&quot;do_clear_done&quot; type=&quot;object&quot;                                string=&quot;Clear All Done&quot;/&gt;                    &lt;/header&gt;                    &lt;sheet&gt;                        &lt;group name=&quot;group_top&quot;&gt;                            &lt;group name=&quot;group_left&quot;&gt;                                &lt;field name=&quot;name&quot;/&gt;                            &lt;/group&gt;                            &lt;group name=&quot;group_right&quot;&gt;                                &lt;field name=&quot;is_done&quot;/&gt;                                &lt;field name=&quot;active&quot; readonly=&quot;1&quot;/&gt;                            &lt;/group&gt;                        &lt;/group&gt;                    &lt;/sheet&gt;                &lt;/form&gt;            &lt;/field&gt;        &lt;/record&gt;        &lt;!-- Action to open todo task list --&gt;        &lt;act_window id=&quot;action_todo_task&quot;                    name=&quot;To-do Task&quot;                    res_model=&quot;todo.task&quot;                    view_mode=&quot;tree,form&quot; /&gt;        &lt;!-- Menu item to open todo task list --&gt;        &lt;menuitem id=&quot;menu_todo_task&quot;                  name=&quot;To-Do Tasks&quot;                  parent=&quot;mail.mail_feeds&quot;                  sequence=&quot;20&quot;                  action=&quot;action_todo_task&quot; /&gt;    &lt;/data&gt;&lt;/openerp&gt;</code></pre><p>##2.11 创建list和search视图</p><p>当以列表(list)形式查看model时, 将会用到<tree>视图. 树视图可以显示具有层级关系的各行记录, 但大多情况下显示单个的行记录.</tree></p><p>向todo_veiw.xlm中增加以下内容:</p><pre><code>&lt;record id=&quot;view_tree_todo_task&quot; model=&quot;ir.ui.view&quot;&gt;    &lt;field name=&quot;name&quot;&gt;To-do Task Tree&lt;/field&gt;    &lt;field name=&quot;model&quot;&gt;todo.task&lt;/field&gt;    &lt;field name=&quot;arch&quot; type=&quot;xml&quot;&gt;        &lt;tree colors=&quot;gray:is_done==True&quot;&gt;            &lt;field name=&quot;name&quot;/&gt;            &lt;field name=&quot;is_done&quot;/&gt;        &lt;/tree&gt;    &lt;/field&gt;&lt;/record&gt;</code></pre><p>以上代码定义了具有两列(name和is_done)的列表, 并且使用<em>is_done==True</em>来使完成的任务行显示为灰色.</p><p><img src="../image/odoo-dev/view-defined-list.png" alt="List view" title="List view" width="500"></p><p>在列表的右上方显示了一个搜索框, 在<search>节可以定义默认搜索字段及预定义的过滤器.</search></p><p>向todo_veiw.xml中添加如下内容:</p><pre><code>&lt;record id=&quot;view_filter_todo_task&quot; model=&quot;ir.ui.view&quot;&gt;    &lt;field name=&quot;name&quot;&gt;To-do Task Filter&lt;/field&gt;    &lt;field name=&quot;model&quot;&gt;todo.task&lt;/field&gt;    &lt;field name=&quot;arch&quot; type=&quot;xml&quot;&gt;        &lt;search&gt;            &lt;field name=&quot;name&quot;/&gt;            &lt;filter string=&quot;Not Done&quot;                    domain=&quot;[(&apos;is_done&apos;,&apos;=&apos;,False)]&quot;/&gt;            &lt;filter string=&quot;Done&quot;                    domain=&quot;[(&apos;is_done&apos;,&apos;!=&apos;,False)]&quot;/&gt;        &lt;/search&gt;    &lt;/field&gt;&lt;/record&gt;</code></pre><p><search>节中的<field>定义在搜索框中输入时搜索的字段, <filter>定义一些特定的预定义搜索方法, 例如上面预定义了两个检索条件, 一个是is_done字段为False(Not Done), 另一个表示is_done字段不为False(Done), 在搜索框中输入内容”Not Done”或”Done”的部分字符后, 将会在下拉的选项内容中看到这两个filter, 如下图所示.</filter></field></search></p><p><img src="../image/odoo-dev/view-defined-search.png" alt="Search view" title="Search view" width="500"></p><p>##2.12 增加业务逻辑</p><p>前面在form视图中定义了两个按钮, 现在为这两个按钮增加按下时触发的动作逻辑. 通过编辑todo_model.py, 向其中定义的类中添加相应的函数即可. 以下的内容使用odoo 8.0中新增的API.</p><p>首先导入新API:</p><pre><code>from openerp import models, fields, api</code></pre><p><em>Toggle Done</em>按钮的动作很简单, 仅仅将<em>Is Done?</em>字段的值取反. 对于处理<font color="blue">一条记录</font>的业务逻辑, 可以通过在函数上增加<em>@api.one</em>装饰, @api.one将会使传入函数的self参数代表一条记录. 代码如下:</p><pre><code>@api.onedef do_toggle_done(self):    self.is_done = not self.is_done    return True</code></pre><p>该方法简单地将is_done字段取反, 这种方法被<font color="blue">客户端调用</font>, 并且必须有返回值. 如果返回的是None, 客户端使用XMLRPC协议的调用将不起作用, 所以没有任何可返回对象, 一般的处理方法是返回True.</p><p>对于<em>Clear All Done</em>按钮, 点击它之后应该找到所有active的并且is_done的记录, 把它们标记为inactive. form的按钮一般假定仅处理选中的当前记录, 但是这里可以做一个简单的处理, 处理多条记录而不是当前的那一条. 代码如下:</p><pre><code>@api.multidef do_clear_done(self):    done_recs = self.search([(&apos;is_done&apos;, &apos;=&apos;, True)])    done_recs.write({&apos;active&apos;: False})    return True</code></pre><p>用<em>@api.multi装饰的方法中, self代表一个记录集(recordset), 记录集可以是一条记录(用于form时)或多条记录(tree). 在上面的代码中, 并没有使用self记录集, 而是构造了</em>done_recs*这个自定义的记录集, 它之中包括所有被标记为已完成的记录, 最后将该记录中所有记录的active字段设置为False.</p><font color="blue">Odoo中, <em>active</em>字段是一个特殊的字段, 如果active=False, 那么这条记录自动不显示.</font><p>搜索这个API方法返回满足条件的记录, 搜索的条件使用domain(多个元组的列表)来给定.</p><p><em>write</em>方法一次设置记录集中所有元素的值, 修改的值用字典来给定. 这里使用<em>write</em>的方法比迭代记录集中各条记录进行修改更有效.</p><p>do_clear_done没有使用self, 也就是说其实用@api.one也可以, 但为什么用@api.multi而不是@api.one:</p><pre><code>@api.one修饰的方法将会为每个选中记录运行一次, 而@api.multi确保其修饰的方法即使有多条选中的记录也仅会执行一次.</code></pre><p>##2.13 设定访问控制</p><p>现在载入模块时, 服务器控制台会有一条关于访问权限的警告日志, 如下:</p><p><img src="../image/odoo-dev/ac-log.png" alt="AC Log" title="AC Log" width="500"></p><p>这条警告说明我们的新模型没有访问控制, 所以它只能被admin超级用户使用, 超级用户忽略数据访问规则. 为了让非超级用户能够使用todo, 需要做一些修改.</p><p>先来看看向一个model添加访问规则需要哪些信息, 设置=&gt;技术=&gt;安全=&gt;访问控制列表, 打开访问控制列表并设置搜索条件为mail.mail, 如下图所示.</p><p><img src="../image/odoo-dev/acl-mail.png" alt="ACL mail" title="ACL mail" width="500"></p><p>图中是mail.mail模型的ACL, 从中可以看出各个组在这个模型记录上允许哪些动作(读写创建删除)</p><p>这些信息应当由模块来提供, 利用一个data file来将其数据载入到ir.model.access模型中. 在这里, 为所有的employee组添加完整的todo访问, employee组是最基本的访问组, 几乎所有用户都属于该组.</p><p>创建一个CSV文件(security/ir.model.access.csv), 内容如下:</p><pre><code>id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlinkaccess_todo_task_group_user,todo.task.user,model_todo_task,base.group_user,1,1,1,1</code></pre><ul><li>模型id自动产生, 模型todo.task对应的id是<em>model_todo_task</em>;</li><li>employee组由base模块创建, id为base.group_user;</li><li>name仅用于信息作用, 但最好保持唯一, 核心模块中name一般使用点分隔的模型字符串和组字符串.</li></ul><p>最后, 向__openerp__.py中添加这个data file:</p><pre><code>&apos;data&apos;: [       &apos;views/todo_view.xml&apos;,       &apos;security/ir.model.access.csv&apos;,],</code></pre><p>现在升级模块, 然后用另一个用户登录, 之前看不到的To-Do Tasks菜单可以在消息下看到了, 并且可以进行操作.</p><p>##2.14 行级访问规则</p><p>Odoo是一个多用户系统, 所以todo应用中的任务应当是属于某个用户的, Odoo提供了行级的访问控制来支持这个需求.</p><p>在技术=&gt;安全=&gt;记录规则中可以看到各个规则, 记录规则定义在ir.rule模型中, 创建一个security/todo_access_rules.xml数据文件来为todo定义行级访问规则, 该文件内容如下:</p><pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;&lt;openerp&gt;    &lt;data noupdate=&quot;1&quot;&gt;        &lt;record id=&quot;todo_task_user_rule&quot; model=&quot;ir.rule&quot;&gt;            &lt;field name=&quot;name&quot;&gt;ToDo Tasks only for owner&lt;/field&gt;            &lt;field name=&quot;model_id&quot; ref=&quot;model_todo_task&quot;/&gt;            &lt;field name=&quot;domain_force&quot;&gt;                [(&apos;create_uid&apos;,&apos;=&apos;,user.id)]            &lt;/field&gt;            &lt;field name=&quot;groups&quot; eval=&quot;[(4, ref(&apos;base.group_user&apos;))]&quot;/&gt;        &lt;/record&gt;    &lt;/data&gt;&lt;/openerp&gt;</code></pre><p>注意noupdate=”1”, 它说明此项数据在升级模块是不会被更新, 也即这项数据随后在webclient中的修改可以保存下来, 而不会因为应用下一次开发迭代的内容更新时将所做修改重新初始化.<br>当然, 如果希望每次都重新初始化该规则, 可以将其设置为noupdate=”0”.</p><p>在<em>groups</em>字段有一个特殊的表达式([(4, ref(‘base.group_user’))]), 这是一个一对多关系字段. (4, x)元组表示将x附加到记录上, 其中这里的x是用base.group_user表示的employee组.</p><p>最后, 向__openerp__.py添加将数据文件载入模块的配置:</p><pre><code>&apos;data&apos;: [              &apos;views/todo_view.xml&apos;,              &apos;security/ir.model.access.csv&apos;,              &apos;security/todo_access_rules.xml&apos;,       ],</code></pre><p>##2.15 模块图标</p><p>模块目录下的”static/description/icon.png”文件.</p><p><img src="../image/odoo-dev/module-icon.png" alt="Custom Icon" title="Custom Icon" width="500"></p><p>##2.16 删除模块</p><p><em>Step 1</em> 首先点击”升级”按钮旁的”卸载”按钮卸载模块;<br><em>Step 2</em> 然后点击header部分的”更多=&gt;删除”删除模块;</p><p><img src="../image/odoo-dev/remove-module.png" alt="Remove Module" title="Remove Module" width="500"></p><p><em>Step 3</em> 最后在文件系统删除插件模块目录.</p>]]></content>
    
    <summary type="html">
    
      准备Odoo入门课程时为学生整理的资料
    
    </summary>
    
      <category term="OpenERP" scheme="http://www.jeffzhang.cn/categories/OpenERP/"/>
    
    
      <category term="Python" scheme="http://www.jeffzhang.cn/tags/Python/"/>
    
      <category term="OpenERP" scheme="http://www.jeffzhang.cn/tags/OpenERP/"/>
    
      <category term="Odoo" scheme="http://www.jeffzhang.cn/tags/Odoo/"/>
    
  </entry>
  
</feed>
