odoo17 | 模型之间的交互

news2024/11/20 7:12:00

前言

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

具体示例:账户移动

目标:

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

链接模块

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

锻炼

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

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

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

创建发票

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

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

from odoo import models

class InheritedModel(models.Model):
    _inherit = "estate.property"

    def inherited_action(self):
        return super().inherited_action()

这里是一个完整代码

# -*- coding: utf-8 -*-

from odoo import api, models


class 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 = 0
        for order in self:
            order = order.with_company(order.company_id)
            current_section_vals = None
            down_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)
                    continue
                if line.display_type != 'line_note' and float_is_zero(line.qty_to_invoice, precision_digits=precision):
                    continue
                if 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 += line
                        continue
                    if current_section_vals:
                        invoice_item_sequence += 1
                        invoice_lines_vals.append(current_section_vals)
                        current_section_vals = None
                    invoice_item_sequence += 1
                    prepared_line = line._prepare_invoice_line(sequence=invoice_item_sequence)
                    invoice_lines_vals.append(prepared_line)

            # 如果销售订单中有预付款,请将它们分组到共同部分下
            if down_payments:
                invoice_item_sequence += 1
                down_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 += 1
                    invoice_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 = None
                for invoice_vals in invoices:
                    if not ref_invoice_vals:
                        ref_invoice_vals = invoice_vals
                    else:
                        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 Command

def 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.coloradmin.cn/o/1384300.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

笔记:flutter中一些流行的 UI 相关库推荐(不断更新)

笔记&#xff1a;flutter中一些流行的 UI 相关库&#xff08;不断更新&#xff09; CSDN&#xff1a;https://jclee95.blog.csdn.net本文收录了很多在 flutter pub 上显示流行度较高的第三方UI库和UI框架&#xff0c;对它们的效果进行了截图&#xff0c;目的是方便开发时进行查…

如何利用小程序介绍公司品牌形象?

企业小程序的建设对于现代企业来说已经成为了一项必不可少的工作。随着移动互联网的快速发展&#xff0c;越来越多的职场人士和创业老板希望通过小程序来提升企业形象&#xff0c;增强与用户的互动&#xff0c;实现更好的商业效果。在这个过程中&#xff0c;使用第三方制作平台…

录第第五十八天——每日温度,下一个更大元素|

单调栈 栈里的元素保持单调递增或者递减&#xff0c;栈内元素是元素下标。单调栈的本质是空间换时间&#xff0c;因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元素&#xff0c;优点是整个数组只需要遍历一次求一个元素右边第一个更大元素&#xff0c;单调栈…

高效构建Java应用:Maven入门和进阶(五)

高效构建Java应用&#xff1a;Maven入门和进阶&#xff08;五&#xff09; Maven实战案例&#xff1a;搭建微服务Maven工程框架5.1 项目需求和结构分析5.2项目搭建和统一构建 总结 Maven实战案例&#xff1a;搭建微服务Maven工程框架 5.1 项目需求和结构分析 需求案例&#xf…

verilog编程题

verilog编程题 文章目录 verilog编程题序列检测电路&#xff08;状态机实现&#xff09;分频电路计数器译码器选择器加减器触发器寄存器 序列检测电路&#xff08;状态机实现&#xff09; module Detect_101(input clk,input rst_n,input data,o…

yolov5模型Detection输出内容与源码详细解读

文章目录 前言一、Detiction类源码说明二、Detection类初始化参数解读三、Detection的训练输出源码解读四、Detection的预测输出源码解读1、self.grid内容解读2、xy/wh内容解读3、推理输出解读 总结 前言 最近&#xff0c;需要修改yolov5推理结果&#xff0c;通过推理特征添加…

BDD(Behavior-Driven Development)行为驱动开发介绍

为什么需要BDD&#xff1f; “开发软件系统最困难的部分就是准确说明开发什么” (“The hardest single part of building a software system is deciding precisely what to build” — No Silver Bullet, Fred Brooks) 。 看一下下面的开发场景&#xff1a; 场景一&#xf…

python 通过定时任务执行pytest case

这段Python代码使用了schedule库来安排一个任务&#xff0c;在每天的22:50时运行。这个任务执行一个命令来运行pytest&#xff0c;并生成一个报告。 代码开始时将job_done变量设为False&#xff0c;然后运行预定的任务。一旦任务完成&#xff0c;将job_done设置为True并跳出循…

【昕宝爸爸小模块】线程的几种状态,状态之间怎样流转

