Odoo发票管理增强套件 - 全面提升企业开票效率
Odoo发票管理增强套件
项目描述
account-invoicing是一个专为Odoo企业资源规划系统设计的发票管理增强套件。该项目包含多个功能模块,全面覆盖发票处理的各个环节,从创建、折扣计算、发送到后期管理,为企业提供完整的发票解决方案。所有模块均基于Odoo Community Association (OCA)开发,确保代码质量和兼容性。
功能特性
核心功能模块
- 全局折扣管理 - 支持在发票级别应用全局折扣,自动计算折扣金额并更新相关会计科目
- 自动邮件发送 - 对配置为邮件传输方式的发票自动发送,支持队列作业处理
- 到期日期编辑 - 允许在发票过账后编辑到期日期,增强灵活性
- 税种强制检查 - 确保每张发票行都设置了正确的税种,避免税务错误
- 供应商信息更新 - 自动检测并更新产品供应商信息,保持采购数据同步
- 退款原因管理 - 为贷项通知单定义标准化的退款原因,便于统计分析
- 多销售订单分组 - 在合并开票时按销售订单分组显示,提高可读性
- 汇率显示 - 清晰展示发票使用的货币汇率,支持多币种环境
- 供应商发票号唯一性检查 - 防止重复录入供应商发票,避免重复付款风险
技术特色
- 完整的权限控制体系,确保数据安全
- 支持预初始化钩子和后初始化钩子
- 与Odoo标准模块无缝集成
- 多公司、多币种支持
- 响应式设计,适配各种设备
安装指南
系统要求
- Odoo 18.0 或更高版本
- Python 3.7+
- PostgreSQL 9.6+
安装步骤
- 将项目克隆到Odoo的addons目录:
cd /path/to/odoo/addons
git clone https://github.com/OCA/account-invoicing.git
- 在Odoo中安装所需模块:
# 安装全局折扣模块
module install account_global_discount
# 安装自动邮件发送模块
module install account_invoice_auto_send_by_email
# 安装其他需要的模块...
- 配置依赖项:
各模块的依赖关系已在manifest文件中定义,Odoo会自动处理。
平台注意事项
- 所有模块均支持Linux、Windows和macOS平台
- 建议在生产环境使用前在测试环境充分验证
- 确保有足够的数据库权限执行表结构变更
使用说明
全局折扣配置
- 进入 设置 > 参数 > 全局折扣
- 添加新的折扣百分比
- 选择折扣适用范围(销售或采购)
- 可限制特定公司使用
# 为合作伙伴设置全局折扣示例
partner_id = self.env['res.partner'].browse(partner_id)
partner_id.customer_global_discount_ids = [(6, 0, [discount_id])]
自动邮件发送
配置传输方法为邮件的发票会自动加入发送队列:
# 检查待发送的发票
invoices = self.env['account.move'].search([
('transmit_method_id', '=', email_method_id),
('state', '=', 'posted'),
('is_move_sent', '=', False)
])
到期日期管理
启用到期日期编辑功能:
<!-- 安全组配置 -->
<record id="group_account_invoice_date_due" model="res.groups">
<field name="name">Allow to change due date</field>
</record>
供应商信息更新
在供应商发票中检查和更新产品信息:
def update_supplierinfo(self):
"""更新产品供应商信息"""
for line in self.line_ids:
if not line.supplierinfo_id:
# 创建新的供应商信息
vals = line._prepare_supplierinfo()
self.env['product.supplierinfo'].create(vals)
else:
# 更新现有供应商信息
vals = line._prepare_supplierinfo_update()
line.supplierinfo_id.write(vals)
核心代码
全局折扣预初始化
def _pre_init_global_discount_fields(env):
"""预初始化全局折扣字段"""
if not column_exists(env.cr, "account_move", "amount_global_discount"):
env.cr.execute("""
ALTER TABLE "account_move"
ADD COLUMN "amount_global_discount" double precision DEFAULT 0
""")
env.cr.execute("""
ALTER TABLE "account_move" ALTER COLUMN "amount_global_discount" DROP DEFAULT
""")
# 初始化税前金额字段
if not column_exists(env.cr, "account_move", "amount_untaxed_before_global_discounts"):
env.cr.execute("""
ALTER TABLE "account_move"
ADD COLUMN "amount_untaxed_before_global_discounts" double precision
""")
env.cr.execute("""
update account_move set amount_untaxed_before_global_discounts = amount_untaxed
""")
退款原因管理
class AccountMoveReversal(models.TransientModel):
_inherit = "account.move.reversal"
reason_id = fields.Many2one("account.move.refund.reason", string="退款原因")
reason = fields.Char(
compute="_compute_reason", precompute=True, store=True, readonly=False
)
@api.depends("reason_id")
def _compute_reason(self):
"""计算退款原因"""
for record in self:
if record.reason_id:
record.reason = record.reason_id.name
def reverse_moves(self, is_modify=False):
"""重写退款创建方法,设置原因字段"""
res = super().reverse_moves(is_modify=is_modify)
# 为新创建的退款设置原因
self.move_ids.reversal_move_ids.filtered(lambda x: not x.reason_id).write(
{"reason_id": self.reason_id.id}
)
return res
供应商信息更新向导
class WizardUpdateInvoiceSupplierinfo(models.TransientModel):
_name = "wizard.update.invoice.supplierinfo"
_description = "更新供应商信息向导"
line_ids = fields.One2many(
comodel_name="wizard.update.invoice.supplierinfo.line",
inverse_name="wizard_id",
string="明细行",
)
invoice_id = fields.Many2one(
comodel_name="account.move",
required=True,
readonly=True,
ondelete="cascade",
)
def update_supplierinfo(self):
"""执行供应商信息更新"""
self.ensure_one()
supplierinfo_obj = self.env["product.supplierinfo"]
for line in self.line_ids:
supplierinfo = line.supplierinfo_id
if not supplierinfo:
# 创建新的供应商信息记录
vals = line._prepare_supplierinfo()
supplierinfo_obj.create(vals)
else:
# 更新现有供应商信息
vals = line._prepare_supplierinfo_update()
supplierinfo.write(vals)
# 更新产品的采购单位
if ("product_uom" in vals and
vals["product_uom"] != line.product_id.uom_po_id.id):
line.product_id.uom_po_id = vals["product_uom"]
# 标记发票为已检查
self.invoice_id.write({"supplierinfo_ok": True})
发票行与原始行匹配
def match_origin_lines(refund):
"""通过产品或描述匹配退款行与原始发票行"""
invoice = refund.reversed_entry_id
invoice_lines = invoice.invoice_line_ids
for refund_line in refund.invoice_line_ids:
for invoice_line in invoice_lines:
# 通过产品或名称匹配
match = (
refund_line.product_id
and refund_line.product_id == invoice_line.product_id
or refund_line.name == invoice_line.name
)
if match:
invoice_lines -= invoice_line
refund_line.origin_line_id = invoice_line.id
break
if not invoice_lines:
break
这些核心代码展示了项目的技术实现,包括数据库操作、业务逻辑处理和用户交互设计,体现了代码的专业性和可维护性。
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号(网络安全技术点滴分享)
公众号二维码
公众号二维码