Skip to content
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
12 changes: 6 additions & 6 deletions sale_invoice_plan/README.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

==================
Sales Invoice Plan
==================
Expand All @@ -16,8 +12,8 @@ Sales Invoice Plan

.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png
:target: https://odoo-community.org/page/development-status
:alt: Production/Stable
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
:alt: Alpha
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The alt text here changed from Production/Stable to Alpha, and license became licence (line 16). The __manifest__.py still declares "development_status": "Production/Stable". This looks like an unintended side effect of regenerating the README with possibly stale or misconfigured oca-gen-addon-readme. Could you verify and regenerate with the correct settings?

.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsale--workflow-lightgray.png?logo=github
Expand Down Expand Up @@ -102,6 +98,10 @@ Contributors

- Son Ho <sonhd@trobz.com>

- `Solvos <https://solvos.es>`__:

- Christian Santamaría <christian.santamaria@solvos.es>

Other credits
-------------

Expand Down
2 changes: 1 addition & 1 deletion sale_invoice_plan/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{
"name": "Sales Invoice Plan",
"summary": "Add to sales order, ability to manage future invoice plan",
"version": "17.0.1.0.1",
"version": "17.0.1.1.0",
"author": "Ecosoft,Odoo Community Association (OCA)",
"license": "AGPL-3",
"website": "https://github.com/OCA/sale-workflow",
Expand Down
32 changes: 32 additions & 0 deletions sale_invoice_plan/migrations/17.0.1.1.0/pre-migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# © 2025 Solvos Consultoría Informática (<http://www.solvos.es>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import logging

from openupgradelib import openupgrade

_logger = logging.getLogger(__name__)


@openupgrade.migrate()
def migrate(env, version):
if not openupgrade.column_exists(env.cr, "sale_order", "invoice_plan_pending"):
openupgrade.add_columns(
env, [("sale_order", "invoice_plan_pending", "integer")]
)
_logger.info("Create invoice_plan_pending column in sale_order table.")
openupgrade.logged_query(
env.cr,
"""
UPDATE sale_order so
SET invoice_plan_pending = sub.count
FROM (
SELECT sale_id, COUNT(*) AS count
FROM sale_invoice_plan
WHERE NOT invoiced
GROUP BY sale_id
) AS sub
WHERE so.id = sub.sale_id;
""",
)
_logger.info("Updated invoice_plan_pending for existing sale orders.")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: this UPDATE only sets invoice_plan_pending for orders that have at least one non-invoiced plan. Orders with zero pending plans (all invoiced or no plans at all) will keep invoice_plan_pending = NULL. The ORM compute method would return 0 for those.

Consider adding a follow-up statement to zero out NULLs for consistency:

openupgrade.logged_query(
    env.cr,
    "UPDATE sale_order SET invoice_plan_pending = 0 WHERE invoice_plan_pending IS NULL",
)

This ensures migrated data matches what the compute method would produce.

12 changes: 12 additions & 0 deletions sale_invoice_plan/models/sale.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ class SaleOrder(models.Model):
compute="_compute_invoice_plan_total",
string="Total Amount",
)
invoice_plan_pending = fields.Integer(
compute="_compute_invoice_plan_pending",
string="Pending plans",
store=True,
)

@api.depends("invoice_plan_ids")
def _compute_invoice_plan_total(self):
Expand All @@ -53,6 +58,13 @@ def _compute_invoice_plan_process(self):
rec.state == "sale" and has_invoice_plan and to_invoice and inv_or_adv
)

@api.depends("invoice_plan_ids.invoiced")
def _compute_invoice_plan_pending(self):
for order in self:
order.invoice_plan_pending = len(
order.invoice_plan_ids.filtered(lambda x: not x.invoiced)
)

