Creating TimeEntry through REST API is taking too long - acumatica

The client is pushing Time Entries through Web Service Endpoints. Client is using PHP. I have created the .Net project for only Create Time Entry, but it never came back when Push, and HTTPClient is throwing exception "The task has been cancelled.".
Here is the PHP code for creating timeentry-
<?php
require('config.php');
require __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client as Guzzle;
use GuzzleHttp\Cookie\CookieJar;
$cookies = new CookieJar();
define('AUTH', [
'name' => USERNAME,
'password' => PASSWORD,
'company' => COMPANY,
'branch' => BRANCH
]);
login($cookies);
$timeEntry = makeTimeEntryArray();
// $acumaticaNoteID = sendTimeEntryToAcumatica($timeEntry, $cookies);
// $timeEntry['NoteID'] = ['value' => $acumaticaNoteID];
$timeEntry['NoteID'] = ['value' => 'e5eefc3b-7750-e711-8130-129da5b884cd'];
$timeEntry['BillableTime']['value'] = '0300';
$timeEntry['TimeSpent']['value'] = '0300';
$entityId = setTimeEntryStatusToOpen($timeEntry, $cookies);
$timeEntry['id'] = $entityId;
updateTimeEntryInAcumatica($timeEntry, $cookies);
$entityId = setTimeEntryStatusToOpen($timeEntry, $cookies);
$timeEntry['id'] = $entityId;
deleteTimeEntryInAcumatica($timeEntry, $cookies);
logout($cookies);
echo "Finished Create, Update, and Delete\n";
function makeTimeEntryArray()
{
return [
'NoteID' => ['value' => null],
'Note' => ['value' => 'created by me'],
'Billable' => ['value' => true],
'BillableOvertime' => ['value' => '0000'],
'BillableTime' => ['value' => '0200'],
'Date' => ['value' => '2017-06-13T08:30:00-05:00'],
'EarningType' => ['value' => 'RG'],
'Overtime' => ['value' => '0000'],
'Owner' => ['value' => EMPLOYEE_ID],
'RelatedEntityDescription' => ['value' => ORDER_TYPE . ', ' . ORDER_NUMBER],
'Status' => ['value' => 'Completed'],
'Summary' => ['value' => 'Test Time Entry From PHP Sample Code'],
'TimeSpent' => ['value' => '0200'],
'TrackTime' => ['value' => true],
'Type' => ['value' => 'TE'],
'TypeDescription' => ['value' => 'Time Entry'],
'RefNoteID' => ['value' => ORDER_NOTE_ID],
];
}
function login($cookies)
{
echo "-----------------------------------\n";
echo date('H:i:s') . " Logging in, estimated 1s...\n";
$start = microtime(true);
$resource = '<URL>/auth/login';
echo " url: $resource\n";
$guzzle = new Guzzle();
$response = $guzzle->request('POST', $resource, [
'json' => AUTH,
'cookies' => $cookies,
'content-type' => 'application/json',
'http_errors' => false
]);
$elapsed = microtime(true) - $start;
$code = $response->getStatusCode();
if ($code == 200 || $code == 204) {
echo "Success logging in after {$elapsed}s\n";
return;
} else {
throw new Exception("Error {$code} logging in after {$elapsed}s");
}
}
function sendTimeEntryToAcumatica($data, $cookies)
{
echo "-----------------------------------\n";
echo date('H:i:s') . " Attempting to create, estimated 300s...\n";
$start = microtime(true);
$resource = '<URL>/6.00.001/TimeEntry';
$dataJson = json_encode($data);
echo " url: $resource\n";
echo " data: " . json_encode($data) . "\n";
$guzzle = new Guzzle();
$response = $guzzle->request('PUT', $resource, [
'json' => $data,
'cookies' => $cookies,
'content-type' => 'application/json',
'http_errors' => false,
]);
$elapsed = microtime(true) - $start;
$code = $response->getStatusCode();
if ($code == 200 || $code == 204) {
echo "Success creating time entry after {$elapsed}s\n";
$responseJson = json_decode($response->getBody(), true);
$acumaticaNoteID = $responseJson['NoteID']['value'];
return $acumaticaNoteID;
} else {
throw new Exception("Error {$code} creating time entry after {$elapsed}s: {$response->getBody()}");
}
}
function setTimeEntryStatusToOpen($data, $cookies)
{
echo "-----------------------------------\n";
echo date('H:i:s') . " Opening time entry...\n";
$start = microtime(true);
$updateParams = [
'NoteID' => ['value' => $data['NoteID']['value']],
'Status' => ['value' => 'Open'],
];
$resource = '<URL>/6.00.001/TimeEntry';
echo " url: $resource\n";
echo " data: " . json_encode($updateParams) . "\n";
$guzzle = new Guzzle();
$response = $guzzle->request('PUT', $resource, [
'json' => $updateParams,
'cookies' => $cookies,
'content-type' => 'application/json',
'http_errors' => false,
]);
$responseBody = $response->getBody();
$responseData = json_decode($responseBody);
$elapsed = microtime(true) - $start;
$code = $response->getStatusCode();
if ($code == 200 || $code == 204) {
if (array_key_exists('id', $responseData)) {
echo "Success opening time entry after {$elapsed}s\n";
return $responseData->id;
} else {
echo "Expected Acumatica to return an id for the time entry. Response: {$responseBody}\n";
throw new Exception("Expected Acumatica to return an id");
}
} else {
throw new Exception("Error {$code} opening time entry after {$elapsed}s. Response: {$responseBody}");
}
}
function updateTimeEntryInAcumatica($data, $cookies)
{
echo "-----------------------------------\n";
echo date('H:i:s') . " Updating time entry...\n";
$start = microtime(true);
$resource = '<URL>/6.00.001/TimeEntry';
echo " url: $resource\n";
echo " data: " . json_encode($data) . "\n";
$guzzle = new Guzzle();
$response = $guzzle->request('PUT', $resource, [
'json' => $data,
'cookies' => $cookies,
'content-type' => 'application/json',
'http_errors' => false,
]);
$responseBody = $response->getBody();
$elapsed = microtime(true) - $start;
$code = $response->getStatusCode();
if ($code == 200 || $code == 204) {
echo "Success updating time entry after {$elapsed}s\n";
} else {
throw new Exception("Error {$code} updating time entry after {$elapsed}s. Response: {$responseBody}");
}
}
function deleteTimeEntryInAcumatica($data, $cookies)
{
echo "-----------------------------------\n";
echo date('H:i:s') . " Deleting time entry...\n";
$start = microtime(true);
$resource = "<URL>/6.00.001/TimeEntry/{$data['id']}";
echo " url: $resource\n";
$guzzle = new Guzzle();
$response = $guzzle->request('DELETE', $resource, [
'cookies' => $cookies,
'content-type' => 'application/json',
'http_errors' => false,
]);
$elapsed = microtime(true) - $start;
$code = $response->getStatusCode();
$json = json_encode($response);
if ($code == 200 || $code == 204) {
echo "Success deleting time entry after {$elapsed}s\n";
} else {
throw new Exception("Error {$code} deleting time entry after {$elapsed}s. Response: {$json}");
}
}
function logout($cookies)
{
echo "-----------------------------------\n";
echo date('H:i:s') . " Logging out...\n";
$start = microtime(true);
$resource = '<URL>/auth/logout';
echo " url: $resource\n";
$guzzle = new Guzzle();
$response = $guzzle->request('POST', $resource, [
'cookies' => $cookies,
'http_errors' => false
]);
$elapsed = microtime(true) - $start;
$code = $response->getStatusCode();
$json = json_encode($response);
if ($code == 200 || $code == 204) {
echo "Success logging out after {$elapsed}s\n";
return;
} else {
throw new Exception("Error {$code} logging out after {$elapsed}s. . Response: {$json}");
}
}
Unfortunately it is taking too long to process. In a nutshell, when time entries are created, updated, or deleted, we place jobs onto a queue that handles them one at a time.
Push - makes one POST to the REST API with a json payload of the Time Entry's attributes.
Delete - makes one DELETE request to the REST API with a json payload of just the timeEntry id.
Update - makes two requests, one PUT to with a json payload setting the time entry to open, and a second PUT with a payload of the time entry attributes to make the changes.
Pushes today take around 350 seconds
Delete Jobs are around 500 seconds
Update Jobs are around 550 seconds to open, 250 seconds to update
Those times are not around any of code - that's how long its waiting for a response after the call to the REST API.
Here is the log from client, including JSON payload for the Create:-
[2017-06-09 18:12:16] local.INFO: [PushTimeEntryToAcumatica] [0] Starting PushTimeEntryToAcumatica
[2017-06-09 18:12:16] local.INFO: Current job queue count: 1
[2017-06-09 18:12:16] local.INFO: [PushTimeEntryToAcumatica] [0.05] Handling timeEntry {id: 174, user: Paul Wray, order: SO300276}
[2017-06-09 18:12:16] local.INFO: login url: /entity/auth/login
[2017-06-09 18:12:17] local.INFO: Success 204 from Acumatica: test
[2017-06-09 18:12:17] local.INFO: create url: /6.00.001/TimeEntry data: {"NoteID":{"value":null},"Note":{"value":"created by me"},"Billable":{"value":true},"BillableOvertime":{"value":"0000"},"BillableTime":{"value":"0180"},"Date":{"value":"2017-06-09T08:00:00-05:00"},"EarningType":{"value":"RG"},"Overtime":{"value":"0000"},"Owner":{"value":"EMP0000096"},"RelatedEntityDescription":{"value":"SO, SO300276"},"Status":{"value":"Completed"},"Summary":{"value":"Ryder Truck Rental - Des Moines-NW 49th- Paul Wray- 3hrs"},"TimeSpent":{"value":"0180"},"TrackTime":{"value":true},"Type":{"value":"TE"},"TypeDescription":{"value":"Time Entry"},"RefNoteID":{"value":"9af5eb4f-3934-e711-812f-12e7ddd1194d"}}
[2017-06-09 18:15:30] local.INFO: Success 200 from Acumatica: {"NoteID":{"value":null},"Note":{"value":"created by me"},"Billable":{"value":true},"BillableOvertime":{"value":"0000"},"BillableTime":{"value":"0180"},"Date":{"value":"2017-06-09T08:00:00-05:00"},"EarningType":{"value":"RG"},"Overtime":{"value":"0000"},"Owner":{"value":"EMP0000096"},"RelatedEntityDescription":{"value":"SO, SO300276"},"Status":{"value":"Completed"},"Summary":{"value":"Ryder Truck Rental - Des Moines-NW 49th- Paul Wray- 3hrs"},"TimeSpent":{"value":"0180"},"TrackTime":{"value":true},"Type":{"value":"TE"},"TypeDescription":{"value":"Time Entry"},"RefNoteID":{"value":"9af5eb4f-3934-e711-812f-12e7ddd1194d"}}
[2017-06-09 18:15:30] local.INFO: [UpdateTimeEntryInAcumatica] job dispatched for timeEntry id: 174
[2017-06-09 18:15:30] local.INFO: Current job queue count: 1
[2017-06-09 18:15:30] local.INFO: Success 204 from Acumatica: test
[2017-06-09 18:15:30] local.INFO: [PushTimeEntryToAcumatica] [193.89] SUCCESS handling timeEntry id:174
[2017-06-09 18:15:30] local.INFO: [PushTimeEntryToAcumatica] [193.89] SUCCESS handling id: 174
[2017-06-09 18:15:30] local.INFO: [PushTimeEntryToAcumatica] [193.89] Finished PushTimeEntryToAcumatica in 193.89 seconds
Acumatica Request Profiler
Part of WSDL for TimeEntry Endpoint
<s:complexType name="TimeEntry">
<s:complexContent mixed="false">
<s:extension base="q4:Entity" xmlns:q4="http://www.acumatica.com/entity/Maestro/6.00.001/">
<s:sequence>
<s:element name="ActivityDetails" type="q4:StringValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="Approver" type="q4:StringValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="Billable" type="q4:BooleanValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="BillableOvertime" type="q4:StringValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="BillableTime" type="q4:StringValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="Date" type="q4:DateTimeValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="EarningType" type="q4:StringValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="Internal" type="q4:BooleanValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="NoteID" type="q4:GuidValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="Overtime" type="q4:StringValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="Owner" type="q4:StringValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="Project" type="q4:StringValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="ProjectTask" type="q4:StringValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="ReferenceNbr" type="q4:StringValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="RefNoteID" type="q4:GuidValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="RelatedEntityDescription" type="q4:StringValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="Released" type="q4:BooleanValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="StartTime" type="q4:DateTimeValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="Status" type="q4:StringValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="Summary" type="q4:StringValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="Task" type="q4:GuidValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="TaskSummary" type="q4:StringValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="TimeSpent" type="q4:StringValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="TrackTime" type="q4:BooleanValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="Type" type="q4:StringValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="TypeDescription" type="q4:StringValue" maxOccurs="1" minOccurs="0" nillable="true"/>
<s:element name="Workgroup" type="q4:StringValue" maxOccurs="1" minOccurs="0" nillable="true"/>
</s:sequence>
</s:extension>
</s:complexContent>
</s:complexType>
Endpoint Definition generated from utility-
<?xml version="1.0" encoding="IBM437"?>
<Endpoint xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Maestro" version="6.00.001" systemContractVersion="2" xmlns="http://www.acumatica.com/entity/maintenance/5.31">
<ExtendsEndpoint name="Default" version="6.00.001" />
<TopLevelEntity name="MaestroSalesOrder" screen="SO301000">
<Fields>
<Field name="AddressLine1" type="StringValue" />
<Field name="AddressLine2" type="StringValue" />
<Field name="City" type="StringValue" />
<Field name="CustomerName" type="StringValue" />
<Field name="Date" type="DateTimeValue" />
<Field name="DatePromised" type="DateTimeValue" />
<Field name="Description" type="StringValue" />
<Field name="Email" type="StringValue" />
<Field name="Hold" type="BooleanValue" />
<Field name="LicenseNumber" type="StringValue" />
<Field name="Name" type="StringValue" />
<Field name="Odometer" type="IntValue" />
<Field name="OrderNbr" type="StringValue" />
<Field name="OrderType" type="StringValue" />
<Field name="Phone1" type="StringValue" />
<Field name="PostalCode" type="StringValue" />
<Field name="State" type="StringValue" />
<Field name="Status" type="StringValue" />
<Field name="TankModel" type="StringValue" />
<Field name="TankSpec" type="StringValue" />
<Field name="TankVIN" type="StringValue" />
<Field name="TankYearMake" type="StringValue" />
<Field name="TAS" type="StringValue" />
<Field name="TRS" type="StringValue" />
<Field name="TruckModel" type="StringValue" />
<Field name="TruckVIN" type="StringValue" />
<Field name="TruckYearMake" type="StringValue" />
<Field name="UnitNumber" type="StringValue" />
</Fields>
<Mappings>
<Mapping field="AddressLine1">
<To object="Shipping_Address" field="AddressLine1" />
</Mapping>
<Mapping field="AddressLine2">
<To object="Shipping_Address" field="AddressLine2" />
</Mapping>
<Mapping field="City">
<To object="Shipping_Address" field="City" />
</Mapping>
<Mapping field="CustomerName">
<To object="Document" field="CustomerID%AcctName" />
</Mapping>
<Mapping field="Date">
<To object="Document" field="OrderDate" />
</Mapping>
<Mapping field="DatePromised">
<To object="CurrentDocument: 11" field="UsrDatePromised" />
</Mapping>
<Mapping field="Description">
<To object="Document" field="OrderDesc" />
</Mapping>
<Mapping field="Email">
<To object="Shipping_Contact" field="Email" />
</Mapping>
<Mapping field="Hold">
<To object="Document" field="Hold" />
</Mapping>
<Mapping field="LicenseNumber">
<To object="CurrentDocument: 7" field="UsrLicenseNbr" />
</Mapping>
<Mapping field="Name">
<To object="Document" field="SalesPersonID%Descr" />
</Mapping>
<Mapping field="Odometer">
<To object="CurrentDocument: 7" field="UsrOdometer" />
</Mapping>
<Mapping field="OrderNbr">
<To object="Document" field="OrderNbr" />
</Mapping>
<Mapping field="OrderType">
<To object="Document" field="OrderType" />
</Mapping>
<Mapping field="Phone1">
<To object="Shipping_Contact" field="Phone1" />
</Mapping>
<Mapping field="PostalCode">
<To object="Shipping_Address" field="PostalCode" />
</Mapping>
<Mapping field="State">
<To object="Shipping_Address" field="State%StateID" />
</Mapping>
<Mapping field="Status">
<To object="Document" field="Status" />
</Mapping>
<Mapping field="TankModel">
<To object="CurrentDocument: 9" field="UsrTankModel" />
</Mapping>
<Mapping field="TankSpec">
<To object="CurrentDocument: 9" field="UsrTankSpec" />
</Mapping>
<Mapping field="TankVIN">
<To object="CurrentDocument: 9" field="UsrTankVINInfo" />
</Mapping>
<Mapping field="TankYearMake">
<To object="CurrentDocument: 9" field="UsrTankYearMake" />
</Mapping>
<Mapping field="TAS">
<To object="CurrentDocument: 9" field="UsrTankTAS" />
</Mapping>
<Mapping field="TRS">
<To object="CurrentDocument: 10" field="UsrTruckTRS" />
</Mapping>
<Mapping field="TruckModel">
<To object="CurrentDocument: 10" field="UsrTruckModel" />
</Mapping>
<Mapping field="TruckVIN">
<To object="CurrentDocument: 10" field="UsrTruckVINInfo" />
</Mapping>
<Mapping field="TruckYearMake">
<To object="CurrentDocument: 10" field="UsrTruckYearMake" />
</Mapping>
<Mapping field="UnitNumber">
<To object="CurrentDocument: 7" field="UsrUnitNbr" />
</Mapping>
</Mappings>
</TopLevelEntity>
<TopLevelEntity name="TimeEntry" screen="CR306010">
<Fields>
<Field name="ActivityDetails" type="StringValue" />
<Field name="Approver" type="StringValue" />
<Field name="Billable" type="BooleanValue" />
<Field name="BillableOvertime" type="StringValue" />
<Field name="BillableTime" type="StringValue" />
<Field name="Date" type="DateTimeValue" />
<Field name="EarningType" type="StringValue" />
<Field name="Internal" type="BooleanValue" />
<Field name="NoteID" type="GuidValue" />
<Field name="Overtime" type="StringValue" />
<Field name="Owner" type="StringValue" />
<Field name="Project" type="StringValue" />
<Field name="ProjectTask" type="StringValue" />
<Field name="ReferenceNbr" type="StringValue" />
<Field name="RefNoteID" type="GuidValue" />
<Field name="RelatedEntityDescription" type="StringValue" />
<Field name="Released" type="BooleanValue" />
<Field name="StartTime" type="DateTimeValue" />
<Field name="Status" type="StringValue" />
<Field name="Summary" type="StringValue" />
<Field name="Task" type="GuidValue" />
<Field name="TaskSummary" type="StringValue" />
<Field name="TimeSpent" type="StringValue" />
<Field name="TrackTime" type="BooleanValue" />
<Field name="Type" type="StringValue" />
<Field name="TypeDescription" type="StringValue" />
<Field name="Workgroup" type="StringValue" />
</Fields>
<Mappings>
<Mapping field="ActivityDetails">
<To object="CurrentActivity" field="Body" />
</Mapping>
<Mapping field="Approver">
<To object="TimeActivity" field="ApproverID" />
</Mapping>
<Mapping field="Billable">
<To object="TimeActivity" field="IsBillable" />
</Mapping>
<Mapping field="BillableOvertime">
<To object="TimeActivity" field="OvertimeBillable" />
</Mapping>
<Mapping field="BillableTime">
<To object="TimeActivity" field="TimeBillable" />
</Mapping>
<Mapping field="Date">
<To object="Activities" field="StartDate_Date" />
</Mapping>
<Mapping field="EarningType">
<To object="TimeActivity" field="EarningTypeID" />
</Mapping>
<Mapping field="Internal">
<To object="Activities" field="IsPrivate" />
</Mapping>
<Mapping field="NoteID">
<To object="Activities" field="NoteID" />
</Mapping>
<Mapping field="Overtime">
<To object="TimeActivity" field="OvertimeSpent" />
</Mapping>
<Mapping field="Owner">
<To object="Activities" field="OwnerID" />
</Mapping>
<Mapping field="Project">
<To object="TimeActivity" field="ProjectID" />
</Mapping>
<Mapping field="ProjectTask">
<To object="TimeActivity" field="ProjectTaskID" />
</Mapping>
<Mapping field="ReferenceNbr">
<To object="TimeActivity" field="ARRefNbr" />
</Mapping>
<Mapping field="RefNoteID">
<To object="Activities" field="RefNoteID" />
</Mapping>
<Mapping field="RelatedEntityDescription">
<To object="Activities" field="Source" />
</Mapping>
<Mapping field="Released">
<To object="TimeActivity" field="Released" />
</Mapping>
<Mapping field="StartTime">
<To object="Activities" field="ParentNoteID%StartDate" />
</Mapping>
<Mapping field="Status">
<To object="TimeActivity" field="ApprovalStatus" />
</Mapping>
<Mapping field="Summary">
<To object="Activities" field="Subject" />
</Mapping>
<Mapping field="Task">
<To object="Activities" field="Subject" />
</Mapping>
<Mapping field="TaskSummary">
<To object="Activities" field="ParentNoteID!Subject" />
</Mapping>
<Mapping field="TimeSpent">
<To object="TimeActivity" field="TimeSpent" />
</Mapping>
<Mapping field="TrackTime">
<To object="TimeActivity" field="TrackTime" />
</Mapping>
<Mapping field="Type">
<To object="Activities" field="Type" />
</Mapping>
<Mapping field="TypeDescription">
<To object="Activities" field="Type_description" />
</Mapping>
<Mapping field="Workgroup">
<To object="Activities" field="WorkgroupID" />
</Mapping>
</Mappings>
</TopLevelEntity>
</Endpoint>
JSON Object
{
"NoteID": {
"value": null
},
"Note": {
"value": "Created by Krunal"
},
"Billable": {
"value": true
},
"BillableOvertime": {
"value": "0000"
},
"BillableTime": {
"value": "0200"
},
"Date": {
"value": "2017-06-27T08:00:00-05:00"
},
"EarningType": {
"value": "RG"
},
"Overtime": {
"value": "0000"
},
"Owner": {
"value": "EMP0000002"
},
"RelatedEntityDescription": {
"value": "SO, SO300279"
},
"Status": {
"value": "Completed"
},
"Summary": {
"value": "Test Please Ignore"
},
"TimeSpent": {
"value": "0200"
},
"TrackTime": {
"value": true
},
"Type": {
"value": "TE"
},
"TypeDescription": {
"value": "Time Entry"
},
"RefNoteID": {
"value": "052F4A9B-8635-E711-812F-12E7DDD1194D"
}
}
Response time for above request
After removing StartTime and TaskSummary

