Initial commit: Odoo 18.0-20251222 extra-addons
This commit is contained in:
6
partner_invoicing_mode/models/__init__.py
Normal file
6
partner_invoicing_mode/models/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from . import account_invoice
|
||||
from . import queue_job
|
||||
from . import res_partner
|
||||
from . import sale_order
|
||||
from . import res_company
|
||||
from . import res_config_settings
|
||||
11
partner_invoicing_mode/models/account_invoice.py
Normal file
11
partner_invoicing_mode/models/account_invoice.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# Copyright 2020 Camptocamp SA
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
_inherit = "account.move"
|
||||
|
||||
def _validate_invoice(self):
|
||||
return self.sudo().action_post()
|
||||
15
partner_invoicing_mode/models/queue_job.py
Normal file
15
partner_invoicing_mode/models/queue_job.py
Normal file
@@ -0,0 +1,15 @@
|
||||
# Copyright 2020 Camptocamp SA
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class QueueJob(models.Model):
|
||||
_inherit = "queue.job"
|
||||
|
||||
def related_action_open_invoice(self):
|
||||
"""Open a form view with the invoice related to the job."""
|
||||
action = self.related_action_open_record()
|
||||
if len(self.records.exists()) > 1:
|
||||
action["view_id"] = self.env.ref("account.view_out_invoice_tree").id
|
||||
return action
|
||||
13
partner_invoicing_mode/models/res_company.py
Normal file
13
partner_invoicing_mode/models/res_company.py
Normal file
@@ -0,0 +1,13 @@
|
||||
# Copyright 2020 Camptocamp SA
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
_inherit = "res.company"
|
||||
|
||||
invoicing_mode_standard_last_execution = fields.Datetime(
|
||||
string="Last execution (standard)",
|
||||
help="Last execution of standard invoicing.",
|
||||
)
|
||||
11
partner_invoicing_mode/models/res_config_settings.py
Normal file
11
partner_invoicing_mode/models/res_config_settings.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# Copyright 2020 Camptocamp SA
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = "res.config.settings"
|
||||
|
||||
invoicing_mode_standard_last_execution = fields.Datetime(
|
||||
related="company_id.invoicing_mode_standard_last_execution"
|
||||
)
|
||||
30
partner_invoicing_mode/models/res_partner.py
Normal file
30
partner_invoicing_mode/models/res_partner.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# Copyright 2020 Camptocamp SA
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
_inherit = "res.partner"
|
||||
|
||||
invoicing_mode = fields.Selection([("standard", "Standard")], default="standard")
|
||||
one_invoice_per_order = fields.Boolean(
|
||||
help="Do not group sale order into one invoice.",
|
||||
)
|
||||
next_invoice_date = fields.Date(
|
||||
help="This is the date at which the next invoice will be generated."
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _commercial_fields(self):
|
||||
return super()._commercial_fields() + [
|
||||
"invoicing_mode",
|
||||
"one_invoice_per_order",
|
||||
]
|
||||
|
||||
def _update_next_invoice_date(self):
|
||||
"""
|
||||
This will update the next invoice date from the configuration set on
|
||||
the partner if needed (not for standard invoicing_mode).
|
||||
"""
|
||||
return
|
||||
142
partner_invoicing_mode/models/sale_order.py
Normal file
142
partner_invoicing_mode/models/sale_order.py
Normal file
@@ -0,0 +1,142 @@
|
||||
# Copyright 2020 Camptocamp SA
|
||||
# Copyright 2023 ACSONE SA/NV
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
||||
from odoo import api, fields, models
|
||||
from odoo.fields import Datetime
|
||||
from odoo.osv.expression import AND
|
||||
|
||||
from odoo.addons.queue_job.job import identity_exact
|
||||
|
||||
|
||||
class SaleOrder(models.Model):
|
||||
_inherit = "sale.order"
|
||||
|
||||
invoicing_mode = fields.Selection(
|
||||
related="partner_invoice_id.invoicing_mode",
|
||||
store=True,
|
||||
index=True,
|
||||
)
|
||||
|
||||
one_invoice_per_order = fields.Boolean(
|
||||
compute="_compute_one_invoice_per_order",
|
||||
readonly=False,
|
||||
store=True,
|
||||
help="You can check or uncheck this if you want the periodic invoicing"
|
||||
" grouping this sale order with other ones or not.",
|
||||
)
|
||||
|
||||
@api.depends("partner_invoice_id")
|
||||
def _compute_one_invoice_per_order(self):
|
||||
# We depends only on partner as if we change it, we should recompute
|
||||
# but not if the parameter on it has changed (this allows to set a different
|
||||
# value on each sale order).
|
||||
for order in self:
|
||||
order.one_invoice_per_order = order.partner_invoice_id.one_invoice_per_order
|
||||
|
||||
@api.model
|
||||
def cron_generate_standard_invoices(self):
|
||||
company_ids = self._get_companies_standard_invoicing()
|
||||
if company_ids:
|
||||
self.generate_invoices(company_ids)
|
||||
|
||||
@api.model
|
||||
def _get_generate_invoices_state_domain(self):
|
||||
return [("invoice_status", "=", "to invoice")]
|
||||
|
||||
@api.model
|
||||
def _get_generate_invoices_domain(self, companies, invoicing_mode="standard"):
|
||||
return AND(
|
||||
[
|
||||
self._get_generate_invoices_state_domain(),
|
||||
[
|
||||
("invoicing_mode", "=", invoicing_mode),
|
||||
("company_id", "in", companies.ids),
|
||||
],
|
||||
]
|
||||
)
|
||||
|
||||
@api.model
|
||||
def generate_invoices(
|
||||
self,
|
||||
companies=None,
|
||||
invoicing_mode="standard",
|
||||
last_execution_field="invoicing_mode_standard_last_execution",
|
||||
):
|
||||
"""
|
||||
Generate invoices in job queues depending on the invoicing
|
||||
mode (stadndard by default)
|
||||
"""
|
||||
if companies is None:
|
||||
companies = self.env.company
|
||||
domain = self._get_generate_invoices_domain(
|
||||
companies=companies, invoicing_mode=invoicing_mode
|
||||
)
|
||||
saleorder_groups = self.read_group(
|
||||
domain,
|
||||
["partner_invoice_id", "sale_ids:array_agg(id)"],
|
||||
groupby=self._get_invoice_grouping_keys(),
|
||||
lazy=False,
|
||||
)
|
||||
for saleorder_group in saleorder_groups:
|
||||
self.with_delay(
|
||||
identity_key=identity_exact,
|
||||
description=self.env._("Generate invoices by partner"),
|
||||
)._generate_invoices_by_partner(saleorder_group["sale_ids"])
|
||||
companies.write({last_execution_field: Datetime.now()})
|
||||
return saleorder_groups
|
||||
|
||||
@api.model
|
||||
def _get_invoice_grouping_keys(self) -> list:
|
||||
"""
|
||||
We override the standard (in sale) grouping function in order to
|
||||
add some missing keys. We remove also the partner_id key.
|
||||
"""
|
||||
keys = super()._get_invoice_grouping_keys()
|
||||
if "partner_invoice_id" not in keys:
|
||||
keys.append("partner_invoice_id")
|
||||
if "payment_term_id" not in keys:
|
||||
keys.append("payment_term_id")
|
||||
# Removing unwanted keys as we group on invoiced partner
|
||||
if "partner_id" in keys:
|
||||
keys.remove("partner_id")
|
||||
return keys
|
||||
|
||||
def _get_generated_invoices(self, partition):
|
||||
"""
|
||||
Hook to get the generated invoices as in some extended modules,
|
||||
those invoices should not be validated yet.
|
||||
"""
|
||||
return self._create_invoices(grouped=partition, final=True)
|
||||
|
||||
def _generate_invoices_by_partner(self, saleorder_ids):
|
||||
"""Generate invoices for a group of sale order belonging to a customer."""
|
||||
today = fields.Date.context_today(self)
|
||||
sale_orders = (
|
||||
self.browse(saleorder_ids)
|
||||
.exists()
|
||||
.filtered_domain(self._get_generate_invoices_state_domain())
|
||||
)
|
||||
if not sale_orders:
|
||||
return "No sale order found to invoice ?"
|
||||
# Create invoices using grouping when needed, so partition sales
|
||||
invoice_ids = set()
|
||||
for partition, partitioned_sales in sale_orders.partition(
|
||||
lambda sale: sale.one_invoice_per_order
|
||||
).items():
|
||||
invoices = partitioned_sales._get_generated_invoices(partition=partition)
|
||||
# Update invoices date to be sure no long validation (jobs) put
|
||||
# the further day date on invoices.
|
||||
invoices.write({"invoice_date": today})
|
||||
# Update each partner next invoice date
|
||||
partitioned_sales.partner_invoice_id._update_next_invoice_date()
|
||||
invoice_ids.update(invoices.ids)
|
||||
for invoice in invoices:
|
||||
description = self.env._("Validate %s invoice", invoice.display_name)
|
||||
invoice.with_delay(
|
||||
identity_key=identity_exact, description=description
|
||||
)._validate_invoice()
|
||||
return self.env["account.move"].browse(invoice_ids)
|
||||
|
||||
@api.model
|
||||
def _get_companies_standard_invoicing(self):
|
||||
return self.env["res.company"].search([])
|
||||
Reference in New Issue
Block a user