Skip to content

Commit

Permalink
[IMP] sale_stock_picking_invoicing: Make module compatible with 'Down…
Browse files Browse the repository at this point in the history
… Payments', 'Line Section' and 'Line Note' cases.
  • Loading branch information
mbcosta committed Apr 3, 2024
1 parent 5a0adf6 commit 092aa34
Show file tree
Hide file tree
Showing 6 changed files with 227 additions and 113 deletions.
1 change: 0 additions & 1 deletion sale_stock_picking_invoicing/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
"data": [
"views/res_company_view.xml",
"views/res_config_settings_view.xml",
"views/sale_order_view.xml",
],
"demo": [
"demo/sale_order_demo.xml",
Expand Down
17 changes: 15 additions & 2 deletions sale_stock_picking_invoicing/models/res_company.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,31 @@
# @author Magno Costa <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import fields, models
from odoo import api, fields, models


class ResCompany(models.Model):
_inherit = "res.company"

@api.model
def _default_sale_invoicing_policy(self):
# In order to avoid errors in the CI tests environment when Created
# Invoice from Sale Order using sale.advance.payment.inv object
# is necessary let default policy as sale_order
# TODO: Is there other form to avoid this problem?
result = "stock_picking"
module_base = self.env["ir.module.module"].search([("name", "=", "base")])
if module_base.demo:
result = "sale_order"
return result

sale_invoicing_policy = fields.Selection(
selection=[
("sale_order", "Sale Order"),
("stock_picking", "Stock Picking"),
],
string="Sale Invoicing Policy",
help="Define, when Product Type are not service, if Invoice"
" should be created from Sale Order or from Stock Picking.",
default="stock_picking",
default=_default_sale_invoicing_policy,
)
56 changes: 25 additions & 31 deletions sale_stock_picking_invoicing/models/sale_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,34 @@
# @author Magno Costa <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import api, fields, models
from odoo import _, models
from odoo.exceptions import UserError


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

# Make Invisible Invoice Button
button_create_invoice_invisible = fields.Boolean(
compute="_compute_get_button_create_invoice_invisible"
)

@api.depends("state", "order_line.invoice_status")
def _compute_get_button_create_invoice_invisible(self):
for record in self:
button_create_invoice_invisible = False

lines = record.order_line.filtered(
lambda line: line.invoice_status == "to invoice"
)

# Only after Confirmed Sale Order the button appear
if record.state != "sale":
button_create_invoice_invisible = True
def _get_invoiceable_lines(self, final=False):
"""Return the invoiceable lines for order `self`."""
lines = super()._get_invoiceable_lines(final)
model = self.env.context.get("active_model")
if (
self.company_id.sale_invoicing_policy == "stock_picking"
and model != "stock.picking"
):
new_lines = lines.filtered(lambda ln: ln.product_id.type != "product")
if new_lines:
# Case lines with Product Type 'service'
lines = new_lines
else:
if record.company_id.sale_invoicing_policy == "stock_picking":
# The creation of Invoice to Services should
# be possible in Sale Order
if not any(line.product_id.type == "service" for line in lines):
button_create_invoice_invisible = True
else:
# In the case of Sale Create Invoice Policy based on Sale Order
# when the Button to Create Invoice clicked will be create
# automatic Invoice for Products and Services
if not lines:
button_create_invoice_invisible = True

record.button_create_invoice_invisible = button_create_invoice_invisible
# Case only Products Type 'product'
raise UserError(
_(
"When 'Sale Invoicing Policy' is defined as"
"'Stock Picking' the Invoice can only be created"
" from the Stock Picking, if necessary you can change"
" in the Company or Sale Settings."
)
)

return lines
126 changes: 89 additions & 37 deletions sale_stock_picking_invoicing/tests/test_sale_stock.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# @author Magno Costa <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import exceptions

# TODO: In v16 check the possiblity to use the commom.py
# from stock_picking_invoicing
# https://github.com/OCA/account-invoicing/blob/16.0/
Expand All @@ -17,6 +19,15 @@ def setUpClass(cls):
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 _run_picking_onchanges(self, record):
record.onchange_picking_type()
Expand Down Expand Up @@ -242,6 +253,8 @@ def test_picking_sale_order_product_and_service(self):
# Necessary after call onchange_partner_id
"write_date",
"__last_update",
# Field sequence add in creation of Invoice
"sequence",
]

common_fields = list(set(acl_fields) & set(sol_fields) - set(skipped_fields))
Expand Down Expand Up @@ -369,7 +382,7 @@ def test_ungrouping_pickings_partner_shipping_different(self):
# Invoice that has different Partner Shipping
# should be not groupping
invoice_pick_1 = invoices.filtered(
lambda t: t.partner_shipping_id == picking.partner_id
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)
Expand All @@ -383,47 +396,86 @@ def test_ungrouping_pickings_partner_shipping_different(self):
self.assertIn(invoice_pick_3_4, picking3.invoice_ids)
self.assertIn(invoice_pick_3_4, picking4.invoice_ids)

def test_button_create_bill_in_view(self):
"""
Test Field to make Button Create Bill invisible.
"""
sale_products = self.env.ref(
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"
)
# Caso do Pedido de Compra em Rascunho
self.assertTrue(
sale_products.button_create_invoice_invisible,
"Field to make invisible the Button Create Bill should be"
" invisible when Sale Order is not in state Sale.",
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,
}
# Test Create Invoice Policy
payment = (
self.env["sale.advance.payment.inv"]
.with_context(context)
.create(
{
"advance_payment_method": "delivered",
}
)
)
# Caso somente com Produtos
sale_products.action_confirm()
self.assertTrue(
sale_products.button_create_invoice_invisible,
"Field to make invisible the button Create Bill should be"
" invisible when Sale Order has only products.",
with self.assertRaises(exceptions.UserError):
payment.with_context(context).create_invoices()

# DownPayment
payment_wizard = (
self.env["sale.advance.payment.inv"]
.with_context(context)
.create(
{
"advance_payment_method": "percentage",
"amount": 50,
}
)
)
picking = sale_products.picking_ids
self.picking_move_state(picking)
self.create_invoice_wizard(picking)

# Service and Product
sale_service_product = self.env.ref(
"sale_stock_picking_invoicing.main_company-sale_order_2"
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,
)
)
sale_service_product.action_confirm()
self.assertFalse(
sale_service_product.button_create_invoice_invisible,
"Field to make invisible the Button Create Bill should be"
" False when the Sale Order has Service and Product.",
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_method_manual_in = self.env.ref(
"account.account_payment_method_manual_in"
)
payment_register.payment_method_id = payment_method_manual_in
payment_register.amount = invoice_down_payment.amount_total
payment_register.save()._create_payments()

# Sale Invoice Policy based on sale_order
sale = self.env.ref("sale_stock_picking_invoicing.main_company-sale_order_3")
sale.company_id.sale_invoicing_policy = "sale_order"
sale.action_confirm()
self.assertTrue(
sale.button_create_invoice_invisible,
"Field to make invisible the button Create Bill should be"
" invisible when Sale Invoice Policy based on sale_order.",
picking = sale_order_1.picking_ids
self.picking_move_state(picking)
invoice = self.create_invoice_wizard(picking)
# 2 Lines of Products and 2 lines of Down Payment
self.assertEqual(len(invoice.invoice_line_ids), 4)
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")
42 changes: 0 additions & 42 deletions sale_stock_picking_invoicing/views/sale_order_view.xml

This file was deleted.

Loading

0 comments on commit 092aa34

Please sign in to comment.