[ADD] website_sale_digital: allows the user to set a product as digital product and use the attachments to sell files. The download link is added on the My Orders page when the payment is confirmed
This commit is contained in:
committed by
Antony Lesuisse
parent
9c70e9242b
commit
8a1b8e880c
@@ -0,0 +1,4 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
import controllers
|
||||
import product
|
||||
import account_invoice
|
||||
@@ -0,0 +1,21 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
{
|
||||
'name': 'Website Sale Digital - Sell digital products',
|
||||
'version': '0.1',
|
||||
'description': """
|
||||
Sell digital product using attachments to virtual products
|
||||
""",
|
||||
'author': 'Odoo S.A.',
|
||||
'depends': [
|
||||
'document',
|
||||
'website_sale',
|
||||
],
|
||||
'installable': True,
|
||||
'data': [
|
||||
'views/website_sale_digital.xml',
|
||||
'views/website_sale_digital_view.xml',
|
||||
],
|
||||
'demo': [
|
||||
'demo.xml',
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
from openerp import models, fields, api, _
|
||||
|
||||
class account_invoice_line(models.Model):
|
||||
_inherit = ['account.invoice.line']
|
||||
|
||||
def get_digital_purchases(self,uid):
|
||||
user = self.env['res.users'].browse(uid)
|
||||
partner = user.partner_id
|
||||
|
||||
# Get paid invoices
|
||||
purchases = self.sudo().search_read(
|
||||
domain=[('invoice_id.state', '=', 'paid'), ('invoice_id.partner_id', '=', partner.id), ('product_id.product_tmpl_id.digital_content', '=', True)],
|
||||
fields=['product_id'],
|
||||
)
|
||||
|
||||
# I only want product_ids, but search_read insists in giving me a list of
|
||||
# (product_id: <id>, name: <product code> <template_name> <attributes>)
|
||||
return map(lambda x: x['product_id'][0], purchases)
|
||||
@@ -0,0 +1,2 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
import main
|
||||
@@ -0,0 +1,112 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import base64
|
||||
from openerp.addons.web import http
|
||||
from openerp.addons.web.http import request
|
||||
from openerp.addons.website.controllers.main import Website
|
||||
from openerp.addons.website_sale.controllers.main import website_sale
|
||||
from cStringIO import StringIO
|
||||
from werkzeug.utils import redirect
|
||||
|
||||
|
||||
class website_sale_digital(website_sale):
|
||||
|
||||
orders_page = '/shop/orders'
|
||||
|
||||
@http.route([
|
||||
'/shop/confirmation',
|
||||
], type='http', auth="public", website=True)
|
||||
def payment_confirmation(self, **post):
|
||||
response = super(website_sale_digital, self).payment_confirmation(**post)
|
||||
order_lines = response.qcontext['order'].order_line
|
||||
digital_content = map(lambda x: x.product_id.digital_content, order_lines)
|
||||
response.qcontext.update(digital=any(digital_content))
|
||||
return response
|
||||
|
||||
@http.route([
|
||||
'/shop/orders',
|
||||
'/shop/orders/page/<int:page>',
|
||||
], type='http', auth='user', website=True)
|
||||
def orders_followup(self, page=1, **post):
|
||||
response = super(website_sale_digital, self).orders_followup(**post)
|
||||
|
||||
order_products_attachments = {}
|
||||
for o in response.qcontext['orders']:
|
||||
invoiced_lines = request.env['account.invoice.line'].sudo().search([('invoice_id', 'in', o.invoice_ids.ids), ('invoice_id.state', '=', 'paid')])
|
||||
|
||||
purchased_products_attachments = {}
|
||||
for il in invoiced_lines:
|
||||
p_obj = il.product_id
|
||||
# Ignore products that do not have digital content
|
||||
if not p_obj.product_tmpl_id.digital_content:
|
||||
continue
|
||||
|
||||
# Search for product attachments
|
||||
A = request.env['ir.attachment']
|
||||
p_id = p_obj.id
|
||||
template = p_obj.product_tmpl_id
|
||||
att = A.search_read(
|
||||
domain=['|', '&', ('res_model', '=', p_obj._name), ('res_id', '=', p_id), '&', ('res_model', '=', template._name), ('res_id', '=', template.id)],
|
||||
fields=['name', 'write_date'],
|
||||
order='write_date desc',
|
||||
)
|
||||
|
||||
# Ignore products with no attachments
|
||||
if not att:
|
||||
continue
|
||||
|
||||
purchased_products_attachments[p_id] = att
|
||||
|
||||
order_products_attachments[o.id] = purchased_products_attachments
|
||||
|
||||
response.qcontext.update({
|
||||
'digital_attachments': order_products_attachments,
|
||||
})
|
||||
return response
|
||||
|
||||
@http.route([
|
||||
'/shop/download',
|
||||
], type='http', auth='public')
|
||||
def download_attachment(self, attachment_id):
|
||||
# Check if this is a valid attachment id
|
||||
attachment = request.env['ir.attachment'].sudo().search_read(
|
||||
[('id', '=', int(attachment_id))],
|
||||
["name", "datas", "file_type", "res_model", "res_id", "type", "url"]
|
||||
)
|
||||
|
||||
if attachment:
|
||||
attachment = attachment[0]
|
||||
else:
|
||||
return redirect(self.orders_page)
|
||||
|
||||
|
||||
# Check if the user has bought the associated product
|
||||
res_model = attachment['res_model']
|
||||
res_id = attachment['res_id']
|
||||
purchased_products = request.env['account.invoice.line'].get_digital_purchases(request.uid)
|
||||
|
||||
if res_model == 'product.product':
|
||||
if res_id not in purchased_products:
|
||||
return redirect(self.orders_page)
|
||||
|
||||
# Also check for attachments in the product templates
|
||||
elif res_model == 'product.template':
|
||||
P = request.env['product.product']
|
||||
template_ids = map(lambda x: P.browse(x).product_tmpl_id.id, purchased_products)
|
||||
if res_id not in template_ids:
|
||||
return redirect(self.orders_page)
|
||||
|
||||
else:
|
||||
return redirect(self.orders_page)
|
||||
|
||||
# The client has bought the product, otherwise it would have been blocked by now
|
||||
if attachment["type"] == "url":
|
||||
if attachment["url"]:
|
||||
return redirect(attachment["url"])
|
||||
else:
|
||||
return request.not_found()
|
||||
elif attachment["datas"]:
|
||||
data = StringIO(base64.standard_b64decode(attachment["datas"]))
|
||||
return http.send_file(data, filename=attachment['name'], as_attachment=True)
|
||||
else:
|
||||
return request.not_found()
|
||||
@@ -0,0 +1,21 @@
|
||||
<odoo>
|
||||
<record id="website_sale_digital.product_1" model="product.template">
|
||||
<field name="name">Alice's Adventures in Wonderland - Lewis Caroll</field>
|
||||
<field name="standard_price">2</field>
|
||||
<field name="list_price">4.50</field>
|
||||
<field name="type">service</field>
|
||||
<field name="digital_content" eval="True"/>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="image" type="base64" file="website_sale_digital/static/digital_product_1.jpg"/>
|
||||
<field name="description">Novel by Lewis Caroll.</field>
|
||||
<field name="description_sale">Alice's Adventures in Wonderland (commonly shortened to Alice in Wonderland) is an 1865 novel written by English author Charles Lutwidge Dodgson under the pseudonym Lewis Carroll. It tells of a girl named Alice falling through a rabbit hole into a fantasy world populated by peculiar, anthropomorphic creatures. The tale plays with logic, giving the story lasting popularity with adults as well as with children. It is considered to be one of the best examples of the literary nonsense genre. Its narrative course and structure, characters and imagery have been enormously influential in both popular culture and literature, especially in the fantasy genre.</field>
|
||||
</record>
|
||||
<record id="website_sale_digital.attach1" model="ir.attachment">
|
||||
<field name="name">alice.pdf</field>
|
||||
<field name="datas_fname">alice.pdf</field>
|
||||
<field name="type">binary</field>
|
||||
<field name="datas" type="base64" file="website_sale_digital/static/alice.pdf"/>
|
||||
<field name="res_id" ref="website_sale_digital.product_1"/>
|
||||
<field name="res_model">product.template</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,8 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
from openerp import models, fields, api, _
|
||||
|
||||
|
||||
class product_template(models.Model):
|
||||
_inherit = ['product.template']
|
||||
|
||||
digital_content = fields.Boolean('Digital Content', help="If checked, it will allow clients to download the product attachments when they have bought it.")
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 185 KiB |
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="orders_followup_downloads" name="Orders Downloads Followup" inherit_id="website_sale.order_invoice">
|
||||
<xpath expr="//div[@id='product_name']" position="inside">
|
||||
<t t-set="attachments" t-value="digital_attachments[o.id].get(ol.product_id.id)"/>
|
||||
<t t-if="attachments">
|
||||
<span class="dropdown">
|
||||
<button class="btn btn-xs btn-default dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown">
|
||||
Downloads <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1">
|
||||
<t t-foreach="attachments" t-as="a">
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1" t-att-href="'/shop/download?attachment_id=%i' % a['id']"><t t-esc="a['name']"/></a></li>
|
||||
</t>
|
||||
</ul>
|
||||
</span>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" ?>
|
||||
<odoo>
|
||||
<record model="ir.ui.view" id="view_product_form_expiry">
|
||||
<field name="name">product.normal.form</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_form_view" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="type" position="after">
|
||||
<field name="digital_content" />
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user