OCA reporting engine fork for dev and update.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

667 lines
30 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. # Copyright 2009-2018 Noviat.
  2. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
  3. import re
  4. from datetime import date, datetime
  5. from types import CodeType
  6. from xlsxwriter.utility import xl_rowcol_to_cell
  7. from odoo import _, fields, models
  8. from odoo.exceptions import UserError
  9. class ReportXlsxAbstract(models.AbstractModel):
  10. _inherit = "report.report_xlsx.abstract"
  11. def generate_xlsx_report(self, workbook, data, objects):
  12. self._define_formats(workbook)
  13. for ws_params in self._get_ws_params(workbook, data, objects):
  14. ws_name = ws_params.get("ws_name")
  15. ws_name = self._check_ws_name(ws_name)
  16. ws = workbook.add_worksheet(ws_name)
  17. generate_ws_method = getattr(self, ws_params["generate_ws_method"])
  18. generate_ws_method(workbook, ws, ws_params, data, objects)
  19. def _check_ws_name(self, name, sanitize=True):
  20. pattern = re.compile(r"[/\\*\[\]:?]") # invalid characters: /\*[]:?
  21. max_chars = 31
  22. if sanitize:
  23. # we could drop these two lines since a similar
  24. # sanitize is done in tools.misc PatchedXlsxWorkbook
  25. name = pattern.sub("", name)
  26. name = name[:max_chars]
  27. else:
  28. if len(name) > max_chars:
  29. raise UserError(
  30. _(
  31. "Programming Error:\n\n"
  32. "Excel Sheet name '%s' should not exceed %s characters."
  33. )
  34. % (name, max_chars)
  35. )
  36. special_chars = pattern.findall(name)
  37. if special_chars:
  38. raise UserError(
  39. _(
  40. "Programming Error:\n\n"
  41. "Excel Sheet name '%s' contains unsupported special "
  42. "characters: '%s'."
  43. )
  44. % (name, special_chars)
  45. )
  46. return name
  47. def _get_ws_params(self, workbook, data, objects):
  48. """
  49. Return list of dictionaries with parameters for the
  50. worksheets.
  51. Keywords:
  52. - 'generate_ws_method': mandatory
  53. - 'ws_name': name of the worksheet
  54. - 'title': title of the worksheet
  55. - 'wanted_list': list of column names
  56. - 'col_specs': cf. XXX
  57. The 'generate_ws_method' must be present in your report
  58. and contain the logic to generate the content of the worksheet.
  59. """
  60. return []
  61. def _define_xls_headers(self, workbook):
  62. """
  63. Predefined worksheet headers/footers.
  64. """
  65. hf_params = {
  66. "font_size": 8,
  67. "font_style": "I", # B: Bold, I: Italic, U: Underline
  68. }
  69. self.xls_headers = {"standard": ""}
  70. report_date = fields.Datetime.context_timestamp(
  71. self.env.user, datetime.now()
  72. ).strftime("%Y-%m-%d %H:%M")
  73. self.xls_footers = {
  74. "standard": (
  75. "&L&%(font_size)s&%(font_style)s"
  76. + report_date
  77. + "&R&%(font_size)s&%(font_style)s&P / &N"
  78. )
  79. % hf_params
  80. }
  81. def _define_formats(self, workbook):
  82. """
  83. This section contains a number of pre-defined formats.
  84. It is recommended to use these in order to have a
  85. consistent look & feel between your XLSX reports.
  86. """
  87. self._define_xls_headers(workbook)
  88. border_grey = "#D3D3D3"
  89. border = {"border": True, "border_color": border_grey}
  90. theader = dict(border, bold=True)
  91. bg_yellow = "#FFFFCC"
  92. bg_blue = "#CCFFFF"
  93. num_format = "#,##0.00"
  94. num_format_conditional = "{0};[Red]-{0};{0}".format(num_format)
  95. pct_format = "#,##0.00%"
  96. pct_format_conditional = "{0};[Red]-{0};{0}".format(pct_format)
  97. int_format = "#,##0"
  98. int_format_conditional = "{0};[Red]-{0};{0}".format(int_format)
  99. date_format = "YYYY-MM-DD"
  100. theader_yellow = dict(theader, bg_color=bg_yellow)
  101. theader_blue = dict(theader, bg_color=bg_blue)
  102. # format for worksheet title
  103. self.format_ws_title = workbook.add_format({"bold": True, "font_size": 14})
  104. # no border formats
  105. self.format_left = workbook.add_format({"align": "left"})
  106. self.format_center = workbook.add_format({"align": "center"})
  107. self.format_right = workbook.add_format({"align": "right"})
  108. self.format_amount_left = workbook.add_format(
  109. {"align": "left", "num_format": num_format}
  110. )
  111. self.format_amount_center = workbook.add_format(
  112. {"align": "center", "num_format": num_format}
  113. )
  114. self.format_amount_right = workbook.add_format(
  115. {"align": "right", "num_format": num_format}
  116. )
  117. self.format_amount_conditional_left = workbook.add_format(
  118. {"align": "left", "num_format": num_format_conditional}
  119. )
  120. self.format_amount_conditional_center = workbook.add_format(
  121. {"align": "center", "num_format": num_format_conditional}
  122. )
  123. self.format_amount_conditional_right = workbook.add_format(
  124. {"align": "right", "num_format": num_format_conditional}
  125. )
  126. self.format_percent_left = workbook.add_format(
  127. {"align": "left", "num_format": pct_format}
  128. )
  129. self.format_percent_center = workbook.add_format(
  130. {"align": "center", "num_format": pct_format}
  131. )
  132. self.format_percent_right = workbook.add_format(
  133. {"align": "right", "num_format": pct_format}
  134. )
  135. self.format_percent_conditional_left = workbook.add_format(
  136. {"align": "left", "num_format": pct_format_conditional}
  137. )
  138. self.format_percent_conditional_center = workbook.add_format(
  139. {"align": "center", "num_format": pct_format_conditional}
  140. )
  141. self.format_percent_conditional_right = workbook.add_format(
  142. {"align": "right", "num_format": pct_format_conditional}
  143. )
  144. self.format_integer_left = workbook.add_format(
  145. {"align": "left", "num_format": int_format}
  146. )
  147. self.format_integer_center = workbook.add_format(
  148. {"align": "center", "num_format": int_format}
  149. )
  150. self.format_integer_right = workbook.add_format(
  151. {"align": "right", "num_format": int_format}
  152. )
  153. self.format_integer_conditional_left = workbook.add_format(
  154. {"align": "right", "num_format": int_format_conditional}
  155. )
  156. self.format_integer_conditional_center = workbook.add_format(
  157. {"align": "center", "num_format": int_format_conditional}
  158. )
  159. self.format_integer_conditional_right = workbook.add_format(
  160. {"align": "right", "num_format": int_format_conditional}
  161. )
  162. self.format_date_left = workbook.add_format(
  163. {"align": "left", "num_format": date_format}
  164. )
  165. self.format_date_center = workbook.add_format(
  166. {"align": "center", "num_format": date_format}
  167. )
  168. self.format_date_right = workbook.add_format(
  169. {"align": "right", "num_format": date_format}
  170. )
  171. self.format_left_bold = workbook.add_format({"align": "left", "bold": True})
  172. self.format_center_bold = workbook.add_format({"align": "center", "bold": True})
  173. self.format_right_bold = workbook.add_format({"align": "right", "bold": True})
  174. self.format_amount_left_bold = workbook.add_format(
  175. {"align": "left", "bold": True, "num_format": num_format}
  176. )
  177. self.format_amount_center_bold = workbook.add_format(
  178. {"align": "center", "bold": True, "num_format": num_format}
  179. )
  180. self.format_amount_right_bold = workbook.add_format(
  181. {"align": "right", "bold": True, "num_format": num_format}
  182. )
  183. self.format_amount_conditional_left_bold = workbook.add_format(
  184. {"align": "left", "bold": True, "num_format": num_format_conditional}
  185. )
  186. self.format_amount_conditional_center_bold = workbook.add_format(
  187. {"align": "center", "bold": True, "num_format": num_format_conditional}
  188. )
  189. self.format_amount_conditional_right_bold = workbook.add_format(
  190. {"align": "right", "bold": True, "num_format": num_format_conditional}
  191. )
  192. self.format_percent_left_bold = workbook.add_format(
  193. {"align": "left", "bold": True, "num_format": pct_format}
  194. )
  195. self.format_percent_center_bold = workbook.add_format(
  196. {"align": "center", "bold": True, "num_format": pct_format}
  197. )
  198. self.format_percent_right_bold = workbook.add_format(
  199. {"align": "right", "bold": True, "num_format": pct_format}
  200. )
  201. self.format_percent_conditional_left_bold = workbook.add_format(
  202. {"align": "left", "bold": True, "num_format": pct_format_conditional}
  203. )
  204. self.format_percent_conditional_center_bold = workbook.add_format(
  205. {"align": "center", "bold": True, "num_format": pct_format_conditional}
  206. )
  207. self.format_percent_conditional_right_bold = workbook.add_format(
  208. {"align": "right", "bold": True, "num_format": pct_format_conditional}
  209. )
  210. self.format_integer_left_bold = workbook.add_format(
  211. {"align": "left", "bold": True, "num_format": int_format}
  212. )
  213. self.format_integer_center_bold = workbook.add_format(
  214. {"align": "center", "bold": True, "num_format": int_format}
  215. )
  216. self.format_integer_right_bold = workbook.add_format(
  217. {"align": "right", "bold": True, "num_format": int_format}
  218. )
  219. self.format_integer_conditional_left_bold = workbook.add_format(
  220. {"align": "left", "bold": True, "num_format": int_format_conditional}
  221. )
  222. self.format_integer_conditional_center_bold = workbook.add_format(
  223. {"align": "center", "bold": True, "num_format": int_format_conditional}
  224. )
  225. self.format_integer_conditional_right_bold = workbook.add_format(
  226. {"align": "right", "bold": True, "num_format": int_format_conditional}
  227. )
  228. self.format_date_left_bold = workbook.add_format(
  229. {"align": "left", "bold": True, "num_format": date_format}
  230. )
  231. self.format_date_center_bold = workbook.add_format(
  232. {"align": "center", "bold": True, "num_format": date_format}
  233. )
  234. self.format_date_right_bold = workbook.add_format(
  235. {"align": "right", "bold": True, "num_format": date_format}
  236. )
  237. # formats for worksheet table column headers
  238. self.format_theader_yellow_left = workbook.add_format(theader_yellow)
  239. self.format_theader_yellow_center = workbook.add_format(
  240. dict(theader_yellow, align="center")
  241. )
  242. self.format_theader_yellow_right = workbook.add_format(
  243. dict(theader_yellow, align="right")
  244. )
  245. self.format_theader_yellow_amount_left = workbook.add_format(
  246. dict(theader_yellow, num_format=num_format, align="left")
  247. )
  248. self.format_theader_yellow_amount_center = workbook.add_format(
  249. dict(theader_yellow, num_format=num_format, align="center")
  250. )
  251. self.format_theader_yellow_amount_right = workbook.add_format(
  252. dict(theader_yellow, num_format=num_format, align="right")
  253. )
  254. self.format_theader_yellow_amount_conditional_left = workbook.add_format(
  255. dict(theader_yellow, num_format=num_format_conditional, align="left")
  256. )
  257. self.format_theader_yellow_amount_conditional_center = workbook.add_format(
  258. dict(theader_yellow, num_format=num_format_conditional, align="center")
  259. )
  260. self.format_theader_yellow_amount_conditional_right = workbook.add_format(
  261. dict(theader_yellow, num_format=num_format_conditional, align="right")
  262. )
  263. self.format_theader_yellow_percent_left = workbook.add_format(
  264. dict(theader_yellow, num_format=pct_format, align="left")
  265. )
  266. self.format_theader_yellow_percent_center = workbook.add_format(
  267. dict(theader_yellow, num_format=pct_format, align="center")
  268. )
  269. self.format_theader_yellow_percent_right = workbook.add_format(
  270. dict(theader_yellow, num_format=pct_format, align="right")
  271. )
  272. self.format_theader_yellow_percent_conditional_left = workbook.add_format(
  273. dict(theader_yellow, num_format=pct_format_conditional, align="left")
  274. )
  275. self.format_theader_yellow_percent_conditional_center = workbook.add_format(
  276. dict(theader_yellow, num_format=pct_format_conditional, align="center")
  277. )
  278. self.format_theader_yellow_percent_conditional_right = workbook.add_format(
  279. dict(theader_yellow, num_format=pct_format_conditional, align="right")
  280. )
  281. self.format_theader_yellow_integer_left = workbook.add_format(
  282. dict(theader_yellow, num_format=int_format, align="left")
  283. )
  284. self.format_theader_yellow_integer_center = workbook.add_format(
  285. dict(theader_yellow, num_format=int_format, align="center")
  286. )
  287. self.format_theader_yellow_integer_right = workbook.add_format(
  288. dict(theader_yellow, num_format=int_format, align="right")
  289. )
  290. self.format_theader_yellow_integer_conditional_left = workbook.add_format(
  291. dict(theader_yellow, num_format=int_format_conditional, align="left")
  292. )
  293. self.format_theader_yellow_integer_conditional_center = workbook.add_format(
  294. dict(theader_yellow, num_format=int_format_conditional, align="center")
  295. )
  296. self.format_theader_yellow_integer_conditional_right = workbook.add_format(
  297. dict(theader_yellow, num_format=int_format_conditional, align="right")
  298. )
  299. self.format_theader_blue_left = workbook.add_format(theader_blue)
  300. self.format_theader_blue_center = workbook.add_format(
  301. dict(theader_blue, align="center")
  302. )
  303. self.format_theader_blue_right = workbook.add_format(
  304. dict(theader_blue, align="right")
  305. )
  306. self.format_theader_blue_amount_left = workbook.add_format(
  307. dict(theader_blue, num_format=num_format, align="left")
  308. )
  309. self.format_theader_blue_amount_center = workbook.add_format(
  310. dict(theader_blue, num_format=num_format, align="center")
  311. )
  312. self.format_theader_blue_amount_right = workbook.add_format(
  313. dict(theader_blue, num_format=num_format, align="right")
  314. )
  315. self.format_theader_blue_amount_conditional_left = workbook.add_format(
  316. dict(theader_blue, num_format=num_format_conditional, align="left")
  317. )
  318. self.format_theader_blue_amount_conditional_center = workbook.add_format(
  319. dict(theader_blue, num_format=num_format_conditional, align="center")
  320. )
  321. self.format_theader_blue_amount_conditional_right = workbook.add_format(
  322. dict(theader_blue, num_format=num_format_conditional, align="right")
  323. )
  324. self.format_theader_blue_percent_left = workbook.add_format(
  325. dict(theader_blue, num_format=pct_format, align="left")
  326. )
  327. self.format_theader_blue_percent_center = workbook.add_format(
  328. dict(theader_blue, num_format=pct_format, align="center")
  329. )
  330. self.format_theader_blue_percent_right = workbook.add_format(
  331. dict(theader_blue, num_format=pct_format, align="right")
  332. )
  333. self.format_theader_blue_percent_conditional_left = workbook.add_format(
  334. dict(theader_blue, num_format=pct_format_conditional, align="left")
  335. )
  336. self.format_theader_blue_percent_conditional_center = workbook.add_format(
  337. dict(theader_blue, num_format=pct_format_conditional, align="center")
  338. )
  339. self.format_theader_blue_percent_conditional_right = workbook.add_format(
  340. dict(theader_blue, num_format=pct_format_conditional, align="right")
  341. )
  342. self.format_theader_blue_integer_left = workbook.add_format(
  343. dict(theader_blue, num_format=int_format, align="left")
  344. )
  345. self.format_theader_blue_integer_center = workbook.add_format(
  346. dict(theader_blue, num_format=int_format, align="center")
  347. )
  348. self.format_theader_blue_integer_right = workbook.add_format(
  349. dict(theader_blue, num_format=int_format, align="right")
  350. )
  351. self.format_theader_blue_integer_conditional_left = workbook.add_format(
  352. dict(theader_blue, num_format=int_format_conditional, align="left")
  353. )
  354. self.format_theader_blue_integer_conditional_center = workbook.add_format(
  355. dict(theader_blue, num_format=int_format_conditional, align="center")
  356. )
  357. self.format_theader_blue_integer_conditional_right = workbook.add_format(
  358. dict(theader_blue, num_format=int_format_conditional, align="right")
  359. )
  360. # formats for worksheet table cells
  361. self.format_tcell_left = workbook.add_format(dict(border, align="left"))
  362. self.format_tcell_center = workbook.add_format(dict(border, align="center"))
  363. self.format_tcell_right = workbook.add_format(dict(border, align="right"))
  364. self.format_tcell_amount_left = workbook.add_format(
  365. dict(border, num_format=num_format, align="left")
  366. )
  367. self.format_tcell_amount_center = workbook.add_format(
  368. dict(border, num_format=num_format, align="center")
  369. )
  370. self.format_tcell_amount_right = workbook.add_format(
  371. dict(border, num_format=num_format, align="right")
  372. )
  373. self.format_tcell_amount_conditional_left = workbook.add_format(
  374. dict(border, num_format=num_format_conditional, align="left")
  375. )
  376. self.format_tcell_amount_conditional_center = workbook.add_format(
  377. dict(border, num_format=num_format_conditional, align="center")
  378. )
  379. self.format_tcell_amount_conditional_right = workbook.add_format(
  380. dict(border, num_format=num_format_conditional, align="right")
  381. )
  382. self.format_tcell_percent_left = workbook.add_format(
  383. dict(border, num_format=pct_format, align="left")
  384. )
  385. self.format_tcell_percent_center = workbook.add_format(
  386. dict(border, num_format=pct_format, align="center")
  387. )
  388. self.format_tcell_percent_right = workbook.add_format(
  389. dict(border, num_format=pct_format, align="right")
  390. )
  391. self.format_tcell_percent_conditional_left = workbook.add_format(
  392. dict(border, num_format=pct_format_conditional, align="left")
  393. )
  394. self.format_tcell_percent_conditional_center = workbook.add_format(
  395. dict(border, num_format=pct_format_conditional, align="center")
  396. )
  397. self.format_tcell_percent_conditional_right = workbook.add_format(
  398. dict(border, num_format=pct_format_conditional, align="right")
  399. )
  400. self.format_tcell_integer_left = workbook.add_format(
  401. dict(border, num_format=int_format, align="left")
  402. )
  403. self.format_tcell_integer_center = workbook.add_format(
  404. dict(border, num_format=int_format, align="center")
  405. )
  406. self.format_tcell_integer_right = workbook.add_format(
  407. dict(border, num_format=int_format, align="right")
  408. )
  409. self.format_tcell_integer_conditional_left = workbook.add_format(
  410. dict(border, num_format=int_format_conditional, align="left")
  411. )
  412. self.format_tcell_integer_conditional_center = workbook.add_format(
  413. dict(border, num_format=int_format_conditional, align="center")
  414. )
  415. self.format_tcell_integer_conditional_right = workbook.add_format(
  416. dict(border, num_format=int_format_conditional, align="right")
  417. )
  418. self.format_tcell_date_left = workbook.add_format(
  419. dict(border, num_format=date_format, align="left")
  420. )
  421. self.format_tcell_date_center = workbook.add_format(
  422. dict(border, num_format=date_format, align="center")
  423. )
  424. self.format_tcell_date_right = workbook.add_format(
  425. dict(border, num_format=date_format, align="right")
  426. )
  427. self.format_tcell_left_bold = workbook.add_format(
  428. dict(border, align="left", bold=True)
  429. )
  430. self.format_tcell_center_bold = workbook.add_format(
  431. dict(border, align="center", bold=True)
  432. )
  433. self.format_tcell_right_bold = workbook.add_format(
  434. dict(border, align="right", bold=True)
  435. )
  436. self.format_tcell_amount_left_bold = workbook.add_format(
  437. dict(border, num_format=num_format, align="left", bold=True)
  438. )
  439. self.format_tcell_amount_center_bold = workbook.add_format(
  440. dict(border, num_format=num_format, align="center", bold=True)
  441. )
  442. self.format_tcell_amount_right_bold = workbook.add_format(
  443. dict(border, num_format=num_format, align="right", bold=True)
  444. )
  445. self.format_tcell_amount_conditional_left_bold = workbook.add_format(
  446. dict(border, num_format=num_format_conditional, align="left", bold=True)
  447. )
  448. self.format_tcell_amount_conditional_center_bold = workbook.add_format(
  449. dict(border, num_format=num_format_conditional, align="center", bold=True)
  450. )
  451. self.format_tcell_amount_conditional_right_bold = workbook.add_format(
  452. dict(border, num_format=num_format_conditional, align="right", bold=True)
  453. )
  454. self.format_tcell_percent_left_bold = workbook.add_format(
  455. dict(border, num_format=pct_format, align="left", bold=True)
  456. )
  457. self.format_tcell_percent_center_bold = workbook.add_format(
  458. dict(border, num_format=pct_format, align="center", bold=True)
  459. )
  460. self.format_tcell_percent_right_bold = workbook.add_format(
  461. dict(border, num_format=pct_format, align="right", bold=True)
  462. )
  463. self.format_tcell_percent_conditional_left_bold = workbook.add_format(
  464. dict(border, num_format=pct_format_conditional, align="left", bold=True)
  465. )
  466. self.format_tcell_percent_conditional_center_bold = workbook.add_format(
  467. dict(border, num_format=pct_format_conditional, align="center", bold=True)
  468. )
  469. self.format_tcell_percent_conditional_right_bold = workbook.add_format(
  470. dict(border, num_format=pct_format_conditional, align="right", bold=True)
  471. )
  472. self.format_tcell_integer_left_bold = workbook.add_format(
  473. dict(border, num_format=int_format, align="left", bold=True)
  474. )
  475. self.format_tcell_integer_center_bold = workbook.add_format(
  476. dict(border, num_format=int_format, align="center", bold=True)
  477. )
  478. self.format_tcell_integer_right_bold = workbook.add_format(
  479. dict(border, num_format=int_format, align="right", bold=True)
  480. )
  481. self.format_tcell_integer_conditional_left_bold = workbook.add_format(
  482. dict(border, num_format=int_format_conditional, align="left", bold=True)
  483. )
  484. self.format_tcell_integer_conditional_center_bold = workbook.add_format(
  485. dict(border, num_format=int_format_conditional, align="center", bold=True)
  486. )
  487. self.format_tcell_integer_conditional_right_bold = workbook.add_format(
  488. dict(border, num_format=int_format_conditional, align="right", bold=True)
  489. )
  490. self.format_tcell_date_left_bold = workbook.add_format(
  491. dict(border, num_format=date_format, align="left", bold=True)
  492. )
  493. self.format_tcell_date_center_bold = workbook.add_format(
  494. dict(border, num_format=date_format, align="center", bold=True)
  495. )
  496. self.format_tcell_date_right_bold = workbook.add_format(
  497. dict(border, num_format=date_format, align="right", bold=True)
  498. )
  499. def _set_column_width(self, ws, ws_params):
  500. """
  501. Set width for all columns included in the 'wanted_list'.
  502. """
  503. col_specs = ws_params.get("col_specs")
  504. wl = ws_params.get("wanted_list") or []
  505. for pos, col in enumerate(wl):
  506. if col not in col_specs:
  507. raise UserError(
  508. _(
  509. "Programming Error:\n\n"
  510. "The '%s' column is not defined in the worksheet "
  511. "column specifications."
  512. )
  513. % col
  514. )
  515. ws.set_column(pos, pos, col_specs[col]["width"])
  516. def _write_ws_title(self, ws, row_pos, ws_params, merge_range=False):
  517. """
  518. Helper function to ensure consistent title formats
  519. troughout all worksheets.
  520. Requires 'title' keyword in ws_params.
  521. """
  522. title = ws_params.get("title")
  523. if not title:
  524. raise UserError(
  525. _(
  526. "Programming Error:\n\n"
  527. "The 'title' parameter is mandatory "
  528. "when calling the '_write_ws_title' method."
  529. )
  530. )
  531. if merge_range:
  532. wl = ws_params.get("wanted_list")
  533. if wl and len(wl) > 1:
  534. ws.merge_range(
  535. row_pos, 0, row_pos, len(wl) - 1, title, self.format_ws_title
  536. )
  537. else:
  538. ws.write_string(row_pos, 0, title, self.format_ws_title)
  539. return row_pos + 2
  540. def _write_line(
  541. self,
  542. ws,
  543. row_pos,
  544. ws_params,
  545. col_specs_section=None,
  546. render_space=None,
  547. default_format=None,
  548. col_specs='col_specs',
  549. wanted_list='wanted_list',
  550. ):
  551. """
  552. Write a line with all columns included in the 'wanted_list'.
  553. Use the entry defined by the col_specs_section.
  554. An empty cell will be written if no col_specs_section entry
  555. for a column.
  556. """
  557. col_specs = ws_params.get(col_specs)
  558. wl = ws_params.get(wanted_list) or []
  559. pos = 0
  560. for col in wl:
  561. if col not in col_specs:
  562. raise UserError(
  563. _(
  564. "Programming Error:\n\n"
  565. "The '%s' column is not defined the worksheet "
  566. "column specifications."
  567. )
  568. % col
  569. )
  570. colspan = col_specs[col].get("colspan") or 1
  571. cell_spec = col_specs[col].get(col_specs_section) or {}
  572. if not cell_spec:
  573. cell_value = None
  574. cell_type = "blank"
  575. cell_format = default_format
  576. else:
  577. cell_value = cell_spec.get("value")
  578. if isinstance(cell_value, CodeType):
  579. cell_value = self._eval(cell_value, render_space)
  580. cell_type = cell_spec.get("type")
  581. cell_format = cell_spec.get("format") or default_format
  582. if not cell_type:
  583. # test bool first since isinstance(val, int) returns
  584. # True when type(val) is bool
  585. if isinstance(cell_value, bool):
  586. cell_type = "boolean"
  587. elif isinstance(cell_value, str):
  588. cell_type = "string"
  589. elif isinstance(cell_value, (int, float)):
  590. cell_type = "number"
  591. elif isinstance(cell_value, datetime):
  592. cell_type = "datetime"
  593. elif isinstance(cell_value, date):
  594. cell_value = datetime.combine(cell_value, datetime.min.time())
  595. cell_type = "datetime"
  596. else:
  597. if not cell_value:
  598. cell_type = "blank"
  599. else:
  600. msg = _(
  601. "%s, _write_line : programming error "
  602. "detected while processing "
  603. "col_specs_section %s, column %s"
  604. ) % (__name__, col_specs_section, col)
  605. if cell_value:
  606. msg += _(", cellvalue %s") % cell_value
  607. raise UserError(msg)
  608. colspan = cell_spec.get("colspan") or colspan
  609. args_pos = [row_pos, pos]
  610. args_data = [cell_value]
  611. if cell_format:
  612. if isinstance(cell_format, CodeType):
  613. cell_format = self._eval(cell_format, render_space)
  614. args_data.append(cell_format)
  615. if colspan > 1:
  616. args_pos += [row_pos, pos + colspan - 1]
  617. args = args_pos + args_data
  618. ws.merge_range(*args)
  619. else:
  620. ws_method = getattr(ws, "write_%s" % cell_type)
  621. args = args_pos + args_data
  622. ws_method(*args)
  623. pos += colspan
  624. return row_pos + 1
  625. @staticmethod
  626. def _render(code):
  627. return compile(code, "<string>", "eval")
  628. @staticmethod
  629. def _eval(val, render_space):
  630. if not render_space:
  631. render_space = {}
  632. if "datetime" not in render_space:
  633. render_space["datetime"] = datetime
  634. # the use of eval is not a security thread as long as the
  635. # col_specs template is defined in a python module
  636. return eval(val, render_space) # pylint: disable=W0123,W8112
  637. @staticmethod
  638. def _rowcol_to_cell(row, col, row_abs=False, col_abs=False):
  639. return xl_rowcol_to_cell(row, col, row_abs=row_abs, col_abs=col_abs)