odoo17 | 模型之间的交互

前言

在前一章中,我们使用继承来修改模块的行为。在我们的房地产场景中,我们希望更进一步,能够为我们的客户生成发票。Odoo提供了一个发票(Invoicing)模块,所以直接从我们的房地产模块创建一个发票会很简洁,也就是说,一旦一个属性被设置为“已售出”,发票就会在发票应用程序中被创建。

具体示例:账户移动

目标:

  • 应该创建一个新的模块estate_account
  • 房产出售时,应给买方开具发票
    在这里插入图片描述
  • 无论何时,我们与其他模块交互时,都需要牢记模块化。如果我们打算将我们的应用程序出售给房地产中介,一些人可能想要发票功能,但其他人可能不想要。

链接模块

此类用例的常见方法是创建一个“链接”模块。在我们的例子中,该模块将依赖于房地产和账户,并将包括房地产的发票创建逻辑。这样,房地产和会计模块可以独立安装。当两者都安装时,链接模块提供新功能。

锻炼

  • 创建链接模块。
  • 创建estate_account模块,该模块依赖于 estate和account模块。 现在,它将是一个空壳。

提示:您在本教程的开头已经这样做了。这个过程非常 类似。《odoo17 | 创建一个新应用程序》

estate_account模块出现在列表中时,继续安装它! 你会注意到发票(Invoicing)应用程序也已安装。这是意料之中的,因为您的模块依赖于它。如果您卸载了发票(Invoicing)应用程序,您的estate_account模块也将被卸载。

创建发票

现在是生成发票的时候了。我们想为房地产模型添加功能,即我们想为房产出售时添加一些额外的逻辑。这听起来很熟悉吗?如果没有,最好回到上一章,因为你可能错过了什么!

作为第一步,我们需要扩展在按下房产上的“出售”按钮时调用的动作。为此,我们需要在 estate_account 模块中为 estate.property 模型创建一个模型继承。目前,重写的动作将简单地返回 super 调用。也许一个例子会使事情变得更清楚。
示例代码

from odoo import modelsclass InheritedModel(models.Model):_inherit = "estate.property"def inherited_action(self):return super().inherited_action()

这里是一个完整代码

# -*- coding: utf-8 -*-from odoo import api, modelsclass AccountMove(models.Model):_inherit = 'account.move'def action_invoice_paid(self):""" 当发票链接到销售订单时,销售注册是付费确认与会者。与会者确实不应该事先确认完整的付款。 """res = super(AccountMove, self).action_invoice_paid()self.mapped('line_ids.sale_line_ids')._update_registrations(confirm=True, mark_as_paid=True)return res

锻炼

添加发票创建的第一步。

  • estate_account 模块的models文件夹中创建 estate_property.py 文件。
  • _inherit 继承房地产estate.property模型。
  • 重写 action_sold 方法(您可能已将其重命名)以返回 super 调用。

提示:为了确保它工作正常,在重写的方法中添加一个打印或调试器断点。

启动项目是否报错?如果不是,请检查是否正确导入了所有Python文件。

如果覆盖有效,我们可以继续前进并创建发票。不幸的是,没有简单的方法知道如何在Odoo中创建任何给定的对象。大多数时候,有必要查看其模型以找到所需的字段并提供适当的值。

