[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:
committed by
Christophe Simonis
parent
6cca3ee0f5
commit
c3c0408471
@@ -0,0 +1 @@
|
||||
import controllers
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
})();
|
||||
@@ -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 > 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 < 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) < 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 > 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) < 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 > 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) < 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>
|
||||
@@ -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',
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user