Initial commit: Odoo 18.0-20251222 extra-addons
This commit is contained in:
1
sale_stock_picking_invoicing/tests/__init__.py
Normal file
1
sale_stock_picking_invoicing/tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import test_sale_stock
|
||||
442
sale_stock_picking_invoicing/tests/test_sale_stock.py
Normal file
442
sale_stock_picking_invoicing/tests/test_sale_stock.py
Normal file
@@ -0,0 +1,442 @@
|
||||
# Copyright (C) 2021-TODAY Akretion
|
||||
# @author Magno Costa <magno.costa@akretion.com.br>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import exceptions
|
||||
from odoo.tests import Form
|
||||
|
||||
from odoo.addons.stock_picking_invoicing.tests.common import TestPickingInvoicingCommon
|
||||
|
||||
|
||||
class TestSaleStock(TestPickingInvoicingCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.account_move_model = cls.env["account.move"]
|
||||
cls.invoice_wizard = cls.env["stock.invoice.onshipping"]
|
||||
cls.stock_return_picking = cls.env["stock.return.picking"]
|
||||
cls.stock_picking = cls.env["stock.picking"]
|
||||
# In order to avoid errors in the tests CI environment when the tests
|
||||
# Create of Invoice by Sale Order using sale.advance.payment.inv object
|
||||
# is necessary let default policy as sale_order, just affect demo data.
|
||||
# TODO: Is there other form to avoid this problem?
|
||||
cls.companies = cls.env["res.company"].search(
|
||||
[("sale_invoicing_policy", "=", "sale_order")]
|
||||
)
|
||||
for company in cls.companies:
|
||||
company.sale_invoicing_policy = "stock_picking"
|
||||
|
||||
def test_01_sale_stock_return(self):
|
||||
"""
|
||||
Test a SO with a product invoiced on delivery. Deliver and invoice
|
||||
the SO, then do a return of the picking. Check that a refund
|
||||
invoice is well generated.
|
||||
"""
|
||||
# intial so
|
||||
self.partner = self.env.ref(
|
||||
"sale_stock_picking_invoicing.res_partner_2_address"
|
||||
)
|
||||
self.product = self.env.ref("product.product_delivery_01")
|
||||
so_vals = {
|
||||
"partner_id": self.partner.id,
|
||||
"partner_invoice_id": self.partner.id,
|
||||
"partner_shipping_id": self.partner.id,
|
||||
"order_line": [
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"name": self.product.name,
|
||||
"product_id": self.product.id,
|
||||
"product_uom_qty": 3.0,
|
||||
"product_uom": self.product.uom_id.id,
|
||||
"price_unit": self.product.list_price,
|
||||
},
|
||||
)
|
||||
],
|
||||
"pricelist_id": self.env.ref(
|
||||
"sale_stock_picking_invoicing.demo_pricelist"
|
||||
).id,
|
||||
}
|
||||
self.so = self.env["sale.order"].create(so_vals)
|
||||
|
||||
# confirm our standard so, check the picking
|
||||
self.so.action_confirm()
|
||||
self.assertTrue(
|
||||
self.so.picking_ids,
|
||||
'Sale Stock: no picking created for "invoice on '
|
||||
'delivery" storable products',
|
||||
)
|
||||
|
||||
# set stock.picking to be invoiced
|
||||
self.assertTrue(
|
||||
len(self.so.picking_ids) == 1,
|
||||
"More than one stock " "picking for sale.order",
|
||||
)
|
||||
|
||||
# Check Sale Invoicing Policy Warning to force create Invoice from Picking
|
||||
with self.assertRaises(exceptions.UserError):
|
||||
self.so._create_invoices(final=True)
|
||||
|
||||
self.so.picking_ids.set_to_be_invoiced()
|
||||
|
||||
# validate stock.picking
|
||||
stock_picking = self.so.picking_ids
|
||||
|
||||
# compare sale.order.line with stock.move
|
||||
stock_move = stock_picking.move_ids
|
||||
sale_order_line = self.so.order_line
|
||||
|
||||
sm_fields = [key for key in self.env["stock.move"]._fields.keys()]
|
||||
sol_fields = [key for key in self.env["sale.order.line"]._fields.keys()]
|
||||
|
||||
skipped_fields = [
|
||||
"id",
|
||||
# 'S00029/FURN_7777: Stock>Customers' != 'S00029 - Office Chair'
|
||||
"display_name",
|
||||
"state",
|
||||
# Price Unit in move is different from sale line
|
||||
# TODO: Should be equal? After Confirmed stock picking
|
||||
# the value will be change based Stock Valuation
|
||||
# configuration.
|
||||
"price_unit",
|
||||
# There are a diference for field Name
|
||||
# '[FURN_7777] Office Chair' != 'Office Chair'
|
||||
"name",
|
||||
]
|
||||
common_fields = list(set(sm_fields) & set(sol_fields) - set(skipped_fields))
|
||||
|
||||
for field in common_fields:
|
||||
self.assertEqual(
|
||||
stock_move[field],
|
||||
sale_order_line[field],
|
||||
f"Field {field} failed to transfer from sale.order.line "
|
||||
"to stock.move",
|
||||
)
|
||||
|
||||
def test_picking_sale_order_product_and_service(self):
|
||||
"""
|
||||
Test Sale Order with product and service
|
||||
"""
|
||||
|
||||
# Ensure the company's sale_invoicing_policy is set to "stock_picking"
|
||||
company = self.env.ref("base.main_company")
|
||||
company.sale_invoicing_policy = "stock_picking"
|
||||
sale_order_form = sale_order_form = Form(
|
||||
self.env.ref("sale_stock_picking_invoicing.main_company-sale_order_2")
|
||||
)
|
||||
# Necessary to get the currency
|
||||
sale_order_form.pricelist_id = self.env.ref(
|
||||
"sale_stock_picking_invoicing.demo_pricelist"
|
||||
)
|
||||
sale_order_2 = sale_order_form.save()
|
||||
sale_order_2.action_confirm()
|
||||
# Method to create invoice in sale order should work only
|
||||
# for lines where products are of TYPE Service
|
||||
sale_order_2._create_invoices()
|
||||
# Should be exist one Invoice
|
||||
self.assertEqual(1, sale_order_2.invoice_count)
|
||||
for invoice in sale_order_2.invoice_ids:
|
||||
line = invoice.invoice_line_ids.filtered(
|
||||
lambda ln: ln.product_id.type == "service"
|
||||
)
|
||||
self.assertEqual(line.product_id.type, "service")
|
||||
# Invoice of Service
|
||||
invoice.action_post()
|
||||
self.assertEqual(
|
||||
invoice.state, "posted", "Invoice should be in state Posted"
|
||||
)
|
||||
|
||||
picking = sale_order_2.picking_ids
|
||||
# Only the line of Type Product
|
||||
self.assertEqual(len(picking.move_ids_without_package), 1)
|
||||
self.assertEqual(picking.invoice_state, "2binvoiced")
|
||||
self.picking_move_state(picking)
|
||||
|
||||
# Test Create Invoice from Sale when raise UseError
|
||||
context = {
|
||||
"active_model": "sale.order",
|
||||
"active_id": sale_order_2.id,
|
||||
"active_ids": sale_order_2.ids,
|
||||
}
|
||||
payment = (
|
||||
self.env["sale.advance.payment.inv"]
|
||||
.with_context(**context)
|
||||
.create(
|
||||
{
|
||||
"advance_payment_method": "delivered",
|
||||
}
|
||||
)
|
||||
)
|
||||
with self.assertRaises(exceptions.UserError):
|
||||
payment.with_context(**context).create_invoices()
|
||||
|
||||
invoice = self.create_invoice_wizard(picking)
|
||||
self.assertEqual(picking.invoice_state, "invoiced")
|
||||
self.assertIn(invoice, picking.invoice_ids)
|
||||
self.assertIn(picking, invoice.picking_ids)
|
||||
# Picking with Partner Shipping from Sale Order
|
||||
self.assertEqual(picking.partner_id, sale_order_2.partner_shipping_id)
|
||||
# Invoice created with Partner Invoice from Sale Order
|
||||
self.assertEqual(invoice.partner_id, sale_order_2.partner_invoice_id)
|
||||
# Invoice created with Partner Shipping from Picking
|
||||
self.assertEqual(invoice.partner_shipping_id, picking.partner_id)
|
||||
# When informed Payment Term in Sale Orde should be
|
||||
# used instead of the default in Partner.
|
||||
self.assertEqual(invoice.invoice_payment_term_id, sale_order_2.payment_term_id)
|
||||
|
||||
# 1 Product 1 Note should be created
|
||||
self.assertEqual(len(invoice.invoice_line_ids), 2)
|
||||
|
||||
# In the Sale Order should be exist two Invoices, one
|
||||
# for Product other for Service
|
||||
self.assertEqual(2, sale_order_2.invoice_count)
|
||||
|
||||
# Confirm Invoice
|
||||
invoice.action_post()
|
||||
self.assertEqual(invoice.state, "posted", "Invoice should be in state Posted.")
|
||||
|
||||
# Check Invoiced QTY
|
||||
for line in sale_order_2.order_line.filtered(
|
||||
lambda ln: ln.product_id.type == "product"
|
||||
):
|
||||
self.assertEqual(line.product_uom_qty, line.qty_invoiced)
|
||||
# Test the qty_to_invoice
|
||||
line.product_id.invoice_policy = "order"
|
||||
self.assertEqual(line.qty_to_invoice, 0.0)
|
||||
|
||||
# Check if the Sale Lines fields are equals to Invoice Lines
|
||||
sol_fields = [key for key in self.env["sale.order.line"]._fields.keys()]
|
||||
|
||||
acl_fields = [key for key in self.env["account.move.line"]._fields.keys()]
|
||||
|
||||
skipped_fields = [
|
||||
"id",
|
||||
"display_name",
|
||||
"state",
|
||||
"create_date",
|
||||
# By th TAX 15% automatic add in invoice the value change
|
||||
"price_total",
|
||||
# Necessary after call onchange_partner_id
|
||||
"write_date",
|
||||
"__last_update",
|
||||
# Field sequence add in creation of Invoice
|
||||
"sequence",
|
||||
# In the sale.orde.line display_type has only line_section
|
||||
# and line_note, the acccount.move.line has more options
|
||||
"display_type",
|
||||
]
|
||||
|
||||
common_fields = list(set(acl_fields) & set(sol_fields) - set(skipped_fields))
|
||||
sale_order_line = picking.move_ids_without_package.filtered(
|
||||
lambda ln: ln.sale_line_id
|
||||
).sale_line_id
|
||||
invoice_lines = picking.invoice_ids.invoice_line_ids.filtered(
|
||||
lambda ln: ln.product_id
|
||||
)
|
||||
# Necessary for get analytic_precision
|
||||
# this problem only occours in the tests, by some reason not
|
||||
# identify yet, but works in the screen the default behavior
|
||||
with Form(invoice_lines) as line:
|
||||
line.save()
|
||||
|
||||
for field in common_fields:
|
||||
self.assertEqual(
|
||||
sale_order_line[field],
|
||||
invoice_lines[field],
|
||||
f"Field {field} failed to transfer from sale.order.line "
|
||||
"to account.invoice.line",
|
||||
)
|
||||
|
||||
# Return Picking
|
||||
picking_devolution = self.return_picking_wizard(picking)
|
||||
|
||||
self.assertEqual(picking_devolution.invoice_state, "2binvoiced")
|
||||
for line in picking_devolution.move_ids:
|
||||
self.assertEqual(line.invoice_state, "2binvoiced")
|
||||
|
||||
self.picking_move_state(picking_devolution)
|
||||
self.assertEqual(picking_devolution.state, "done", "Change state fail.")
|
||||
|
||||
invoice_devolution = self.create_invoice_wizard(picking_devolution)
|
||||
# Confirm Invoice
|
||||
invoice_devolution.action_post()
|
||||
self.assertEqual(
|
||||
invoice_devolution.state, "posted", "Invoice should be in state Posted"
|
||||
)
|
||||
# Test need to be comment because there are a problem with module
|
||||
# sale_line_refund_to_invoice_qty
|
||||
# https://github.com/OCA/account-invoicing/blob/
|
||||
# 14.0/sale_line_refund_to_invoice_qty/models/sale.py#L20
|
||||
# when the tests run in CI of the repo the test fail.
|
||||
# TODO: The module should be compatible with this case?
|
||||
# Check Invoiced QTY update after Refund
|
||||
# for line in sale_order_2.order_line:
|
||||
# # Check Product line
|
||||
# if line.product_id.type == "product":
|
||||
# # self.assertEqual(0.0, line.qty_invoiced)
|
||||
|
||||
def test_picking_invoicing_partner_shipping_invoiced(self):
|
||||
"""
|
||||
Test the invoice generation grouped by partner/product with 2
|
||||
picking and 2 moves per picking, but Partner to Shipping is
|
||||
different from Partner to Invoice.
|
||||
"""
|
||||
sale_order_1 = self.env.ref(
|
||||
"sale_stock_picking_invoicing.main_company-sale_order_1"
|
||||
)
|
||||
sale_order_1.action_confirm()
|
||||
picking = sale_order_1.picking_ids
|
||||
self.picking_move_state(picking)
|
||||
sale_order_2 = self.env.ref(
|
||||
"sale_stock_picking_invoicing.main_company-sale_order_2"
|
||||
)
|
||||
sale_order_2.action_confirm()
|
||||
picking2 = sale_order_2.picking_ids
|
||||
self.picking_move_state(picking2)
|
||||
pickings = picking | picking2
|
||||
invoice = self.create_invoice_wizard(pickings)
|
||||
# Groupping Invoice
|
||||
self.assertEqual(len(invoice), 1)
|
||||
# Invoice should be create with the partner_invoice_id
|
||||
self.assertEqual(invoice.partner_id, sale_order_1.partner_invoice_id)
|
||||
# Invoice partner shipping should be the same of picking
|
||||
self.assertEqual(invoice.partner_shipping_id, picking.partner_id)
|
||||
self.assertIn(invoice, picking.invoice_ids)
|
||||
self.assertIn(picking, invoice.picking_ids)
|
||||
self.assertIn(invoice, picking2.invoice_ids)
|
||||
self.assertIn(picking2, invoice.picking_ids)
|
||||
|
||||
# TODO: Grouping sale line with KEY should be analise
|
||||
# self.assertEqual(len(invoice.invoice_line_ids), 2)
|
||||
# 3 Products, 2 Note and 2 Section
|
||||
self.assertEqual(len(invoice.invoice_line_ids), 7)
|
||||
for inv_line in invoice.invoice_line_ids.filtered(lambda ln: ln.product_id):
|
||||
self.assertTrue(inv_line.tax_ids, "Error to map Sale Tax in invoice.line.")
|
||||
# Post the Invoice to validate the fields
|
||||
invoice.action_post()
|
||||
|
||||
def test_ungrouping_pickings_partner_shipping_different(self):
|
||||
"""
|
||||
Test the invoice generation grouped by partner/product with 3
|
||||
picking and 2 moves per picking, the 3 has the same Partner to
|
||||
Invoice but one has Partner to Shipping so shouldn't be grouping.
|
||||
"""
|
||||
|
||||
sale_order_1 = self.env.ref(
|
||||
"sale_stock_picking_invoicing.main_company-sale_order_1"
|
||||
)
|
||||
sale_order_1.action_confirm()
|
||||
picking = sale_order_1.picking_ids
|
||||
self.picking_move_state(picking)
|
||||
|
||||
sale_order_3 = self.env.ref(
|
||||
"sale_stock_picking_invoicing.main_company-sale_order_3"
|
||||
)
|
||||
sale_order_3.action_confirm()
|
||||
picking3 = sale_order_3.picking_ids
|
||||
self.picking_move_state(picking3)
|
||||
|
||||
sale_order_4 = self.env.ref(
|
||||
"sale_stock_picking_invoicing.main_company-sale_order_4"
|
||||
)
|
||||
sale_order_4.action_confirm()
|
||||
picking4 = sale_order_4.picking_ids
|
||||
self.picking_move_state(picking4)
|
||||
|
||||
pickings = picking | picking3 | picking4
|
||||
invoices = self.create_invoice_wizard(pickings)
|
||||
# Even with same Partner Invoice if the Partner Shipping
|
||||
# are different should not be Groupping
|
||||
self.assertEqual(len(invoices), 2)
|
||||
|
||||
# Invoice that has different Partner Shipping
|
||||
# should be not groupping
|
||||
invoice_pick_1 = invoices.filtered(
|
||||
lambda t: t.partner_id != t.partner_shipping_id
|
||||
)
|
||||
# Invoice should be create with partner_invoice_id
|
||||
self.assertEqual(invoice_pick_1.partner_id, sale_order_1.partner_invoice_id)
|
||||
# Invoice create with Partner Shipping used in Picking
|
||||
self.assertEqual(invoice_pick_1.partner_shipping_id, picking.partner_id)
|
||||
|
||||
# Groupping Invoice
|
||||
invoice_pick_3_4 = invoices.filtered(
|
||||
lambda t: t.partner_id == t.partner_shipping_id
|
||||
)
|
||||
self.assertIn(invoice_pick_3_4, picking3.invoice_ids)
|
||||
self.assertIn(invoice_pick_3_4, picking4.invoice_ids)
|
||||
|
||||
def test_down_payment(self):
|
||||
"""Test the case with Down Payment"""
|
||||
sale_order_1 = self.env.ref(
|
||||
"sale_stock_picking_invoicing.main_company-sale_order_1"
|
||||
)
|
||||
sale_order_1.action_confirm()
|
||||
# Create Invoice Sale
|
||||
context = {
|
||||
"active_model": "sale.order",
|
||||
"active_id": sale_order_1.id,
|
||||
"active_ids": sale_order_1.ids,
|
||||
}
|
||||
# DownPayment
|
||||
payment_wizard = (
|
||||
self.env["sale.advance.payment.inv"]
|
||||
.with_context(**context)
|
||||
.create(
|
||||
{
|
||||
"advance_payment_method": "percentage",
|
||||
"amount": 50,
|
||||
}
|
||||
)
|
||||
)
|
||||
payment_wizard.create_invoices()
|
||||
|
||||
invoice_down_payment = sale_order_1.invoice_ids[0]
|
||||
invoice_down_payment.action_post()
|
||||
payment_register = Form(
|
||||
self.env["account.payment.register"].with_context(
|
||||
active_model="account.move",
|
||||
active_ids=invoice_down_payment.ids,
|
||||
)
|
||||
)
|
||||
journal_cash = self.env["account.journal"].search(
|
||||
[
|
||||
("type", "=", "cash"),
|
||||
("company_id", "=", invoice_down_payment.company_id.id),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
payment_register.journal_id = journal_cash
|
||||
payment_register.amount = invoice_down_payment.amount_total
|
||||
payment_register.save()._create_payments()
|
||||
|
||||
picking = sale_order_1.picking_ids
|
||||
self.picking_move_state(picking)
|
||||
invoice = self.create_invoice_wizard(picking)
|
||||
# 2 Products, 2 Down Payment, 1 Note and 1 Section
|
||||
self.assertEqual(len(invoice.invoice_line_ids), 6)
|
||||
line_section = invoice.invoice_line_ids.filtered(
|
||||
lambda line: line.display_type == "line_section"
|
||||
)
|
||||
assert line_section, "Invoice without Line Section for Down Payment."
|
||||
down_payment_line = invoice.invoice_line_ids.filtered(
|
||||
lambda line: line.sale_line_ids.is_downpayment
|
||||
)
|
||||
assert down_payment_line, "Invoice without Down Payment line."
|
||||
|
||||
def test_default_value_sale_invoicing_policy(self):
|
||||
"""Test default value for sale_invoicing_policy"""
|
||||
company = self.env["res.company"].create(
|
||||
{
|
||||
"name": "Test",
|
||||
}
|
||||
)
|
||||
self.assertEqual(company.sale_invoicing_policy, "sale_order")
|
||||
|
||||
def test_picking_invocing_without_sale_order(self):
|
||||
"""Test Picking Invoicing without Sale Order"""
|
||||
picking = self.env.ref("stock_picking_invoicing.stock_picking_invoicing_1")
|
||||
self.picking_move_state(picking)
|
||||
invoice = self.create_invoice_wizard(picking)
|
||||
self.assertEqual(len(invoice), 1)
|
||||
Reference in New Issue
Block a user