How Can I parse and convert Dates & Times? - datetime

I met Odoo for first time a little less than a month ago, and, a DOS Systems Analyst from the 90's, am helping to implement it for a small but fast growing local manufacturer. Largely out of the industry the last 15 years, I'm not pro, but fast learning Python, HTML (some past exper) and Java...
I've been through the Developer Docs, as well as the Developer's Cookbook & Essentials, and a variety of online tutorials (just about everything Google could come up with from several search word combinations).
I read the closed post asking about this and get that there's apparently no actual Odoo reference...
Can someone please tell me where I might find 'date/time' parse and conversion functions that I can access from a report that is using the 'canned' 'hr.employee' model?
<?xml version="1.0"?>
<t t-name="hr_attendance.report_attendancelog">
<t t-call="report.html_container">
<t t-call="report.external_layout">
<div class="page">
<div class="oe_structure"/>
<div class="row">
<div class="col-xs-6">
<h2><br/>Attendance Log: </h2>
</div>
</div>
<table class="table table-condensed mt32">
<thead>
<th><strong>Date / Time</strong></th>
<th><strong> Operation</strong></th>
</thead>
<tbody>
<t t-foreach="docs" t-as="o">
<t t-set="DspDate" t-value="o.name"/>
<t t-set="DspTime" t-value="o.name"/>
<!-- I want to parse 'o.name', which is 'date time' format (from
Attendance record) to separate 'Date' and 'Time' fields... -->
<!-- t t-set="DspDate" t-value="FUNC?(o.name)"/ -->
<!-- t t-set="DspTime" t-value="FUNC?(o.name)"/ -->
<!-- and do calcs with date & time...) -->
<!-- t t-set="ClcDt1" t-value="FUNC?('PrvDt')"/ -->
<!-- t t-set="ClcDt2" t-value="FUNC?(DspDate)"/ -->
<!-- t t-set="ClcTm1" t-value="FUNC?('PrvTm')"/ -->
<!-- t t-set="ClcTm2" t-value="FUNC?(DspTime)"/ -->
<tr>
<t t-if="ClcDt1 == ClcDt2">
<td><span t-esc="DspDate"/></td>
<td><span t-esc="DspTime"/></td>
<td><span t-esc="o.action"/></td>
</t
</tr>
</t>
<tr class="border-black">
<td colspan="3"><strong>Total period</strong></td>
<td><strong t-esc="o.worked_hours"/></td -->
</tr>
</tbody>
</table>
</div>
</t>
</t>
</t>
I prefixed 'br/' to H2 to keep the it properly positioned (only shows on the first page), but subsequent pages obscure the col headers behind the layout header... this is probably obvious and I'm oblivious, but how can I adjust the 'H2' position to be below the 'header' setup by 'report.external_layout'??

On QWeb rendering you can use the python libs time, datetime and relativedelta. They are included in the so called QWeb context which is used for the evaluation (python eval) of the code in reports. You can see this in Odoo code here.
That means you do something like this:
<t t-set="DspDate" t-value="datetime.datetime.strptime(o.name, '%Y-%m-%d %H:%M:%S').strftime('%d/%m/%Y')" />
I prefer parser classes for reports to define some common functions. For example: you can define the function getDate doing your parsing or transforming stuff. The report code will be much better with parser functions:
<t t-set="DspDate" t-value="getDate(o.name)" />

Related

How to effectively use foreach in laravel-livewire in a table when the data dynamically changes?

