[IMP] sale_timesheet: adapt timesheet sections in portal
Prior to this commit:
* The portal timesheet section was only visible in invoices when
invoicing policy was set to delivery.
* The portal timesheet section was not visible on orders.
After this commit:
* The portal timesheet section will be displayed for both delivery and order invoicing
policies.
* The portal timesheet section will be available for orders.
task-2388500
closes odoo/odoo#62516
Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
This commit is contained in:
@@ -33,6 +33,39 @@ class CustomerPortal(portal.CustomerPortal):
|
||||
|
||||
return values
|
||||
|
||||
def _order_get_page_view_values(self, order, access_token, **kwargs):
|
||||
values = {
|
||||
'sale_order': order,
|
||||
'token': access_token,
|
||||
'return_url': '/shop/payment/validate',
|
||||
'bootstrap_formatting': True,
|
||||
'partner_id': order.partner_id.id,
|
||||
'report_type': 'html',
|
||||
'action': order._get_portal_return_action(),
|
||||
}
|
||||
if order.company_id:
|
||||
values['res_company'] = order.company_id
|
||||
|
||||
if order.has_to_be_paid():
|
||||
domain = expression.AND([
|
||||
['&', ('state', 'in', ['enabled', 'test']), ('company_id', '=', order.company_id.id)],
|
||||
['|', ('country_ids', '=', False), ('country_ids', 'in', [order.partner_id.country_id.id])]
|
||||
])
|
||||
acquirers = request.env['payment.acquirer'].sudo().search(domain)
|
||||
|
||||
values['acquirers'] = acquirers.filtered(lambda acq: (acq.payment_flow == 'form' and acq.view_template_id) or
|
||||
(acq.payment_flow == 's2s' and acq.registration_view_template_id))
|
||||
values['pms'] = request.env['payment.token'].search([('partner_id', '=', order.partner_id.id)])
|
||||
values['acq_extra_fees'] = acquirers.get_acquirer_extra_fees(order.amount_total, order.currency_id, order.partner_id.country_id.id)
|
||||
|
||||
if order.state in ('draft', 'sent', 'cancel'):
|
||||
history = request.session.get('my_quotations_history', [])
|
||||
else:
|
||||
history = request.session.get('my_orders_history', [])
|
||||
values.update(get_records_pager(history, order))
|
||||
|
||||
return values
|
||||
|
||||
#
|
||||
# Quotations and Sales Orders
|
||||
#
|
||||
@@ -166,36 +199,8 @@ class CustomerPortal(portal.CustomerPortal):
|
||||
partner_ids=order_sudo.user_id.sudo().partner_id.ids,
|
||||
)
|
||||
|
||||
values = {
|
||||
'sale_order': order_sudo,
|
||||
'message': message,
|
||||
'token': access_token,
|
||||
'return_url': '/shop/payment/validate',
|
||||
'bootstrap_formatting': True,
|
||||
'partner_id': order_sudo.partner_id.id,
|
||||
'report_type': 'html',
|
||||
'action': order_sudo._get_portal_return_action(),
|
||||
}
|
||||
if order_sudo.company_id:
|
||||
values['res_company'] = order_sudo.company_id
|
||||
|
||||
if order_sudo.has_to_be_paid():
|
||||
domain = expression.AND([
|
||||
['&', ('state', 'in', ['enabled', 'test']), ('company_id', '=', order_sudo.company_id.id)],
|
||||
['|', ('country_ids', '=', False), ('country_ids', 'in', [order_sudo.partner_id.country_id.id])]
|
||||
])
|
||||
acquirers = request.env['payment.acquirer'].sudo().search(domain)
|
||||
|
||||
values['acquirers'] = acquirers.filtered(lambda acq: (acq.payment_flow == 'form' and acq.view_template_id) or
|
||||
(acq.payment_flow == 's2s' and acq.registration_view_template_id))
|
||||
values['pms'] = request.env['payment.token'].search([('partner_id', '=', order_sudo.partner_id.id)])
|
||||
values['acq_extra_fees'] = acquirers.get_acquirer_extra_fees(order_sudo.amount_total, order_sudo.currency_id, order_sudo.partner_id.country_id.id)
|
||||
|
||||
if order_sudo.state in ('draft', 'sent', 'cancel'):
|
||||
history = request.session.get('my_quotations_history', [])
|
||||
else:
|
||||
history = request.session.get('my_orders_history', [])
|
||||
values.update(get_records_pager(history, order_sudo))
|
||||
values = self._order_get_page_view_values(order_sudo, access_token, **kw)
|
||||
values['message'] = message
|
||||
|
||||
return request.render('sale.sale_order_portal_template', values)
|
||||
|
||||
|
||||
@@ -12,7 +12,29 @@ class PortalAccount(portal.PortalAccount):
|
||||
def _invoice_get_page_view_values(self, invoice, access_token, **kwargs):
|
||||
values = super(PortalAccount, self)._invoice_get_page_view_values(invoice, access_token, **kwargs)
|
||||
domain = request.env['account.analytic.line']._timesheet_get_portal_domain()
|
||||
domain = expression.AND([domain, [('timesheet_invoice_id', '=', invoice.id)]])
|
||||
domain = expression.AND([
|
||||
domain,
|
||||
request.env['account.analytic.line']._timesheet_get_sale_domain(
|
||||
invoice.mapped('line_ids.sale_line_ids'),
|
||||
request.env['account.move'].browse([invoice.id])
|
||||
)
|
||||
])
|
||||
values['timesheets'] = request.env['account.analytic.line'].sudo().search(domain)
|
||||
values['is_uom_day'] = request.env['account.analytic.line'].sudo()._is_timesheet_encode_uom_day()
|
||||
return values
|
||||
|
||||
|
||||
class CustomerPortal(portal.CustomerPortal):
|
||||
def _order_get_page_view_values(self, order, access_token, **kwargs):
|
||||
values = super(CustomerPortal, self)._order_get_page_view_values(order, access_token, **kwargs)
|
||||
domain = request.env['account.analytic.line']._timesheet_get_portal_domain()
|
||||
domain = expression.AND([
|
||||
domain,
|
||||
request.env['account.analytic.line']._timesheet_get_sale_domain(
|
||||
order.mapped('order_line'),
|
||||
order.invoice_ids
|
||||
)
|
||||
])
|
||||
values['timesheets'] = request.env['account.analytic.line'].sudo().search(domain)
|
||||
values['is_uom_day'] = request.env['account.analytic.line'].sudo()._is_timesheet_encode_uom_day()
|
||||
return values
|
||||
|
||||
@@ -131,7 +131,20 @@ class AccountAnalyticLine(models.Model):
|
||||
thus there is no meaning of showing invoice with ordered quantity.
|
||||
"""
|
||||
domain = super(AccountAnalyticLine, self)._timesheet_get_portal_domain()
|
||||
return expression.AND([domain, [('timesheet_invoice_type', 'in', ['billable_time', 'non_billable'])]])
|
||||
return expression.AND([domain, [('timesheet_invoice_type', 'in', ['billable_time', 'non_billable', 'billable_fixed'])]])
|
||||
|
||||
@api.model
|
||||
def _timesheet_get_sale_domain(self, order_lines_ids, invoice_ids):
|
||||
return [
|
||||
'|',
|
||||
'&',
|
||||
('timesheet_invoice_id', 'in', invoice_ids.ids),
|
||||
#TODO Question to Reviewer: non_billable was part of domain in _timesheet_get_portal_domain so I kept it here, does this make sense?
|
||||
('timesheet_invoice_type', 'in', ['billable_time', 'non_billable']),
|
||||
'&',
|
||||
('timesheet_invoice_type', '=', 'billable_fixed'),
|
||||
('so_line', 'in', order_lines_ids.ids)
|
||||
]
|
||||
|
||||
def _get_timesheets_to_merge(self):
|
||||
res = super(AccountAnalyticLine, self)._get_timesheets_to_merge()
|
||||
|
||||
@@ -59,4 +59,56 @@
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="sale_order_portal_template_inherit" inherit_id="sale.sale_order_portal_template">
|
||||
<xpath expr="//t[@t-call='portal.portal_record_sidebar']//div[hasclass('o_download_pdf')]" position="after">
|
||||
<li t-if="timesheets" class="list-group-item flex-grow-1" >
|
||||
<a href="#accordion">Timesheets</a>
|
||||
</li>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//div[@id='sale_order_communication']" position="before">
|
||||
<div t-if="timesheets" class="container">
|
||||
<div id="accordion" class="o_timesheet_accordion mt-4">
|
||||
<div class="card mb-0">
|
||||
<div class="card-header">
|
||||
<h5 class="mb0">
|
||||
<a class="card-title" data-toggle="collapse" href="#collapseTimesheet">
|
||||
Timesheets
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
<div id="collapseTimesheet" class="card-body show" data-parent="#accordion">
|
||||
<t t-set="nr_tasks" t-value="len(timesheets.mapped('task_id'))"/>
|
||||
<t t-set="nr_projects" t-value="len(timesheets.mapped('project_id'))"/>
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Employee</th>
|
||||
<th t-if="nr_projects > 1">Project</th>
|
||||
<th t-if="nr_tasks > 0">Task</th>
|
||||
<th>Description</th>
|
||||
<th t-if="timesheets[0]._is_timesheet_encode_uom_day()" class="text-right">Duration (days)</th>
|
||||
<th t-else="" class="text-right">Duration (hours)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr t-foreach="timesheets" t-as="timesheet">
|
||||
<td><t t-esc="timesheet.date" t-options='{"widget": "date"}'/></td>
|
||||
<td><t t-esc="timesheet.employee_id.name"/></td>
|
||||
<td t-if="nr_projects > 1"><span t-field="timesheet.project_id"/></td>
|
||||
<td t-if="nr_tasks > 0"><span t-field="timesheet.task_id"/></td>
|
||||
<td><t t-esc="timesheet.name"/></td>
|
||||
<td class="text-right">
|
||||
<span t-if="timesheet._is_timesheet_encode_uom_day()" t-esc="timesheet._get_timesheet_time_day()" t-options='{"widget": "timesheet_uom"}'/>
|
||||
<span t-else="" t-field="timesheet.unit_amount" t-options='{"widget": "float_time"}'/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user