@api.constrains("invoice_plan_ids")
def _check_invoice_plan_total_percent(self):
for rec in self:
Expand Down
4 changes: 4 additions & 0 deletions sale_invoice_plan/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@
- [Trobz](https://trobz.com):

> - Son Ho \<sonhd@trobz.com\>

- [Solvos](https://solvos.es):

> - Christian Santamaría \<christian.santamaria@solvos.es\>
43 changes: 22 additions & 21 deletions sale_invoice_plan/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>README.rst</title>
<title>Sales Invoice Plan</title>
<style type="text/css">

/*
Expand Down Expand Up @@ -360,21 +360,16 @@
</style>
</head>
<body>
<div class="document">
<div class="document" id="sales-invoice-plan">
<h1 class="title">Sales Invoice Plan</h1>


<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
</a>
<div class="section" id="sales-invoice-plan">
<h1>Sales Invoice Plan</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:0d40b044d18a251365e884dd417a558cf686fd9e084b094b23a7f7146b4ef9b0
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/sale-workflow/tree/17.0/sale_invoice_plan"><img alt="OCA/sale-workflow" src="https://img.shields.io/badge/github-OCA%2Fsale--workflow-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/sale-workflow-17-0/sale-workflow-17-0-sale_invoice_plan"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/sale-workflow&amp;target_branch=17.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Alpha" src="https://img.shields.io/badge/maturity-Alpha-red.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/sale-workflow/tree/17.0/sale_invoice_plan"><img alt="OCA/sale-workflow" src="https://img.shields.io/badge/github-OCA%2Fsale--workflow-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/sale-workflow-17-0/sale-workflow-17-0-sale_invoice_plan"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/sale-workflow&amp;target_branch=17.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>By standard feature, user can gradually create partial invoices, one by
one. This module add ability to create invoices based on the predefined
invoice plan, either all at once, or one by one. The plan support both
Expand All @@ -400,12 +395,12 @@ <h1>Sales Invoice Plan</h1>
</ul>
</div>
<div class="section" id="installation">
<h2><a class="toc-backref" href="#toc-entry-1">Installation</a></h2>
<h1><a class="toc-backref" href="#toc-entry-1">Installation</a></h1>
<p>Just install this module, and the sales order will have new option “Use
Invoice Plan”</p>
</div>
<div class="section" id="usage">
<h2><a class="toc-backref" href="#toc-entry-2">Usage</a></h2>
<h1><a class="toc-backref" href="#toc-entry-2">Usage</a></h1>
<ul class="simple">
<li>Create new sales quotation as per normal process</li>
<li>Select option “Use Invoice Plan”, a new Invoice Plan tab will appear</li>
Expand All @@ -420,38 +415,38 @@ <h2><a class="toc-backref" href="#toc-entry-2">Usage</a></h2>
</ul>
</div>
<div class="section" id="changelog">
<h2><a class="toc-backref" href="#toc-entry-3">Changelog</a></h2>
<h1><a class="toc-backref" href="#toc-entry-3">Changelog</a></h1>
<div class="section" id="section-1">
<h3><a class="toc-backref" href="#toc-entry-4">15.0.1.1.0 (2022-05-10)</a></h3>
<h2><a class="toc-backref" href="#toc-entry-4">15.0.1.1.0 (2022-05-10)</a></h2>
<ul class="simple">
<li>remove dependency on stock</li>
</ul>
</div>
<div class="section" id="section-2">
<h3><a class="toc-backref" href="#toc-entry-5">12.0.1.0.0 (2019-03-08)</a></h3>
<h2><a class="toc-backref" href="#toc-entry-5">12.0.1.0.0 (2019-03-08)</a></h2>
<ul class="simple">
<li>Start of the history</li>
</ul>
</div>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-6">Bug Tracker</a></h2>
<h1><a class="toc-backref" href="#toc-entry-6">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/sale-workflow/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/sale-workflow/issues/new?body=module:%20sale_invoice_plan%0Aversion:%2017.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h2><a class="toc-backref" href="#toc-entry-7">Credits</a></h2>
<h1><a class="toc-backref" href="#toc-entry-7">Credits</a></h1>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#toc-entry-8">Authors</a></h3>
<h2><a class="toc-backref" href="#toc-entry-8">Authors</a></h2>
<ul class="simple">
<li>Ecosoft</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#toc-entry-9">Contributors</a></h3>
<h2><a class="toc-backref" href="#toc-entry-9">Contributors</a></h2>
<ul>
<li><p class="first">Kitti Upariphutthiphong. &lt;<a class="reference external" href="mailto:kittiu&#64;gmail.com">kittiu&#64;gmail.com</a>&gt; (<a class="reference external" href="http://ecosoft.co.th">http://ecosoft.co.th</a>)</p>
</li>
Expand All @@ -462,15 +457,22 @@ <h3><a class="toc-backref" href="#toc-entry-9">Contributors</a></h3>
</ul>
</blockquote>
</li>
<li><p class="first"><a class="reference external" href="https://solvos.es">Solvos</a>:</p>
<blockquote>
<ul class="simple">
<li>Christian Santamaría &lt;<a class="reference external" href="mailto:christian.santamaria&#64;solvos.es">christian.santamaria&#64;solvos.es</a>&gt;</li>
</ul>
</blockquote>
</li>
</ul>
</div>
<div class="section" id="other-credits">
<h3><a class="toc-backref" href="#toc-entry-10">Other credits</a></h3>
<h2><a class="toc-backref" href="#toc-entry-10">Other credits</a></h2>
<p>The migration of this module from 13.0 to 14.0 was financially supported
by Camptocamp</p>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#toc-entry-11">Maintainers</a></h3>
<h2><a class="toc-backref" href="#toc-entry-11">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
Expand All @@ -485,6 +487,5 @@ <h3><a class="toc-backref" href="#toc-entry-11">Maintainers</a></h3>
</div>
</div>
</div>
</div>
</body>
</html>
33 changes: 33 additions & 0 deletions sale_invoice_plan/tests/test_sale_invoice_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,11 @@ def test_00_invoice_plan(self):
num_installment,
"Wrong number of installment created",
)
self.assertEqual(
self.so_service.invoice_plan_pending,
num_installment,
"Pending invoice plan count mismatch after creation",
)
# Change plan, so that the 1st installment is 1000 and 5th is 3000
self.assertEqual(len(self.so_service.invoice_plan_ids), 5)
self.so_service.invoice_plan_ids[0].amount = 280.0
Expand All @@ -189,6 +194,11 @@ def test_00_invoice_plan(self):
self.so_service.amount_total,
sum(self.so_service.invoice_ids.mapped("amount_total")),
)
self.assertEqual(
self.so_service.invoice_plan_pending,
num_installment - 1,
"Pending invoice plan count mismatch after 1st invoice",
)
invoices = self.so_service.invoice_ids
self.assertEqual(len(invoices), 1, "Only 1 invoice should be created")

Expand All @@ -209,11 +219,23 @@ def test_01_invoice_plan(self):
f.num_installment = num_installment
plan = f.save()
plan.with_context(**ctx).sale_create_invoice_plan()
self.assertEqual(
self.so_service.invoice_plan_pending,
num_installment,
"Pending invoice plan count mismatch after creation",
)

# Confirm the SO
self.so_service.action_confirm()
# Create all invoices
make_wizard = self.env["sale.make.planned.invoice"].create({})
make_wizard.with_context(**ctx).create_invoices_by_plan()
# Valid number of invoices plan pending
self.assertEqual(
self.so_service.invoice_plan_pending,
0,
"Pending invoice plan should be 0 after invoicing all",
)
# Valid number of invoices
invoices = self.so_service.invoice_ids
self.assertEqual(
Expand Down Expand Up @@ -242,6 +264,11 @@ def test_02_invoice_plan_with_advance(self):
num_installment + 1,
"Wrong number of installment created",
)
self.assertEqual(
self.so_service.invoice_plan_pending,
num_installment + 1,
"Pending invoice plan count mismatch (with advance)",
)
# If advance percent is not filled, show error
with self.assertRaises(ValidationError):
self.so_service.action_confirm()
Expand All @@ -256,6 +283,12 @@ def test_02_invoice_plan_with_advance(self):
# Create all invoice plan
wizard = self.env["sale.make.planned.invoice"].create({})
wizard.with_context(**ctx).create_invoices_by_plan()
# Valid number of invoices plan pending
self.assertEqual(
self.so_service.invoice_plan_pending,
0,
"Pending invoice plan should be 0 after all invoiced (with advance)",
)
# Valid number of invoices, including advance
invoices = self.so_service.invoice_ids
self.assertEqual(
Expand Down
33 changes: 33 additions & 0 deletions sale_invoice_plan/views/sale_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,39 @@
</xpath>
</field>
</record>
<record id="view_order_tree_invoice_plan" model="ir.ui.view">
<field name="name">view.order.tree.invoice.plan</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_tree" />
<field name="arch" type="xml">
<xpath expr="//field[@name='invoice_status']" position="after">
<field
name="invoice_plan_pending"
optional="hide"
string="Pend."
decoration-bf="invoice_plan_pending &gt; 0"
/>
</xpath>
</field>
</record>
<record id="view_order_filter_invoice_plan" model="ir.ui.view">
<field name="name">view.order.search.invoice.plan</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_sales_order_filter" />
<field name="arch" type="xml">
<xpath expr="//field[@name='order_line']" position="after">
<field name="invoice_plan_pending" string="Pending plans" />
</xpath>
<xpath expr="//filter[@name='activities_upcoming_all']" position="after">
<separator />
<filter
name="invoice_plan_pending"
string="With pending plans"
domain="[('invoice_plan_pending','&gt;',0)]"
/>
</xpath>
</field>
</record>
<!-- Invoice Plan Lines -->
<record id="view_sale_invoice_plan_filter" model="ir.ui.view">
<field name="name">view.sale.invoice.plan.filter</field>
Expand Down