19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:32:12 +01:00
parent 79f83631d5
commit 73afc09215
6267 changed files with 1534193 additions and 1130106 deletions

View file

@ -0,0 +1,4 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import test_reinvoice
from . import test_sale_project_stock_profitability

View file

@ -0,0 +1,81 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.stock.tests.common import TestStockCommon
class TestReInvoice(TestStockCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.partner = cls.env['res.partner'].create({'name': 'Test Partner'})
cls.sale_order = cls.env['sale.order'].create({
'partner_id': cls.partner.id,
'partner_invoice_id': cls.partner.id,
'partner_shipping_id': cls.partner.id,
})
cls.project = cls.env['project.project'].create({
'name': 'Project',
'reinvoiced_sale_order_id': cls.sale_order.id,
})
cls.picking_out = cls.PickingObj.create({
'picking_type_id': cls.picking_type_out.id,
'location_id': cls.stock_location.id,
'location_dest_id': cls.customer_location.id,
'project_id': cls.project.id,
})
cls.picking_out.picking_type_id.analytic_costs = True
cls.reinvoicable_product_at_cost, cls.reinvoicable_product_sales_price = cls.env['product.product'].create([
{
'name': 'product_order_cost',
'standard_price': 100.0,
'expense_policy': 'cost',
},
{
'name': 'product_order_cost',
'list_price': 500.0,
'expense_policy': 'sales_price',
},
])
cls.sale_order.action_confirm()
def test_picking_reinvoicing(self):
move_values = {
'product_uom': self.uom_unit.id,
'picking_id': self.picking_out.id,
'location_id': self.stock_location.id,
'location_dest_id': self.customer_location.id,
}
self.MoveObj.create([
{
**move_values,
'product_id': self.reinvoicable_product_at_cost.id,
'product_uom_qty': 3,
},
{
**move_values,
'product_id': self.reinvoicable_product_sales_price.id,
'product_uom_qty': 5,
},
])
self.picking_out.with_user(self.user_stock_user).action_confirm()
self.picking_out.with_user(self.user_stock_user).button_validate()
self.assertEqual(len(self.sale_order.order_line), 2, 'There should be 2 lines on the SO')
new_sale_order_line1 = self.sale_order.order_line.filtered(lambda sol: sol.product_id == self.reinvoicable_product_at_cost)
self.assertTrue(new_sale_order_line1, 'A new sale line should have been created with the reinvoicable product at cost')
self.assertEqual(
(new_sale_order_line1.price_unit, new_sale_order_line1.qty_delivered, new_sale_order_line1.product_uom_qty, new_sale_order_line1.qty_invoiced),
(self.reinvoicable_product_at_cost.standard_price, 3, 3, 0),
'Sale line is wrong after confirming the picking',
)
self.assertEqual(new_sale_order_line1.qty_delivered_method, 'stock_move', 'Delivered quantity of SO line should be computed by stock move')
new_sale_order_line2 = self.sale_order.order_line.filtered(lambda sol: sol.product_id == self.reinvoicable_product_sales_price)
self.assertTrue(new_sale_order_line2, 'A new sale line should have been created with the reinvoicable product at sales price')
self.assertEqual(
(new_sale_order_line2.price_unit, new_sale_order_line2.qty_delivered, new_sale_order_line2.product_uom_qty, new_sale_order_line2.qty_invoiced),
(self.reinvoicable_product_sales_price.list_price, 5, 5, 0),
'Sale line is wrong after confirming the picking',
)
self.assertEqual(new_sale_order_line2.qty_delivered_method, 'stock_move', 'Delivered quantity of SO line should be computed by stock move')

View file

@ -0,0 +1,98 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import Command, fields
from odoo.addons.sale_project.tests.test_project_profitability import TestProjectProfitabilityCommon
from odoo.addons.stock_account.tests.test_anglo_saxon_valuation_reconciliation_common import ValuationReconciliationTestCommon
from odoo.tests import tagged
@tagged('-at_install', 'post_install')
class TestSaleProjectStockProfitability(TestProjectProfitabilityCommon, ValuationReconciliationTestCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
project_template = cls.env['project.project'].create({
'name': 'sale_project_stock project template',
'account_id': cls.analytic_account.id,
})
cls.cogs_account = cls.env['account.account'].search([
('name', '=', 'Cost of Goods Sold'),
('company_ids', 'in', cls.env.company.id),
], limit=1)
cls.auto_valuated_product = cls.env['product.product'].create({
'name': 'auto valuated product',
'is_storable': True,
'categ_id': cls.stock_account_product_categ.id,
'standard_price': 12.0,
'list_price': 24.0,
})
cls.product_superb_service = cls.env['product.product'].create({
'name': 'product that creates project on order',
'type': 'service',
'standard_price': 10.0,
'list_price': 20.0,
'service_tracking': 'project_only',
'project_template_id': project_template.id,
})
def test_report_invoice_items_anglo_saxon_automatic_valuation(self):
""" An invoice can have some lines which should be classified/displayed under the 'Costs'
section of a project's profitability report (specifically, COGS lines).
"""
self.env.company.anglo_saxon_accounting = True
self.stock_account_product_categ.write({
'property_account_expense_categ_id': self.cogs_account.id,
'property_cost_method': 'average',
})
service_product = self.product_superb_service
avco_product = self.auto_valuated_product
other_avco_product = self.env['product.product'].create({
'name': 'other avco product',
'is_storable': True,
'categ_id': avco_product.categ_id.id,
'standard_price': 16.0,
'list_price': 32.0,
})
sale_order = self.env['sale.order'].create({
'partner_id': self.partner.id,
'order_line': [Command.create({
'product_id': service_product.id,
'product_uom_qty': 10,
}), Command.create({
'product_id': avco_product.id,
'product_uom_qty': 10,
}), Command.create({
'product_id': other_avco_product.id,
'product_uom_qty': 10,
})],
})
sale_order.action_confirm()
delivery = sale_order.picking_ids
delivery.move_ids.quantity = 10
delivery.button_validate()
sale_order._create_invoices()
invoice = sale_order.invoice_ids[0]
invoice.invoice_date = fields.Date.today()
invoice.action_post()
panel_data = sale_order.project_ids.get_panel_data()
self.assertEqual(
panel_data['profitability_items']['costs'],
{
'data': [{
'action': {
'args': f'["cost_of_goods_sold", [["id", "in", [{invoice.id}]]], {invoice.id}]',
'name': 'action_profitability_items',
'type': 'object',
},
'id': 'cost_of_goods_sold',
'billed': -280.0,
'sequence': 21,
'to_bill': 0.0,
}],
'total': {
'billed': -280.0,
'to_bill': 0.0,
}
}
)