Skip to content

Commit 5ea21e5

Browse files
committed
[16.0][ADD] sale_invoice_plan_batch
1 parent 627b3be commit 5ea21e5

15 files changed

+1014
-0
lines changed

sale_invoice_plan_batch/README.rst

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
============================================
2+
Sales Invoice Plan - Create Invoice in Batch
3+
============================================
4+
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7+
!! This file is generated by oca-gen-addon-readme !!
8+
!! changes will be overwritten. !!
9+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
!! source digest: sha256:734046be8463ba4f5d82a22dbb8a437a1e9facc68f5563080a9ada68f2780931
11+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
12+
13+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
14+
:target: https://odoo-community.org/page/development-status
15+
:alt: Beta
16+
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
17+
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
18+
:alt: License: AGPL-3
19+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsale--workflow-lightgray.png?logo=github
20+
:target: https://github.com/OCA/sale-workflow/tree/16.0/sale_invoice_plan_batch
21+
:alt: OCA/sale-workflow
22+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
23+
:target: https://translation.odoo-community.org/projects/sale-workflow-16-0/sale-workflow-16-0-sale_invoice_plan_batch
24+
:alt: Translate me on Weblate
25+
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
26+
:target: https://runboat.odoo-community.org/builds?repo=OCA/sale-workflow&target_branch=16.0
27+
:alt: Try me on Runboat
28+
29+
|badge1| |badge2| |badge3| |badge4| |badge5|
30+
31+
This module add new way to create invoice from sales invoice plan in batch
32+
rather than having to open each sales order to create invoice by plan.
33+
34+
A new "Sales Invoice Plan" Batch document allow user to filter all matched installments
35+
with many criteria, i.e., plan date from, plan date to, customers, sales orders.
36+
37+
Then, all installment can be created as invoices in one button click.
38+
39+
**Table of contents**
40+
41+
.. contents::
42+
:local:
43+
44+
Usage
45+
=====
46+
47+
1. Go to menu Sales / Orders / Sales Invoice Plan Batch
48+
2. Key in Filter Installments
49+
3. Click button Seach Installments, matched installments will be listed.
50+
4. Click button Create Invoices
51+
52+
Note: This feature is not used for invoice type "advance", but only "installment"
53+
54+
Bug Tracker
55+
===========
56+
57+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/sale-workflow/issues>`_.
58+
In case of trouble, please check there if your issue has already been reported.
59+
If you spotted it first, help us to smash it by providing a detailed and welcomed
60+
`feedback <https://github.com/OCA/sale-workflow/issues/new?body=module:%20sale_invoice_plan_batch%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
61+
62+
Do not contact contributors directly about support or help with technical issues.
63+
64+
Credits
65+
=======
66+
67+
Authors
68+
~~~~~~~
69+
70+
* Ecosoft
71+
72+
Contributors
73+
~~~~~~~~~~~~
74+
75+
* `Ecosoft <http://ecosoft.co.th>`_:
76+
77+
* Kitti U. <[email protected]>
78+
79+
Maintainers
80+
~~~~~~~~~~~
81+
82+
This module is maintained by the OCA.
83+
84+
.. image:: https://odoo-community.org/logo.png
85+
:alt: Odoo Community Association
86+
:target: https://odoo-community.org
87+
88+
OCA, or the Odoo Community Association, is a nonprofit organization whose
89+
mission is to support the collaborative development of Odoo features and
90+
promote its widespread use.
91+
92+
This module is part of the `OCA/sale-workflow <https://github.com/OCA/sale-workflow/tree/16.0/sale_invoice_plan_batch>`_ project on GitHub.
93+
94+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

