近日接到财务需求,要从销售订单增加部门,并将部门与原始的合约带入到会计凭证以便进行财务分析。
首先厘清需求:
- SO新增部门栏位
- 会计凭证增加部门及合约栏位
- 出货单验证后会产生凭证
- 开立发票后产生凭证
开发步骤:
- 在sale_order先增加department_id(Odoo本身就已经在SO上面存在project_id了)
- 在stock.picking增加department_id和project_id,以便于产生会计凭证时可以取得栏位
- 在account_move增加department_id和project_id
代码追踪及功能实现:
- Odoo在确认订单的时候会产生Picking,所以追踪到此按钮的对应method: action_button_confirm
- 此方法启动了order_confirm的workflow signal
self.signal_workflow(cr, uid, ids, 'order_confirm')
- 进入workflow的编辑页面,沿着流程找下去,下一步到了route,此阶段执行了action_wait(),但此方法中没有产生picking相关的部分
- 此时流程尚未结束,继续沿着流程找到了wait_ship节点,此节点未执行动作
- 继续沿着节点找下去,到了ship节点,此节点执行了action_ship_create(),但此方法仅产生了procurement,然后执行了procurement中run的动作,追踪的动作到这里卡住了,因为没有任何关于产生picking的代码判断,虽然凭经验知道picking应该是在run procurement的时候产生。不过我们不需要继续看下去就可以切入了,我们知道SO和Picking是通过procurement_group_id来关联,因此可以继承此方法,用procurement来找到SO对应的picking,并更新我们刚刚新增的department_id及project_id两个栏位
def action_ship_create(self):
res = super(BelstarSaleOrderExtend, self).action_ship_create()
picking = self.env['stock.picking'].search([('group_id', '=', self.procurement_group_id.id)])
picking.write({'department_id': self.department_id.id, 'project_id': self.project_id.id})
return res
- 在验证出货单的时候又遇到了问题,追踪页面验收功能,从do_detailed_transfer到do_transfer的执行流程中都没有找到产生会计凭证的相关代码,于是调整追踪代码的方向,我们知道warehouse的模块名称是stock,那么就去stock模块里面查找会计凭证相关的模块account.move,但查询结果显示stock模块里面没有出现account.move的任何内容。但跟stock相关的模块还有stock_account,根据模块名可判断出此模块为warehouse中对account的扩展,在此模块中继续搜寻account.move,bingo! 找到目标出现在_create_account_move_line方法中,原来是在stock.quants的model下面,通过查看此段代码知道最终写入的资料来自于另一个辅助方法_prepare_account_move_line
move_lines = self._prepare_account_move_line(cr, uid, move, qty, cost, credit_account_id, debit_account_id, context=context)
- 至此终于找到了最关键的代码片段
debit_line_vals = {
'name': move.name,
'product_id': move.product_id.id,
'quantity': qty,
'product_uom_id': move.product_id.uom_id.id,
'ref': move.picking_id and move.picking_id.name or False,
'date': fields.date.context_today(self, cr, uid, context=context),
'partner_id': partner_id,
'debit': valuation_amount > 0 and valuation_amount or 0,
'credit': valuation_amount < 0 and -valuation_amount or 0,
'account_id': debit_account_id,
}
credit_line_vals = {
'name': move.name,
'product_id': move.product_id.id,
'quantity': qty,
'product_uom_id': move.product_id.uom_id.id,
'ref': move.picking_id and move.picking_id.name or False,
'date': fields.date.context_today(self, cr, uid, context=context),
'partner_id': partner_id,
'credit': valuation_amount > 0 and valuation_amount or 0,
'debit': valuation_amount < 0 and -valuation_amount or 0,
'account_id': credit_account_id,
}
- 找到了需要修改的代码片段,就可以着手修改了,这边还有2个小问题,stock_account里面stock.quants是继承stock模块的,我们也要继承相同的代码并覆盖掉stock_account里面的代码,因此需要确认odoo载入顺序,另一个问题是官方模块是old api的代码格式需要用old api的方式来继承,比较不会有new api转换上的问题。继承顺序的部分经过google查询之后,只要在模块声明文件中depends stock_account就可以确保当前模块在stock_account加载之后才加载,以确保加载的顺序。
- 资讯从SO带到出货单再带到会计凭证的部分没有问题之后,要开始看如何在发票验证的时候把project_id和department_id带入会计凭证的部分
- 通过Odoo销售订单产生发票,当点了创建发票按钮后我们会看到有几种开票方式:整单建立发票,百分比建立发票,固定金额建立发票以及订单行建立发票,通过开立发票按钮的方法create_invoices我们追踪到代码,可以看到如果为整单建立发票,Odoo会调用sale.order下面的方法manual_invoice,此发票又去触发了workflow中的manual_invoice,此时节点会跑到invoice节点并触发方法action_invoice_create(),回过头检查sale_order里面的此方法找到发票建立是在其中调用_make_invoice方法,而此方法中的发票数据处理是调用了_prepare_invoice,至此找到了整单建立发票的关键代码片段
- 如果是固定金额或百分比方式,回头看create_invoices方法中,如果为固定金额或百分比,可以看到方法中会调用_prepare_advance_invoice_vals来准备发票的数据,所以我们也找到了固定金额或百分比中建立发票的关键代码片段
- 如果是以发票明细行的方式开发票,系统会先导入到选择发票明细行的页面,选择之后按下开立发票按钮,会触发方法make_invoices,实际追踪发现此方法生效为sale_line_invoice.py中的同方法名,此方法中有一个子方法make_invoice,会调用_prepare_invoice准备数据,至此全部发票开立方式的关键代码片段都已经找到
- 在开发发票的数据中写入project_id及department_id之后,就可以从验证发票并产生会计凭证这边下手了,先去跟踪验证发票按钮,进入Edit Form可以查看到验证按钮为workflow中invoice_open的触发点,再进入发票的workflow查看节点会进入到open中,此节点共计执行了4个方法action_date_assign() action_move_create() action_number() invoice_validate(),直接透过字面含义可知我们要修改的代码存在于action_move_create中
- 进入action_move_create中,分析了一下代码发现我们修改的最终代码片段就是在这个方法中,我们需要在account_move及account.move.line中增加project_id及department_id,于是我们继承这个方法并进行修改
line = inv.finalize_invoice_move_lines(line)
for el in line:
el[2].update({'analytic_account_3': inv.department_id and inv.department_id.id or False,
'analytic_account': inv.project_id and inv.project_id.id or False
})
move_vals = {
'ref': inv.reference or inv.name,
'line_id': line,
'journal_id': journal.id,
'date': inv.date_invoice,
'narration': inv.comment,
'company_id': inv.company_id.id,
'analytic_account': inv.project_id and inv.project_id.id or False,
'analytic_account_3': inv.department_id and inv.department_id.id or False,
}
总结:
- Odoo8同时存在的old api及new api对开发造成相当的困扰,一个Model有时要开两个文件来继承修改以区分api/old api
- workflow在后续版本拿掉真的是必然,在debug的时候要从程式代码和workflow切来切去很不方便,并且对版本管控来说workflow也不是良好的实现方式
- 当原生模块就已经继承的时候,要找到关键的代码片段也不是一件容易的事情