1
1
# Copyright 2017 Creu Blanca
2
2
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
3
3
4
+ import io
4
5
import logging
5
6
from collections import OrderedDict
6
7
7
8
from odoo import _ , api , fields , models
8
- from odoo .exceptions import UserError
9
+ from odoo .exceptions import AccessError , UserError
10
+ from odoo .tools .safe_eval import safe_eval , time
9
11
10
12
_logger = logging .getLogger (__name__ )
11
13
@@ -18,58 +20,127 @@ class ReportAction(models.Model):
18
20
)
19
21
20
22
@api .model
21
- def render_fillpdf (self , docids , data ):
22
- # Here we add pdf attachment support
23
- self_sudo = self . sudo ()
24
- save_in_attachment = OrderedDict ()
25
- # Maps the streams in `save_in_attachment` back to the records they came from
26
- stream_record = dict ()
23
+ def _render_fillpdf_stream (self , report_ref , docids , data ):
24
+ report_sudo = self . _get_report ( report_ref )
25
+ collected_streams = OrderedDict ()
26
+
27
+ # Fetch the existing attachments from the database for later use.
28
+ # Reload the stream from the attachment in case of 'attachment_use'.
27
29
if docids :
28
- # Dispatch the records by ones having an attachment and ones requesting a call to
29
- # fillpdf.
30
- Model = self .env [self_sudo .model ]
31
- record_ids = Model .browse (docids )
32
- fp_record_ids = Model
33
- if self_sudo .attachment :
34
- for record_id in record_ids :
35
- attachment = self_sudo .retrieve_attachment (record_id )
36
- if attachment :
37
- stream = self_sudo ._retrieve_stream_from_attachment (attachment )
38
- save_in_attachment [record_id .id ] = stream
39
- stream_record [stream ] = record_id
40
- if not self_sudo .attachment_use or not attachment :
41
- fp_record_ids += record_id
42
-
43
- else :
44
- fp_record_ids = record_ids
45
- docids = fp_record_ids .ids
46
-
47
- if save_in_attachment and not docids :
48
- _logger .info ("The PDF report has been generated from attachments." )
49
- self ._raise_on_unreadable_pdfs (save_in_attachment .values (), stream_record )
50
- return self_sudo ._post_pdf (save_in_attachment ), "pdf"
51
-
52
- # We generate pdf with fillpdf
53
- report_model_name = "report.%s" % self .report_name
54
- report_model = self .env .get (report_model_name )
55
- if report_model is None :
56
- raise UserError (_ ("%s model was not found" % report_model_name ))
57
-
58
- pdf_content = report_model .with_context (
59
- {"active_model" : self .model }
60
- ).fill_report (docids , data )
30
+ records = self .env [report_sudo .model ].browse (docids )
31
+ for record in records :
32
+ stream = None
33
+ attachment = None
34
+ if report_sudo .attachment :
35
+ attachment = report_sudo .retrieve_attachment (record )
36
+
37
+ # If existing attachement, extract the stream from the attachment.
38
+ if attachment and report_sudo .attachment_use :
39
+ stream = io .BytesIO (attachment .raw )
40
+
41
+ # If no attachment, generate the pdf
42
+ else :
43
+ report_model_name = "report.%s" % self .report_name
44
+ report_model = self .env .get (report_model_name )
45
+ if report_model is None :
46
+ raise UserError (
47
+ _ ("%s model was not found" ) % report_model_name
48
+ )
49
+
50
+ stream = io .BytesIO (
51
+ report_model .with_context (
52
+ ** {"active_model" : self .model }
53
+ ).fill_report (docids , data )
54
+ )
55
+ _logger .info (
56
+ "The PDF report has been generated for model: %s, records %s."
57
+ % (report_model , str (docids ))
58
+ )
59
+
60
+ collected_streams [record .id ] = {
61
+ "stream" : stream ,
62
+ "attachment" : attachment ,
63
+ }
64
+ return collected_streams
65
+
66
+ @api .model
67
+ def render_fillpdf (self , report_ref , docids , data ):
68
+ report_sudo = self ._get_report (report_ref )
69
+ collected_streams = self ._render_fillpdf_stream (report_ref , docids , data )
70
+
71
+ if report_sudo .attachment :
72
+ attachment_vals_list = []
73
+ for res_id , stream_data in collected_streams .items ():
74
+ # An attachment already exists.
75
+ if stream_data ["attachment" ]:
76
+ continue
77
+
78
+ # if res_id is false
79
+ # we are unable to fetch the record,
80
+ # it won't be saved as we can't split the documents unambiguously
81
+ if not res_id :
82
+ _logger .warning (
83
+ "These documents were not saved as an attachment\
84
+ because the template of %s doesn't "
85
+ "have any headers seperating different instances\
86
+ of it. If you want it saved,"
87
+ "please print the documents separately" ,
88
+ report_sudo .report_name ,
89
+ )
90
+ continue
91
+ record = self .env [report_sudo .model ].browse (res_id )
92
+ attachment_name = safe_eval (
93
+ report_sudo .attachment , {"object" : record , "time" : time }
94
+ )
95
+
96
+ # Unable to compute a name for the attachment.
97
+ if not attachment_name :
98
+ continue
99
+
100
+ attachment_vals_list .append (
101
+ {
102
+ "name" : attachment_name ,
103
+ "raw" : stream_data ["stream" ].getvalue (),
104
+ "res_model" : report_sudo .model ,
105
+ "res_id" : record .id ,
106
+ "type" : "binary" ,
107
+ }
108
+ )
109
+
110
+ if attachment_vals_list :
111
+ attachment_names = ", " .join (x ["name" ] for x in attachment_vals_list )
112
+ try :
113
+ self .env ["ir.attachment" ].create (attachment_vals_list )
114
+ except AccessError :
115
+ _logger .info (
116
+ "Cannot save PDF report %r attachments for user %r" ,
117
+ attachment_names ,
118
+ self .env .user .display_name ,
119
+ )
120
+ else :
121
+ _logger .info (
122
+ "The PDF documents %r are now saved in the database" ,
123
+ attachment_names ,
124
+ )
125
+
126
+ # Merge all streams together for a single record.
127
+ streams_to_merge = [
128
+ x ["stream" ] for x in collected_streams .values () if x ["stream" ]
129
+ ]
130
+ if len (streams_to_merge ) == 1 :
131
+ pdf_content = streams_to_merge [0 ].getvalue ()
132
+ else :
133
+ with self ._merge_pdfs (streams_to_merge ) as pdf_merged_stream :
134
+ pdf_content = pdf_merged_stream .getvalue ()
135
+
136
+ for stream in streams_to_merge :
137
+ stream .close ()
61
138
62
139
if docids :
63
- self ._raise_on_unreadable_pdfs (save_in_attachment .values (), stream_record )
64
140
_logger .info (
65
- "The PDF report has been generated for model: %s, records %s."
66
- % (self_sudo .model , str (docids ))
67
- )
68
- return (
69
- self_sudo ._post_pdf (
70
- save_in_attachment , pdf_content = pdf_content , res_ids = docids
71
- ),
72
- "pdf" ,
141
+ "The PDF report has been generated for model: %s, records %s." ,
142
+ report_sudo .model ,
143
+ str (docids ),
73
144
)
74
145
75
146
return pdf_content , "pdf"
@@ -86,4 +157,4 @@ def _get_report_from_name(self, report_name):
86
157
("report_name" , "=" , report_name ),
87
158
]
88
159
context = self .env ["res.users" ].context_get ()
89
- return report_obj .with_context (context ).search (conditions , limit = 1 )
160
+ return report_obj .with_context (** context ).search (conditions , limit = 1 )
0 commit comments