purchase_batch_invoicing.py
4.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from logging import getLogger
from odoo import _, api, fields, models
from odoo.exceptions import UserError
_logger = getLogger(__name__)
class PurchaseBatchInvoicing(models.TransientModel):
_name = "purchase.batch_invoicing"
_description = "Purchase Batch Invoicing"
purchase_order_ids = fields.Many2many(
comodel_name="purchase.order",
string="Purchase orders",
domain="[('invoice_status', '=', 'to invoice')]",
required=True,
readonly=True,
default=lambda self: self._default_purchase_order_ids(),
)
grouping = fields.Selection(
selection=[
("id", "Purchase Order"),
("partner_id", "Vendor"),
],
required=True,
default="id",
help="Make one invoice for each...",
)
@api.model
def _default_purchase_order_ids(self):
"""Get purchase orders from active ids."""
try:
return self.env["purchase.order"].search(
self._purchase_order_domain(self.env.context["active_ids"])
).ids
except KeyError:
return False
@api.model
def _purchase_order_domain(self, ids=None):
"""Helper to filter current ids by those that are to invoice."""
domain = [("invoice_status", "=", "to invoice")]
if ids:
domain += [("id", "in", ids)]
pos = self.env["purchase.order"].search(domain)
# Use only POs with less qty invoiced than the expected
pos = pos.filtered(lambda order: (
any(
line.qty_invoiced < (
line.qty_received
if line.product_id.purchase_method == 'receive'
else line.product_qty
)
) for line in order.order_line
))
if len(domain) > 1:
domain[1] = ("id", "in", pos.ids)
return domain
def _prepare_batch_invoice_vals(self, partner):
"""Allow to override the invoice defaults by a third module.
i.e.: set invoice type to in_refund"""
return {"partner_id": partner.id, "type": "in_invoice"}
@api.multi
def grouped_purchase_orders(self):
"""Purchase orders, applying current grouping.
:return generator:
Generator of grouped ``purchase.order`` recordsets.
If :attr:`grouping` is ``id``, the generator will yield recordsets
with 1 order each; if it is ``partner_id``, the yielded recordsets
will contain all purchase orders from each vendor.
"""
PurchaseOrder = self.env["purchase.order"]
domain = self._purchase_order_domain(self.purchase_order_ids.ids)
for group in self.mapped("purchase_order_ids.%s" % self.grouping):
pos = PurchaseOrder.search(
domain + [(self.grouping, "=", int(group))])
if pos:
yield pos
@api.multi
def action_batch_invoice(self):
"""Generate invoices for all selected purchase orders.
:return dict:
Window action to see the generated invoices.
"""
invoices = self.env["account.invoice"]
for pogroup in self.grouped_purchase_orders():
invoice = invoices.create(
self._prepare_batch_invoice_vals(pogroup.mapped("partner_id")))
invoice._onchange_partner_id()
for po in pogroup:
invoice.currency_id = po.currency_id
invoice.purchase_id = po
invoice.purchase_order_change()
invoices |= invoice
if not invoices:
raise UserError(_("No ready-to-invoice purchase orders selected."))
invoices.compute_taxes()
return {
"type": "ir.actions.act_window",
"res_model": "account.invoice",
"name": _("Generated Invoices"),
"views": [[False, "tree"], [False, "form"]],
"domain": [["id", "in", invoices.ids]],
}
@api.model
def cron_invoice_all_pending(self, grouping="partner_id"):
"""Invoice all pending purchase orders."""
_logger.info("Starting to invoice all pending purchase orders.")
wizard = self.create({
"purchase_order_ids": [
(6, False, self.env["purchase.order"].search(
self._purchase_order_domain()).ids)],
"grouping": grouping,
})
try:
result = wizard.action_batch_invoice()
_logger.info("Finished invoicing all pending purchase orders.")
_logger.debug("Result: %r", result)
except UserError as error:
_logger.info(error.name)
_logger.debug("Traceback:", exc_info=True)