[ADD] website_portal

- manage your sale documents from the frontend in the 'My Account' page
- move 'My Orders' page from website_sale to website_portal to manage your orders and quotations
- update website_sale_digital and website_sale_stock controllers and view
This commit is contained in:
Damien Bouvy
2015-06-15 14:57:15 +02:00
committed by Christophe Simonis
parent 6cca3ee0f5
commit c3c0408471
17 changed files with 1090 additions and 458 deletions
+1
View File
@@ -0,0 +1 @@
import controllers
+24
View File
@@ -0,0 +1,24 @@
{
'name': 'Website Portal',
'category': 'Website',
'summary': 'Account Management Frontend for your Customers',
'version': '1.0',
'description': """
Allows your customers to manage their account from a beautiful web interface.
""",
'author': 'Odoo SA',
'website': 'https://www.odoo.com/',
'depends': [
'sale',
'website',
],
'data': [
'views/templates.xml',
],
'qweb': [
],
'demo': [
'data/demo.xml'
],
'installable': True,
}
@@ -0,0 +1 @@
import main
+129
View File
@@ -0,0 +1,129 @@
# -*- coding: utf-8 -*-
import datetime
from openerp import http
from openerp.http import request
from openerp import tools
from openerp.tools.translate import _
class website_account(http.Controller):
@http.route(['/my', '/my/home'], type='http', auth="public", website=True)
def account(self):
partner = request.env.user.partner_id
values = {
'date': datetime.date.today().strftime('%Y-%m-%d')
}
res_sale_order = request.env['sale.order']
res_invoices = request.env['account.invoice']
quotations = res_sale_order.search([
('partner_id.id', '=', partner.id),
('state', 'in', ['sent', 'cancel'])
])
orders = res_sale_order.search([
('partner_id.id', '=', partner.id),
('state', 'in', ['progress', 'manual', 'shipping_except', 'invoice_except', 'done'])
])
invoices = res_invoices.search([
('partner_id.id', '=', partner.id),
('state', 'in', ['open', 'paid', 'cancelled'])
])
values.update({
'quotations': quotations,
'orders': orders,
'invoices': invoices
})
# get customer sales rep
if partner.user_id:
sales_rep = partner.user_id
else:
sales_rep = False
values.update({
'sales_rep': sales_rep,
'company': request.website.company_id,
'user': request.env.user
})
return request.website.render("website_portal.account", values)
@http.route(['/my/orders/<int:order>'], type='http', auth="user", website=True)
def orders_followup(self, order=None):
partner = request.env['res.users'].browse(request.uid).partner_id
domain = [
('partner_id.id', '=', partner.id),
('state', 'not in', ['draft', 'cancel']),
('id', '=', order)
]
order = request.env['sale.order'].search(domain)
invoiced_lines = request.env['account.invoice.line'].search([('invoice_id', 'in', order.invoice_ids.ids)])
order_invoice_lines = {il.product_id.id: il.invoice_id for il in invoiced_lines}
return request.website.render("website_portal.orders_followup", {
'order': order.sudo(),
'order_invoice_lines': order_invoice_lines,
})
@http.route(['/my/account'], type='http', auth='user', website=True)
def details(self, **post):
partner = request.env['res.users'].browse(request.uid).partner_id
values = {
'error': {},
'error_message': []
}
if post:
error, error_message = self.details_form_validate(post)
values.update({'error': error, 'error_message': error_message})
values.update(post)
if not error:
post.update({'zip': post.pop('zipcode', '')})
partner.sudo().write(post)
return request.redirect('/my/home')
countries = request.env['res.country'].sudo().search([])
states = request.env['res.country.state'].sudo().search([])
values.update({
'partner': partner,
'countries': countries,
'states': states,
'has_check_vat': hasattr(request.env['res.partner'], 'check_vat'),
})
return request.website.render("website_portal.details", values)
def details_form_validate(self, data):
error = dict()
error_message = []
mandatory_billing_fields = ["name", "phone", "email", "street2", "city", "country_id"]
# Validation
for field_name in mandatory_billing_fields:
if not data.get(field_name):
error[field_name] = 'missing'
# email validation
if data.get('email') and not tools.single_email_re.match(data.get('email')):
error["email"] = 'error'
error_message.append(_('Invalid Email! Please enter a valid email address.'))
# vat validation
if data.get("vat") and hasattr(request.env["res.partner"], "check_vat"):
if request.website.company_id.vat_check_vies:
# force full VIES online check
check_func = request.env["res.partner"].vies_vat_check
else:
# quick and partial off-line checksum validation
check_func = request.env["res.partner"].simple_vat_check
vat_country, vat_number = request.env["res.partner"]._split_vat(data.get("vat"))
if not check_func(vat_country, vat_number): # simple_vat_check
error["vat"] = 'error'
# error message for empty required fields
if [err for err in error.values() if err == 'missing']:
error_message.append(_('Some required fields are empty.'))
return error, error_message
+123
View File
@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="sale_order_1" model="sale.order">
<field name="partner_id" ref="portal.partner_demo_portal"/>
<field name="partner_invoice_id" ref="portal.partner_demo_portal"/>
<field name="partner_shipping_id" ref="portal.partner_demo_portal"/>
<field name="user_id" ref="base.user_root"/>
<field name="pricelist_id" ref="product.list0"/>
<field name="state">sent</field>
<field name="team_id" ref="sales_team.team_sales_department"/>
<field name="date_order" eval="(DateTime.today() - relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
<field name="message_follower_ids" eval="[(4, ref('portal.partner_demo_portal'))]"/>
</record>
<record id="sale_order_line_1" model="sale.order.line">
<field name="order_id" ref="sale_order_1"/>
<field name="name">Laptop E5023</field>
<field name="product_id" ref="product.product_product_25"/>
<field name="product_uom_qty">3</field>
<field name="product_uos_qty">3</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="price_unit">2950.00</field>
</record>
<record id="sale_order_line_2" model="sale.order.line">
<field name="order_id" ref="sale_order_1"/>
<field name="name">Pen drive, 16GB</field>
<field name="product_id" ref="product.product_product_30"/>
<field name="product_uom_qty">5</field>
<field name="product_uos_qty">5</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="price_unit">145.00</field>
</record>
<record id="sale_order_line_3" model="sale.order.line">
<field name="order_id" ref="sale_order_1"/>
<field name="name">Headset USB</field>
<field name="product_id" ref="product.product_product_33"/>
<field name="product_uom_qty">2</field>
<field name="product_uos_qty">2</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="price_unit">65.00</field>
</record>
<record id="sale_order_2" model="sale.order">
<field name="partner_id" ref="portal.partner_demo_portal"/>
<field name="partner_invoice_id" ref="portal.partner_demo_portal"/>
<field name="partner_shipping_id" ref="portal.partner_demo_portal"/>
<field name="user_id" ref="base.user_root"/>
<field name="pricelist_id" ref="product.list0"/>
<field name="order_policy">manual</field>
<field name="team_id" ref="sales_team.team_sales_department"/>
<field name="date_order" eval="(DateTime.today() - relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
<field name="message_follower_ids" eval="[(4, ref('portal.partner_demo_portal'))]"/>
</record>
<record id="sale_order_line_4" model="sale.order.line">
<field name="order_id" ref="sale_order_2"/>
<field name="name">Service on demand</field>
<field name="product_id" ref="product.product_product_1"/>
<field name="product_uom_qty">24</field>
<field name="product_uos_qty">24</field>
<field name="product_uom" ref="product.product_uom_hour"/>
<field name="price_unit">75.00</field>
</record>
<record id="sale_order_line_5" model="sale.order.line">
<field name="order_id" ref="sale_order_2"/>
<field name="name">On Site Assistance</field>
<field name="product_id" ref="product.product_product_2"/>
<field name="product_uom_qty">30</field>
<field name="product_uos_qty">30</field>
<field name="product_uom" ref="product.product_uom_hour"/>
<field name="price_unit">38.25</field>
</record>
<!-- Confirm some Sale Orders-->
<workflow action="order_confirm" model="sale.order" ref="sale_order_2"/>
<!-- Invoice for Indirect Marketing -->
<record id="account_invoice_1" model="account.invoice">
<field name="currency_id" ref="base.EUR"/>
<field name="company_id" ref="base.main_company"/>
<field name="partner_id" ref="portal.partner_demo_portal"/>
<field name="journal_id" ref="account.sales_journal"/>
<field name="team_id" ref="sales_team.crm_team_1"/>
<field name="state">open</field>
<field name="type">out_invoice</field>
<field name="account_id" ref="account.a_recv"/>
<field name="name">Test invoice 1</field>
</record>
<record id="account_invoice_1_line_1" model="account.invoice.line">
<field name="name">Basic formation with Dvorak</field>
<field name="invoice_id" ref="account_invoice_1"/>
<field name="price_unit">500</field>
<field name="quantity">1</field>
<field name="account_id" ref="account.a_sale"/>
</record>
<record id="account_invoice_2" model="account.invoice">
<field name="currency_id" ref="base.EUR"/>
<field name="company_id" ref="base.main_company"/>
<field name="partner_id" ref="portal.partner_demo_portal"/>
<field name="journal_id" ref="account.sales_journal"/>
<field name="team_id" ref="sales_team.crm_team_1"/>
<field name="state">paid</field>
<field name="type">out_invoice</field>
<field name="account_id" ref="account.a_recv"/>
<field name="name">Test invoice 1</field>
</record>
<record id="account_invoice_2_line_1" model="account.invoice.line">
<field name="name">Service on demand</field>
<field name="invoice_id" ref="account_invoice_2"/>
<field name="price_unit">75</field>
<field name="quantity">6</field>
<field name="account_id" ref="account.a_sale"/>
</record>
</odoo>
@@ -0,0 +1,30 @@
/* ---- My Orders page ---- */
.orders_vertical_align {
display: flex;
align-items: center;
}
.orders_label_text_align {
vertical-align: 15%;
}
.status_table {
th,td {
text-align: right;
}
th:nth-last-child(2) {
width: 150px;
}
th:nth-last-child(1), {
width: 120px;
}
th:nth-child(1),
td:nth-child(1) {
width: auto;
min-width: 150px;
text-align: left;
}
table-layout: fixed;
}
@@ -0,0 +1,24 @@
(function () {
'use strict';
var website = openerp.website;
var qweb = openerp.qweb;
website.ready().done(function() {
$('.oe_website_portal').on('change', "select[name='country_id']", function () {
var $select = $("select[name='state_id']");
$select.find("option:not(:first)").hide();
var nb = $select.find("option[data-country_id="+($(this).val() || 0)+"]").show().size();
$select.parent().toggle(nb>1);
});
$('.oe_website_portal').find("select[name='country_id']").change();
$('.wp_show_more').on('click', function(ev) {
ev.preventDefault();
$(this).parents('table').find(".to_hide").toggleClass('hidden');
$(this).find('span').toggleClass('hidden');
});
});
})();
+467
View File
@@ -0,0 +1,467 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="assets_frontend" inherit_id="website.assets_frontend" name="Account Manager">
<xpath expr="." position="inside">
<link rel='stylesheet' href='/website_portal/static/src/css/website_portal.less'/>
<script type="text/javascript" src="/website_portal/static/src/js/website_portal.js"></script>
</xpath>
</template>
<template id="account" name="Account">
<t t-call="website.layout">
<div id="wrap">
<div class="container mb64">
<div class="col-md-8" id="documents_list">
<t t-call="website_portal.quotations" />
<t t-call="website_portal.sale_orders" />
<t t-call="website_portal.invoices" />
</div>
<div t-if="sales_rep" class="col-md-3">
<t t-call="website_portal.contact" />
<div class="oe_structure"/>
</div>
<div class="col-md-3 col-md-offset-1" id="details">
<h3 class="page-header">Your Details <a href="/my/account" class="btn btn-default btn-xs">Change</a></h3>
<div t-field="user.partner_id" t-field-options='{"widget": "contact", "fields": ["email", "phone", "address", "name"]}'/>
</div>
<div class="oe_structure"/>
</div>
</div>
</t>
</template>
<template id="website_portal.custom_panel" inherit_id='website_portal.account' customize_show="True" name="Editable Panel" active="False">
<xpath expr="//div[@class='col-md-3']" position="after">
<div class="col-md-3">
<h3 class="page-header">You Custom Title</h3>
<p>Place your content here.</p>
</div>
</xpath>
</template>
<template id="contact" name="Contact">
<h3 class="page-header">Contact</h3>
<h5 t-esc="sales_rep.name"/>
<p><a t-att-href="'mailto:'+sales_rep.email" t-esc="sales_rep.email"/></p>
<p t-esc="sales_rep.phone"/>
<p t-esc="sales_rep.city"/>
</template>
<template id="quotations" name="Quotations">
<h3 class="page-header">Your Quotes</h3>
<t t-if="not quotations">
<p>There are currently no quotes for your account.</p>
</t>
<t t-if="quotations">
<table class="table table-hover status_table">
<thead>
<tr class="active">
<th>Quotation #</th>
<th>Valid Until</th>
<th></th>
<th>Total</th>
</tr>
</thead>
<t t-foreach="quotations" t-as="quotation">
<tr t-att-class="'hidden to_hide' if quotation.state!='sent' and quotation_index &gt; 4 else ''">
<td>
<a t-att-href="'/my/orders/'+str(quotation.id)"><t t-esc="quotation.name"/></a>
</td>
<td><span t-field="quotation.validity_date"/></td>
<td>
<t t-if="quotation.state == 'cancel'">
<span class="label label-default"><i class="fa fa-fw fa-remove"/> Cancelled</span>
</t>
<t t-if="quotation.validity_date and quotation.validity_date &lt; date">
<span class="label label-default"><i class="fa fa-fw fa-clock-à"/> Expired</span>
</t>
</td>
<td><span t-esc="quotation.amount_total" t-esc-options='{"widget": "monetary", "display_currency": "quotation.pricelist_id.currency_id"}'/>
</td>
</tr>
</t>
<tr t-att-class="'hidden' if len(quotations) &lt; 5 else ''">
<td colspan="5"><a href="" class="wp_show_more btn btn-xs btn-default"><span>Show more</span><span class="hidden">Show less</span></a></td>
</tr>
</table>
</t>
</template>
<template id="sale_orders" name="Sales Orders">
<h3 class="page-header">Your Orders</h3>
<t t-if="not orders">
<p>There are currently no orders for your account.</p>
</t>
<t t-if="orders">
<table class="table table-hover status_table">
<thead>
<tr class="active">
<th>Sale Order #</th>
<th>Confirmation Date</th>
<th></th>
<th>Total</th>
</tr>
</thead>
<t t-foreach="orders" t-as="order">
<tr t-att-class="'hidden to_hide' if order.state not in ['manual', 'progress'] and order_index &gt; 4 else ''">
<td>
<a t-att-href="'/my/orders/'+str(order.id)"><t t-esc="order.name"/></a>
</td>
<td><span t-field="order.date_confirm"/></td>
<td>
<t t-if="order.state == 'progress'">
<span class="label label-info"><i class="fa fa-fw fa-clock-o"/> Invoiced</span>
</t>
<t t-if="order.state in ['shipping_except','invoice_except']">
<span class="label label-danger"><i class="fa fa-fw fa-warning"/> Problem</span>
</t>
<t t-if="order.state == 'done'">
<span class="label label-default"><i class="fa fa-fw fa-check"/> Done</span>
</t>
</td>
<td><span t-esc="order.amount_total" t-esc-options='{"widget": "monetary", "display_currency": "order.pricelist_id.currency_id"}'/></td>
</tr>
</t>
<tr t-att-class="'hidden' if len(orders) &lt; 5 else ''">
<td colspan="4"><a href="" class="wp_show_more btn btn-xs btn-default"><span>Show more</span><span class="hidden">Show less</span></a></td>
</tr>
</table>
</t>
</template>
<template id="invoices" name="Invoices and Payments">
<h3 class="page-header">Your Invoices and Payments</h3>
<t t-if="not invoices">
<p>There are currently no invoices for your account.</p>
</t>
<t t-if="invoices">
<table class="table table-hover status_table">
<thead>
<tr class="active">
<th>Invoice #</th>
<th>Invoice Date</th>
<th>Due Date</th>
<th></th>
<th>Total</th>
</tr>
</thead>
<t t-foreach="invoices" t-as="invoice">
<tr t-att-class="'hidden to_hide' if invoice.state!='open' and invoice_index &gt; 4 else ''">
<td>
<a t-att-href="'/report/pdf/account.report_invoice/'+str(invoice.id)"><t t-esc="invoice.number"/></a>
</td>
<td><span t-field="invoice.date_invoice"/></td>
<td><span t-field="invoice.date_due"/></td>
<td>
<t t-if="invoice.state == 'open'">
<span class="label label-info"><i class="fa fa-fw fa-clock-o"/> Waiting for Payment</span>
</t>
<t t-if="invoice.state == 'paid'">
<span class="label label-default"><i class="fa fa-fw fa-check"/> Paid</span>
</t>
<t t-if="invoice.state == 'cancel'">
<span class="label label-default"><i class="fa fa-fw fa-remove"/> Cancelled</span>
</t>
</td>
<td><span t-esc="invoice.amount_total" t-esc-options='{"widget": "monetary", "display_currency": "invoice.currency_id"}'/></td>
</tr>
</t>
<tr t-att-class="'hidden' if len(invoices) &lt; 5 else ''">
<td colspan="5"><a href="" class="wp_show_more btn btn-xs btn-default"><span>Show more</span><span class="hidden">Show less</span></a></td>
</tr>
</table>
</t>
</template>
<template id="orders_followup" name="Sales Order">
<t t-call="website.layout">
<div id="wrap">
<div class="container">
<div class="oe_structure">
<div class="row">
<div class="col-sm-4">
<ol class="breadcrumb mt8">
<li><a href="/my/home">My Account</a></li>
<li>
<t t-if="order.state == 'sent'">
Quotation
</t>
<t t-if="order.state != 'sent'">
Order
</t>
<t t-esc="order.name"/>
</li>
</ol>
</div>
</div>
</div>
<div id="optional_placeholder"></div>
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<div class="row">
<div class="col-md-12">
<h4>
<t t-if="order.state == 'sent'">
Quotation
</t>
<t t-if="order.state != 'sent'">
Order
</t>
<span t-esc="order.name"/>
</h4>
</div>
</div>
</div>
<div class="panel-body">
<!-- <hr/> -->
<div class="mb8">
<strong>Date:</strong> <span t-field="order.create_date" t-field-options='{"widget": "date"}'/>
</div>
<div class='row'>
<div class="col-md-6">
<div>
<strong>Invoicing Address</strong>
</div>
<div>
<address t-field="order.partner_invoice_id" t-field-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": true}'/>
</div>
<t t-set="invoices" t-value="[i for i in order.invoice_ids if i.state not in ['draft', 'cancel']]"/>
<t t-if="invoices">
<div>
<strong>Invoices</strong>
</div>
<div>
<t t-foreach="invoices" t-as="i">
<t t-set="report_url" t-value="'/report/pdf/account.report_invoice/%s' % i.id"/>
<div>
<a t-att-href="report_url"><span class="fa fa-download"/></a>
<a t-att-href="report_url"><span t-esc="i.number"/></a>
<span class="text-muted" t-field="i.date_invoice"/>
<t t-if="i.state == 'paid'">
<span class="label label-success orders_label_text_align"><i class="fa fa-fw fa-check"/> Paid</span>
</t>
<t t-if="i.state != 'paid'">
<span class="label label-info orders_label_text_align"><i class="fa fa-fw fa-clock-o"/> Waiting</span>
</t>
</div>
</t>
</div>
</t>
</div>
<div id="shipping_address" class="col-md-6">
<div>
<strong>Shipping Address</strong>
</div>
<div>
<address t-field="order.partner_shipping_id" t-field-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": true}'/>
</div>
</div>
</div>
<hr/>
<div class="row">
<div class="col-md-6">
<strong>Product</strong>
</div>
<div class="col-md-2 text-right">
<strong>Unit Price</strong>
</div>
<div class="col-md-2 text-right">
<strong>Quantity</strong>
</div>
<div class="col-md-2 text-right">
<strong>Subtotal</strong>
</div>
</div>
<t t-foreach="order.order_line" t-as="ol">
<div class="row orders_vertical_align">
<div class="col-md-1 text-center">
<img t-att-src="'/website/image/product.product/%s/image_small/48x48' % ol.product_id.id"/>
</div>
<div id='product_name' class="col-md-5">
<span t-esc="ol.product_id.name"/>
</div>
<div class="col-md-2 text-right">
<span t-field="ol.price_unit" t-field-options='{"widget": "monetary", "display_currency": "order.pricelist_id.currency_id"}'/>
</div>
<div class="col-md-2 text-right">
<t t-if="ol._name == 'sale.order.line'">
<span t-esc="ol.product_uom_qty"/>
</t>
<t t-if="ol._name == 'account.invoice.line'">
<span t-esc="ol.quantity"/>
</t>
</div>
<div class="col-md-2 text-right">
<span t-field="ol.price_subtotal" t-field-options='{"widget": "monetary", "display_currency": "order.pricelist_id.currency_id"}'/>
</div>
</div>
</t>
<hr/>
<div class="row">
<div class="col-md-6">
<div>
<strong>Contact</strong>
</div>
<div t-field="order.user_id.partner_id" t-field-options='{"widget": "contact", "fields": ["email", "phone"]}'/>
</div>
<div class="col-md-6">
<div class="row">
<div class="col-md-10 text-right">
Untaxed Amount:
</div>
<div class="col-md-2 text-right">
<span t-field="order.amount_untaxed" t-field-options='{"widget": "monetary", "display_currency": "order.pricelist_id.currency_id"}'/>
</div>
</div>
<div class="row">
<div class="col-md-10 text-right">
Taxes:
</div>
<div class="col-md-2 text-right">
<span t-field="order.amount_tax" t-field-options='{"widget": "monetary", "display_currency": "order.pricelist_id.currency_id"}'/>
</div>
</div>
<div class="row">
<div class="col-md-10 text-right">
<strong>Total:</strong>
</div>
<div class="col-md-2 text-right">
<strong><span t-field="order.amount_total" t-field-options='{"widget": "monetary", "display_currency": "order.pricelist_id.currency_id"}'/></strong>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="oe_structure mb32"/>
</div>
</div>
</t>
</template>
<template id="details">
<t t-call="website.layout">
<t t-set="additional_title">Contact Details</t>
<div id="wrap">
<div class="container">
<div class="row">
<div class="col-sm-6">
<ol class="breadcrumb mt8">
<li><a href="/my/home">My Account</a></li>
<li>Details</li>
</ol>
</div>
</div>
<h1>Contact Details</h1>
<form action="/my/account" method="post">
<div class="row oe_website_portal">
<div class="col-md-8">
<div class="row">
<div class="col-md-12">
<div t-if="error_message" class="alert alert-danger">
<t t-foreach="error_message" t-as="err"><t t-esc="err"/><br /></t>
</div>
</div>
<div t-attf-class="form-group #{error.get('name') and 'has-error' or ''} col-lg-6">
<label class="control-label" for="contact_name">Your Name</label>
<input type="text" name="name" class="form-control" t-att-value="name or partner.name" />
</div>
<div t-attf-class="form-group #{error.get('email') and 'has-error' or ''} col-lg-6">
<label class="control-label" for="contact_name">Email</label>
<input type="email" name="email" class="form-control" t-att-value="email or partner.email" />
</div>
<div t-attf-class="form-group #{error.get('phone') and 'has-error' or ''} col-lg-6">
<label class="control-label" for="phone">Phone</label>
<input type="tel" name="phone" class="form-control" t-att-value="phone or partner.phone" />
</div>
<div class="clearfix" />
<div t-attf-class="form-group #{error.get('street') and 'has-error' or ''} col-lg-6">
<label class="control-label label-optional" for="street">Company Name</label>
<input type="text" name="street" class="form-control" t-att-value="street or partner.street"/>
</div>
<div t-if="has_check_vat" t-attf-class="form-group #{error.get('vat') and 'has-error' or ''} col-lg-6">
<label class="control-label label-optional" for="vat">VAT Number</label>
<input type="text" name="vat" class="form-control" t-att-value="vat or partner.vat" />
</div>
<div class="clearfix" />
<div t-attf-class="form-group #{error.get('street') and 'has-error' or ''} col-lg-6">
<label class="control-label" for="street">Street</label>
<input type="text" name="street2" class="form-control" t-att-value="street2 or partner.street2" />
</div>
<div t-attf-class="form-group #{error.get('city') and 'has-error' or ''} col-lg-6">
<label class="control-label" for="city">City</label>
<input type="text" name="city" class="form-control" t-att-value="city or partner.city" />
</div>
<div t-attf-class="form-group #{error.get('zip') and 'has-error' or ''} col-lg-6">
<label class="control-label" for="zipcode">Zip / Postal Code</label>
<input type="text" name="zipcode" class="form-control" t-att-value="zipcode or partner.zip" />
</div>
<div t-attf-class="form-group #{error.get('country_id') and 'has-error' or ''} col-lg-6">
<label class="control-label" for="country_id">Country</label>
<select name="country_id" class="form-control">
<option value="">Country...</option>
<t t-foreach="countries or []" t-as="country">
<option t-att-value="country.id" t-att-selected="country.id == partner.country_id.id">
<t t-esc="country.name" />
</option>
</t>
</select>
</div>
<div t-attf-class="form-group #{error.get('state_id') and 'has-error' or ''} col-lg-6">
<label class="control-label label-optional" for="state_id">State / Province</label>
<select name="state_id" class="form-control">
<option value="">select...</option>
<t t-foreach="states or []" t-as="state">
<option t-att-value="state.id" style="display:none;" t-att-data-country_id="state.country_id.id" t-att-selected="state.id == partner.state_id.id">
<t t-esc="state.name" />
</option>
</t>
</select>
</div>
</div>
<div class="clearfix">
<button type="submit" class="btn btn-default btn-primary pull-right mb32 ">
Confirm
<span class="fa fa-long-arrow-right" />
</button>
</div>
</div>
</div>
</form>
</div>
</div>
</t>
</template>
<template id="my_account_link" name="Link to frontend portal" inherit_id="website.layout">
<xpath expr="//li[@class='dropdown']/ul/li[contains(.,'Logout')]" position="before">
<li><a href="/my/home" role="menuitem">My Account</a></li>
</xpath>
</template>
<template id="sale_backend_link" inherit_id='website_portal.sale_orders' customize_show="True" name="Link to Sales Backend" active="False">
<xpath expr="//h3[@class='page-header']" position="inside">
<a href="/web#action=portal_sale.action_orders_portal" target="_blank" class="btn btn-default btn-xs pull-right"> Access the complete Sale application</a>
</xpath>
</template>
<template id="sales_backend_access" inherit_id='website_portal.orders_followup' customize_show="True" name="Backend Access" active="False">
<xpath expr="//div[@id='optional_placeholder']" position="inside">
<h4 class="text-muted text-center mt64 mb64">
If you wish to access the complete application to manage your orders, you can follow
<a href="/web#action=portal_sale.action_orders_portal">this link</a>.
</h4>
</xpath>
</template>
</odoo>
+1 -1
View File
@@ -10,7 +10,7 @@ OpenERP E-Commerce
""",
'author': 'OpenERP SA',
'depends': ['website', 'sale', 'payment'],
'depends': ['website', 'sale', 'payment', 'website_portal'],
'data': [
'data/data.xml',
'views/views.xml',
-27
View File
@@ -346,33 +346,6 @@ class website_sale(http.Controller):
})
return value
@http.route([
'/shop/orders',
'/shop/orders/page/<int:page>',
], type='http', auth="user", website=True)
def orders_followup(self, page=1, by = 5, **post):
partner = request.env['res.users'].browse(request.uid).partner_id
orders = request.env['sale.order'].sudo().search([('partner_id', '=', partner.id), ('state', 'not in', ['draft', 'cancel'])])
nbr_pages = max((len(orders) / by) + (1 if len(orders) % by > 0 else 0), 1)
page = min(page, nbr_pages)
pager = request.website.pager(
url='/shop/orders', total=nbr_pages, page=page, step=1,
scope=by, url_args=post
)
orders = orders[by*(page-1):by*(page-1)+by]
order_invoice_lines = {}
for o in orders:
invoiced_lines = request.env['account.invoice.line'].sudo().search([('invoice_id', 'in', o.invoice_ids.ids)])
order_invoice_lines[o.id] = {il.product_id.id: il.invoice_id for il in invoiced_lines}
return request.website.render("website_sale.orders_followup", {
'orders': orders,
'order_invoice_lines': order_invoice_lines,
'pager': pager,
})
#------------------------------------------------------
# Checkout
#------------------------------------------------------
@@ -1,83 +1,101 @@
@charset "utf-8";
/* ---- Default Styles ---- */
.oe_product {
border: 1px solid rgba(100, 100, 100, 0.2); }
border: 1px solid rgba(100, 100, 100, 0.2);
}
.products_pager {
text-align: center; }
.products_pager > div {
margin: 0 auto;
display: inline-block; }
.products_pager > div > * {
vertical-align: middle;
float: none;
padding: 0; }
.products_pager > div > ul {
padding-left: 10px; }
.products_pager > ul {
margin-left: auto;
margin-right: auto; }
text-align: center;
}
.products_pager > div {
margin: 0 auto;
display: inline-block;
}
.products_pager > div > * {
vertical-align: middle;
float: none;
padding: 0;
}
.products_pager > div > ul {
padding-left: 10px;
}
.products_pager > ul {
margin-left: auto;
margin-right: auto;
}
.oe_website_sale ul ul {
margin-left: 1.5em; }
margin-left: 1.5em;
}
/* ---- Default Styles ---- */
.oe_product {
position: relative; }
.oe_product .oe_product_image {
position: absolute;
left: 15px;
right: 15px;
top: 15px;
bottom: 55px;
text-align: center; }
.oe_product .oe_product_image img {
max-width: 100%;
max-height: 100%;
margin: auto;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 4; }
.oe_product section {
position: absolute;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
padding: 0 15px 24px 10px;
min-height: 56px;
border-top: 1px solid rgba(255, 255, 255, 0.2);
background: rgba(255, 255, 255, 0.75);
z-index: 5; }
.oe_product .product_price {
padding: 5px 0;
position: absolute;
bottom: 0; }
.oe_product .oe_product_cart {
position: relative;
width: 100%;
height: 100%; }
.oe_product .oe_subdescription {
font-size: 0.8em;
overflow: hidden;
margin-bottom: 10px; }
position: relative;
}
.oe_product .oe_product_image {
position: absolute;
left: 15px;
right: 15px;
top: 15px;
bottom: 55px;
text-align: center;
}
.oe_product .oe_product_image img {
max-width: 100%;
max-height: 100%;
margin: auto;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 4;
}
.oe_product section {
position: absolute;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
padding: 0 15px 24px 10px;
min-height: 56px;
border-top: 1px solid rgba(255, 255, 255, 0.2);
background: rgba(255, 255, 255, 0.75);
z-index: 5;
}
.oe_product .product_price {
padding: 5px 0;
position: absolute;
bottom: 0;
}
.oe_product .oe_product_cart {
position: relative;
width: 100%;
height: 100%;
}
.oe_product .oe_subdescription {
font-size: 0.8em;
overflow: hidden;
margin-bottom: 10px;
}
.oe_mycart .input-group-addon {
padding-left: 6px;
padding-right: 6px; }
padding-right: 6px;
}
/* Products Options */
.oe_product.oe_image_full .oe_product_image {
left: 0;
right: 0;
top: 0;
bottom: 0; }
bottom: 0;
}
/* Ribbon for promotionso on products */
.oe_product.oe_ribbon_promo .ribbon-wrapper {
display: block; }
display: block;
}
.oe_product .ribbon-wrapper {
display: none;
width: 85px;
@@ -86,7 +104,8 @@
overflow: hidden;
position: absolute;
top: 0;
right: 0; }
right: 0;
}
.oe_product .ribbon {
font: bold 15px Sans-Serif;
color: white;
@@ -100,97 +119,137 @@
left: -5px;
top: 15px;
width: 120px;
cursor: default; }
cursor: default;
}
/* ---- Product Sizes ---- */
.col-md-12 .oe-height-1 {
height: 75px; }
height: 75px;
}
.col-md-12 .oe-height-2 {
height: 150px; }
height: 150px;
}
.col-md-12 .oe-height-4 {
height: 300px; }
height: 300px;
}
.col-md-12 .oe-height-6 {
height: 450px; }
height: 450px;
}
.col-md-12 .oe-height-8 {
height: 600px; }
height: 600px;
}
.col-md-9 .oe-height-1 {
height: 70px; }
height: 70px;
}
.col-md-9 .oe-height-2 {
height: 140px; }
height: 140px;
}
.col-md-9 .oe-height-4 {
height: 280px; }
height: 280px;
}
.col-md-9 .oe-height-6 {
height: 420px; }
height: 420px;
}
.col-md-9 .oe-height-8 {
height: 560px; }
height: 560px;
}
@media (max-width: 768px) {
#products_grid table, #products_grid tbody, #products_grid tr, #products_grid td {
float: left;
width: 100%;
display: inline-block; }
display: inline-block;
}
#products_grid .oe_product {
float: left;
width: 100%;
height: 300px;
display: inline-block; }
display: inline-block;
}
.products_pager .pagination {
margin: 5px auto !important; } }
margin: 5px auto !important;
}
}
@media (max-width: 400px) {
#products_grid .oe_product {
height: 270px; }
#products_grid .oe_product section {
background: rgba(100, 100, 100, 0.2); }
#products_grid .oe_product .oe_product_image {
top: 0;
bottom: 65px; }
height: 270px;
}
#products_grid .oe_product section {
background: rgba(100, 100, 100, 0.2);
}
#products_grid .oe_product .oe_product_image {
top: 0;
bottom: 65px;
}
#products_grid .oe_product.oe_image_full .oe_product_image {
bottom: 50px; } }
bottom: 50px;
}
}
@media (min-width: 992px) {
.col-md-12 .oe-height-1 {
height: 95px; }
height: 95px;
}
.col-md-12 .oe-height-2 {
height: 190px; }
height: 190px;
}
.col-md-12 .oe-height-4 {
height: 380px; }
height: 380px;
}
.col-md-12 .oe-height-6 {
height: 570px; }
height: 570px;
}
.col-md-12 .oe-height-8 {
height: 760px; }
height: 760px;
}
.col-md-9 .oe-height-1 {
height: 72px; }
height: 72px;
}
.col-md-9 .oe-height-2 {
height: 144px; }
height: 144px;
}
.col-md-9 .oe-height-4 {
height: 288px; }
height: 288px;
}
.col-md-9 .oe-height-6 {
height: 432px; }
height: 432px;
}
.col-md-9 .oe-height-8 {
height: 576px; } }
height: 576px;
}
}
@media (min-width: 1200px) {
.col-md-12 .oe-height-1 {
height: 125px; }
height: 125px;
}
.col-md-12 .oe-height-2 {
height: 250px; }
height: 250px;
}
.col-md-12 .oe-height-4 {
height: 500px; }
height: 500px;
}
.col-md-12 .oe-height-6 {
height: 750px; }
height: 750px;
}
.col-md-12 .oe-height-8 {
height: 1000px; }
height: 1000px;
}
.col-md-9 .oe-height-1 {
height: 95px; }
height: 95px;
}
.col-md-9 .oe-height-2 {
height: 190px; }
height: 190px;
}
.col-md-9 .oe-height-4 {
height: 380px; }
height: 380px;
}
.col-md-9 .oe-height-6 {
height: 570px; }
height: 570px;
}
.col-md-9 .oe-height-8 {
height: 760px; } }
/* ---- Cart ---- */
@@ -274,98 +333,115 @@
width: 100%;
min-height: 100px;
position: relative;
padding-bottom: 5px; }
.oe_product.oe_list .oe_product_image {
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 170px; }
.oe_product.oe_list section {
position: relative;
border: 0;
top: 0;
bottom: auto;
left: 180px;
background: transparent; } }
padding-bottom: 5px;
}
.oe_product.oe_list .oe_product_image {
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 170px;
}
.oe_product.oe_list section {
position: relative;
border: 0;
top: 0;
bottom: auto;
left: 180px;
background: transparent;
}
}
.oe_website_sale .row .row .col-md-12 {
float: none; }
float: none;
}
/* product detail */
.css_attribute_color {
display: inline-block;
border: 1px solid #999999;
text-align: center; }
.css_attribute_color input {
margin: 8px;
height: 13px;
opacity: 0; }
.css_attribute_color.active {
border: 3px ridge #66ee66; }
.css_attribute_color.active input {
margin: 6px; }
text-align: center;
}
.css_attribute_color input {
margin: 8px;
height: 13px;
opacity: 0;
}
.css_attribute_color.active {
border: 3px ridge #66ee66;
}
.css_attribute_color.active input {
margin: 6px;
}
.css_not_available_msg {
display: none; }
display: none;
}
.css_not_available.js_product > *:nth-child(4) > * {
display: none; }
display: none;
}
.css_not_available.js_product .product_price, .css_not_available.js_product .css_quantity {
display: none; }
display: none;
}
.css_not_available.js_product .css_not_available_msg {
display: block; }
display: block;
}
option.css_not_available {
color: #ccc; }
color: #cccccc;
}
label.css_not_available {
opacity: 0.6; }
opacity: 0.6;
}
label.css_attribute_color.css_not_available {
opacity: 1;
background-image: url("/website_sale/static/src/img/redcross.png");
background-size: cover; }
background-size: cover;
}
.product_detail_img {
margin-left: auto;
margin-right: auto;
display: block;
max-height: 500px; }
max-height: 500px;
}
.oe_default_price {
display: none; }
display: none;
}
.discount .oe_default_price {
display: block; }
display: block;
}
.oe_website_sale input.js_quantity {
min-width: 48px;
text-align: center; }
text-align: center;
}
/* ---- Publish managment and options ---- */
.oe_overlay_options .dropdown ul[name="size"] table {
margin-left: 20px; }
margin-left: 20px;
}
.oe_overlay_options .dropdown ul[name="size"] td {
margin: 0;
padding: 0;
width: 20px;
height: 20px;
border: 1px #dddddd solid;
cursor: pointer; }
.oe_overlay_options .dropdown ul[name="size"] td.selected {
background-color: #B1D4F1; }
cursor: pointer;
}
.oe_overlay_options .dropdown ul[name="size"] td.selected {
background-color: #b1d4f1;
}
.oe_overlay_options .dropdown ul[name="size"] table.oe_hover td.selected {
background-color: transparent; }
background-color: transparent;
}
.oe_overlay_options .dropdown ul[name="size"] table.oe_hover td.select {
background-color: #B1D4F1; }
/* ---- My Orders page ---- */
.orders_vertical_align {
display: flex;
align-items: center; }
.orders_label_text_align {
vertical-align: 15%; }
background-color: #b1d4f1;
}
/* Wizard */
ul.wizard {
@@ -377,7 +453,8 @@ ul.wizard {
border-radius: 4px;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.065);
-moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.065);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.065); }
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.065);
}
ul.wizard li {
border: 1px solid #d4d4d4;
@@ -387,7 +464,8 @@ ul.wizard li {
padding: 0 10px 0 20px;
margin: 0;
line-height: 38px;
background: #fbfbfb; }
background: #fbfbfb;
}
ul.wizard li .chevron {
position: absolute;
@@ -397,7 +475,8 @@ ul.wizard li .chevron {
display: block;
border: 20px solid transparent;
border-right: 0;
border-left: 10px solid #d4d4d4; }
border-left: 10px solid #d4d4d4;
}
ul.wizard li .chevron:before {
position: absolute;
@@ -407,19 +486,24 @@ ul.wizard li .chevron:before {
border: 20px solid transparent;
border-right: 0;
border-left: 10px solid #fbfbfb;
content: ""; }
content: "";
}
ul.wizard li.text-success {
background: #f3f4f5; }
background: #f3f4f5;
}
ul.wizard li.text-success .chevron:before {
border-left: 10px solid #f5f5f5; }
border-left: 10px solid whitesmoke;
}
ul.wizard li.text-primary {
background: #f1f6fc; }
background: #f1f6fc;
}
ul.wizard li.text-primary .chevron:before {
border-left: 10px solid #f1f6fc; }
border-left: 10px solid #f1f6fc;
}
ul.wizard li:first-child {
padding-left: 15px;
@@ -367,17 +367,6 @@ label.css_attribute_color.css_not_available
table.oe_hover td.select
background-color: #B1D4F1
/* ---- My Orders page ---- */
.orders_vertical_align
display: flex
align-items: center
.orders_label_text_align
vertical-align: 15%
/* Wizard */
-212
View File
@@ -1245,217 +1245,5 @@
<li><a id="create-new-product" href="#" data-action="new_product">New Product</a></li>
</xpath>
</template>
<template id="orders_followup" name="Odoo - Follow your Orders">
<t t-call="website.layout">
<div id="wrap">
<div class="oe_structure">
<h1 class="text-center">
Odoo Orders History
</h1>
<h2 class="text-center text-muted">
Follow your Orders
</h2>
</div>
<div class="container oe_website_sale">
<t t-if="not orders">
<h4 class="text-muted text-center mt64 mb64">
You have yet to order anything, why not <a href="/shop/">start now</a> ?
</h4>
</t>
<t t-call="website_sale.orders_followup_navbar"/>
<t t-foreach="orders" t-as="o">
<div class='row'>
<div class="panel panel-default">
<div class="panel-heading">
<div class="row">
<div class="col-md-12">
<h4>
<t t-if="o.state == 'sent'">
Quotation N°
</t>
<t t-if="o.state != 'sent'">
Order N°
</t>
<span t-field="o.name"/>
</h4>
</div>
</div>
</div>
<div class="panel-body">
<!-- <hr/> -->
<div class="mb8">
<strong>Date:</strong> <span t-field="o.create_date" t-field-options='{"widget": "date"}'/>
</div>
<div class='row'>
<div class="col-md-6">
<div>
<strong>Invoicing Address</strong>
</div>
<div>
<address t-field="o.partner_invoice_id" t-field-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": true}'/>
</div>
<t t-set="invoices" t-value="[i for i in o.invoice_ids if i.state not in ['draft', 'cancel']]"/>
<t t-if="invoices">
<div>
<strong>Invoices</strong>
</div>
<div>
<t t-foreach="invoices" t-as="i">
<t t-set="report_url" t-value="'/report/pdf/account.report_invoice/%s' % i.id"/>
<div>
<a t-att-href="report_url"><span class="fa fa-download"/></a>
<a t-att-href="report_url"><span t-field="i.number"/></a>
<span class="text-muted" t-field="i.date_invoice"/>
<t t-if="i.state == 'paid'">
<span class="label label-success orders_label_text_align"><i class="fa fa-fw fa-check"/> Paid</span>
</t>
<t t-if="i.state != 'paid'">
<span class="label label-info orders_label_text_align"><i class="fa fa-fw fa-clock-o"/> Waiting</span>
</t>
</div>
</t>
</div>
</t>
</div>
<div id="shipping_address" class="col-md-6">
<div>
<strong>Shipping Address</strong>
</div>
<div>
<address t-field="o.partner_shipping_id" t-field-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": true}'/>
</div>
</div>
</div>
<hr/>
<div id="product_lines">
<!-- Product lines come here -->
</div>
<hr/>
<div class="row">
<div class="col-md-6">
<div>
<strong>Contact</strong>
</div>
<div t-field="o.user_id.partner_id" t-field-options='{"widget": "contact", "fields": ["email", "phone"]}'/>
</div>
<div class="col-md-6">
<div class="row">
<div class="col-md-10 text-right">
Untaxed Amount:
</div>
<div class="col-md-2 text-right">
<span t-field="o.amount_untaxed" t-field-options='{"widget": "monetary", "display_currency": "o.company_id.currency_id"}'/>
</div>
</div>
<div class="row">
<div class="col-md-10 text-right">
Taxes:
</div>
<div class="col-md-2 text-right">
<span t-field="o.amount_tax" t-field-options='{"widget": "monetary", "display_currency": "o.company_id.currency_id"}'/>
</div>
</div>
<div class="row">
<div class="col-md-10 text-right">
<strong>Total:</strong>
</div>
<div class="col-md-2 text-right">
<strong><span t-field="o.amount_total" t-field-options='{"widget": "monetary", "display_currency": "o.company_id.currency_id"}'/></strong>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</t>
<t t-if="len(orders) >= 2">
<t t-call="website_sale.orders_followup_navbar"/>
</t>
</div>
<div class="oe_structure mb32"/>
</div>
</t>
</template>
<template id="sales_backend_access" inherit_id='website_sale.orders_followup' customize_show="True" name="Backend Access" active="False">
<xpath expr="//div[@class='container oe_website_sale']" position="before">
<h4 class="text-muted text-center mt64 mb64">
If you wish to access the complete application to manage your orders, you can follow
<a href="/web#action=portal_sale.action_orders_portal">this link</a>.
</h4>
</xpath>
</template>
<template id="order_invoice" name="Display Products similarly to Invoice form view" inherit_id="website_sale.orders_followup">
<xpath expr="//div[@id='product_lines']" position="inside">
<div class="row">
<div class="col-md-8 col-md-offset-1">
<strong>Product</strong>
</div>
<div class="col-md-1 text-right">
<strong>Unit Price</strong>
</div>
<div class="col-md-1 text-right">
<strong>Quantity</strong>
</div>
<div class="col-md-1 text-right">
<strong>Subtotal</strong>
</div>
</div>
<t t-foreach="o.order_line" t-as="ol">
<div class="row orders_vertical_align">
<div class="col-md-1 text-center">
<a t-att-href="'/shop/product/%s' % str(ol.product_id.product_tmpl_id.id)">
<img t-att-src="'/website/image/product.product/%s/image_small/48x48' % ol.product_id.id"/>
</a>
</div>
<div id='product_name' class="col-md-8">
<a t-att-href="'/shop/product/%s' % str(ol.product_id.product_tmpl_id.id)"><span t-field="ol.product_id.name"/></a>
</div>
<div class="col-md-1 text-right">
<span t-field="ol.price_unit" t-field-options='{"widget": "monetary", "display_currency": "o.company_id.currency_id"}'/>
</div>
<div class="col-md-1 text-right">
<t t-if="ol._name == 'sale.order.line'">
<span t-field="ol.product_uom_qty"/>
</t>
<t t-if="ol._name == 'account.invoice.line'">
<span t-field="ol.quantity"/>
</t>
</div>
<div class="col-md-1 text-right">
<span t-field="ol.price_subtotal" t-field-options='{"widget": "monetary", "display_currency": "o.company_id.currency_id"}'/>
</div>
</div>
</t>
</xpath>
</template>
<template id="orders_followup_navbar" name="Navbar for My Orders page">
<div class="row">
<div class="col-md-12">
<span class="pull-right">
<t t-call="website.pager"/>
</span>
</div>
</div>
</template>
<template id="orders_followup_link" name="Orders Followup Link from User Account" inherit_id="website.layout">
<xpath expr="//li[@class='dropdown']/ul/li[contains(.,'Logout')]" position="before">
<li><a href="/shop/orders" role="menuitem">My Orders</a></li>
</xpath>
</template>
</data>
</openerp>
+34 -33
View File
@@ -4,68 +4,69 @@ 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_portal.controllers.main import website_account
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'
class website_sale_digital_confirmation(website_sale):
@http.route([
'/shop/confirmation',
], type='http', auth="public", website=True)
def payment_confirmation(self, **post):
response = super(website_sale_digital, self).payment_confirmation(**post)
response = super(website_sale_digital_confirmation, 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
class website_sale_digital(website_account):
orders_page = '/my/orders'
@http.route([
'/shop/orders',
'/shop/orders/page/<int:page>',
'/my/orders/<int:order>',
], type='http', auth='user', website=True)
def orders_followup(self, page=1, **post):
response = super(website_sale_digital, self).orders_followup(**post)
def orders_followup(self, order=None, **post):
response = super(website_sale_digital, self).orders_followup(order=order, **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')])
order = response.qcontext['order']
invoiced_lines = request.env['account.invoice.line'].sudo().search([('invoice_id', 'in', order.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
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',
)
# 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
# Ignore products with no attachments
if not att:
continue
purchased_products_attachments[p_id] = att
order_products_attachments[o.id] = purchased_products_attachments
purchased_products_attachments[p_id] = att
response.qcontext.update({
'digital_attachments': order_products_attachments,
'digital_attachments': purchased_products_attachments,
})
return response
@http.route([
'/shop/download',
'/my/download',
], type='http', auth='public')
def download_attachment(self, attachment_id):
# Check if this is a valid attachment id
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="orders_followup_downloads" name="Orders Downloads Followup" inherit_id="website_sale.order_invoice">
<template id="orders_followup_downloads" name="Orders Downloads Followup" inherit_id="website_portal.orders_followup">
<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-set="attachments" t-value="digital_attachments.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">
@@ -10,7 +10,7 @@
</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>
<li role="presentation"><a role="menuitem" tabindex="-1" t-att-href="'/my/download?attachment_id=%i' % a['id']"><t t-esc="a['name']"/></a></li>
</t>
</ul>
</span>
+9 -11
View File
@@ -3,24 +3,22 @@
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 openerp.addons.website_portal.controllers.main import website_account
class website_sale_stock(website_sale):
class website_sale_stock(website_account):
@http.route([
'/shop/orders',
'/shop/orders/page/<int:page>',
'/my/orders/<int:order>',
], type='http', auth='user', website=True)
def orders_followup(self, page=1, **post):
response = super(website_sale_stock, self).orders_followup(**post)
def orders_followup(self, order=None, **post):
response = super(website_sale_stock, self).orders_followup(order=order, **post)
order_shipping_lines = {}
for o in response.qcontext['orders']:
shipping_lines = request.env['stock.move'].sudo().search([('picking_id', 'in', o.picking_ids.ids)])
order_shipping_lines[o.id] = {sl.product_id.id: sl.picking_id for sl in shipping_lines}
order = response.qcontext['order']
shipping_lines = request.env['stock.move'].sudo().search([('picking_id', 'in', order.picking_ids.ids)])
order_shipping_lines = {sl.product_id.id: sl.picking_id for sl in shipping_lines}
response.qcontext.update({
'order_shipping_lines': order_shipping_lines,
})
return response
return response
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="orders_followup_shipping" name="Orders Shipping Followup" inherit_id="website_sale.order_invoice">
<template id="orders_followup_shipping" name="Orders Shipping Followup" inherit_id="website_portal.orders_followup">
<xpath expr="//div[@id='shipping_address']" position="inside">
<t t-if="o.picking_ids">
<t t-if="order.picking_ids">
<div>
<strong>Delivery Orders</strong>
</div>
<div>
<t t-foreach="o.picking_ids" t-as="i">
<t t-foreach="order.picking_ids" t-as="i">
<t t-set="report_url" t-value="'/report/pdf/stock.report_picking/%s' % i.id"/>
<div>
<a t-att-href="report_url"><span class="fa fa-download"/></a>