Related

Callback when Ext.NET Combobox local query returns no data

Using Ext.NET combobox.
<ext:ComboBox runat="server"
ID="ComboBoxCategorizedList"
QueryMode="Local"
ValueField="Id"
EmptyText="Type to begin search..."
TypeAhead="false"
DisplayField="Name"
Width="500"
NoteAlign="Down" EnableKeyEvents="true"
Note="Press 'Search' icon or Press ENTER for more results"
RemoveClearTrigger="true">
<%--Note="Type '*' for a full list"--%>
<HtmlBin>
<ext:XScript runat="server">
<script type="text/javascript">
$(window).on("__refresh__", function () {
#{ StoreComboBoxCategorizedList }.reload();
});
</script>
</ext:XScript>
</HtmlBin>
<Store>
<ext:Store runat="server" ID="StoreComboBoxCategorizedList" OnReadData="ComboBoxCategorizedList_ReadData">
<Proxy>
<ext:PageProxy />
</Proxy>
<Model>
<ext:Model Id="ModelCategorizedComboBox" runat="server" IDProperty="Id">
<Fields>
<ext:ModelField Name="Id" />
<ext:ModelField Name="Name" />
<ext:ModelField Name="Type" />
<ext:ModelField Name="RefId" />
<ext:ModelField Name="Description" />
</Fields>
</ext:Model>
</Model>
<Listeners>
<Update Handler="#{ComboBoxCategorizedList}.expand();" />
<EndUpdate Handler="categorizedList();" />
</Listeners>
<Parameters>
<ext:StoreParameter Mode="Raw" Name="filter" Value="#{ComboBoxCategorizedList}.getValue()" />
</Parameters>
</ext:Store>
</Store>
<Triggers>
<ext:FieldTrigger Icon="Clear"/>
<ext:FieldTrigger Icon="Search"></ext:FieldTrigger>
</Triggers>
<Listeners>
<SpecialKey Fn="enterKeyPressHandler" />
<Expand Handler="categorizedList();" Delay="100" />
<BeforeSelect Fn="onBeforeSelect" />
<KeyPress Handler="#{ComboBoxCategorizedList}.getTrigger(1).onClick();" Buffer="1000" />
<Change Handler="filterComboxBoxFunction(#{StoreComboBoxCategorizedList}, #{ComboBoxCategorizedList}.getValue()); #{ComboBoxCategorizedList}.expand(); categorizedList();" Delay="100" />
</Listeners>
Not asking for debugging help, but I want to know if an Ext.NET or Extjs dev has a generic solution: Very simply ... I want to initiate a remote search only when the local search returns no records. So I am looking for the best way to wire this up to the combobox. I've looked at using the Expand event and BeforeQuery event but this seems to come up short.
I'm looking for best practices so i can add a "OnLocalQuery" event to my comboboxes; and then take action if the local query returns 0 matches.
I use this function and it works fine .. and without QueryMode="Local":
<ext:ComboBox ID="cmb_name" runat="server" FieldLabel="ComboBox" EmptyText="-Select-" HideTrigger="true" TriggerAction="All" SelectOnFocus="true" DisplayField="Name" ValueField="Id" Editable="true" TabIndex="11">
<Store>
<ext:Store ID="str_ComboBox" runat="server" PageSize="10">
<Model>
<ext:Model ID="mdl_ComboBox" runat="server">
<Fields>
<ext:ModelField Name="Id" />
<ext:ModelField Name="Name" />
<ext:ModelField Name="Type" />
<ext:ModelField Name="RefId" />
<ext:ModelField Name="Description" />
</Fields>
</ext:Model>
</Model>
</ext:Store>
</Store>
<Listeners>
<Change Fn="fn_filter" />
</Listeners>
</ext:ComboBox>
<script type="text/javascript">
var fn_filter = function (_this, newValue, oldValue, eOpts) {
var n = 0;
if (_this.lastQuery == undefined || _this.lastQuery == null) {
_this.lastQuery = "";
}
_this.getStore().clearFilter(true);
_this.getStore().load();
_this.store.filter(function (item) {
if (item.get('Name').includes(_this.getRawValue())) {
n = n + 1;
return true;
}
else {
return false;
}
});
_this.expand();
if (n == 0) {
//returns no records
//enter code here Callback
}
}
</script>

