main.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. # -*- coding: utf-8 -*-
  2. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  3. import base64
  4. import io
  5. import os
  6. import mimetypes
  7. from odoo import http
  8. from odoo.exceptions import AccessError
  9. from odoo.http import request
  10. from odoo.addons.sale.controllers.portal import CustomerPortal
  11. class WebsiteSaleDigital(CustomerPortal):
  12. orders_page = '/my/orders'
  13. @http.route([
  14. '/my/orders/<int:order_id>',
  15. ], type='http', auth='public', website=True)
  16. def portal_order_page(self, order_id=None, **post):
  17. response = super(WebsiteSaleDigital, self).portal_order_page(order_id=order_id, **post)
  18. if not 'sale_order' in response.qcontext:
  19. return response
  20. order = response.qcontext['sale_order']
  21. invoiced_lines = request.env['account.move.line'].sudo().search([('move_id', 'in', order.invoice_ids.ids), ('move_id.payment_state', 'in', ['paid', 'in_payment'])])
  22. products = invoiced_lines.mapped('product_id') | order.order_line.filtered(lambda r: not r.price_subtotal).mapped('product_id')
  23. if not order.amount_total:
  24. # in that case, we should add all download links to the products
  25. # since there is nothing to pay, so we shouldn't wait for an invoice
  26. products = order.order_line.mapped('product_id')
  27. Attachment = request.env['ir.attachment'].sudo()
  28. purchased_products_attachments = {}
  29. for product in products.filtered(lambda p: p.attachment_count):
  30. # Search for product attachments
  31. product_id = product.id
  32. template = product.product_tmpl_id
  33. att = Attachment.sudo().search_read(
  34. domain=['|', '&', ('res_model', '=', product._name), ('res_id', '=', product_id), '&', ('res_model', '=', template._name), ('res_id', '=', template.id), ('product_downloadable', '=', True)],
  35. fields=['name', 'write_date'],
  36. order='write_date desc',
  37. )
  38. # Ignore products with no attachments
  39. if not att:
  40. continue
  41. purchased_products_attachments[product_id] = att
  42. response.qcontext.update({
  43. 'digital_attachments': purchased_products_attachments,
  44. })
  45. return response
  46. @http.route([
  47. '/my/download',
  48. ], type='http', auth='public')
  49. def download_attachment(self, attachment_id):
  50. # Check if this is a valid attachment id
  51. attachment = request.env['ir.attachment'].sudo().search_read(
  52. [('id', '=', int(attachment_id))],
  53. ["name", "datas", "mimetype", "res_model", "res_id", "type", "url"]
  54. )
  55. if attachment:
  56. attachment = attachment[0]
  57. else:
  58. return request.redirect(self.orders_page)
  59. try:
  60. request.env['ir.attachment'].browse(attachment_id).check('read')
  61. except AccessError: # The user does not have read access on the attachment.
  62. # Check if access can be granted through their purchases.
  63. res_model = attachment['res_model']
  64. res_id = attachment['res_id']
  65. digital_purchases = request.env['account.move.line'].get_digital_purchases()
  66. if res_model == 'product.product':
  67. purchased_product_ids = digital_purchases
  68. elif res_model == 'product.template':
  69. purchased_product_ids = request.env['product.product'].sudo().browse(
  70. digital_purchases
  71. ).mapped('product_tmpl_id').ids
  72. else:
  73. purchased_product_ids = [] # The purchases must be related to products.
  74. if res_id not in purchased_product_ids: # No related purchase was found.
  75. return request.redirect(self.orders_page) # Prevent the user from downloading.
  76. # The user has bought the product, or has the rights to the attachment
  77. if attachment["type"] == "url":
  78. if attachment["url"]:
  79. return request.redirect(attachment["url"])
  80. else:
  81. return request.not_found()
  82. elif attachment["datas"]:
  83. data = io.BytesIO(base64.standard_b64decode(attachment["datas"]))
  84. # we follow what is done in ir_http's binary_content for the extension management
  85. extension = os.path.splitext(attachment["name"] or '')[1]
  86. extension = extension if extension else mimetypes.guess_extension(attachment["mimetype"] or '')
  87. filename = attachment['name']
  88. filename = filename if os.path.splitext(filename)[1] else filename + extension
  89. return http.send_file(data, filename=filename, as_attachment=True)
  90. else:
  91. return request.not_found()