Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[16.0][FW] account_invoice_section_sale_order: multiple ports from 15.0 #1803

Open
wants to merge 4 commits into
base: 16.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
40 changes: 40 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,40 @@
# 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,
)
19 changes: 19 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,19 @@
# 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})

Check warning on line 80 in account_invoice_section_sale_order/models/sale_order.py

View check run for this annotation

Codecov / codecov/patch

account_invoice_section_sale_order/models/sale_order.py#L80

Added line #L80 was not covered by tests
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`).
2 changes: 2 additions & 0 deletions account_invoice_section_sale_order/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
* `Camptocamp <https://www.camptocamp.com>`_

* Thierry Ducrest <thierry.ducrest@camptocamp.com>
* Akim Juillerat <akim.juillerat@camptocamp.com>
* Hiep Nguyen Hoang <hiepnh@trobz.com>

* `Dynapps <https://www.dynapps.eu>`_

* Jeroen Evens
Expand Down
3 changes: 3 additions & 0 deletions account_invoice_section_sale_order/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ to know which invoice line belongs to which sale order.

This module helps by grouping invoicing lines into sections with the name of the
targeted sale order.

The name of the section can be customized by company and partner.

This is only done when an invoice targets multiple sale order.
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
Loading