Finder in Liferay's ServiceBuilder

I know I had already asked this question, but I have misunderstandings yet.
My previous question:
Liferay and relationships in it
In two words:
I have a portlet, which can add/update/delete books and add authors. Moreover, you can choose existing authors when you try to add book.
http://i.stack.imgur.com/vzUjn.png
And now I need to show how many books were written by each author in "author" table.
My service.xml:
<entity name="Book" local-service="true" remote-service="true" cache-enabled="false">
<column name="bookId" type="long" primary="true" />
<column name="bookName" type="String" />
<column name="bookDescription" type="String" />
<column name="authors" type="Collection" entity="Author" mapping-table="Books_Authors" />
<finder return-type="Collection" name="bookName">
<finder-column name="bookName"></finder-column>
</finder>
</entity>
<entity name="Author" local-service="true" remote-service="true" cache-enabled="false">
<column name="authorId" type="long" primary="true" />
<column name="authorName" type="String" />
<column name="books" type="Collection" entity="Book" mapping-table="Books_Authors" />
</entity>
What finder should I create to achieve my goal? If I create bookName finder Im able to count how many different books I have. If I create authorName finder Im able to count how many authors I have. Im a little lost.
Thank you for your help, but I still have some questions:
How and where can I get authorName with authorId?
How can I use my count variable in my table in view.jsp?
long count = BookLocalServiceUtil.countByAuthor(authorId);
public void addBook(ActionRequest actionRequest, ActionResponse actionResponse)
throws IOException, PortletException {
String bookName = ParamUtil.getString(actionRequest,"bookName");
String bookDescription = ParamUtil.getString(actionRequest, "bookDescription");
Long authorId = ParamUtil.getLong(actionRequest, "author");
try {
Book book = BookLocalServiceUtil.createBook(CounterLocalServiceUtil.increment());
book.setBookName(bookName);
book.setBookDescription(bookDescription);
book.setAuthorId(authorId);
book=BookLocalServiceUtil.addBook(book);
String author = ParamUtil.getString(actionRequest, "authorId");
} catch (Exception e) {
log.info(ADD_BOOK_ERROR, e);
SessionErrors.add(actionRequest, "PortalExceptionError");
}
}
In-case, if your Book can have only one Author (many-to-one), then following entity structure will work for you:
service.xml
<entity name="Book" local-service="true" remote-service="true" cache-enabled="false">
<column name="bookId" type="long" primary="true"></column>
<column name="bookName" type="String"></column>
<column name="bookDescription" type="String"></column>
<column name="authorId" type="long"></column>
<finder return-type="Collection" name="Author">
<finder-column name="authorId"></finder-column>
</finder>
</entity>
<entity name="Author" local-service="true" remote-service="true" cache-enabled="false">
<column name="authorId" type="long" primary="true"></column>
<column name="authorName" type="String"></column>
</entity>
On successful build, above finder will generate two methods findByAuthor(long authorId) and countByAuthor(long authorId) in BookUtil. Then, you can implement these methods in BookLocalServiceImpl as following:
BookLocalServiceImpl:
public List<Book> findByAuthor(long authorId) {
try {
return BookUtil.findByAuthor(authorId);
} catch (Exception ex) {}
return null;
}
public int countByAuthor(long authorId) {
try {
return BookUtil.countByAuthor(authorId);
} catch (Exception ex) {}
return 0;
}
And on building service once again, you can use these methods in your action class and on view from BookLocalServiceUtil.
Also, you are using JSTL on your views, you need to add dependencies of the jars in liferay-plugin-package.properties as following:
liferay-plugin-package.properties:
portal-dependency-jars=\
jstl-api.jar,\
jstl-impl.jar
Your Questions:
How and where can I get authorName with authorId?
<liferay-ui:search-container>
<liferay-ui:search-container-results results="${bookListArray}" />
<liferay-ui:search-container-row className="builder.model.Book"
keyProperty="bookId" modelVar="aBook">
<liferay-ui:search-container-column-text property="bookName"
name="book-Name" />
<liferay-ui:search-container-column-text property="bookDescription"
name="description" />
<%
Author bookAuthor = AuthorLocalServiceUtil.getAuthor(aBook.getAuthorId());
%>
<liferay-ui:search-container-column-text name="Author"
value="<%=bookAuthor.getAuthorName() %>" />
<liferay-ui:search-container-column-jsp path="/html/actionBook.jsp" />
</liferay-ui:search-container-row>
<liferay-ui:search-iterator />
</liferay-ui:search-container>
How can I use my count variable in my table in view.jsp?
<liferay-ui:search-container>
<liferay-ui:search-container-results results="${authorListArray}" />
<liferay-ui:search-container-row className="builder.model.Author"
keyProperty="authorId" modelVar="aAuthor">
<liferay-ui:search-container-column-text property="authorName"
name="author-Name" />
<%
int count = BookLocalServiceUtil.countByAuthor(aAuthor.getAuthorId());
%>
<liferay-ui:search-container-column-text name="count"
value="<%=String.valueOf(count) %>" />
<liferay-ui:search-container-column-jsp path="/html/actionAuthor.jsp" />
</liferay-ui:search-container-row>
<liferay-ui:search-iterator />
</liferay-ui:search-container>

