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.

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