Skip to content

Commit e5a2245

Browse files
[ADD] account_billling_from_cutoff
1 parent 3c2b0c4 commit e5a2245

9 files changed

Lines changed: 168 additions & 0 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from . import models
2+
from . import wizards
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright 2025 Quartile (https://www.quartile.co)
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
4+
{
5+
"name": "Account Billing From Cutoff",
6+
"version": "16.0.1.0.0",
7+
"depends": ["account_billing", "account_payment_term_cutoff_day"],
8+
"author": "Quartile, Odoo Community Association (OCA)",
9+
"license": "AGPL-3",
10+
"website": "https://github.com/OCA/l10n-japan",
11+
"category": "Accounting",
12+
"data": [
13+
"security/ir.model.access.csv",
14+
"wizards/account_billing_cutoff.xml",
15+
"views/res_partner_views.xml",
16+
],
17+
"installable": True,
18+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import res_partner
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright 2025 Quartile (https://www.quartile.co)
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
4+
from odoo import fields, models
5+
6+
7+
class ResPartner(models.Model):
8+
_inherit = "res.partner"
9+
10+
is_summary_invoicing = fields.Boolean(
11+
string="Summary Invoicing",
12+
help="Check this box to enable summary invoicing for this partner. "
13+
"When enabled, invoices will be grouped into a single summary invoice "
14+
"based on the cutoff date specified in the billing wizard.",
15+
)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
2+
access_wiz_account_billing_cutoff,access_wiz_account_billing_cutoff,model_wiz_account_billing_cutoff,account.group_account_invoice,1,1,1,1
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<odoo>
2+
<record id="view_partner_property_form" model="ir.ui.view">
3+
<field name="name">res.partner.form</field>
4+
<field name="model">res.partner</field>
5+
<field name="inherit_id" ref="account.view_partner_property_form" />
6+
<field name="arch" type="xml">
7+
<xpath expr="//group[@name='credit_limits']" position="after">
8+
<group
9+
string="Summary Invoicing"
10+
name="is_summary_invoicing"
11+
groups="account.group_account_invoice"
12+
>
13+
<field name="is_summary_invoicing" />
14+
</group>
15+
</xpath>
16+
</field>
17+
</record>
18+
</odoo>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import account_billing_cutoff
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Copyright 2025 Quartile (https://www.quartile.co)
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
4+
from odoo import fields, models
5+
from collections import defaultdict
6+
7+
8+
class AccountBillingCutoff(models.TransientModel):
9+
_name = "wiz.account.billing.cutoff"
10+
_description = "Account Billing From Cutoff"
11+
12+
cutoff_date = fields.Date(required=True, default=fields.Date.context_today)
13+
bill_type = fields.Selection(
14+
selection=[("out_invoice", "Customer Invoice"), ("in_invoice", "Vendor Bill")],
15+
readonly=True,
16+
default=lambda self: self._context.get("bill_type", False),
17+
)
18+
19+
def _allowed_move_types(self):
20+
if self.bill_type == "out_invoice":
21+
return ["out_invoice", "out_refund"]
22+
return ["in_invoice", "in_refund"]
23+
24+
def _search_moves_domain(self):
25+
return [
26+
("partner_id.is_summary_invoicing", "=", True),
27+
("company_id", "=", self.env.company.id),
28+
("state", "=", "posted"),
29+
("payment_state", "!=", "paid"),
30+
("move_type", "in", self._allowed_move_types()),
31+
("cutoff_date", "<=", self.cutoff_date),
32+
]
33+
34+
def action_create_billings(self):
35+
Move = self.env["account.move"]
36+
moves = Move.search(self._search_moves_domain(), order="partner_id, currency_id, id")
37+
moves = moves.filtered(
38+
lambda m: not any(b.state in ("draft", "billed") for b in m.billing_ids)
39+
)
40+
if not moves:
41+
return {"type": "ir.actions.act_window_close"}
42+
groups = defaultdict(lambda: self.env["account.move"])
43+
for m in moves:
44+
groups[(m.partner_id.id, m.currency_id.id, m.move_type)] |= m
45+
created = self.env["account.billing"]
46+
for (partner_id, _currency_id, _move_type), recs in groups.items():
47+
partner = self.env["res.partner"].browse(partner_id)
48+
billing = recs._create_billing(partner)
49+
created |= billing
50+
return {
51+
"type": "ir.actions.act_window",
52+
"name": "Billings",
53+
"res_model": "account.billing",
54+
"view_mode": "tree,form",
55+
"domain": [("id", "in", created.ids)],
56+
"target": "current",
57+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<odoo>
3+
<record
4+
id="account_billing_cutoff_view_form" model="ir.ui.view">
5+
<field name="name">wiz.account.billing.cutoff</field>
6+
<field name="model">wiz.account.billing.cutoff</field>
7+
<field name="arch" type="xml">
8+
<form>
9+
<sheet>
10+
<group>
11+
<group>
12+
<field name="bill_type" readonly="1" />
13+
<field name="cutoff_date" />
14+
</group>
15+
</group>
16+
</sheet>
17+
<footer>
18+
<button name="action_create_billings"
19+
string="Create Billings"
20+
type="object"
21+
class="btn-primary"
22+
/>
23+
<button string="Cancel" special="cancel" class="btn-secondary"/>
24+
</footer>
25+
</form>
26+
</field>
27+
</record>
28+
<record id="action_customer_account_billing_cutoff" model="ir.actions.act_window">
29+
<field name="name">Create Account Billing From Cutoff</field>
30+
<field name="res_model">wiz.account.billing.cutoff</field>
31+
<field name="view_mode">form</field>
32+
<field name="context" >{'bill_type':'out_invoice'}</field>
33+
<field name="target">new</field>
34+
</record>
35+
<record id="action_supplier_account_billing_cutoff" model="ir.actions.act_window">
36+
<field name="name">Create Account Billing From Cutoff</field>
37+
<field name="res_model">wiz.account.billing.cutoff</field>
38+
<field name="view_mode">form</field>
39+
<field name="context" >{'bill_type':'in_invoice'}</field>
40+
<field name="target">new</field>
41+
</record>
42+
<menuitem
43+
action="action_customer_account_billing_cutoff"
44+
id="menu_action_customer_billing"
45+
parent="account.menu_finance_receivables"
46+
sequence="12"
47+
/>
48+
<menuitem
49+
action="action_supplier_account_billing_cutoff"
50+
id="menu_action_supplier_billing"
51+
parent="account.menu_finance_payables"
52+
sequence="12"
53+
/>
54+
</odoo>

0 commit comments

Comments
 (0)