Ext Net TimeField in an editable grid

I have a grid panel which display two TimeSpans in two columns and all is well when I load the page but when I go server side the validation on my editors always fail. I don't understand why the store can display the TimeSpans but cannot return it after...
Here my code :
<ext:GridPanel ID="WeekParams" runat="server" >
<Store>
<ext:Store runat="server">
<Reader>
<ext:JsonReader IDProperty="GUID">
<Fields>
<ext:RecordField Name="Day" Type="String" />
<ext:RecordField Name="UATNumber" Type="int" />
<ext:RecordField Name="From" Type="Date" />
<ext:RecordField Name="To" Type="Date" />
</Fields>
</ext:JsonReader>
</Reader>
</ext:Store>
</Store>
<ColumnModel>
<Columns>
<ext:Column ColumnID="Day" DataIndex="Day" />
<ext:NumberColumn ColumnID="UATNumber" DataIndex="UATNumber" Format="0" />
<ext:DateColumn ColumnID="From" DataIndex="From" Format="dd/MM/yyyy" >
<Renderer Format="Date" FormatArgs="'HH:mm'" />
<Editor>
<ext:TimeField runat="server" />
</Editor>
</ext:DateColumn>
<ext:DateColumn ColumnID="To" DataIndex="To" Format="dd/MM/yyyy" >
<Renderer Format="Date" FormatArgs="'HH:mm'" />
<Editor>
<ext:TimeField runat="server" />
</Editor>
</ext:DateColumn>
</Columns>
</ColumnModel>
<Plugins>
<ext:EditableGrid runat="server" />
</Plugins>
</ext:GridPanel>
And here the result when I'm going to the server side :

