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.

253 lines
9.3 KiB

  1. # Copyright 2018 Eficent Business and IT Consulting Services S.L.
  2. # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
  3. from odoo import api, fields, models, _
  4. from odoo.exceptions import UserError
  5. import ast
  6. class PrivacyPartnerReport(models.TransientModel):
  7. _name = "privacy.partner.report"
  8. _description = "Privacy Partner Report"
  9. company_id = fields.Many2one(
  10. comodel_name='res.company',
  11. string='Company',
  12. required=True,
  13. default=lambda self: self.env.user.company_id,
  14. )
  15. partner_id = fields.Many2one(
  16. comodel_name='res.partner',
  17. string='Partner',
  18. required=True,
  19. )
  20. table_ids = fields.Many2many(
  21. comodel_name='privacy.partner.data',
  22. string='Models with related partner data',
  23. )
  24. @api.onchange('partner_id')
  25. def _onchange_partner_id(self):
  26. if self.partner_id:
  27. data = self._get_tables_from_partner(self.partner_id)
  28. names = self._get_table_names(data)
  29. tables = self.env['privacy.partner.data']
  30. for name in sorted(names):
  31. vals = self._get_default_table(
  32. name=name,
  33. data=[t for t in data if t[0] == name and not t[5]],
  34. )
  35. if vals:
  36. tables |= self.env['privacy.partner.data'].create(vals)
  37. self.table_ids = tables
  38. else:
  39. self.table_ids = self.env['privacy.partner.data']
  40. return {
  41. 'domain': {
  42. 'table_ids': [
  43. ('id', 'in', self.table_ids.ids)],
  44. },
  45. }
  46. @api.onchange('company_id')
  47. def _onchange_company_id(self):
  48. if not self.company_id:
  49. self.company_id = self.env.user.company_id
  50. return {
  51. 'domain': {
  52. 'partner_id': [
  53. ('company_id', 'in', [self.company_id.id, False])],
  54. },
  55. }
  56. @api.multi
  57. def button_export_xlsx(self):
  58. self.ensure_one()
  59. return self.check_report(xlsx_report=True)
  60. def _build_contexts(self, data):
  61. result = {}
  62. result['partner_id'] = data['form']['partner_id'][0] or False
  63. result['company_id'] = data['form']['company_id'][0] or False
  64. result['table_ids'] = 'table_ids' in data['form'] and \
  65. data['form']['table_ids'] or False
  66. return result
  67. @staticmethod
  68. def _transform_binary(binary):
  69. # TODO: Implement if needed
  70. return False
  71. def _clean_data(self, model, rows):
  72. cleaned_rows = []
  73. for i, row in enumerate(rows):
  74. cleaned_rows.append({})
  75. for key, value in row.items():
  76. label = self.env[model]._fields[key].string or key
  77. if self.env[model]._fields[key].store:
  78. if 'many2one' == self.env[model]._fields[key].type:
  79. comodel = self.env[model]._fields[key].comodel_name
  80. if value:
  81. record = self.env[comodel].sudo().browse(value)
  82. cleaned_rows[i][label] = \
  83. record.display_name.encode('utf8')
  84. else:
  85. cleaned_rows[i][label] = rows[i][key]
  86. elif 'binary' == self.env[model]._fields[key].type:
  87. binary = self._transform_binary(rows[i][key])
  88. if binary:
  89. cleaned_rows[i][label] = binary
  90. elif '2many' not in self.env[model]._fields[key].type:
  91. cleaned_rows[i][label] = rows[i][key]
  92. return cleaned_rows
  93. @api.multi
  94. def check_report(self, xlsx_report=False):
  95. self.ensure_one()
  96. data = {}
  97. data['ids'] = self.env.context.get('active_ids', [])
  98. data['model'] = self.env.context.get('active_model', 'ir.ui.menu')
  99. data['form'] = self.read(['partner_id', 'company_id', 'table_ids'])[0]
  100. used_context = self._build_contexts(data)
  101. data['form']['id'] = str(data['form']['id'])
  102. data['form']['used_context'] = dict(
  103. used_context, lang=self.env.context.get('lang', 'en_US'))
  104. return self._print_report(data=data, xlsx_report=xlsx_report)
  105. @api.multi
  106. def compute_data_for_report(self, data):
  107. if not data.get('form'):
  108. raise UserError(
  109. _("Form content is missing, this report cannot be printed."))
  110. partner = data['form'].get('partner_id', False)
  111. if not partner:
  112. raise UserError(
  113. _("No provided partner."))
  114. partner = self.env['res.partner'].sudo().browse(partner[0])
  115. tables = data['form'].get('table_ids', False)
  116. if tables:
  117. tables = self.env['privacy.partner.data'].browse(tables)
  118. tables = self._get_rows_from_tables(tables, partner)
  119. data.update({'tables': tables, })
  120. return data
  121. def _exclude_column(self, model, column):
  122. # https://github.com/odoo/odoo/issues/24927
  123. if model in ('mail.compose.message', 'survey.mail.compose.message'):
  124. if column in ('needaction_partner_ids', 'starred_partner_ids'):
  125. return True
  126. # feel free to add more specific cases meanwhile the issue is not fixed
  127. return False
  128. def _get_default_table(self, name, data):
  129. if data:
  130. field_type = data[0][4]
  131. res = self.env[data[0][1]]
  132. for t in data:
  133. res |= self.env[t[1]].sudo().browse(t[3])
  134. if res:
  135. values = {
  136. 'name': name,
  137. 'model_id': self.env['ir.model'].sudo().search(
  138. [('model', '=', res._name)]).id,
  139. 'count_rows': len(res.ids),
  140. 'field_type': field_type,
  141. 'res_ids': res.ids,
  142. }
  143. return values
  144. return {}
  145. def _get_model_from_table(self, table, partner):
  146. new_tables = {}
  147. for model in table.model_id:
  148. rows = self._get_rows_from_model(model, partner)
  149. new_tables[model.display_name] = rows
  150. return new_tables
  151. def _get_rows_from_model(self, model, partner):
  152. lines = self.env[model.model]
  153. columns = [k for k, v in self.env[model.model]._fields.items()
  154. if v.comodel_name == 'res.partner' and
  155. v.store and not self._exclude_column(model.model, k)]
  156. for column in columns:
  157. lines |= self.env[model.model].sudo().search(
  158. [(column, '=', partner.id)])
  159. rows = lines.sudo().read(load=False)
  160. rows = self._clean_data(model.model, rows)
  161. return rows
  162. def _get_rows_from_tables(self, tables, partner):
  163. new_tables = {}
  164. for table in tables:
  165. data_table = self._get_model_from_table(table, partner)
  166. new_tables[str(table.name)] = data_table
  167. return new_tables
  168. def _get_table_names(self, data):
  169. names = []
  170. for t in data:
  171. if t[3] and not t[5] and t[0] not in names:
  172. names.append(t[0])
  173. return names
  174. def _get_tables_from_partner(self, partner):
  175. tables = [t[0] for t in [
  176. [[self.env[m]._table, m, k, self.env[m].sudo().search(
  177. [(k, '=', partner.id)]).ids, v.type, self.env[m]._transient]
  178. for k, v in self.env[m]._fields.items()
  179. if v.comodel_name == 'res.partner' and self.env[m]._auto and
  180. v.store and not self._exclude_column(m, k)]
  181. for m in [x for x in self.env.registry.keys()]] if t]
  182. for i, t in enumerate(tables):
  183. if t[4] == 'many2many':
  184. if t[3]:
  185. relation = self.env[t[1]]._fields[t[2]].relation
  186. if relation:
  187. tables[i][0] = relation
  188. return tables
  189. def _print_report(self, data, xlsx_report=False):
  190. records = self.env[data['model']].sudo().browse(data.get('ids', []))
  191. processed_data = self.compute_data_for_report(data)
  192. if xlsx_report:
  193. return self.env.ref('privacy_partner_report.report_partner_xlsx').\
  194. with_context(landscape=True).report_action(
  195. records, data=processed_data)
  196. class PrivacyPartnerData(models.TransientModel):
  197. _name = "privacy.partner.data"
  198. _description = "Privacy Partner Data"
  199. name = fields.Char(
  200. string='Database Table',
  201. )
  202. model_id = fields.Many2one(
  203. comodel_name='ir.model',
  204. ondelete='cascade',
  205. string='Models',
  206. )
  207. field_type = fields.Char(
  208. string="Type", oldname='type',
  209. )
  210. count_rows = fields.Integer(
  211. default=0,
  212. string='Number of lines',
  213. )
  214. res_ids = fields.Char('Related Document IDs', index=True,
  215. help='List of Related Document IDs')
  216. @api.multi
  217. def action_view_records(self):
  218. self.ensure_one()
  219. response = {
  220. 'name': self.model_id.display_name,
  221. 'type': 'ir.actions.act_window',
  222. 'res_model': self.model_id.model,
  223. 'view_mode': 'tree,form',
  224. 'domain': [('id', 'in', ast.literal_eval(self.res_ids))],
  225. 'target': 'current',
  226. 'context': {'delete': True},
  227. }
  228. return response