[ADD] website_portal_purchase: purchase orders available in portal
PO are now available in customer portal. It follows the same convention as the rest of exposed documents (list, pager, form, filters, ...) To insert it after the SO menu item, some ordering was required. Also, rename 'Orders' into 'Sales Orders' to avoid confusion with 'Purchase Orders'.
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import controllers
|
||||
import models
|
||||
@@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
{
|
||||
'name': 'Website Portal for Purchases',
|
||||
'category': 'Website',
|
||||
'summary': 'Add your purchase orders in the frontend portal',
|
||||
'version': '1.0',
|
||||
'description': """
|
||||
Display purchase orders on your website. Customers will be able to access their purchase orders in their own portal, see their state and communicate with salesmen.
|
||||
""",
|
||||
'depends': [
|
||||
'purchase',
|
||||
'website_portal',
|
||||
],
|
||||
'data': [
|
||||
'views/website_portal_purchase_templates.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'security/website_portal_purchase_security.xml',
|
||||
],
|
||||
'installable': True,
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import website_portal
|
||||
@@ -0,0 +1,110 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from odoo import http
|
||||
from odoo.exceptions import AccessError
|
||||
from odoo.http import request
|
||||
from odoo.tools.translate import _
|
||||
|
||||
from odoo.addons.website_portal.controllers.main import website_account, get_records_pager
|
||||
|
||||
|
||||
class WebsitePortal(website_account):
|
||||
|
||||
def _prepare_portal_layout_values(self):
|
||||
values = super(WebsitePortal, self)._prepare_portal_layout_values()
|
||||
partner = request.env.user.partner_id
|
||||
values['purchase_count'] = request.env['purchase.order'].search_count([
|
||||
'|',
|
||||
('message_partner_ids', 'child_of', [partner.commercial_partner_id.id]),
|
||||
('partner_id', 'child_of', [partner.commercial_partner_id.id]),
|
||||
('state', 'in', ['purchase', 'done', 'cancel'])
|
||||
])
|
||||
return values
|
||||
|
||||
@http.route(['/my/purchase', '/my/purchase/page/<int:page>'], type='http', auth="user", website=True)
|
||||
def portal_my_purchase_orders(self, page=1, date_begin=None, date_end=None, sortby=None, filterby=None, **kw):
|
||||
values = self._prepare_portal_layout_values()
|
||||
partner = request.env.user.partner_id
|
||||
PurchaseOrder = request.env['purchase.order']
|
||||
|
||||
domain = [
|
||||
'|',
|
||||
('message_partner_ids', 'child_of', [partner.commercial_partner_id.id]),
|
||||
('partner_id', 'child_of', [partner.commercial_partner_id.id]),
|
||||
]
|
||||
|
||||
archive_groups = self._get_archive_groups('purchase.order', domain)
|
||||
if date_begin and date_end:
|
||||
domain += [('create_date', '>', date_begin), ('create_date', '<=', date_end)]
|
||||
|
||||
searchbar_sortings = {
|
||||
'date': {'label': _('Newest'), 'order': 'create_date desc, id desc'},
|
||||
'name': {'label': _('Name'), 'order': 'name asc, id asc'},
|
||||
'amount_total': {'label': _('Total'), 'order': 'amount_total desc, id desc'},
|
||||
}
|
||||
# default sort by value
|
||||
if not sortby:
|
||||
sortby = 'date'
|
||||
order = searchbar_sortings[sortby]['order']
|
||||
|
||||
searchbar_filters = {
|
||||
'all': {'label': _('All'), 'domain': [('state', 'in', ['purchase', 'done', 'cancel'])]},
|
||||
'purchase': {'label': _('Purchase Order'), 'domain': [('state', '=', 'purchase')]},
|
||||
'cancel': {'label': _('Cancelled'), 'domain': [('state', '=', 'cancel')]},
|
||||
'done': {'label': _('Locked'), 'domain': [('state', '=', 'done')]},
|
||||
}
|
||||
# default filter by value
|
||||
if not filterby:
|
||||
filterby = 'all'
|
||||
domain += searchbar_filters[filterby]['domain']
|
||||
|
||||
# count for pager
|
||||
purchase_count = PurchaseOrder.search_count(domain)
|
||||
# make pager
|
||||
pager = request.website.pager(
|
||||
url="/my/purchase",
|
||||
url_args={'date_begin': date_begin, 'date_end': date_end},
|
||||
total=purchase_count,
|
||||
page=page,
|
||||
step=self._items_per_page
|
||||
)
|
||||
# search the purchase orders to display, according to the pager data
|
||||
orders = PurchaseOrder.search(
|
||||
domain,
|
||||
order=order,
|
||||
limit=self._items_per_page,
|
||||
offset=pager['offset']
|
||||
)
|
||||
request.session['my_purchases_history'] = orders.ids[:100]
|
||||
|
||||
values.update({
|
||||
'date': date_begin,
|
||||
'orders': orders,
|
||||
'page_name': 'purchase',
|
||||
'pager': pager,
|
||||
'archive_groups': archive_groups,
|
||||
'searchbar_sortings': searchbar_sortings,
|
||||
'sortby': sortby,
|
||||
'searchbar_filters': OrderedDict(sorted(searchbar_filters.items())),
|
||||
'filterby': filterby,
|
||||
'default_url': '/my/purchase',
|
||||
})
|
||||
return request.render("website_portal_purchase.portal_my_purchase_orders", values)
|
||||
|
||||
@http.route(['/my/purchase/<int:order_id>'], type='http', auth="user", website=True)
|
||||
def portal_my_purchase_order(self, order_id=None, **kw):
|
||||
order = request.env['purchase.order'].browse(order_id)
|
||||
try:
|
||||
order.check_access_rights('read')
|
||||
order.check_access_rule('read')
|
||||
except AccessError:
|
||||
return request.render("website.403")
|
||||
history = request.session.get('my_purchases_history', [])
|
||||
values = {
|
||||
'order': order.sudo(),
|
||||
}
|
||||
values.update(get_records_pager(history, order))
|
||||
return request.render("website_portal_purchase.portal_my_purchase_order", values)
|
||||
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import purchase
|
||||
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class PurchaseOrder(models.Model):
|
||||
|
||||
_inherit = 'purchase.order'
|
||||
|
||||
website_url = fields.Char('Website URL', compute='_website_url', help='The full URL to access the document through the website.')
|
||||
|
||||
def _website_url(self):
|
||||
for order in self:
|
||||
order.website_url = '/my/purchase/%s' % (order.id)
|
||||
|
||||
|
||||
class PurchaseOrderLine(models.Model):
|
||||
|
||||
_inherit = 'purchase.order.line'
|
||||
|
||||
# Non-stored related field to allow portal user to see the image of the product he has ordered
|
||||
product_image = fields.Binary('Product Image', related="product_id.image")
|
||||
@@ -0,0 +1,3 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_purchase_order_portal,purchase.order.portal,purchase.model_purchase_order,base.group_portal,1,0,0,0
|
||||
access_purchase_order_line_portal,purchase.order.line.portal,purchase.model_purchase_order_line,base.group_portal,1,0,0,0
|
||||
|
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="portal_purchase_order_user_rule" model="ir.rule">
|
||||
<field name="name">Portal Purchase Orders</field>
|
||||
<field name="model_id" ref="purchase.model_purchase_order"/>
|
||||
<field name="domain_force">[('message_partner_ids','child_of',[user.commercial_partner_id.id])]</field>
|
||||
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
|
||||
<field name="perm_unlink" eval="1"/>
|
||||
<field name="perm_write" eval="1"/>
|
||||
<field name="perm_read" eval="1"/>
|
||||
<field name="perm_create" eval="0"/>
|
||||
</record>
|
||||
|
||||
<record id="portal_purchase_order_line_rule" model="ir.rule">
|
||||
<field name="name">Portal Purhcase Orders Line</field>
|
||||
<field name="model_id" ref="purchase.model_purchase_order_line"/>
|
||||
<field name="domain_force">[('order_id.message_partner_ids','child_of',[user.commercial_partner_id.id])]</field>
|
||||
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,6 @@
|
||||
/* ---- My Purchase Orders page ---- */
|
||||
|
||||
.purchases_vertical_align {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<template id="portal_my_home_menu_purchase" name="Portal layout : purchase menu entries" inherit_id="website_portal.portal_layout" priority="25">
|
||||
<xpath expr="//ul[contains(@class,'o_portal_submenu')]" position="inside">
|
||||
<t t-if="purchase_count">
|
||||
<li t-att-class="page_name == 'purchase' and 'active'">
|
||||
<a href="/my/purchase">Purchase Orders</a>
|
||||
</li>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="portal_my_home_purchase" name="Portal My Home : purchase entry" inherit_id="website_portal.portal_my_home" priority="25">
|
||||
<xpath expr="//div[contains(@class,'o_my_home_content')]" position="inside">
|
||||
<h3 t-if="purchase_count" class="page-header">
|
||||
<a href="/my/purchase">Your Purchase Orders
|
||||
<small class="ml8">
|
||||
<span class='badge'><t t-esc="purchase_count"/></span>
|
||||
</small>
|
||||
</a>
|
||||
</h3>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="portal_my_purchase_orders" name="Portal: My Purchase Orders">
|
||||
<t t-call="website_portal.portal_layout">
|
||||
<h3 class="page-header">
|
||||
Your Purchase Orders
|
||||
<t t-call="website_portal.portal_searchbar"/>
|
||||
</h3>
|
||||
<table class="table table-hover o_my_status_table">
|
||||
<thead>
|
||||
<tr class="active">
|
||||
<th>Purchase Orders #</th>
|
||||
<th>Order Date</th>
|
||||
<th></th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<t t-foreach="orders" t-as="order">
|
||||
<tr>
|
||||
<td>
|
||||
<a t-attf-href="/my/purchase/#{order.id}?#{keep_query()}"><t t-esc="order.name"/></a>
|
||||
</td>
|
||||
<td><span t-field="order.date_order"/></td>
|
||||
<td>
|
||||
<t t-if="order.state == 'cancel'">
|
||||
<span class="label label-default"><i class="fa fa-fw fa-remove"/> Cancelled</span>
|
||||
</t>
|
||||
</td>
|
||||
<td><span t-field="order.amount_total" t-options='{"widget": "monetary", "display_currency": order.currency_id}'/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</table>
|
||||
<div t-if="pager" class="o_portal_pager text-center">
|
||||
<t t-call="website.pager"/>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="portal_my_purchase_order" name="Portal: My Purchase 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"><i class="fa fa-home"/></a></li>
|
||||
<li>
|
||||
<a t-attf-href="/my/purchase?{{ keep_query()}}">
|
||||
My Purchase Orders
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<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-call='website_portal.record_pager'/>
|
||||
Purchase Order
|
||||
<span t-esc="order.name"/>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="mb8">
|
||||
<strong>Date:</strong> <span t-field="order.date_order" t-options='{"widget": "date"}'/>
|
||||
</div>
|
||||
<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 purchases_vertical_align">
|
||||
<div class="col-md-1 text-center">
|
||||
<img t-att-src="'/web/image/purchase.order.line/%s/product_image/48x48' % ol.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-options='{"widget": "monetary", "display_currency": order.currency_id}'/>
|
||||
</div>
|
||||
<div class="col-md-2 text-right">
|
||||
<span t-esc="ol.product_qty"/>
|
||||
</div>
|
||||
<div class="col-md-2 text-right">
|
||||
<span t-field="ol.price_subtotal" t-options='{"widget": "monetary", "display_currency": order.currency_id}'/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<hr/>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12 text-right">
|
||||
<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-options='{"widget": "monetary", "display_currency": order.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-options='{"widget": "monetary", "display_currency": order.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-options='{"widget": "monetary", "display_currency": order.currency_id}'/></strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oe_structure mb32"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
@@ -12,8 +12,13 @@
|
||||
<a href="/my/quotes">Quotes</a>
|
||||
</li>
|
||||
<li t-att-class="page_name == 'order' and 'active'">
|
||||
<a href="/my/orders">Orders</a>
|
||||
<a href="/my/orders">Sales Orders</a>
|
||||
</li>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="portal_my_home_menu_invoice" name="Portal layout : invoice menu entries" inherit_id="website_portal.portal_layout" priority="30">
|
||||
<xpath expr="//ul[contains(@class,'o_portal_submenu')]" position="inside">
|
||||
<li t-if="invoice_count" t-att-class="page_name == 'invoice' and 'active'">
|
||||
<a href="/my/invoices">Invoices</a>
|
||||
</li>
|
||||
@@ -30,7 +35,7 @@
|
||||
</a>
|
||||
</h3>
|
||||
<h3 class="page-header">
|
||||
<a href="/my/orders">Your Orders
|
||||
<a href="/my/orders">Your Sales Orders
|
||||
<small class="ml8">
|
||||
<t t-if="order_count">
|
||||
<span class='badge'><t t-esc="order_count"/></span>
|
||||
@@ -41,6 +46,11 @@
|
||||
</small>
|
||||
</a>
|
||||
</h3>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="portal_my_home_invoice" name="Portal My Home : invoice entries" inherit_id="website_portal.portal_my_home" priority="30">
|
||||
<xpath expr="//div[contains(@class,'o_my_home_content')]" position="inside">
|
||||
<h3 t-if="invoice_count" class="page-header">
|
||||
<a href="/my/invoices">Your Invoices and Payments
|
||||
<small class="ml8">
|
||||
@@ -101,7 +111,7 @@
|
||||
<template id="portal_my_orders" name="My Sales Orders">
|
||||
<t t-call="website_portal.portal_layout">
|
||||
<div class="page-header">
|
||||
<span class="h3">Your Orders</span>
|
||||
<span class="h3">Your Sales Orders</span>
|
||||
<t t-call="website_portal.portal_searchbar"/>
|
||||
</div>
|
||||
<t t-if="not orders">
|
||||
@@ -219,7 +229,7 @@
|
||||
My Quotations
|
||||
</t>
|
||||
<t t-if="order.state != 'sent'">
|
||||
My Orders
|
||||
My Sales Orders
|
||||
</t>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
Reference in New Issue
Block a user