I've been struggling with the foreach blade directive when combined with Laravel-Livewire and dynamically changing data rendered in an html table.
Specific scenario:
A table is presented to the user
Each row of the table includes a clickable icon that toggles a variable associated with that row
Toggling this icon visibly removes the row from the table
It sounds simple enough but I cannot get it to work correctly.
<?php
namespace App\Http\Livewire;
use Livewire\Component;
class UnreconciledServices extends Component
{
public $services;
public function render()
{
return view('livewire.unreconciled-services');
}
//
// receives an invoice number and changes the "reconciled" value for the $aaaServices record to true
//
public function test($a)
{
}
}
This is pretty simple - there is a function called "test" that accepts a variable (the function does nothing at all) and there is a collection of records called $services that has been passed in from the parent.
The blade file is as follows:
<div>
<table style="width:100%">
<thead>
<tr>
<th width="5%">
</th>
<th>
Service Date
</th>
<th>
Call #
</th>
<th>
Payment
</th>
</tr>
</thead>
<tbody>
#foreach ($services as $service)
{{-- <span wire:key="{{$service->invoice_number}}"> --}}
{{-- #if ($service->reconciled == false) --}}
<tr id="{{$service->invoice_number}}" class="text-center" style="{{ $service->reconciled == 1 ? 'display:none' : 'show'}}">
<td>
{{-- <input class="m-2" type="checkbox"> --}}
{{-- <span class="m-2"><i class="far fa-trash-alt text-red-500"></i></span> --}}
<span wire:key="{{$service->invoice_number}}" wire:click="test('{{$service->invoice_number}}')" class="m-2 cursor-pointer"><i class="lm-2 rm-2 fas fa-plus text-green-500"></i></span>
</td>
<td>
{{$service->invoice_date}}
</td>
<td>
{{$service->call_number}}
</td>
<td>
{{$service->service_price}}
</td>
</tr>
{{-- #endif --}}
{{-- </span> --}}
#endforeach
</tbody>
</table>
</div>
This is a simple table with 4 columns. The 1st is a clickable icon, the other 3 are data. Rows should only be visible if the "reconciled" boolean is 0.
When initially rendered, the appropriate records are displayed (there are 2 that qualify and 177 that do not). However, as soon as I click the icon - even when the function it links to has zero actual content - all records are suddenly visible except for the one that I've just hidden.
Looking at the html change when I click the icon, the tag changes from
<tr id="21031251674" class="text-center" style="display:none">
to
<tr id="21031253205" class="text-center" style="show">.
I have tried using livewire keys, though I'm not sure how this would affect the functionality since I'm not rendering components, but rather data driven html.
Surely someone else has encountered this and overcome. Please point me in the right direction.
Sorry for the delay. This whole thread should be deleted as bone-headed. But it was written in earnest...
When all was said and done, I was unclear on the differences b/w an html / ajax - based approach such as livewire and something more javascript-based such as Vue. I was expecting functionality that didn't exist in the framework as I was attempting to use it.
To solve the problem, I moved towards individual row items being components in and of themselves, using emit to communicate between components and adjust the backend data source.
Once I embraced the html / ajax approach and stopped trying to force / expect javascript functionality & dom manipulation then all was well. Just a mindset difference from what I am familiar with, and it took me some time to turn the corner.

Thymeleaf - How to dynamically add a tooltip to a table cell upon a condition

We are building a dynamic table with Thymeleaf.
Some cells, of null values, are holding "-" sign, while cells with values hold some data from the object:
<td th:text="${person.getAddress()} ? ${person.getAddress().getCity() : '-'"}
So the current state is this:
<table border=1>
<tr><td>John</td><td>London</td></tr>
<tr><td>Paul</td><td>-</td></tr>
</table>
Now, we like to add a tooltip, that when hovering the relevant table cell, more data can be seen (e.g. the person's full address).
We found this CSS example for tooltip and we figure out our final result should be something like that:
<td class="tooltip">London
<div class="tooltiptext">
<div>Street: Green</div>
<div>Number: 123</div>
</div>
</td>
But when trying to implement it in Thymeleaf we got stuck.
This is what we tried:
<div th:switch="${person.getAddress()}">
<div th:case="null"><td>-</td></div>
<div th:case="*">
<td> // now what? how to both inject value and the sub divs? </td>
</div>
</div>
Another option we thought of is to create by concatenation the full HTML within a td th:text=...
But both of the ways seems very cumbersome.
You can use the safe navigation operator in combination with the elvis operator instead of your null check.
No need for switch or any logic like this. Create a couple extra tags and move your logic deeper into the html.
Don't use .getAddress(), you can just use .address for properties with correctly named getters/setters.
For example:
<td>
<span th:text="${person.address?.city} ?: '-'" />
<div th:unless="${person.address == null}" class="tooltiptext">
<div>Street: Green</div>
<div>Number: 123</div>
</div>
</td>
Without all the fancy stuff, you could also simply do something like this:
<td th:if="${person.address == null}">-</td>
<td th:if="${person.address != null}">
<span th:text="${person.address.city}" />
<div class="tooltiptext">
<div>Street: Green</div>
<div>Number: 123</div>
</div>
</td>

Page Break Issue On Custom Cheque Page in Odoo 10 Qweb PDF Report

I have faced some of the issue page break in Qweb report in last few days.
I am trying to print my Qweb report for Cheque format and single Qweb page divided into 3 different sections
Section 1 : Displaying invoice list details
Section 2 : Bank details and amount which we will pay to our owner/tenant in MICR FONT 13B FONT
Section 3 : Displaying invoice list details
section 1 & 3 are the common and displaying the same invoices details into both of the sections and about the section2 will change as per the
different amount which we will pay to owner/tenant.
Expected Result :
I have 23 invoice details are attached it into single cheque then I want to bifurcate my invoice details into different slot
sloat 1 : Display first 10 invoice details into first page
sloat 2 : Display next 10 invoice details into second page
sloat 3 : Display remaning 3 invoice details into third page
I want to bifurcate my invoice details into different sloat wise page if the total number of invoices are more than 10 lines
What I have tried from my side ?
Attempt 1 : Using counter variable and update the counter through iterating the loop and break it when 10 is reach divide by 0
Applied this code inside into loping
<t t-set="count" t-value="count+1" />
<t t-if="count%10== 0">
<div style="page-break-after:auto;"/>
</t>
</t>
Attempt 2:
<span t-esc="line_index+1"/>
<t t-if="line_index+1%10 ==0">
<div style="page-break-inside:auto !important;">
</t>
I think you need to provide a little bit more of the context and info, perhaps the problem is other than that one are you exposing.
have you tried using a custom python report?
class IncrementReports(models.AbstractModel):
_name = 'report.your_model.your_report_name'
#api.model
def render_html(self, docids, data=None):
report_obj = self.env['report']
report = report_obj._get_report_from_name(
'your_model.your_report_name')
docs = []
objects = self.env[report.model].browse(docids)
for o in objects:
docs.append({...})
docargs = {
'doc_ids': docids,
'doc_model': report.model,
'docs': objects,
'custom_docs': docs,
'datetime': datetime.datetime,
}
return report_obj.render('your_model.your_report_name', docargs)
Using a custom report you can create a function to divide the 3 blocks of information, sending this in different variable inside de docargs and iterating over them without check the condition in the XML report.
I don't know if I'm clear, my English is not good enough.
I have also fixed the issue from my end after tried a lot more attempts.
If any one will face the same issue in your future development
so that they can also able to fix it quickly.
Create a method into specific model : (cheque.cheque model)
def get_invoice_details(self, invoice_ids,cheque):
vals,container,result,val=[],[],[],1
invoice_no=''
for line in invoice_ids:
desc=''
if line.is_vendor:
invoice_no=line.vendor_reference
else:
invoice_no=line.number
pay_amt=line.payment_ids.filtered(lambda m: m.cheque_issued_id.id ==cheque.id).amount or 0.00
for l in line.invoice_line_ids:
desc+=str(l.product_id.default_code)+',' or ''
vals.append({
'date':str(line.date_invoice),
'invoice_no':invoice_no,
'inv_amt':str(line.amount_total),
'description':desc,
'pay_amt':float(pay_amt)
})
invoice_no=''
for l in vals:
if val<=len(vals):
container.append(l)
if val % 9 == 0:
result.append({'section':9,'vals':container})
container=[]
val+=1
if container:
result.append({'section':4,'vals':container})
return result
In this method I have set section key with its value into result of list for dictionary
where we are using the same section to making the page break perfectly
Call the same method and iterate it into Qweb Template
<t t-foreach="o.get_invoice_details(o.invoice_ids,o)" t-as="line" >
<div class="page">
<div class="col-xs-12">
<table style="width:100%">
<thead>
<tr>
<th>Invoice Date</th>
<th>Invoice # </th>
<th>Invoice Amt</th>
<th>Description </th>
<th style="text-align:right">Payment Amt</th>
</tr>
</thead>
<t t-foreach="line.get('vals')" t-as="inv">
<tbody class="sale_tbody">
<tr>
<td>
<span t-esc="inv.get('date')" />
</td>
<td>
<span t-esc="inv.get('invoice_no')" />
</td>
<td>
<span t-esc="o.decimal_formated_amount(float(inv.get('inv_amt',0.00)))" />
</td>
<td>
<span t-esc="inv.get('description')" />
</td>
<td style="text-align:right">
<span t-esc="o.decimal_formated_amount2(float(inv.get('pay_amt',0.00)))" />
</td>
</tr>
</tbody>
</t>
</table>
</div>
<span t-if="line.get('section') % 9 == 0" style="page-break-after: always;">
</span>
</div>
As per the business logic of get_invoice_details() method
which is return the data in list form and then we can user the same and render it into the XML template.
Odoo will manage the page break automatically when the condition will satisfy over the XML template
system will automatically bifurcate the page according to source code
I hope my answer may helpful for you :)

Conditinal Formatting Odoo form view

I want to achieve the below scenario. Have tried in many ways but no luck.
<t t-if="q1_percent > 75">
<td style="background-color:#52be80"><field name="q1_percent" nolabel="1"/></td>
</t>
<t t-elif="'q1_percent' > 50 and 'q1_percent' < 75">
<td class="td_act" style="background-color:#f4d03f"><field name="q1_percent" nolabel="1"/></td>
</t>
<t t-elif="'q1_percent' < 50">
<td class="td_act" style="background-color:#e74c3c"><field name="q1_percent" nolabel="1"/></td>
</t>
I am using odoo 10. And the above code is for form view.
How can I achieve this? Any ideas any help is most appreciated. Thanks!
Until Odoo 12 There is a difference between a regular view(tree, form, etc) and a QWeb view meaning that regular views cannot be mixed with QWeb content to be evaluated as for Reports and Website Pages.
You still could be able to acquire what you are looking for by simply defining a computed HTML Field that will contains the HTML result of evaluate that QWeb code or directly build the HTML without using QWeb at all. Or without QWeb just generating the HTML by yourself.
For example:
from lxml import etree
q1_percent_html = fields.HTML("Q1 Percent HTML", compute='_compute_q1_percent_html')
#api.depends('q1_percent')
def _compute_q1_percent_html(self):
for elem in self:
# QWeb version
t = etree.fromstring("""
<div>
<t t-if="q1_percent > 75">
<td style="background-color:#52be80"><t t-esc="q1_percent"/></td>
</t>
<t t-elif="'q1_percent' > 50 and 'q1_percent' < 75">
<td class="td_act" style="background-color:#f4d03f"><t t-esc="q1_percent"/></td>
</t>
<t t-elif="'q1_percent' < 50">
<td class="td_act" style="background-color:#e74c3c"><t t-esc="q1_percent"/></td>
</t>
<div>
""")
elem.q1_percent_html = self.env['ir.qweb'].render(t, {'q1_percent': elem.q1_percent})
# Python direct version
if elem.q1_percent >= 75:
background_color = "#52be80"
elif elem.q1_percent >= 50 and elem.q1_percent <= 75:
background_color = "#f4d03f"
elif elem.q1_percent <= 50:
background_color = "#e74c3c"
elem.q1_percent_html = """<div><td style="background-color:%s">%s</td></div>"""% (background_color, elem.q1_percent)
Use that field in your form view like:
<field name="q1_percent_html" nolabel="1" readonly="1"/>

Binding not working for collections in knockoutjs

I am trying to use knockout.js to show a specification object on the UI. The specification has a name and it has a few parameterInfo rows. Each ParameterInfo row has a ParameterPartNumber and a bunch of SignalInputs. Each SignalInput has just one property called Name. I am able to show the specification name, the parameterInfo rows and ParameterPartNumber but am not able to show the bunch of SignalInput Names that I have even though the SpecificationModel has the values. I am using the following code:
HTML code:
<div id="specificationHeader">
Name : <input data-bind='value: Name' />
<br />
<br />
</div>
<table>
<thead>
<tr>
<th>
Parameter Part
</th>
<th>
Signal Inputs
</th>
</tr>
</thead>
<tbody data-bind="foreach: ParameterInfos">
<tr>
<td>
<input data-bind='value: ParameterPartNumber' />
</td>
<td>
<ul data-bind="foreach: SignalInputs">
<li><span data-bind='text: Name' /></li>
</ul>
</td>
</tr>
</tbody>
</table>
Javascript/Knockout code:
<script type="text/javascript">
var SpecificationModel = function (specification) {
var self = this;
self.Name = ko.observable(specification.Name);
self.ParameterInfos = ko.observableArray(ko.utils.arrayMap(specification.ParameterInfos, function (ParameterInfo) {
return { ParameterPartNumber: ParameterInfo.ParameterPartNumber, SignalInputs: ko.observableArray(ParameterInfo.SignalInputs) };
}));
};
var specificationData = '#Html.Raw(ViewBag.SpecificationData)';
var viewModel = new SpecificationModel($.parseJSON(specificationData))
ko.applyBindings(viewModel);
</script>
When I run the program in debug mode, I can see the following values:
var specificationData = '{"Name":"Specification One",
"ParameterInfos": [{"ParameterPartNumber":"26-20700-002", "SignalInputs":[{"Name":"Park Brake"},{"Name":"Neutral"}]} ]}';
It's strange because I was able to get an almost similar example working thanks to the answers for the following question:
Need to pass initial viewmodel data from ASP.NET MVC to knockout.js
Still, somehow the binding code is not working. What am I missing?
EDIT:
Ok, the following lines work:
<td data-bind="foreach: SignalInputs">
<ul >
<li><span data-bind='text: Name' /></li>
</ul>
</td>
But, the following lines don't
<td>
<ul data-bind="foreach: SignalInputs">
<li><span data-bind='text: Name' /></li>
</ul>
</td>
Any idea why? The latter site of lines work in the other stackoverflow example question I cited.
In my experience when you run into weird binding errors, it often stems from the for-each binding. Because I've had so many issues with it, I pretty much just go the "containerless" route:
<!-- ko foreach: myItems -->
<li>Item <span data-bind="text: $data"></span></li>
<!-- /ko -->
I was having problems binding, but my issue turned out to be because I was using an early version of (knockout 1.2.1) that didn't support the foreach binding. It didn't throw an error for an unrecognised binding function though, just when I tried to reference an element within the collection I was supposedly binding to making it hard to debug.
In the end I used a template binding as per the answer here: Stackoverflow: knockout javascript foreach binding
I remember reading in the KnockoutJS documentation that the bindings run into issues with empty elements like <span .... /> and they suggest using <span ...></span> or even <span ...> </span> (the latter being a countermeasure to an IE6 bug).
Not sure this is relevant since this answer was posted so long ago but I was having an issue with foreach as well. I noticed that the error was coming from a jquery call in knockout (i was using knockout-2.0.0) and jquery-2.1.1. When I updated to knockout-3.1.0, this problem was solved.
2.0.0 was installed when I installed knockout.mapping via nuget and it probably depends on 2.0.0 at least. Hope this helps someone...

Resources