学习的一个好方法是看看其他模块是如何完成你想要做的事情的。例如,销售的基本流程之一是从销售订单中创建发票。这看起来是一个很好的起点,因为它正是我们想做的事情。花一些时间阅读和理解**_create_invoices**方法。

 def _create_invoices(self, grouped=False, final=False, date=None):"""创建与销售订单关联的发票。:param grouped: 如果为 True,则按 SO ID 对发票进行分组。如果为 False,则按(合作伙伴发票ID,货币):param final: 如果为True,则必要时将生成退款:returns: 创建的发票列表"""if not self.env['account.move'].check_access_rights('create', False):try:self.check_access_rights('write')self.check_access_rule('write')except AccessError:return self.env['account.move']precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')# 1) 创建发票.invoice_vals_list = []invoice_item_sequence = 0for order in self:order = order.with_company(order.company_id)current_section_vals = Nonedown_payments = order.env['sale.order.line']# 发票金额invoice_vals = order._prepare_invoice()# 发票行值(仅保留必要的部分)invoice_lines_vals = []for line in order.order_line:if line.display_type == 'line_section':current_section_vals = line._prepare_invoice_line(sequence=invoice_item_sequence + 1)continueif line.display_type != 'line_note' and float_is_zero(line.qty_to_invoice, precision_digits=precision):continueif line.qty_to_invoice > 0 or (line.qty_to_invoice < 0 and final) or line.display_type == 'line_note':if line.is_downpayment:down_payments += linecontinueif current_section_vals:invoice_item_sequence += 1invoice_lines_vals.append(current_section_vals)current_section_vals = Noneinvoice_item_sequence += 1prepared_line = line._prepare_invoice_line(sequence=invoice_item_sequence)invoice_lines_vals.append(prepared_line)# 如果销售订单中有预付款,请将它们分组到共同部分下if down_payments:invoice_item_sequence += 1down_payments_section = order._prepare_down_payment_section_line(sequence=invoice_item_sequence)invoice_lines_vals.append(down_payments_section)for down_payment in down_payments:invoice_item_sequence += 1invoice_down_payment_vals = down_payment._prepare_invoice_line(sequence=invoice_item_sequence)invoice_lines_vals.append(invoice_down_payment_vals)if not any(new_line['display_type'] is False for new_line in invoice_lines_vals):raise self._nothing_to_invoice_error()invoice_vals['invoice_line_ids'] = [(0, 0, invoice_line_id) for invoice_line_id in invoice_lines_vals]invoice_vals_list.append(invoice_vals)if not invoice_vals_list:raise self._nothing_to_invoice_error()# 2) 管理“grouped”参数:按(partner_id, currency_id)分组。if not grouped:new_invoice_vals_list = []invoice_grouping_keys = self._get_invoice_grouping_keys()for grouping_keys, invoices in groupby(invoice_vals_list, key=lambda x: [x.get(grouping_key) for grouping_key in invoice_grouping_keys]):origins = set()payment_refs = set()refs = set()ref_invoice_vals = Nonefor invoice_vals in invoices:if not ref_invoice_vals:ref_invoice_vals = invoice_valselse:ref_invoice_vals['invoice_line_ids'] += invoice_vals['invoice_line_ids']origins.add(invoice_vals['invoice_origin'])payment_refs.add(invoice_vals['payment_reference'])refs.add(invoice_vals['ref'])ref_invoice_vals.update({'ref': ', '.join(refs)[:2000],'invoice_origin': ', '.join(origins),'payment_reference': len(payment_refs) == 1 and payment_refs.pop() or False,})new_invoice_vals_list.append(ref_invoice_vals)invoice_vals_list = new_invoice_vals_list# 3)创建发票。# 使用sudo管理发票的创建,因为销售人员必须能够在没有“计费”访问权限的情况下从销售订单生成发票。然而,他不应该能够从头开始创建发票。moves = self.env['account.move'].sudo().with_context(default_move_type='out_invoice').create(invoice_vals_list)# 4) 有些动作实际上可能是退款:如果总金额为负,则转换它们# 我们会在创建交易后进行此操作,因为我们需要税收等数据来了解总金额实际上是否为负数if final:moves.sudo().filtered(lambda m: m.amount_total < 0).action_switch_invoice_into_refund_credit_note()for move in moves:move.message_post_with_view('mail.message_origin_link',values={'self': move, 'origin': move.line_ids.mapped('sale_line_ids.order_id')},subtype_id=self.env.ref('mail.mt_note').id)return moves

要创建发票,我们需要以下信息:

  • 一个 partner_id:客户

  • 一个 move_type:它有几个可能的值

  move_type = fields.Selection(selection=[('entry', 'Journal Entry'),('out_invoice', 'Customer Invoice'),('out_refund', 'Customer Credit Note'),('in_invoice', 'Vendor Bill'),('in_refund', 'Vendor Credit Note'),('out_receipt', 'Sales Receipt'),('in_receipt', 'Purchase Receipt'),], string='Type', required=True, store=True, index=True, readonly=True, tracking=True,default="entry", change_default=True)
  • 一个 journal_id:会计账簿

锻炼

添加发票创建的第二步。
action_sold 方法的覆盖中创建一个空的 account.move

  • partner_id 来自当前的 estate.property

  • move_type 应与“客户发票”相对应

提示:

  • 要创建对象,请使用 self.env[model_name].create(values),其中 values 是一个 dict

  • create 方法不接受记录集作为字段值。

当某个房产被设置为“已售出”时,您现在应该在“开票/客户/发票”中创建一张新的客户发票。

显然,到目前为止我们还没有任何发票行。要创建发票行,我们需要以下信息:

  • name:行描述
  • quantity:数量
  • price_unit: 价格单位

此外,发票行需要与发票关联。将行与发票关联的最简单和最有效的方法是在创建发票时包含所有行。为此,在创建帐户时包含 invoice_line_ids 字段。One2manyMany2many 使用特殊的“命令”,这些命令已通过 Command 命名空间转换为人类可读。此命名空间表示一组记录上执行的三元组命令。三元组最初是执行这些命令的唯一选项,但现在使用命名空间是标准。格式是将它们放在一个按顺序执行的列表中。这里是一个简单的例子,在创建 test_model 时包含 One2many 字段 line_ids

from odoo import Commanddef inherited_action(self):self.env["test_model"].create({"name": "Test","line_ids": [Command.create({"field_1": "value_1","field_2": "value_2",})],})return super().inherited_action()

