Skip to content

Commit

Permalink
Merge PR #1804 into 17.0
Browse files Browse the repository at this point in the history
Signed-off-by grindtildeath
  • Loading branch information
OCA-git-bot committed Oct 23, 2024
2 parents 9f94188 + 8cd1abd commit 93ffdf2
Show file tree
Hide file tree
Showing 12 changed files with 318 additions and 31 deletions.
5 changes: 5 additions & 0 deletions account_invoice_section_sale_order/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,9 @@
"license": "AGPL-3",
"category": "Accounting & Finance",
"depends": ["account", "sale"],
"data": [
"security/res_groups.xml",
"views/res_config_settings.xml",
"views/res_partner.xml",
],
}
4 changes: 4 additions & 0 deletions account_invoice_section_sale_order/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
from . import account_move
from . import res_company
from . import res_config_settings
from . import res_partner
from . import sale_order
38 changes: 38 additions & 0 deletions account_invoice_section_sale_order/models/account_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Copyright 2021 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import _, api, models
from odoo.exceptions import UserError


class AccountMove(models.Model):
_inherit = "account.move"

def _get_ordered_invoice_lines(self):
"""Sort invoice lines according to the section ordering"""
return self.invoice_line_ids.sorted(
key=self.env["account.move.line"]._get_section_ordering()
)


class AccountMoveLine(models.Model):
_inherit = "account.move.line"

def _get_section_group(self):
"""Return the section group to be used for a single invoice line"""
self.ensure_one()
return self.mapped(self._get_section_grouping())

def _get_section_grouping(self):
"""Defines the grouping relation from the invoice lines to be used.
Meant to be overriden, in order to allow custom grouping.
"""
invoice_section_grouping = self.company_id.invoice_section_grouping
if invoice_section_grouping == "sale_order":
return "sale_line_ids.order_id"
raise UserError(_("Unrecognized invoice_section_grouping"))

@api.model
def _get_section_ordering(self):
"""Function to sort invoice lines before grouping"""
return lambda r: r.mapped(r._get_section_grouping())
23 changes: 23 additions & 0 deletions account_invoice_section_sale_order/models/res_company.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2021 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import fields, models


class ResCompany(models.Model):
_inherit = "res.company"

invoice_section_name_scheme = fields.Char(
help="This is the name of the sections on invoices when generated from "
"sales orders. Keep empty to use default. You can use a python "
"expression with the 'object' (representing sale order) and 'time'"
" variables."
)

invoice_section_grouping = fields.Selection(
[
("sale_order", "Group by sale Order"),
],
help="Defines object used to group invoice lines",
default="sale_order",
required=True,
)
18 changes: 18 additions & 0 deletions account_invoice_section_sale_order/models/res_config_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright 2021 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import fields, models


class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"

invoice_section_name_scheme = fields.Char(
related="company_id.invoice_section_name_scheme",
readonly=False,
)

invoice_section_grouping = fields.Selection(
related="company_id.invoice_section_grouping",
readonly=False,
required=True,
)
14 changes: 14 additions & 0 deletions account_invoice_section_sale_order/models/res_partner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2021 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import fields, models


class ResPartner(models.Model):
_inherit = "res.partner"

invoice_section_name_scheme = fields.Char(
help="This is the name of the sections on invoices when generated from "
"sales orders. Keep empty to use default. You can use a python "
"expression with the 'object' (representing sale order) and 'time'"
" variables."
)
62 changes: 43 additions & 19 deletions account_invoice_section_sale_order/models/sale_order.py
Original file line number Diff line number Diff line change
@@ -1,60 +1,84 @@
# Copyright 2020 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
from collections import OrderedDict

from odoo import models
from odoo.tools.safe_eval import safe_eval, time


class SaleOrder(models.Model):
_inherit = "sale.order"

def _create_invoices(self, grouped=False, final=False, date=None):
"""Add sections by sale order in the invoice line.
"""Add sections by groups in the invoice line.
Order the invoicing lines by sale order and add lines section with
the sale order name.
Only do this for invoices targetting multiple sale order
Order the invoicing lines by groups and add lines section with
the group name.
Only do this for invoices targetting multiple groups
"""
invoice_ids = super()._create_invoices(grouped=grouped, final=final, date=date)
for invoice in invoice_ids:
if len(invoice.line_ids.mapped("sale_line_ids.order_id.id")) == 1:
if (
len(invoice.line_ids.mapped(invoice.line_ids._get_section_grouping()))
== 1
):
continue
so = None
sequence = 10
move_lines = invoice._get_ordered_invoice_lines()
# Group move lines according to their sale order
section_grouping_matrix = OrderedDict()
for move_line in move_lines:
group = move_line._get_section_group()
section_grouping_matrix.setdefault(group, []).append(move_line.id)
# Prepare section lines for each group
section_lines = []
lines = self._get_ordered_invoice_lines(invoice)
for line in lines:
if line.sale_line_ids.order_id and so != line.sale_line_ids.order_id:
so = line.sale_line_ids.order_id
for group, move_line_ids in section_grouping_matrix.items():
if group:
section_lines.append(
(
0,
0,
{
"name": so._get_saleorder_section_name(),
"name": group._get_invoice_section_name(),
"display_type": "line_section",
"sequence": sequence,
# see test: test_create_invoice_with_default_journal
# forcing the account_id is needed to avoid
# incorrect default value
"account_id": False,
# see test: test_create_invoice_with_currency
# if the currency is not set with the right value
# the total amount will be wrong
# because all line do not have the same currency
"currency_id": invoice.currency_id.id,
},
)
)
sequence += 10
if line.display_type == "line_section":
# add extra indent for existing SO Sections
line.name = f"- {line.name}"
line.sequence = sequence
sequence += 10
for move_line in self.env["account.move.line"].browse(move_line_ids):
if move_line.display_type == "line_section":
# add extra indent for existing SO Sections
move_line.name = f"- {move_line.name}"
move_line.sequence = sequence
sequence += 10
invoice.line_ids = section_lines

return invoice_ids

def _get_ordered_invoice_lines(self, invoice):
return invoice.invoice_line_ids.sorted(
key=lambda r: r.sale_line_ids.order_id.id
)

def _get_saleorder_section_name(self):
def _get_invoice_section_name(self):
"""Returns the text for the section name."""
self.ensure_one()
if self.client_order_ref:
naming_scheme = (
self.partner_invoice_id.invoice_section_name_scheme
or self.company_id.invoice_section_name_scheme
)
if naming_scheme:
return safe_eval(naming_scheme, {"object": self, "time": time})
elif self.client_order_ref:
return "{} - {}".format(self.name, self.client_order_ref or "")
else:
return self.name
9 changes: 9 additions & 0 deletions account_invoice_section_sale_order/readme/CONFIGURATION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
To allow customization of the name of the section, user should be part of group
`Allow customization of invoice section name from sale order`.

A naming scheme can be defined per company on the configuration page in the
`Customer Invoices` section, or per partner in the accounting page, using
python expression.

The object used for the grouping can be customized by installing extra module
(e.g. `account_invoice_section_picking`).
9 changes: 9 additions & 0 deletions account_invoice_section_sale_order/security/res_groups.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="group_sale_order_invoice_section_name" model="res.groups">
<field
name="name"
>Allow customization of invoice section name from sale order</field>
<field name="category_id" ref="base.module_category_usability" />
</record>
</odoo>
Loading

0 comments on commit 93ffdf2

Please sign in to comment.