Skip to content

Commit

Permalink
Merge commit 'refs/pull/1051/head' of github.com:OCA/account-invoicin…
Browse files Browse the repository at this point in the history
…g into merge-branch-2879-BSGZT_180-cc5f9ce1
  • Loading branch information
ajaniszewska-dev committed Jan 20, 2022
2 parents eb30fca + 6913009 commit 8cf2a3e
Show file tree
Hide file tree
Showing 16 changed files with 396 additions and 0 deletions.
1 change: 1 addition & 0 deletions account_invoice_section_picking/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
13 changes: 13 additions & 0 deletions account_invoice_section_picking/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright 2021 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
{
"name": "Acccount Invoice Section Picking",
"version": "14.0.1.0.0",
"summary": "Extension of Acccount Invoice Section Sale Order to allow "
"grouping of invoice lines according to delivery picking.",
"author": "Camptocamp, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/account-invoicing",
"license": "AGPL-3",
"category": "Accounting & Finance",
"depends": ["account_invoice_section_sale_order", "stock"],
}
3 changes: 3 additions & 0 deletions account_invoice_section_picking/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import account_move_line
from . import res_company
from . import stock_picking
48 changes: 48 additions & 0 deletions account_invoice_section_picking/models/account_move_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Copyright 2021 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import models


class AccountMoveLine(models.Model):

_inherit = "account.move.line"

def _get_section_group(self):
"""Group invoice lines according to uninvoiced delivery pickings"""
group = super()._get_section_group()
# If product is invoiced by delivered quantities:
# - filter on done pickings to avoid displaying backorders not yet
# processed
# - Remove pickings linked with same sale order lines if an invoice
# was created after its date_done
invoice_section_grouping = self.company_id.invoice_section_grouping
if (
invoice_section_grouping == "delivery_picking"
and self.product_id.invoice_policy == "delivery"
):
last_invoice_line = self.search(
[
("sale_line_ids", "in", self.sale_line_ids.ids),
("parent_state", "!=", "cancel"),
("id", "!=", self.id),
],
order="create_date",
limit=1,
)
pickings_already_invoiced = self.env["stock.picking"].search(
[
("date_done", "<=", last_invoice_line.create_date),
("id", "in", self.sale_line_ids.mapped("order_id.picking_ids").ids),
]
)
group = group.filtered(
lambda p: p.state == "done"
and p.id not in pickings_already_invoiced.ids
)
return group

def _get_section_grouping(self):
invoice_section_grouping = self.company_id.invoice_section_grouping
if invoice_section_grouping == "delivery_picking":
return "sale_line_ids.move_ids.picking_id"
return super()._get_section_grouping()
12 changes: 12 additions & 0 deletions account_invoice_section_picking/models/res_company.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# 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_grouping = fields.Selection(
selection_add=[("delivery_picking", "Group by delivery picking")],
ondelete={"delivery_picking": "set default"},
)
25 changes: 25 additions & 0 deletions account_invoice_section_picking/models/stock_picking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright 2021 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import models
from odoo.tools.safe_eval import safe_eval, time


class StockPicking(models.Model):

_inherit = "stock.picking"

def _get_invoice_section_name(self):
"""Returns the text for the section name."""
section_names = []
for pick in self:
naming_scheme = (
pick.partner_id.invoice_section_name_scheme
or pick.company_id.invoice_section_name_scheme
)
if naming_scheme:
section_names.append(
safe_eval(naming_scheme, {"object": pick, "time": time})
)
else:
section_names.append(pick.name)
return ", ".join(section_names)
1 change: 1 addition & 0 deletions account_invoice_section_picking/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Akim Juillerat <[email protected]>
3 changes: 3 additions & 0 deletions account_invoice_section_picking/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This module extends `account_invoice_section_sale_order` to allow using pickings
linked to the sale order line for the grouping invoice lines when invoicing sale
orders.
10 changes: 10 additions & 0 deletions account_invoice_section_picking/readme/ROADMAP.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
* The selection of pickings for the section name relies on the last invoice
that was created and is linked to the sale order line. In such case, there's
no guarantee the selection is correct if the quantity is reduced on prior
invoice lines.
* Moreover, as Odoo considers the draft invoices for the computation of
`qty_invoiced` on sales order line, we couldn't base the selection of the last
invoice on another field than the `create_date` although it would have been
cleaner to rely on a `date` field, but this one is only set on the posting.
Finally, defining another field for the generation of invoices wouldn't have
helped solve these issues.
2 changes: 2 additions & 0 deletions account_invoice_section_picking/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import test_account_invoice_section_picking
from . import test_account_invoice_section_picking_backorder
30 changes: 30 additions & 0 deletions account_invoice_section_picking/tests/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright 2021 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo.tests.common import Form, SavepointCase