sale_invoice_plan_batch/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import models
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright 2024 Ecosoft Co., Ltd. (http://ecosoft.co.th)
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
4+
{
5+
"name": "Sales Invoice Plan - Create Invoice in Batch",
6+
"version": "16.0.1.0.0",
7+
"license": "AGPL-3",
8+
"depends": ["sale_invoice_plan"],
9+
"author": "Ecosoft, Odoo Community Association (OCA)",
10+
"website": "https://github.com/OCA/sale-workflow",
11+
"category": "Sales Management",
12+
"data": [
13+
"data/sequence.xml",
14+
"security/sale_invoice_plan_batch_security.xml",
15+
"security/ir.model.access.csv",
16+
"views/sale_invoice_plan_batch.xml",
17+
],
18+
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<odoo noupdate="1">
3+
<record id="seq_sale_invoice_plan_batch" model="ir.sequence">
4+
<field name="name">Sale Invoice Plan Batch</field>
5+
<field name="code">sale.invoice.plan.batch</field>
6+
<field name="prefix">SIB</field>
7+
<field name="padding">5</field>
8+
<field name="company_id" eval="False" />
9+
</record>
10+
</odoo>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import sale_invoice_plan_batch
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
# Copyright 2024 Ecosoft Co., Ltd. (http://ecosoft.co.th)
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3+
4+
from odoo import _, api, fields, models
5+
from odoo.exceptions import ValidationError
6+
7+
8+
class SaleInvoicePlanBatch(models.Model):
9+
_name = "sale.invoice.plan.batch"
10+
_inherit = ["mail.thread", "mail.activity.mixin"]
11+
_description = "Create invoices from invoice plan in batch"
12+
_check_company_auto = True
13+
_order = "id desc"
14+
15+
name = fields.Char(
16+
required=True,
17+
readonly=True,
18+
default=lambda self: _("New"),
19+
copy=False,
20+
)
21+
description = fields.Char(
22+
readonly=True,
23+
states={"draft": [("readonly", False)]},
24+
)
25+
company_id = fields.Many2one(
26+
comodel_name="res.company",
27+
string="Company",
28+
readonly=True,
29+
default=lambda self: self.env.company,
30+
)
31+
batch_line_ids = fields.One2many(
32+
comodel_name="sale.invoice.plan.batch.line",
33+
inverse_name="batch_id",
34+
readonly=True,
35+
states={
36+
"draft": [("readonly", False)],
37+
"ready": [("readonly", False)],
38+
},
39+
help="Selected invoice plan to create invoice",
40+
)
41+
state = fields.Selection(
42+
selection=[
43+
("draft", "Draft"),
44+
("ready", "Ready"),
45+
("done", "Done"),
46+
("done_exception", "Done with Exception"),
47+
],
48+
default="draft",
49+
tracking=True,
50+
index=True,
51+
required=True,
52+
readonly=True,
53+
)
54+
# Filters
55+
plan_date_from = fields.Date(
56+
string="Plan Date From",
57+
required=False,
58+
readonly=True,
59+
states={"draft": [("readonly", False)]},
60+
help="All un-invoiced invoice plan with plan date prior to this date will be included",
61+
)
62+
plan_date_to = fields.Date(
63+
string="Plan Date To",
64+
default=fields.Date.today,
65+
required=True,
66+
readonly=True,
67+
states={"draft": [("readonly", False)]},
68+
help="All un-invoiced invoice plan with plan date prior to this date will be included",
69+
)
70+
sale_ids = fields.Many2many(
71+
comodel_name="sale.order",
72+
readonly=True,
73+
states={"draft": [("readonly", False)]},
74+
)
75+
partner_ids = fields.Many2many(
76+
comodel_name="res.partner",
77+
readonly=True,
78+
states={"draft": [("readonly", False)]},
79+
)
80+
81+
_sql_constraints = [
82+
("name_uniq", "UNIQUE(name)", "Batch name must be unique!"),
83+
]
84+
85+
@api.model_create_multi
86+
def create(self, vals_list):
87+
for vals in vals_list:
88+
if not vals.get("name") or vals["name"] == _("New"):
89+
vals["name"] = self.env["ir.sequence"].next_by_code(
90+
"sale.invoice.plan.batch"
91+
) or _("New")
92+
return super().create(vals_list)
93+
94+
def reset_to_draft(self):
95+
self.write({"state": "draft"})
96+
97+
def _get_filter(self):
98+
self.ensure_one()
99+
domain = [
100+
("invoice_type", "=", "installment"),
101+
("state", "in", ("sale", "done")),
102+
("invoiced", "=", False),
103+
("plan_date", "<=", self.plan_date_to),
104+
("sale_id.invoice_status", "=", "to invoice"),
105+
]
106+
if self.plan_date_from:
107+
domain.append(("plan_date", ">=", self.plan_date_from))
108+
if self.partner_ids:
109+
domain.append(("partner_id", "in", self.partner_ids.ids))
110+
if self.sale_ids:
111+
domain.append(("sale_id", "in", self.sale_ids.ids))
112+
return domain
113+
114+
def get_planned_installments(self):
115+
for batch in self:
116+
domain = batch._get_filter()
117+
installments = self.env["sale.invoice.plan"].search(
118+
domain, order="sale_id desc, installment"
119+
)
120+
batch.batch_line_ids.unlink()
121+
lines = [(0, 0, {"invoice_plan_id": x.id}) for x in installments]
122+
batch.write({"batch_line_ids": lines})
123+
self.write({"state": "ready"})
124+
125+
def create_invoices(self):
126+
MakeInvoice = self.env["sale.advance.payment.inv"]
127+
batch_lines = self.mapped("batch_line_ids")
128+
sales = batch_lines.mapped("invoice_plan_id.sale_id")
129+
for sale in sales: # Process by sale order
130+
sale_batch_lines = batch_lines.filtered(lambda l: l.sale_id == sale)
131+
MakeInvoice = MakeInvoice.with_context(active_ids=[sale.id])
132+
for bl in sale_batch_lines.sorted("installment"):
133+
try:
134+
makeinv_wizard = {"advance_payment_method": "delivered"}
135+
makeinvoice = MakeInvoice.create(makeinv_wizard)
136+
makeinvoice.sudo().with_context(
137+
invoice_plan_id=bl.invoice_plan_id.id
138+
).create_invoices()
139+
self.env.cr.commit()
140+
except Exception as e:
141+
bl.error = True
142+
bl.error_message = e
143+
self.write({"state": "done"})
144+
145+
def open_sales(self):
146+
self.ensure_one()
147+
action = {
148+
"name": _("Sales Order"),
149+
"type": "ir.actions.act_window",
150+
"res_model": "sale.order",
151+
"view_mode": "list,form",
152+
"domain": [("id", "in", self.batch_line_ids.sale_id.ids)],
153+
"context": {"create": False},
154+
}
155+
return action
156+
157+
def open_invoices(self):
158+
self.ensure_one()
159+
action = {
160+
"name": _("Customer Invoices"),
161+
"type": "ir.actions.act_window",
162+
"res_model": "account.move",
163+
"view_mode": "list,form",
164+
"domain": [("id", "in", self.batch_line_ids.invoice_move_ids.ids)],
165+
"context": {"create": False},
166+
}
167+
return action
168+
169+
def unlink(self):
170+
recs = self.filtered(lambda l: l.state in ["done", "done_exception"])
171+
if recs:
172+
raise ValidationError(
173+
_("The batch %s is not in draft state, so you cannot delete it.")
174+
% ", ".join(recs.mapped("name"))
175+
)
176+
return super().unlink()
177+
178+
179+
class SaleInvoicePlanBatchLine(models.Model):
180+
_name = "sale.invoice.plan.batch.line"
181+
_description = "Sale Invoice Plan Batch Line"
182+
_order = "id"
183+
184+
batch_id = fields.Many2one(comodel_name="sale.invoice.plan.batch", index=True)
185+
invoice_plan_id = fields.Many2one(
186+
comodel_name="sale.invoice.plan",
187+
readonly=True,
188+
)
189+
sale_id = fields.Many2one(
190+
related="invoice_plan_id.sale_id",
191+
)
192+
partner_id = fields.Many2one(
193+
related="sale_id.partner_id",
194+
)
195+
installment = fields.Integer(
196+
related="invoice_plan_id.installment",
197+
)
198+
plan_date = fields.Date(
199+
related="invoice_plan_id.plan_date",
200+
)
201+
invoice_type = fields.Selection(
202+
related="invoice_plan_id.invoice_type",
203+
)
204+
percent = fields.Float(
205+
related="invoice_plan_id.percent",
206+
)
207+
amount = fields.Float(
208+
related="invoice_plan_id.amount",
209+
)
210+
invoiced = fields.Boolean(
211+
related="invoice_plan_id.invoiced",
212+
)
213+
invoice_move_ids = fields.Many2many(
214+
comodel_name="account.move",
215+
related="invoice_plan_id.invoice_move_ids",
216+
)
217+
error = fields.Boolean()
218+
error_message = fields.Text()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
* `Ecosoft <http://ecosoft.co.th>`_:
2+
3+
* Kitti U. <[email protected]>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
This module add new way to create invoice from sales invoice plan in batch
2+
rather than having to open each sales order to create invoice by plan.
3+
4+
A new "Sales Invoice Plan" Batch document allow user to filter all matched installments
5+
with many criteria, i.e., plan date from, plan date to, customers, sales orders.
6+
7+
Then, all installment can be created as invoices in one button click.
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
1. Go to menu Sales / Orders / Sales Invoice Plan Batch
2+
2. Key in Filter Installments
3+
3. Click button Seach Installments, matched installments will be listed.
4+
4. Click button Create Invoices
5+
6+
Note: This feature is not used for invoice type "advance", but only "installment"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
2+
access_sale_invoice_plan_batch,sale.invoice.plan.batch,model_sale_invoice_plan_batch,account.group_account_invoice,1,1,1,1
3+
access_sale_invoice_plan_batch_line,sale.invoice.plan.batch.line,model_sale_invoice_plan_batch_line,account.group_account_invoice,1,1,1,1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<odoo noupdate="1">
2+
<record
3+
id="sale_invoice_plan_batch_security_batch_multi_company_rule"
4+
model="ir.rule"
5+
>
6+
<field name="name">Sales Invoice Plan Batch multi-company</field>
7+
<field name="model_id" ref="model_sale_invoice_plan_batch" />
8+
<field eval="True" name="global" />
9+
<field
10+
name="domain_force"
11+
>['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
12+
</record>
13+
</odoo>

0 commit comments

Comments
 (0)