➡️博客首页 https://blog.csdn.net/Java_Yangxiaoyuan 欢迎优秀的你&#x1f44d;点赞、&#x1f5c2;️收藏、加❤️关注哦。 本文章CSDN首发&#xff0c;欢迎转载&#xff0c;要注明出处哦&#xff01; 先感谢优秀的你能认真的看完本文&…

react项目运行卡在编译:您当前运行的TypeScript版本不受@TypeScript eslint/TypeScript estree的官方支持

1.问题 错误信息具体如下&#xff1a; 搜索了一下&#xff0c;是typescript版本的问题&#xff0c;提示我版本需要在3.3.0和4.5.0中间&#xff0c;我查看了package.json&#xff0c;显示版本为4.1.3&#xff0c;然后一直给我提示我的版本是4.9.5&#xff0c;全局搜索一下&…

读写分离的手段——主从复制,解决读流量大大高于写流量的问题

应用场景 假设说有这么一种业务场景&#xff0c;读流量显著高于写流量&#xff0c;你要怎么优化呢。因为写是要加锁的&#xff0c;可能就会阻塞你读请求。而且其实读多写少的场景还很多见&#xff0c;比如电商平台&#xff0c;用户浏览n多个商品才会买一个。 大部分人的思路可…

智慧园区数字孪生智能可视运营平台解决方案:PPT全文82页,附下载

关键词&#xff1a;智慧园区解决方案&#xff0c;数字孪生解决方案&#xff0c;数字孪生应用场景及典型案例&#xff0c;数字孪生可视化平台&#xff0c;数字孪生技术&#xff0c;数字孪生概念&#xff0c;智慧园区一体化管理平台 一、基于数字孪生的智慧园区建设目标 1、实现…

Linux-命名管道

文章目录 前言一、命名管道接口函数介绍二、使用步骤 前言 上章内容&#xff0c;我们介绍与使用了管道。上章内容所讲的&#xff0c;是通过pipe接口函数让操作系统给我们申请匿名管道进行进程间通信。 并且这种进程间通信一般只适用于父子进程之间&#xff0c;那么对于两个没有…

什么是二分查找

一、是什么 在计算机科学中&#xff0c;二分查找算法&#xff0c;也称折半搜索算法&#xff0c;是一种在有序数组中查找某一特定元素的搜索算法 想要应用二分查找法&#xff0c;则这一堆数应有如下特性&#xff1a; 存储在数组中有序排序 搜索过程从数组的中间元素开始&…

Python-AST语法树

一、抽象语法树 1、什么是抽象语法树 在计算机科学中&#xff0c;抽象语法树&#xff08;abstract syntax tree &#xff0c;AST&#xff09;&#xff0c;是源代码的抽象语法结构的树状表现形式&#xff0c;这里特指编程语言的源代码。AST是编译器或解释器在处理源代码时所使…

<软考高项备考>《论文专题 - 65 质量管理(4) 》

4 过程3-管理质量 4.1 问题 4W1H过程做什么为了评估绩效&#xff0c;确保项目输出完整、正确且满足客户期望&#xff0c;而监督和记录质量管理活动执行结果的过程作用&#xff1a;①核实项目可交付成果和工作已经达到主要干系人的质量要求&#xff0c;可供最终验收;②确定项目…

class_4:car类

#include <iostream> using namespace std; class Car{ public://成员数据string color; //颜色string brand; //品牌string type; //车型int year; //年限//其实也是成员数据&#xff0c;指针变量&#xff0c;指向函数的变量&#xff0c;并非真正的成员函数void (*…

剪映国际版,免费无限制使用

随着抖音的爆火短视频的崛起&#xff0c;相信每一个人都感受到了短视频快节奏下的生活洪流。 现如今每个人都能成为自己生活的记录者&#xff0c;每一个人都有掌握着剪辑的基本技能。而剪映就是很多人都会使用的剪辑软件。 相对于PR、AE等剪辑软件来说&#xff0c;作为一款国…

读《Open-Vocabulary Video Anomaly Detection》

2023 西北工业大学和新大 引言 视频异常检测(VAD)旨在检测不符合预期模式的异常事件&#xff0c;由于其在智能视频监控和视频内容审查等应用前景广阔&#xff0c;已成为学术界和工业界日益关注的问题。通过几年蓬勃发展&#xff0c;VAD 在许多不断涌现的工作中取得了重大进展。…

spring-mvc(1):Hello World

虽然目前大多数都是使用springboot来开发java程序&#xff0c;或者使用其来为其他端提供接口&#xff0c;而为其他端提供接口&#xff0c;这些功能都是依靠springmvc实现的&#xff0c;所以有必要学习一下spring-mvc&#xff0c;这样才能更好的学习springboot。 一&#xff0c…