class TestAccountInvoiceSectionPickingCommon(SavepointCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
cls.env.company.invoice_section_grouping = "delivery_picking"
cls.partner_1 = cls.env.ref("base.res_partner_1")
cls.product_1 = cls.env.ref("product.product_delivery_01")
stock = cls.env.ref("stock.stock_location_stock")
cls.env["stock.quant"]._update_available_quantity(cls.product_1, stock, 1000)
cls.product_1.invoice_policy = "order"
cls.order_1 = cls._create_order()
cls.order_2 = cls._create_order(order_line_name=cls.product_1.name)

@classmethod
def _create_order(cls, order_line_name=None):
order_form = Form(cls.env["sale.order"])
order_form.partner_id = cls.partner_1
with order_form:
with order_form.order_line.new() as line_form:
line_form.product_id = cls.product_1
line_form.product_uom_qty = 5
if order_line_name is not None:
line_form.name = order_line_name
return order_form.save()
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Copyright 2021 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)

from .common import TestAccountInvoiceSectionPickingCommon


class TestAccountInvoiceSectionPicking(TestAccountInvoiceSectionPickingCommon):
def test_group_by_delivery_picking(self):
self.order_1.action_confirm()
self.order_2.action_confirm()

invoice = (self.order_1 + self.order_2)._create_invoices()
self.assertEqual(len(invoice), 1)
result = {
10: (self.order_1.picking_ids.name, "line_section"),
20: (self.order_1.order_line.name, False),
30: (self.order_2.picking_ids.name, "line_section"),
40: (self.order_2.order_line.name, False),
}
for line in invoice.invoice_line_ids.sorted("sequence"):
self.assertEqual(line.name, result[line.sequence][0])
self.assertEqual(line.display_type, result[line.sequence][1])

def test_group_by_delivery_picking_multi_steps(self):
warehouse = self.env.ref("stock.warehouse0")
warehouse.write({"delivery_steps": "pick_pack_ship"})

self.order_1.action_confirm()
self.order_2.action_confirm()

invoice = (self.order_1 + self.order_2)._create_invoices()
self.assertEqual(len(invoice), 1)
result = {
10: (self.order_1.order_line.move_ids.picking_id.name, "line_section"),
20: (self.order_1.order_line.name, False),
30: (self.order_2.order_line.move_ids.picking_id.name, "line_section"),
40: (self.order_2.order_line.name, False),
}
for line in invoice.invoice_line_ids.sorted("sequence"):
self.assertEqual(line.name, result[line.sequence][0])
self.assertEqual(line.display_type, result[line.sequence][1])

def test_group_by_delivery_picking_backorder(self):
self.order_1.action_confirm()
self.order_2.action_confirm()

delivery_1_move = self.order_1.order_line.move_ids
delivery_1_move.move_line_ids.qty_done = 2.0
delivery_1 = delivery_1_move.picking_id
backorder_wiz_action = delivery_1.button_validate()
backorder_wiz = (
self.env["stock.backorder.confirmation"]
.with_context(**backorder_wiz_action.get("context"))
.create({})
)
backorder_wiz.process()
delivery_1_backorder_move = self.order_1.order_line.move_ids - delivery_1_move
delivery_1_backorder = delivery_1_backorder_move.picking_id
delivery_2 = self.order_2.order_line.move_ids.picking_id
invoice = (self.order_1 + self.order_2)._create_invoices()
result = {
10: (
", ".join([delivery_1.name, delivery_1_backorder.name]),
"line_section",
),
20: (self.order_1.order_line.name, False),
30: (delivery_2.name, "line_section"),
40: (self.order_2.order_line.name, False),
}
for line in invoice.invoice_line_ids.sorted("sequence"):
self.assertEqual(line.name, result[line.sequence][0])
self.assertEqual(line.display_type, result[line.sequence][1])
Loading

0 comments on commit 8cf2a3e

Please sign in to comment.