Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[14.0][FIX] stock_picking_invoicing qty_invoiced value compute #1672

Open
wants to merge 1 commit into
base: 14.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions stock_picking_invoicing/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
from . import stock_move
from . import stock_picking
from . import stock_picking_type
from . import sale
57 changes: 57 additions & 0 deletions stock_picking_invoicing/models/sale.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Copyright (C) 2019-Today: Odoo Community Association
# @ 2019-Today: Binhex - www.binhex.cloud -
# Christian-RB <c.ramos@binhex.es>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import api, models


class SaleOrder(models.Model):
_inherit = "sale.order"

@api.depends("order_line.invoice_status")
def _get_invoiced(self):
"""
Override to get the invoices related to the stock moves of the sale order
"""
super()._get_invoiced()
for order in self:
linked_invoice_ids = order.invoice_ids
new_invoice_ids = (
order.order_line.move_ids.invoice_line_ids.move_id.filtered(
lambda r: r.move_type in ("out_invoice", "out_refund")
)
)
linked_invoice_ids |= new_invoice_ids
order.invoice_ids = [(6, 0, list(set(linked_invoice_ids.ids)))]
order.invoice_count = len(linked_invoice_ids)


class SaleOrderLine(models.Model):
_inherit = "sale.order.line"

@api.depends(
"move_ids.invoice_line_ids.move_id.state",
"move_ids.invoice_line_ids.quantity",
"invoice_lines.move_id.state",
"invoice_lines.quantity",
"invoice_status",
)
def _get_invoice_qty(self):
"""
Override to get the quantity invoiced related to the stock moves of the sale order
"""
res = super()._get_invoice_qty()
for line in self:
qty_invoiced = line.qty_invoiced
for invoice_line in line.move_ids.invoice_line_ids - line.invoice_lines:
if invoice_line.move_id.state != "cancel":
if invoice_line.move_id.move_type == "out_invoice":
qty_invoiced += invoice_line.product_uom_id._compute_quantity(
invoice_line.quantity, line.product_uom
)
elif invoice_line.move_id.move_type == "out_refund":
qty_invoiced -= invoice_line.product_uom_id._compute_quantity(
invoice_line.quantity, line.product_uom
)
line.qty_invoiced = qty_invoiced
return res
25 changes: 14 additions & 11 deletions stock_picking_invoicing/models/stock_move.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,23 @@
"""
product = self.mapped("product_id")
product.ensure_one()
if inv_type in ("in_invoice", "in_refund"):
result = product.price
if self.sale_line_id:
result = self.sale_line_id.price_unit
else:
# If partner given, search price in its sale pricelist
if partner and partner.property_product_pricelist:
product = product.with_context(
partner=partner.id,
quantity=qty,
pricelist=partner.property_product_pricelist.id,
uom=fields.first(self).product_uom.id,
)
if inv_type in ("in_invoice", "in_refund"):
result = product.price
else:
result = product.lst_price
# If partner given, search price in its sale pricelist
if partner and partner.property_product_pricelist:
product = product.with_context(
partner=partner.id,
quantity=qty,
pricelist=partner.property_product_pricelist.id,
uom=fields.first(self).product_uom.id,
)
result = product.price
else:
result = product.lst_price

Check warning on line 67 in stock_picking_invoicing/models/stock_move.py

View check run for this annotation

Codecov / codecov/patch

stock_picking_invoicing/models/stock_move.py#L67

Added line #L67 was not covered by tests
return result

def _prepare_extra_move_vals(self, qty):
Expand Down
103 changes: 103 additions & 0 deletions stock_picking_invoicing/tests/test_picking_invoicing.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class TestPickingInvoicing(SavepointCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.sale_model = cls.env["sale.order"]
cls.picking_model = cls.env["stock.picking"]
cls.move_model = cls.env["stock.move"]
cls.invoice_wizard = cls.env["stock.invoice.onshipping"]
Expand Down Expand Up @@ -945,3 +946,105 @@ def test_return_supplier_picking(self):
"in_refund",
"Invoice Type should be In Refund",
)

def test_sale_line_get_invoiced_qty(self):
"""
Test invoiced qty in sale order line after picking invoice.
"""
sale_order = self.sale_model.create(
{
"partner_id": self.partner.id,
"order_line": [
(
0,
0,
{
"product_id": self.product_test_1.id,
"product_uom_qty": 2,
"product_uom": self.product_test_1.uom_id.id,
"price_unit": 100,
},
)
],
}
)
sale_order.action_confirm()
picking = sale_order.picking_ids
# Force product availability
for move in picking.move_ids_without_package:
move.quantity_done = move.product_uom_qty
picking.set_to_be_invoiced()
picking.action_confirm()
picking.action_assign()
picking.button_validate()
self.assertEqual(picking.state, "done")
wizard_obj = self.invoice_wizard.with_context(
active_ids=picking.ids, active_model=picking._name, active_id=picking.id
)
fields_list = wizard_obj.fields_get().keys()
wizard_values = wizard_obj.default_get(fields_list)
wizard = wizard_obj.create(wizard_values)
wizard.onchange_group()
wizard.action_generate()
domain = [("picking_ids", "=", picking.id)]
invoice = self.invoice_model.search(domain)
self.assertEqual(picking.invoice_state, "invoiced")
self.assertEqual(invoice.partner_id, self.partner)
self.assertEqual(invoice.invoice_line_ids.price_unit, 100)
self.assertEqual(sale_order.invoice_ids, invoice)
self.assertEqual(sale_order.order_line.qty_invoiced, 2)
invoice.action_post()
refund = invoice._reverse_moves(cancel=True)
self.assertEqual(sale_order.invoice_ids, invoice | refund)
self.assertEqual(sale_order.invoice_count, 2)
self.assertEqual(sale_order.order_line.qty_invoiced, 0)

def test_invoice_line_product_lst_price(self):
"""
Test if the invoice line price is the same as the product lst_price
when the pricelist is not set in the partner.
"""
self.partner2.write({"property_product_pricelist": False})
picking = self.picking_model.create(
{
"partner_id": self.partner2.id,
"picking_type_id": self.pick_type_out.id,
"location_id": self.stock_location.id,
"location_dest_id": self.customers_location.id,
}
)
move_vals = {
"product_id": self.product_test_1.id,
"picking_id": picking.id,
"location_dest_id": self.customers_location.id,
"location_id": self.stock_location.id,
"name": self.product_test_1.name,
"product_uom_qty": 2,
"product_uom": self.product_test_1.uom_id.id,
}
new_move = self.move_model.create(move_vals)
new_move.onchange_product_id()
picking.set_to_be_invoiced()
picking.action_confirm()
# Check product availability
picking.action_assign()
# Force product availability
for move in picking.move_ids_without_package:
move.quantity_done = move.product_uom_qty
picking.button_validate()
self.assertEqual(picking.state, "done")
wizard_obj = self.invoice_wizard.with_context(
active_ids=picking.ids,
active_model=picking._name,
active_id=picking.id,
)
fields_list = wizard_obj.fields_get().keys()
wizard_values = wizard_obj.default_get(fields_list)
wizard = wizard_obj.create(wizard_values)
wizard.onchange_group()
wizard.action_generate()
domain = [("picking_ids", "=", picking.id)]
invoice = self.invoice_model.search(domain)
self.assertEqual(
invoice.invoice_line_ids.price_unit, self.product_test_1.lst_price
)
Loading