锻炼

添加发票创建的第三步。

在创建account.move时添加两条发票行。每个出售的物业都将根据以下条件开具发票:

  • 售价的6%

  • 行政费用另加100.00

提示:按照上面的示例在创建时添加 invoice_line_ids。对于每一行,我们需要一个名称、数量和价格单位。

这一章可能是迄今为止所涵盖的最难的一章,但它与Odoo开发实践最为接近。在下一章中,我们将介绍Odoo中使用的模板机制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/345812.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

金融疆界:在线支付系统渠道网关的创新设计(一)

这是《百图解码支付系统设计与实现》专栏系列文章中的第&#xff08;11.1&#xff09;篇。点击上方关注&#xff0c;深入了解支付系统的方方面面。 整个渠道网关的内容预计会分成5篇来讲&#xff1a;1&#xff09;定位、术语、概要设计。2&#xff09;领域模型、状态机设计。3…

品牌帮助中心:提升企业客户服务水平与效率的实用指南

什么是品牌帮助中心&#xff1f;简单来理解&#xff0c;他就是一种加速问题解决效率的方式&#xff0c;是通过在官网设置文章库或者社区的形式&#xff0c;为客户提供自助服务&#xff0c;自我查找问题答案。是一种既能提升问题解决效率&#xff0c;又能提升品牌形象的方式。接…

10-skywalking告警

https://github.com/apache/skywalking/blob/master/docs/en/setup/backend/backend-alarm.md 5.1&#xff1a;告警指标 ~$ vim /apps/apache-skywalking-apm-bin/config/oal/core.oal service_resp_time # 服务的响应时间 service_sla # 服务http请求成功率SLV&#xff0c;比…

Retinal Structure Detection in OCTA Image viaVoting-Based Multitask Learning

一、摘要 研究背景&#xff1a;自动检测视网膜结构&#xff0c;如视网膜血管(RV)、中央凹血管区(FAZ)和视网膜血管连接(RVJ)&#xff0c;对了解眼部疾病和临床决策具有重要意义。 主要工作&#xff1a;在本文中&#xff0c;提出了一种新的基于投票的自适应特征融合多任务网络…

【MySQL】本地创建MySQL数据库详解

文章目录 下载MySQL安装重置密码本地连接 下载MySQL 下载网址&#xff1a;https://dev.mysql.com/downloads/mysql/ 安装 将下载好的压缩包解压到D盘。 在解压好的文件夹中创建my.ini文件。 将以下代码复制粘贴到创建好的my.ini文件中。注意修改文件路径。 [mysqld] #设置…

在vue3和上挂载方法,以及在页面中怎么使用原型(公共)上的方法

//新建的项目的main.js文件是这样的 //main.js 文件 //befor import { createApp } from vue; import App from ./App.vue;const app createApp(App); app.mount(#app);以下例子用于解释在vue3.0的main.js中挂载公共的方法&#xff08;foo&#xff09; //main.js 文件 //afte…

02-Dapper

1.2&#xff1a;Dapper 1.2.1&#xff1a;设计要求 1、无处不在的部署&#xff1a; 任何服务都应该被监控到&#xff0c;任何服务出问题都要做到有据可查。2、持续的监控&#xff1a;做到7*24小时全天候监控&#xff0c;任何时候出了问题都要基于监控数据追踪问题根源。1.2.2…

设计模式之策略模式【行为型模式】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档> 学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某…

代码随想录 Leetcode142. 环形链表 II

题目&#xff1a; 代码(首刷看解析 2024年1月13日&#xff09;&#xff1a; class Solution { public:ListNode *detectCycle(ListNode *head) {if (head nullptr) return nullptr;ListNode* fast head;ListNode* slow head;while (true) {if(fast->next nullptr || fa…

Binder 机制 javanative

一&#xff1a;Binder介绍 Binder是一套ipc通信方案 Binder框架定义了四个角色&#xff1a; Server &#xff0c;Client&#xff0c;ServiceManager &#xff08;以后简称SMgr&#xff09;以及Binder驱动。其中Server &#xff0c;Client&#xff0c;SMgr运行于用户空间&#…

Modbus协议

一.起源 Modbus由Modicon公司于1979年开发&#xff0c;是一种工业现场总线协议标准。Modbus通信协议具有多个变种&#xff0c;其中有支持串口&#xff0c;以太网多个版本&#xff0c;其中最著名的是Modbus RTU、Modbus ASCII和Modbus TCP三种。其中Modbus TCP是在施耐德收购Mo…

LeetCode讲解篇之39. 组合总和

文章目录 题目描述题解思路题解代码 题目描述 题解思路 首先排序数组&#xff0c;然后开始选择数字&#xff0c;当选择数字num后&#xff0c;在去选择大于等于num的合法数字&#xff0c;计算过程中的数字和&#xff0c;直到选数字和等于target, 加入结果集&#xff0c;若数字和…