[FIX] misc fixes
bzr revid: fp@tinyerp.com-20140125221059-6e9m7g9hy3mic6o2
This commit is contained in:
parent
6af3677c0a
commit
f65b9b1022
|
@ -25,6 +25,7 @@ from openerp.addons.web.http import request
|
||||||
from openerp.addons.website.models import website
|
from openerp.addons.website.models import website
|
||||||
import werkzeug
|
import werkzeug
|
||||||
import datetime
|
import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
from openerp.tools.translate import _
|
from openerp.tools.translate import _
|
||||||
|
|
||||||
|
@ -37,16 +38,17 @@ class sale_quote(http.Controller):
|
||||||
# use SUPERUSER_ID allow to access/view order for public user
|
# use SUPERUSER_ID allow to access/view order for public user
|
||||||
# only if he knows the private token
|
# only if he knows the private token
|
||||||
order = request.registry.get('sale.order').browse(request.cr, token and SUPERUSER_ID or request.uid, order_id)
|
order = request.registry.get('sale.order').browse(request.cr, token and SUPERUSER_ID or request.uid, order_id)
|
||||||
if token and not message:
|
if token:
|
||||||
assert token == order.access_token, 'Access denied!'
|
assert token == order.access_token, 'Access denied!'
|
||||||
body=_('Quotation viewed by customer')
|
if not message:
|
||||||
self.__message_post(body, order_id, type='comment')
|
body=_('Quotation viewed by customer')
|
||||||
|
self.__message_post(body, order_id, type='comment')
|
||||||
days = 0
|
days = 0
|
||||||
if order.validity_date:
|
if order.validity_date:
|
||||||
days = (datetime.datetime.strptime(order.validity_date, '%Y-%m-%d') - datetime.datetime.now()).days + 1
|
days = (datetime.datetime.strptime(order.validity_date, '%Y-%m-%d') - datetime.datetime.now()).days + 1
|
||||||
values = {
|
values = {
|
||||||
'quotation': order,
|
'quotation': order,
|
||||||
'message': message,
|
'message': message and int(message) or False,
|
||||||
'option': bool(filter(lambda x: not x.line_id, order.options)),
|
'option': bool(filter(lambda x: not x.line_id, order.options)),
|
||||||
'order_valid': (not order.validity_date) or (datetime.datetime.now().strftime('%Y-%m-%d') <= order.validity_date),
|
'order_valid': (not order.validity_date) or (datetime.datetime.now().strftime('%Y-%m-%d') <= order.validity_date),
|
||||||
'days_valid': max(days, 0)
|
'days_valid': max(days, 0)
|
||||||
|
@ -133,14 +135,25 @@ class sale_quote(http.Controller):
|
||||||
assert token == order.access_token, 'Access denied, wrong token!'
|
assert token == order.access_token, 'Access denied, wrong token!'
|
||||||
option_obj = request.registry.get('sale.order.option')
|
option_obj = request.registry.get('sale.order.option')
|
||||||
option = option_obj.browse(request.cr, SUPERUSER_ID, option_id)
|
option = option_obj.browse(request.cr, SUPERUSER_ID, option_id)
|
||||||
|
|
||||||
|
res = request.registry.get('sale.order.line').product_id_change(request.cr, SUPERUSER_ID, order_id,
|
||||||
|
False, option.product_id.id, option.quantity, option.uom_id.id, option.quantity, option.uom_id.id,
|
||||||
|
option.name, order.partner_id.id, False, True, time.strftime('%Y-%m-%d'),
|
||||||
|
False, order.fiscal_position.id, True, request.context)
|
||||||
|
vals = res.get('value', {})
|
||||||
|
if 'tax_id' in vals:
|
||||||
|
vals['tax_id'] = [(6, 0, vals['tax_id'])]
|
||||||
|
|
||||||
vals.update({
|
vals.update({
|
||||||
'price_unit': option.price_unit,
|
'price_unit': option.price_unit,
|
||||||
'website_description': option.website_description,
|
'website_description': option.website_description,
|
||||||
'name': option.name,
|
'name': option.name,
|
||||||
'order_id': order.id,
|
'order_id': order.id,
|
||||||
'product_id' : option.product_id.id,
|
'product_id' : option.product_id.id,
|
||||||
|
'product_uos_qty': option.quantity,
|
||||||
|
'product_uos': option.uom_id.id,
|
||||||
'product_uom_qty': option.quantity,
|
'product_uom_qty': option.quantity,
|
||||||
'product_uom_id': option.uom_id.id,
|
'product_uom': option.uom_id.id,
|
||||||
'discount': option.discount,
|
'discount': option.discount,
|
||||||
})
|
})
|
||||||
line = request.registry.get('sale.order.line').create(request.cr, SUPERUSER_ID, vals, context=request.context)
|
line = request.registry.get('sale.order.line').create(request.cr, SUPERUSER_ID, vals, context=request.context)
|
||||||
|
|
|
@ -427,7 +427,6 @@
|
||||||
<field name="product_id" ref="product_product_quote_3"/>
|
<field name="product_id" ref="product_product_quote_3"/>
|
||||||
<field name="quantity">1</field>
|
<field name="quantity">1</field>
|
||||||
<field name="uom_id" ref="product.product_uom_unit"/>
|
<field name="uom_id" ref="product.product_uom_unit"/>
|
||||||
<field name="product_uom_id" ref="product.product_uom_unit"/>
|
|
||||||
<field name="price_unit">9000.00</field>
|
<field name="price_unit">9000.00</field>
|
||||||
<field name="discount">10</field>
|
<field name="discount">10</field>
|
||||||
<field name="website_description" type="html">
|
<field name="website_description" type="html">
|
||||||
|
|
|
@ -31,7 +31,7 @@ class sale_quote_template(osv.osv):
|
||||||
_description = "Sale Quotation Template"
|
_description = "Sale Quotation Template"
|
||||||
_columns = {
|
_columns = {
|
||||||
'name': fields.char('Quotation Template', size=256, required=True),
|
'name': fields.char('Quotation Template', size=256, required=True),
|
||||||
'website_description': fields.html('Description'),
|
'website_description': fields.html('Description', translate=True),
|
||||||
'quote_line': fields.one2many('sale.quote.line', 'quote_id', 'Quote Template Lines'),
|
'quote_line': fields.one2many('sale.quote.line', 'quote_id', 'Quote Template Lines'),
|
||||||
'note': fields.text('Terms and conditions'),
|
'note': fields.text('Terms and conditions'),
|
||||||
'options': fields.one2many('sale.quote.option', 'template_id', 'Optional Products Lines'),
|
'options': fields.one2many('sale.quote.option', 'template_id', 'Optional Products Lines'),
|
||||||
|
@ -49,9 +49,9 @@ class sale_quote_line(osv.osv):
|
||||||
_description = "Quotation Template Lines"
|
_description = "Quotation Template Lines"
|
||||||
_columns = {
|
_columns = {
|
||||||
'quote_id': fields.many2one('sale.quote.template', 'Quotation Template Reference', required=True, ondelete='cascade', select=True),
|
'quote_id': fields.many2one('sale.quote.template', 'Quotation Template Reference', required=True, ondelete='cascade', select=True),
|
||||||
'name': fields.text('Description', required=True),
|
'name': fields.text('Description', required=True, translate=True),
|
||||||
'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], change_default=True),
|
'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], required=True),
|
||||||
'website_description': fields.html('Line Description'),
|
'website_description': fields.html('Line Description', translate=True),
|
||||||
'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price')),
|
'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price')),
|
||||||
'discount': fields.float('Discount (%)', digits_compute= dp.get_precision('Discount')),
|
'discount': fields.float('Discount (%)', digits_compute= dp.get_precision('Discount')),
|
||||||
'product_uom_qty': fields.float('Quantity', required=True, digits_compute= dp.get_precision('Product UoS')),
|
'product_uom_qty': fields.float('Quantity', required=True, digits_compute= dp.get_precision('Product UoS')),
|
||||||
|
@ -66,6 +66,7 @@ class sale_quote_line(osv.osv):
|
||||||
product_obj = self.pool.get('product.product').browse(cr, uid, product, context=context)
|
product_obj = self.pool.get('product.product').browse(cr, uid, product, context=context)
|
||||||
vals.update({
|
vals.update({
|
||||||
'price_unit': product_obj.list_price,
|
'price_unit': product_obj.list_price,
|
||||||
|
'product_uom_id': product_obj.uom_id.id,
|
||||||
'website_description': product_obj.website_description,
|
'website_description': product_obj.website_description,
|
||||||
'name': product_obj.name,
|
'name': product_obj.name,
|
||||||
})
|
})
|
||||||
|
@ -120,11 +121,18 @@ class sale_order(osv.osv):
|
||||||
'url': '/quote/%s' % (quote.id)
|
'url': '/quote/%s' % (quote.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
def onchange_template_id(self, cr, uid, ids, template_id, context=None):
|
def onchange_template_id(self, cr, uid, ids, template_id, partner=False, fiscal_position=False, context=None):
|
||||||
lines = []
|
lines = []
|
||||||
quote_template = self.pool.get('sale.quote.template').browse(cr, uid, template_id, context=context)
|
quote_template = self.pool.get('sale.quote.template').browse(cr, uid, template_id, context=context)
|
||||||
for line in quote_template.quote_line:
|
for line in quote_template.quote_line:
|
||||||
lines.append((0, 0, {
|
res = self.pool.get('sale.order.line').product_id_change(cr, uid, False,
|
||||||
|
False, line.product_id.id, line.product_uom_qty, line.product_uom_id.id, line.product_uom_qty,
|
||||||
|
line.product_uom_id.id, line.name, partner, False, True, time.strftime('%Y-%m-%d'),
|
||||||
|
False, fiscal_position, True, context)
|
||||||
|
data = res.get('value', {})
|
||||||
|
if 'tax_id' in data:
|
||||||
|
data['tax_id'] = [(6, 0, data['tax_id'])]
|
||||||
|
data.update({
|
||||||
'name': line.name,
|
'name': line.name,
|
||||||
'price_unit': line.price_unit,
|
'price_unit': line.price_unit,
|
||||||
'discount': line.discount,
|
'discount': line.discount,
|
||||||
|
@ -133,7 +141,8 @@ class sale_order(osv.osv):
|
||||||
'product_uom': line.product_uom_id.id,
|
'product_uom': line.product_uom_id.id,
|
||||||
'website_description': line.website_description,
|
'website_description': line.website_description,
|
||||||
'state': 'draft',
|
'state': 'draft',
|
||||||
}))
|
})
|
||||||
|
lines.append((0, 0, data))
|
||||||
options = []
|
options = []
|
||||||
for option in quote_template.options:
|
for option in quote_template.options:
|
||||||
options.append((0, 0, {
|
options.append((0, 0, {
|
||||||
|
@ -167,7 +176,7 @@ class sale_quote_option(osv.osv):
|
||||||
_columns = {
|
_columns = {
|
||||||
'template_id': fields.many2one('sale.quote.template', 'Quotation Template Reference', ondelete='cascade', select=True, required=True),
|
'template_id': fields.many2one('sale.quote.template', 'Quotation Template Reference', ondelete='cascade', select=True, required=True),
|
||||||
'name': fields.text('Description', required=True, translate=True),
|
'name': fields.text('Description', required=True, translate=True),
|
||||||
'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)]),
|
'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], required=True),
|
||||||
'website_description': fields.html('Option Description', translate=True),
|
'website_description': fields.html('Option Description', translate=True),
|
||||||
'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price')),
|
'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price')),
|
||||||
'discount': fields.float('Discount (%)', digits_compute= dp.get_precision('Discount')),
|
'discount': fields.float('Discount (%)', digits_compute= dp.get_precision('Discount')),
|
||||||
|
|
|
@ -6,11 +6,12 @@
|
||||||
<h1 class="page-header">Pricing</h1>
|
<h1 class="page-header">Pricing</h1>
|
||||||
</section>
|
</section>
|
||||||
<section id="quote">
|
<section id="quote">
|
||||||
<table class="table table-hover">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Products</th>
|
<th>Products</th>
|
||||||
<th>Quantity</th>
|
<th>Quantity</th>
|
||||||
|
<th>Taxes</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th class="text-right">Unit Price</th>
|
<th class="text-right">Unit Price</th>
|
||||||
<th class="text-right">Price</th>
|
<th class="text-right">Price</th>
|
||||||
|
@ -27,6 +28,11 @@
|
||||||
<span t-field="line.product_uom"/>
|
<span t-field="line.product_uom"/>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<div t-foreach="line.tax_id" t-as="tax">
|
||||||
|
<t t-esc="tax.name"/>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<strong t-if="line.discount" class="text-info">
|
<strong t-if="line.discount" class="text-info">
|
||||||
<t t-esc="((line.discount % 1) and '%s' or '%d') % line.discount"/>% discount
|
<t t-esc="((line.discount % 1) and '%s' or '%d') % line.discount"/>% discount
|
||||||
|
@ -56,14 +62,24 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td></td><td></td><td></td><td></td>
|
||||||
<td></td>
|
<td class="text-right"><strong>Subtotal:</strong></td>
|
||||||
<td></td>
|
<td class="text-right">
|
||||||
<td><h3 class="text-right">Total</h3></td>
|
<strong data-id="total_amount" t-field="quotation.amount_untaxed" t-field-options='{"widget": "monetary","display_currency": "quotation.pricelist_id.currency_id"}'/>
|
||||||
<td class="text-right" colspan="2">
|
</td>
|
||||||
<h3>
|
</tr>
|
||||||
<strong data-id="total_amount" t-field="quotation.amount_total" t-field-options='{"widget": "monetary","display_currency": "quotation.pricelist_id.currency_id"}'/>
|
<tr>
|
||||||
</h3>
|
<td></td><td></td><td></td><td></td>
|
||||||
|
<td class="text-right">Taxes:</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<span data-id="total_amount" t-field="quotation.amount_tax" t-field-options='{"widget": "monetary","display_currency": "quotation.pricelist_id.currency_id"}'/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td><td></td><td></td><td></td>
|
||||||
|
<td class="text-right"><strong>Total:</strong></td>
|
||||||
|
<td class="text-right">
|
||||||
|
<strong data-id="total_amount" t-field="quotation.amount_total" t-field-options='{"widget": "monetary","display_currency": "quotation.pricelist_id.currency_id"}'/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -158,11 +174,11 @@
|
||||||
<a t-if="order_valid" class="btn btn-primary btn-block fa fa-check" data-toggle="modal" data-target="#modelaccept">
|
<a t-if="order_valid" class="btn btn-primary btn-block fa fa-check" data-toggle="modal" data-target="#modelaccept">
|
||||||
Accept Order
|
Accept Order
|
||||||
</a>
|
</a>
|
||||||
<a t-if="not order_valid">
|
<a t-if="not order_valid" href="#discussion" class="btn btn-info btn-block">
|
||||||
<strong>This offer expired!.</strong>
|
<strong>This offer expired!</strong><br/>
|
||||||
<br/><a href="#discussion">Contact us</a> for new quote.
|
Contact us for new quote.
|
||||||
</a>
|
</a>
|
||||||
<div class="mt8">
|
<div class="mt8" t-if="order_valid">
|
||||||
<a type="submit" href="#discussion">
|
<a type="submit" href="#discussion">
|
||||||
Ask Changes
|
Ask Changes
|
||||||
</a> or
|
</a> or
|
||||||
|
@ -203,6 +219,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
|
<p>Message <t t-esc="message"/></p>
|
||||||
<div class="alert alert-success alert-dismissable" t-if="message==1">
|
<div class="alert alert-success alert-dismissable" t-if="message==1">
|
||||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
|
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
|
||||||
Your message has been successfully sent!
|
Your message has been successfully sent!
|
||||||
|
@ -450,26 +467,51 @@
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<div class="bs-sidebar">
|
<div class="bs-sidebar">
|
||||||
<div class="hidden-print navspy" role="complementary">
|
<div class="hidden-print navspy" role="complementary">
|
||||||
<ul class="nav bs-sidenav" data-id="quote_sidebar">
|
<ul class="nav bs-sidenav" data-id="quote_sidebar"/>
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
<div id="template_introduction" t-field="template.website_description"/>
|
<div class="alert alert-info alert-dismissable" t-ignore="True">
|
||||||
<t t-foreach="template.quote_line" t-as="line">
|
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
|
||||||
<a t-att-id="line.id"/>
|
<p>
|
||||||
<div t-field="line.website_description"/>
|
<strong>Template Header:</strong> this content
|
||||||
</t>
|
will appear on all quotations using this
|
||||||
<div class="oe_structure"/>
|
template.
|
||||||
<section id="terms" class="container" t-if="template.note">
|
</p>
|
||||||
<h2 class="page-header">Terms & Conditions</h2>
|
<p class="text-muted">
|
||||||
<p t-field="template.note"/>
|
Titles with style <i>Heading 1</i> and
|
||||||
</section>
|
<i>Heading 2</i> will be used to generate the
|
||||||
|
table of content automatically.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="template_introduction" t-field="template.website_description"/>
|
||||||
|
<t t-foreach="template.quote_line" t-as="line">
|
||||||
|
<div class="alert alert-info alert-dismissable" t-ignore="True">
|
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
|
||||||
|
Product: <strong t-esc="line.product_id.name"/>:
|
||||||
|
this content will appear on the quotation only if this
|
||||||
|
product is put on the quote.
|
||||||
|
</div>
|
||||||
|
<div t-field="line.website_description"/>
|
||||||
|
</t>
|
||||||
|
<t t-foreach="template.options" t-as="option_line">
|
||||||
|
<div class="alert alert-info alert-dismissable" t-ignore="True">
|
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
|
||||||
|
Optional Product: <strong t-esc="option_line.product_id.name"/>:
|
||||||
|
this content will appear on the quotation only if this
|
||||||
|
product is used in the quote.
|
||||||
|
</div>
|
||||||
|
<div t-field="option_line.website_description"/>
|
||||||
|
</t>
|
||||||
|
<section id="terms" class="container" t-if="template.note">
|
||||||
|
<h1 class="page-header" t-ignore="True">Terms & Conditions</h1>
|
||||||
|
<p t-field="template.note"/>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</div>
|
||||||
|
</body>
|
||||||
</t>
|
</t>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<field name="product_id" on_change="on_change_product_id(product_id)"/>
|
<field name="product_id" on_change="on_change_product_id(product_id)"/>
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="quantity"/>
|
<field name="quantity"/>
|
||||||
<field name="uom_id"/>
|
<field name="uom_id" groups="product.group_uom"/>
|
||||||
<field name="price_unit"/>
|
<field name="price_unit"/>
|
||||||
<field name="discount" groups="sale.group_discount_per_so_line"/>
|
<field name="discount" groups="sale.group_discount_per_so_line"/>
|
||||||
<field name="website_description" invisible="1"/>
|
<field name="website_description" invisible="1"/>
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
</page>
|
</page>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='client_order_ref']" position="after">
|
<xpath expr="//field[@name='client_order_ref']" position="after">
|
||||||
<field name="template_id" on_change="onchange_template_id(template_id)"/>
|
<field name="template_id" on_change="onchange_template_id(template_id, partner_id, fiscal_position)"/>
|
||||||
<field name="validity_date"/>
|
<field name="validity_date"/>
|
||||||
<field name="website_description" invisible="1"/>
|
<field name="website_description" invisible="1"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
@ -88,6 +88,8 @@
|
||||||
<field name="product_id" on_change="on_change_product_id(product_id)"/>
|
<field name="product_id" on_change="on_change_product_id(product_id)"/>
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="product_uom_qty"/>
|
<field name="product_uom_qty"/>
|
||||||
|
<field name="product_uom_id" groups="product.group_uom"/>
|
||||||
|
<field name="discount" groups="sale.group_discount_per_so_line"/>
|
||||||
<field name="price_unit"/>
|
<field name="price_unit"/>
|
||||||
<field name="website_description" invisible="1"/>
|
<field name="website_description" invisible="1"/>
|
||||||
</tree>
|
</tree>
|
||||||
|
@ -99,7 +101,7 @@
|
||||||
<field name="product_id" on_change="on_change_product_id(product_id)"/>
|
<field name="product_id" on_change="on_change_product_id(product_id)"/>
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="quantity"/>
|
<field name="quantity"/>
|
||||||
<field name="uom_id"/>
|
<field name="uom_id" groups="product.group_uom"/>
|
||||||
<field name="price_unit"/>
|
<field name="price_unit"/>
|
||||||
<field name="discount" groups="sale.group_discount_per_so_line"/>
|
<field name="discount" groups="sale.group_discount_per_so_line"/>
|
||||||
<field name="website_description" invisible="1"/>
|
<field name="website_description" invisible="1"/>
|
||||||
|
@ -124,7 +126,7 @@
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="action_sale_quotation_template" model="ir.actions.act_window">
|
<record id="action_sale_quotation_template" model="ir.actions.act_window">
|
||||||
<field name="name">Sales Template</field>
|
<field name="name">Quotation Templates</field>
|
||||||
<field name="type">ir.actions.act_window</field>
|
<field name="type">ir.actions.act_window</field>
|
||||||
<field name="res_model">sale.quote.template</field>
|
<field name="res_model">sale.quote.template</field>
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
|
|
Loading…
Reference in New Issue