Nillable SOAP headers

I would like to allow some of my SOAP header elements to be nillable. This is possible for body elements, but I am not sure if it is allowed from header elements.
In the sample message below, I would like to allow the MessageDateTime to be null.
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://mycompany.com/repositoryservice">
<types>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
attributeFormDefault="qualified"
elementFormDefault="qualified"
targetNamespace="http://mycompany.com/repositoryservice">
<element name="MessageDateTime" type="dateTime" />
<element name="SaveRequest">
<!-- complexType -->
</element>
</schema>
</types>
<message name="SaveRequest_Headers">
<part name="MessageDateTime" element="tns:MessageDateTime" />
</message>
<message name="SaveRequest">
<part name="parameters" element="tns:SaveRequest" />
</message>
<binding name="RepositoryServiceBinding" type="tns:IRepositoryService">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
<operation name="Save">
<soap:operation soapAction="http://mycompany.com/repositoryservice/Save" style="document" />
<input name="SaveRequest">
<soap:header message="tns:SaveRequest_Headers" part="MessageDateTime" use="literal" />
<soap:body use="literal" />
</input>
</operation>
</binding>
<!-- service, portType -->
</definitions>
It is allowed as long as the definition allows for it. In your case, all you have to do is add the nillable="true" to the element's definition. The result on .NET w/ WCF would look something like this:
[System.ServiceModel.MessageHeaderAttribute(Namespace="...")]
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
public System.Nullable<System.DateTime> MessageDateTime;

NLog code line logging

I am using NLog to log error messages into csv:
<target name="csv" xsi:type="File"
fileName="${basedir}/Logs/log.csv"
archiveFileName="${basedir}/Logs/Archives/log.{#####}.csv"
archiveAboveSize="10240"
archiveNumbering="Sequence"
concurrentWrites="true"
keepFileOpen="false">
<layout xsi:type="CSVLayout" delimiter="Comma" withHeader="false">
<column name="time" layout="${longdate}" />
<column name="message" layout="${message}" />
<column name="logger" layout="${logger}"/>
<column name="level" layout="${level}"/>
</layout>
</target>
How can I add a column to track the code line number?
Thank you.
Try to add ${callsite} layout renderer.
<column name="source" layout="${callsite:fileName=true}" />

Resources