Initial commit: Odoo 18.0-20251222 extra-addons
This commit is contained in:
5
account_invoice_section_sale_order/models/__init__.py
Normal file
5
account_invoice_section_sale_order/models/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from . import account_move
|
||||
from . import res_company
|
||||
from . import res_config_settings
|
||||
from . import res_partner
|
||||
from . import sale_order
|
||||
38
account_invoice_section_sale_order/models/account_move.py
Normal file
38
account_invoice_section_sale_order/models/account_move.py
Normal file
@@ -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
account_invoice_section_sale_order/models/res_company.py
Normal file
23
account_invoice_section_sale_order/models/res_company.py
Normal file
@@ -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,
|
||||
)
|
||||
@@ -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
account_invoice_section_sale_order/models/res_partner.py
Normal file
14
account_invoice_section_sale_order/models/res_partner.py
Normal file
@@ -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."
|
||||
)
|
||||
84
account_invoice_section_sale_order/models/sale_order.py
Normal file
84
account_invoice_section_sale_order/models/sale_order.py
Normal file
@@ -0,0 +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 groups in the invoice line.
|
||||
|
||||
Order the invoicing lines by groups and add lines section with
|
||||
the group name.
|
||||
Only do this for invoices targetting multiple groups
|
||||
"""
|
||||
invoices = super()._create_invoices(grouped=grouped, final=final, date=date)
|
||||
for invoice in invoices.sudo():
|
||||
if invoice.line_ids and (
|
||||
len(invoice.line_ids.mapped(invoice.line_ids._get_section_grouping()))
|
||||
== 1
|
||||
):
|
||||
continue
|
||||
sequence = 10
|
||||
# Because invoices are already created, this would require
|
||||
# an extra read access in order to read order fields.
|
||||
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 = []
|
||||
for group, move_line_ids in section_grouping_matrix.items():
|
||||
if group:
|
||||
section_lines.append(
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"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
|
||||
for move_line in (
|
||||
self.env["account.move.line"].sudo().browse(move_line_ids)
|
||||
):
|
||||
# Because invoices are already created, this would require
|
||||
# an extra write access in order to read order fields.
|
||||
move_line.sequence = sequence
|
||||
sequence += 10
|
||||
# Because invoices are already created, this would require
|
||||
# an extra write access in order to read order fields.
|
||||
invoice.line_ids = section_lines
|
||||
return invoices
|
||||
|
||||
def _get_invoice_section_name(self):
|
||||
"""Returns the text for the section name."""
|
||||
self.ensure_one()
|
||||
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
|
||||
Reference in New Issue
Block a user