[MIG] project_link_from_invoice: migrate to 18.0
This commit is contained in:
193
project_link_from_invoice/tests/test_account_move.py
Normal file
193
project_link_from_invoice/tests/test_account_move.py
Normal file
@@ -0,0 +1,193 @@
|
||||
# Copyright 2022 Stéphan Sainléger (Elabore)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import Command
|
||||
from odoo.tests import tagged
|
||||
from odoo.addons.sale_project.tests.common import TestSaleProjectCommon
|
||||
|
||||
|
||||
@tagged("post_install", "-at_install")
|
||||
class TestProjectLinkFromInvoice(TestSaleProjectCommon):
|
||||
"""Tests for the project_link_from_invoice module.
|
||||
|
||||
Verifies that invoices correctly expose related project(s) via
|
||||
the computed fields project_ids, project_count and projects_name.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
|
||||
|
||||
# Service product that creates a task in a new project on SO confirmation
|
||||
cls.service_product = cls.env["product.product"].create(
|
||||
{
|
||||
"name": "Service with Project",
|
||||
"type": "service",
|
||||
"invoice_policy": "order",
|
||||
"service_tracking": "task_in_project",
|
||||
}
|
||||
)
|
||||
|
||||
# Partner used for orders and invoices
|
||||
cls.partner = cls.env["res.partner"].create({"name": "Test Partner"})
|
||||
|
||||
def _confirm_sale_order(self, product, qty=1):
|
||||
"""Create and confirm a sale order with one service line."""
|
||||
so = self.env["sale.order"].create(
|
||||
{
|
||||
"partner_id": self.partner.id,
|
||||
"order_line": [
|
||||
Command.create(
|
||||
{
|
||||
"product_id": product.id,
|
||||
"product_uom_qty": qty,
|
||||
}
|
||||
)
|
||||
],
|
||||
}
|
||||
)
|
||||
so.action_confirm()
|
||||
return so
|
||||
|
||||
def _invoice_sale_order(self, sale_order):
|
||||
"""Create and post an invoice from a confirmed sale order."""
|
||||
invoice_wizard = (
|
||||
self.env["sale.advance.payment.inv"]
|
||||
.with_context(
|
||||
active_model="sale.order",
|
||||
active_ids=sale_order.ids,
|
||||
active_id=sale_order.id,
|
||||
)
|
||||
.create({"advance_payment_method": "delivered"})
|
||||
)
|
||||
invoices = invoice_wizard._create_invoices(sale_order)
|
||||
invoices.action_post()
|
||||
return invoices
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Tests
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def test_no_project_on_unrelated_invoice(self):
|
||||
"""An invoice not linked to any sale order has no related project."""
|
||||
invoice = self.env["account.move"].create(
|
||||
{
|
||||
"move_type": "out_invoice",
|
||||
"partner_id": self.partner.id,
|
||||
"invoice_line_ids": [
|
||||
Command.create(
|
||||
{
|
||||
"name": "Manual line",
|
||||
"quantity": 1,
|
||||
"price_unit": 100.0,
|
||||
}
|
||||
)
|
||||
],
|
||||
}
|
||||
)
|
||||
self.assertEqual(invoice.project_count, 0)
|
||||
self.assertFalse(invoice.project_ids)
|
||||
self.assertEqual(invoice.projects_name, "")
|
||||
|
||||
def test_single_project_linked_to_invoice(self):
|
||||
"""An invoice from a SO that created a task has one related project."""
|
||||
so = self._confirm_sale_order(self.service_product)
|
||||
# The SO must have generated a task in a project
|
||||
task = so.tasks_ids[:1]
|
||||
self.assertTrue(task, "Sale order confirmation should have created a task")
|
||||
project = task.project_id
|
||||
self.assertTrue(project, "Task should belong to a project")
|
||||
|
||||
invoice = self._invoice_sale_order(so)
|
||||
self.assertEqual(len(invoice), 1)
|
||||
self.assertEqual(invoice.project_count, 1)
|
||||
self.assertIn(project, invoice.project_ids)
|
||||
self.assertIn(project.name, invoice.projects_name)
|
||||
|
||||
def test_multiple_projects_linked_to_invoice(self):
|
||||
"""An invoice from two SOs with different projects shows both projects."""
|
||||
so1 = self._confirm_sale_order(self.service_product)
|
||||
so2 = self._confirm_sale_order(self.service_product)
|
||||
|
||||
project1 = so1.tasks_ids[:1].project_id
|
||||
project2 = so2.tasks_ids[:1].project_id
|
||||
self.assertTrue(project1 and project2)
|
||||
# Ensure the two SOs created different projects
|
||||
self.assertNotEqual(project1, project2)
|
||||
|
||||
# Create a single invoice covering both sale orders
|
||||
invoice_wizard = (
|
||||
self.env["sale.advance.payment.inv"]
|
||||
.with_context(
|
||||
active_model="sale.order",
|
||||
active_ids=(so1 + so2).ids,
|
||||
)
|
||||
.create({"advance_payment_method": "delivered"})
|
||||
)
|
||||
invoices = invoice_wizard._create_invoices(so1 + so2)
|
||||
invoices.action_post()
|
||||
|
||||
# Find the invoice that covers both SO lines
|
||||
combined_invoice = invoices[:1]
|
||||
self.assertGreaterEqual(combined_invoice.project_count, 2)
|
||||
self.assertIn(project1, combined_invoice.project_ids)
|
||||
self.assertIn(project2, combined_invoice.project_ids)
|
||||
|
||||
def test_action_open_projects_single(self):
|
||||
"""action_open_projects returns a form view when there is one project."""
|
||||
so = self._confirm_sale_order(self.service_product)
|
||||
invoice = self._invoice_sale_order(so)
|
||||
self.assertEqual(invoice.project_count, 1)
|
||||
|
||||
action = invoice.action_open_projects()
|
||||
self.assertEqual(action["res_id"], invoice.project_ids.id)
|
||||
view_types = [v[1] for v in action["views"]]
|
||||
self.assertIn("form", view_types)
|
||||
self.assertNotIn("list", view_types)
|
||||
|
||||
def test_action_open_projects_multiple(self):
|
||||
"""action_open_projects returns a list view when there are multiple projects."""
|
||||
so1 = self._confirm_sale_order(self.service_product)
|
||||
so2 = self._confirm_sale_order(self.service_product)
|
||||
|
||||
invoice_wizard = (
|
||||
self.env["sale.advance.payment.inv"]
|
||||
.with_context(
|
||||
active_model="sale.order",
|
||||
active_ids=(so1 + so2).ids,
|
||||
)
|
||||
.create({"advance_payment_method": "delivered"})
|
||||
)
|
||||
invoices = invoice_wizard._create_invoices(so1 + so2)
|
||||
invoices.action_post()
|
||||
|
||||
combined_invoice = invoices[:1]
|
||||
if combined_invoice.project_count < 2:
|
||||
self.skipTest("Combined invoice does not span multiple projects")
|
||||
|
||||
action = combined_invoice.action_open_projects()
|
||||
view_types = [v[1] for v in action["views"]]
|
||||
self.assertIn("list", view_types)
|
||||
self.assertIn("form", view_types)
|
||||
self.assertNotIn("target", action)
|
||||
|
||||
def test_projects_name_separator(self):
|
||||
"""projects_name joins multiple project names with ' ; '."""
|
||||
so1 = self._confirm_sale_order(self.service_product)
|
||||
so2 = self._confirm_sale_order(self.service_product)
|
||||
|
||||
invoice_wizard = (
|
||||
self.env["sale.advance.payment.inv"]
|
||||
.with_context(
|
||||
active_model="sale.order",
|
||||
active_ids=(so1 + so2).ids,
|
||||
)
|
||||
.create({"advance_payment_method": "delivered"})
|
||||
)
|
||||
invoices = invoice_wizard._create_invoices(so1 + so2)
|
||||
invoices.action_post()
|
||||
|
||||
combined_invoice = invoices[:1]
|
||||
if combined_invoice.project_count >= 2:
|
||||
self.assertIn(" ; ", combined_invoice.projects_name)
|
||||
Reference in New Issue
Block a user