Skip to content

Commit 260e736

Browse files
committed
[MIG] purchase_invoice_new_picking_line: Migration to 16.0
Signed-off-by: Carmen Bianca BAKKER <[email protected]>
1 parent bfa24d7 commit 260e736

File tree

5 files changed

+177
-122
lines changed

5 files changed

+177
-122
lines changed

purchase_invoice_new_picking_line/__manifest__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
{
55
"name": "Purchase Invoice New Picking Line",
66
"summary": "When creating an invoice from incoming picking, also adds invoice lines for product that were not in the purchase order",
7-
"version": "15.0.1.0.0",
7+
"version": "16.0.1.0.0",
88
"author": "Coop IT Easy SCRLfs, Odoo Community Association (OCA)",
99
"category": "Purchase",
1010
"license": "AGPL-3",
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from . import account_invoice
1+
from . import purchase_order

purchase_invoice_new_picking_line/models/account_invoice.py

-79
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# SPDX-FileCopyrightText: 2024 Coop IT Easy
2+
#
3+
# SPDX-License-Identifier: AGPL-3.0-or-later
4+
5+
from odoo import fields, models
6+
from odoo.fields import Command
7+
8+
9+
class PurchaseOrder(models.Model):
10+
_inherit = "purchase.order"
11+
12+
def _prepare_account_move_lines_from_stock_move(
13+
self, stock_move_id, account_move_id
14+
):
15+
self.ensure_one()
16+
17+
product_id = stock_move_id.product_id
18+
qty = max(stock_move_id.quantity_done, 0)
19+
date = fields.Date.to_date(stock_move_id.date) or fields.Date.today()
20+
supplierinfo = self.env["product.supplierinfo"]
21+
price_unit = 0.0
22+
if self.partner_id:
23+
supplierinfo = product_id._select_seller(
24+
partner_id=self.partner_id,
25+
quantity=qty,
26+
date=date,
27+
uom_id=stock_move_id.product_uom,
28+
params={"order_id": self},
29+
)
30+
if supplierinfo:
31+
price_unit = supplierinfo.currency_id._convert(
32+
supplierinfo.price,
33+
self.currency_id,
34+
self.company_id,
35+
date,
36+
round=False,
37+
)
38+
else:
39+
price_unit = product_id.currency_id._convert(
40+
product_id.lst_price,
41+
self.currency_id,
42+
self.company_id,
43+
date,
44+
round=False,
45+
)
46+
taxes = product_id.supplier_taxes_id.filtered(
47+
lambda tax: tax.company_id == self.company_id
48+
)
49+
tax_ids = self.fiscal_position_id.map_tax(taxes) # TODO not sure about this
50+
51+
data = {
52+
"display_type": "product",
53+
# Normally the name is '{order_name}: {order_line_name}'.
54+
"name": "%s: %s" % (stock_move_id.picking_id.name, stock_move_id.name),
55+
"product_id": product_id.id,
56+
"product_uom_id": stock_move_id.product_uom.id,
57+
"quantity": qty,
58+
"price_unit": price_unit,
59+
"tax_ids": [Command.set(tax_ids.ids)],
60+
}
61+
62+
return data
63+
64+
def add_missing_picking_products_to_account_move(self, account_move_id):
65+
stock_moves = self.env["stock.move"]
66+
for picking in self.picking_ids.filtered(lambda x: x.state == "done"):
67+
stock_moves |= picking.move_ids.filtered(
68+
lambda move: move.state == "done" and not move.purchase_line_id
69+
)
70+
new_lines_data = [
71+
self._prepare_account_move_lines_from_stock_move(
72+
stock_move, account_move_id
73+
)
74+
for stock_move in stock_moves
75+
]
76+
account_move_id.write(
77+
{"invoice_line_ids": [Command.create(line) for line in new_lines_data]}
78+
)
79+
80+
def action_create_invoice(self):
81+
result = super().action_create_invoice()
82+
res_id = result.get("res_id")
83+
if not res_id:
84+
return result
85+
invoice_id = self.env["account.move"].browse(res_id)
86+
self.add_missing_picking_products_to_account_move(invoice_id)
87+
return result

purchase_invoice_new_picking_line/tests/test_invoice.py

+88-41
Original file line numberDiff line numberDiff line change
@@ -2,74 +2,121 @@
22
# Robin Keunen <[email protected]>
33
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
44

5+
import datetime
56

6-
from odoo import tests
7-
from odoo.tests.common import Form
7+
from odoo import fields, tests
88

99

1010
class TestAccountInvoice(tests.common.TransactionCase):
11-
def test_move_line_product_is_added_to_invoice(self):
12-
purchase_order = self.browse_ref("purchase.purchase_order_4")
13-
product = self.browse_ref("product.product_product_1")
14-
supplier_loc = self.env.ref("stock.stock_location_suppliers")
15-
stock_loc = self.env.ref("stock.warehouse0").lot_stock_id
11+
@classmethod
12+
def setUpClass(cls):
13+
super().setUpClass()
14+
15+
cls.purchase_order = cls.env.ref("purchase.purchase_order_4")
16+
cls.product = cls.env.ref("product.product_product_1")
17+
cls.supplier_loc = cls.env.ref("stock.stock_location_suppliers")
18+
cls.stock_loc = cls.env.ref("stock.warehouse0").lot_stock_id
1619

1720
# Confirm order
18-
purchase_order.button_confirm()
19-
picking = purchase_order.picking_ids
20-
moves = picking.move_ids_without_package
21-
self.assertEquals(len(purchase_order.order_line), len(moves))
21+
cls.purchase_order.button_confirm()
22+
cls.picking = cls.purchase_order.picking_ids
23+
cls.moves = cls.picking.move_ids
24+
25+
cls.moves[0].quantity_done = cls.moves[0].product_qty
26+
cls.moves[1].quantity_done = cls.moves[1].product_qty
27+
cls.moves[2].quantity_done = cls.moves[2].product_qty - 1
28+
29+
def _validate_picking(self):
30+
wizard = self.picking.button_validate()
31+
backorder_confirmation = (
32+
self.env[wizard["res_model"]].with_context(**wizard["context"]).create({})
33+
)
34+
backorder_confirmation.process_cancel_backorder()
2235

23-
moves[0].quantity_done = moves[0].product_qty
24-
moves[1].quantity_done = moves[1].product_qty
25-
moves[2].quantity_done = moves[2].product_qty - 1
36+
def _create_invoice(self):
37+
res = self.purchase_order.action_create_invoice()
38+
return self.env["account.move"].browse(res["res_id"])
2639

40+
def test_move_line_product_is_added_to_invoice(self):
2741
new_move = self.env["stock.move"].create(
2842
{
29-
"picking_id": picking.id,
30-
"name": product.name,
31-
"product_id": product.id,
32-
"product_uom": product.uom_id.id,
43+
"picking_id": self.picking.id,
44+
"name": self.product.name,
45+
"product_id": self.product.id,
46+
"product_uom": self.product.uom_id.id,
3347
"quantity_done": 20,
34-
"location_id": supplier_loc.id,
35-
"location_dest_id": stock_loc.id,
48+
"location_id": self.supplier_loc.id,
49+
"location_dest_id": self.stock_loc.id,
3650
}
3751
)
52+
self._validate_picking()
3853

39-
wizard = picking.button_validate()
40-
backorder_confirmation = self.env[wizard["res_model"]].browse(
41-
wizard["res_id"]
42-
)
43-
backorder_confirmation.process_cancel_backorder()
44-
45-
res = purchase_order.with_context(
46-
create_bill=True
47-
).action_view_invoice()
48-
ctx = res.get("context")
49-
f = Form(
50-
self.env["account.invoice"].with_context(ctx),
51-
view="account.invoice_supplier_form",
52-
)
53-
invoice = f.save()
54+
invoice = self._create_invoice()
5455

5556
self.assertRecordValues(
5657
invoice.invoice_line_ids,
5758
[
5859
{
59-
"product_id": moves[0].product_id.id,
60-
"quantity": moves[0].quantity_done,
60+
"product_id": self.moves[0].product_id.id,
61+
"quantity": self.moves[0].quantity_done,
6162
},
6263
{
63-
"product_id": moves[1].product_id.id,
64-
"quantity": moves[1].quantity_done,
64+
"product_id": self.moves[1].product_id.id,
65+
"quantity": self.moves[1].quantity_done,
6566
},
6667
{
67-
"product_id": moves[2].product_id.id,
68-
"quantity": moves[2].quantity_done,
68+
"product_id": self.moves[2].product_id.id,
69+
"quantity": self.moves[2].quantity_done,
70+
},
71+
{
72+
"product_id": new_move.product_id.id,
73+
"quantity": new_move.quantity_done,
74+
"price_unit": self.product.lst_price,
75+
"product_uom_id": new_move.product_uom.id,
76+
"display_type": "product",
6977
},
78+
],
79+
)
80+
81+
def test_with_supplierinfo(self):
82+
"""Same test, but the price is now defined on supplierinfo instead of on
83+
the product.
84+
"""
85+
self.env["product.supplierinfo"].create(
86+
{
87+
"product_id": self.product.id,
88+
"product_tmpl_id": self.product.product_tmpl_id.id,
89+
"partner_id": self.purchase_order.partner_id.id,
90+
"price": 1,
91+
"currency_id": self.purchase_order.currency_id.id,
92+
"date_start": fields.Date.today() - datetime.timedelta(days=90),
93+
"date_end": fields.Date.today() + datetime.timedelta(days=90),
94+
}
95+
)
96+
new_move = self.env["stock.move"].create(
97+
{
98+
"picking_id": self.picking.id,
99+
"name": self.product.name,
100+
"product_id": self.product.id,
101+
"product_uom": self.product.uom_id.id,
102+
"quantity_done": 20,
103+
"location_id": self.supplier_loc.id,
104+
"location_dest_id": self.stock_loc.id,
105+
}
106+
)
107+
self._validate_picking()
108+
109+
invoice = self._create_invoice()
110+
111+
self.assertRecordValues(
112+
invoice.invoice_line_ids[-1],
113+
[
70114
{
71115
"product_id": new_move.product_id.id,
72116
"quantity": new_move.quantity_done,
117+
"price_unit": 1,
118+
"product_uom_id": new_move.product_uom.id,
119+
"display_type": "product",
73120
},
74121
],
75122
)

0 commit comments

Comments
 (0)