Setting Sales Tracking Category on Contact - xero-api

While I can set a Sales Setting/Category for a Contact through the UI, I can't seem to do this via the Calcinai PHP library.
I'm using v1.x at the moment and have done for other Contact creates/updates for a couple of years now.
Firstly, should it even be possible to set a Tracking Category selection on a Contact via the API (the Category and Options have already been defined). Most of the tracking category questions here are in relation to Invoicing, not Contact.
There are various hints in Contact.php that this should be possible and I've tried a number of combinations, but just can't get it working.
I'm a little concerned that the name of the elements in returned Contact payload via the API Previewer don't have the same element names in the Models e.g.
<SalesTrackingCategories>
<SalesTrackingCategory>
<TrackingCategoryName>Gift Aid</TrackingCategoryName>
<TrackingOptionName>Declined</TrackingOptionName>
</SalesTrackingCategory>
</SalesTrackingCategories>
!=
'Name' => [false, self::PROPERTY_TYPE_STRING, null, false, false],
'Status' => [false, self::PROPERTY_TYPE_STRING, null, false, false],
'Options' => [false, self::PROPERTY_TYPE_OBJECT, 'Accounting\\TrackingCategory\\TrackingOption', true, true],
'Option' => [false, self::PROPERTY_TYPE_STRING, null, false, true]
I see there's a mention of a 'dirty hack' in Helpers.php, but that alone doesn't seem to solve my problem.
Again, I've tried a number of things, including:
$TrackingOption = new XeroPHP\Models\Accounting\TrackingCategory\TrackingOption();
$TrackingOption->setTrackingOptionID("37ae930d-f576-44fc-b1d8-86b7f96c0205");
$TrackingCategory = new TrackingCategory();
$TrackingCategory->setTrackingCategoryID("4031ea9e-37a0-40cb-8539-d3127a501828");
$TrackingCategory->addOption($TrackingOption);
$contact->setName($contactname)
->setFirstName($primarypersonfirst)
->setLastName($primarypersonlast)
->setEmailAddress($primarypersonemail)
->setContactStatus(\XeroPHP\Models\Accounting\Contact::CONTACT_STATUS_ACTIVE)
->setContactNumber($contactnumber)
->addSalesTrackingCategory($TrackingCategory)
Has anyone managed to get this going? I would be very appreciative of any pointers.

Sorted & yes, it is possible to add a Sales Tracking Category to a Contact.
I've retained the v1.x version of the Calcinai code for now, but patched the following classes from the latest at Master (I need to upgrade all to OAuth2 by EOY).
Models/Accounting/TrackingCategory.php
Models/Accounting/TrackingCategory/TrackingOption.php
The following then works:
$trackingcat = new XeroPHP\Models\Accounting\TrackingCategory($xero);
$trackingcat->setTrackingCategoryName("cat name");
$trackingcat->setTrackingOptionName("Yes");
$contact->addSalesTrackingCategory($trackingcat);

Related

Error trying to reorder items within another list in Keystone 6

I'm using KeystoneJS v6. I'm trying to enable functionality which allow me to reorder the placement of images when used in another list. Currently i'm setting up the image list below, however I'm unable to set the defaultIsOrderable to true due to the error pasted.
KeystoneJS list:
Image: list({
fields: {
title: text({
validation: { isRequired: true },
isIndexed: 'unique',
isFilterable: true,
isOrderable: true,
}),
images: cloudinaryImage({
cloudinary: {
cloudName: process.env.CLOUDINARY_CLOUD_NAME,
apiKey: process.env.CLOUDINARY_API_KEY,
apiSecret: process.env.CLOUDINARY_API_SECRET,
folder: process.env.CLOUDINARY_API_FOLDER,
},
}),
},
defaultIsOrderable: true
}),
Error message:
The expected type comes from property 'defaultIsOrderable' which is declared here on type 'ListConfig<BaseListTypeInfo, BaseFields<BaseListTypeInfo>>'
Peeking at the definition of the field shows
defaultIsOrderable?: false | ((args: FilterOrderArgs<ListTypeInfo>) => MaybePromise<boolean>);
Looking at the schema API docs, the defaultIsOrderable lets you set:
[...] the default value to use for isOrderable for fields on this list.
You're trying to set this to true but, according to the relevant section of the field docs, the isOrderable field option already defaults to true.
I believe this is why the defaultIsOrderable type doesn't allow you to supply the true literal – doing so would be redundant.
So that explains the specific error your getting but I think you also may have misunderstood the purpose of the orderBy option.
The OrderBy Option
The field docs mention the two effects the field OrderBy option has:
If true (default), the GraphQL API and Admin UI will support ordering by this field.
Take, for example, your Image list above.
As the title field is "orderable", it is included in the list's orderBy GraphQL type (ImageOrderByInput).
When querying the list, you can order the results by the values in this field, like this:
query {
images (orderBy: [{ title: desc }]) {
id
title
images { publicUrl }
}
}
The GraphQL API docs have some details on this.
You can also use the field to order items when listing them in the Admin UI, either by clicking the column heading or selecting the field from the "sort" dropdown:
Note though, these features order items at runtime, by the values stored in orderable fields.
They don't allow an admin to "re-order" items in the Admin UI (unless you did so by changing the image titles in this case).
Specifying an Order
If you want to set the order of items within a list you'd need to store separate values in, for example, a displayOrder field like this:
Image: list({
fields: {
title: text({
validation: { isRequired: true },
isIndexed: 'unique',
isFilterable: true,
}),
displayOrder: integer(),
// ...
},
}),
Unfortunately Keystone doesn't yet give you a great way to manage this the Admin UI (ie. you can't "drag and drop" in the list view or anything like that). You need to edit each item individually to set the displayOrder values.
Ordering Within a Relationship
I notice your question says you're trying to "reorder the placement of images when used in another list" (emphasis mine).
In this case you're talking about relationships, which changes the problem somewhat. Some approaches are..
If the relationship is one-to-many, you can use the displayOrder: integer() solution shown above but the UX is worse again. You're still setting the order values against each item but not in the context of the relationship. However, querying based on these order values and setting them via the GraphQL API should be fairly straight forward.
If the relationship is many-to-many, it's similar but you can't store the "displayOrder" value in the Image list as any one image may be linked to multiple other items. You need to store the order info "with" the relationship itself. It's not trivial but my recent answer on storing additional values on a many-to-many relationship may point you in the right direction.
A third option is to not use the relationship field at all but to link items using the inline relationships functionality of the document field. This is a bit different to work with - easier to manage from the Admin UI but less powerful in GraphQL as you can't traverse the relationship as easily. However it does give you a way to manage a small, ordered set of related items in a many-to-many relationship.
You can save an ordered set of ids to a json field. This is similar to using a document field but a more manual.
Hopefully that clears up what's possible with the current "orderBy" functionality and relationship options. Which of these solutions is most appropriate depends heavily on the specifics of your project and use case.
Note too, there are plans to extend Keystone's functionality for sorting and reordering lists from both the DX and UX perspectives.
See "Sortable lists" on the Keystone roadmap.

Laravel TNTsearch custom index creation and usage for large SQL database table

Here is my situation, context, and dilemma.
Situation
I'm fairly new to Laravel and still learning the ropes. I recently installed TNTSearch and Laravel Scout and was able to create a model index using the below config. I created the index using the console command php artisan tntsearch:import "App\Models\Product" and can fuzzy search successfully with App\Models\Product::search($keyword)->get().
config/scout.php
'tntsearch' => [
'storage' => storage_path() . '/index',
'fuzziness' => 'auto',
'fuzzy' => [
'prefix_length' => 2,
'max_expansions' => 50,
'distance' => 4,
],
'asYouType' => true
],
Context
I have an SQL database table with over 30k+ product records segmented per province (Canadian project), and instead of searching the whole index and later filter by market, I’d like to create one index per market and launch a search for a given market. I believe it will speed up the search and avoid returning results which will later be discarded! So basically having one product index file per province (i.e. products_on.index, products_qc.index, ...)
Dilemma/Issue
I am unable to find how to create such an index, have it update automatically and also how to use it. I scoured the Internet for tutorial/guidance and could only find scarce information I can hardly put together. I’d appreciate if someone could point me in the right direction or guide me on how to implement such a thing.
No answer is wrong, and any bits and pieces of information can help me greatly to “get up to speed.”
EDIT (July 30th, 2018):
I still haven't found the answer to my request but the more I search, the more I'm concluding search indexes are "tied" to a model, and it is not possible to have more than one index per model. So I would have to create one model extension per market from the original Listings model (Listings_QC, Listings_ON, ...). Then create an index per markets and search from those (Listings_QC::search(...)).
I'm not keen to create models based on data! Is this a good approach/practice?
RESOLVED !
My inexperience with Laravel search index in general lead me in the wrong direction!
I finally found a document explaining how to use searchBoolean() to search using "and". Modified my config as below to add the searchBoolean:
'tntsearch' => [
'storage' => storage_path() . '/index',
'fuzziness' => 'auto',
'fuzzy' => [
'prefix_length' => 2,
'max_expansions' => 50,
'distance' => 4,
],
'asYouType' => true,
'searchBoolean' => true
],
Then specify the market using the model's method toSearchableArray(), and add the market to any requested seach keyword.
For example, listing search with 'Alsace' for a QC market, I launch the search as
Listings::search('Alsace QC')->get().
Voilà! May help others hitting the same "wall"!

Selecting rows from DataTables fails unless search UI is visible

I want to hide the search user interface, but when I do so, the api based row selection fails. Works fine if searching is enabled. When I read the docs, I thought I was disabling the UI, but it is obviously the search feature instead.
I actually have DataTables for schools and school districts, and want to only display the schools from a specific district when used clicks on district table to select a district. User can also select a school from the school table
// DataTable Initialization for school table
dTable = $tblSchools.DataTable({
select: {
style: 'single',
items: 'row',
},
paging: false,
searching: true, // selecting rows only works when enabled - makes sense
info: false,
destroy: true
});
// snippet from event handler on 'select' event of district table
var dTable = $tblSchools.DataTable();
var district = '123'; // real code messier, console.log(district) shows it works
// district number is always column 1
// the next line is what fails (silently, no exception)
dTable.column(1).search(district, false, false).draw();
Environment, jQuery 1.9.1, DataTables 1.10.12, select plugin 1.2.0
Is there some setting I have overlooked to hide the search UI without disabling the search function entirely.
If I have to do this a different way, would appreciated being pointed to a simple equivalent way that allows me to hide the DataTables search UI, but show schools based on district.
Is there some setting I have overlooked to hide the search UI without
disabling the search function entirely
Yes. Remove the f flag from the dom defaults :
dTable = $tblSchools.DataTable({
dom: 'lrtip',
...
})
Now the filter input / "search UI" is removed but you can still perform searches by code.

Reach through several relationships to get model from ember-data

I am setting the contents of one menu based on the selected value of another one. The first menu is a set of companies. A Company has an OrderProfile; an OrderProfile has a Warehouse. So based on a known Company, I need to reach through the OrderProfile and get a Warehouse.
Here's what happens in the console when I have the Company in a company variable:
company.get('orderProfile.warehouse.name') # Ember-data gets the OrderProfile but not the Warehouse
// => null
var op = company.get('orderProfile')
// => undefined # The OrderProfile assigned to the variable
var warehouse = op.get('warehouse')
// undefined # Now the Warehouse is assigned to the variable
warehouse.get('name')
// => "Warehouse 1" # This was null when we asked for company.orderProfile.warehouse.name
So I know I need to unravel this in several steps, waiting for each to complete. Is there an Ember idiom for doing this?
N.B. this is using:
DEBUG: Ember : 1.5.0-beta.1+canary.13995621 index.js:3496
DEBUG: Ember Data : 1.0.0-beta.7+canary index.js:3496
Here's how we worked this out. As I suspected, the key was handling the promises correctly, but we also had to be sure we had async: true set on the relevant relationships.
warehouse = company.get("orderProfile.warehouse.name") # Doesn't work
# Instead, we need to treat orderProfile as a promise, and ask for the warehouse
# once it resolves:
company.get("orderProfile").then(
(orderProfile) ->
return orderProfile.get("warehouse") if orderProfile
).then(
(warehouse) =>
# Do something with the warehouse
)
As I understand it, the relationships - specifically orderProfile: DS.belongsTo 'orderProfile', async: true in the Company model - need async: true to remind Ember-Data that it might not already have the referenced models, and it needs to fetch them. I was on the right track figuring out the promise handling, but the async: true part was the real key.

Access objects from project in sandbox environment PHP

Is there a way I can access my project under Sandbox? I'm able to use the lookup method, find in order to fetch all the features from a project under the Yahoo! subscription, but how would I be able to do this for projects under Sandbox?
In your PHP code have you used Rally sandbox server URL?
https://sandbox.rallydev.com/
Here is a WebServices URL specific to Sandbox:
https://sandbox.rallydev.com/slm/doc/webservice/
I was able to figure it out. In the query to find specific features, I had to include the query parameter "workspace" to the sandbox reference which is (for 1.43) : "https://rally1.rallydev.com/slm/webservice/1.43/workspace/7189290105.js". I included the reference of the project as well which directly fetched all the features for my project. In addition, if you seek to only fetch features from your specific project and not from the ones on top of it, you have to include the "pageScopeUp" field into the query. You have to set this field to false:
$queryParams = array(
'query' => "",
'fetch' => 'true',
'pagesize' => 100,
'start' => 1,
'workspace' => "https://rally1.rallydev.com/slm/webservice/1.43/workspace/7189290105.js",
'project' => "whatever the project reference is",
'projectScopeUp' => false
);
$results = Connection::rally()->findWithQueryParameters('feature',
$queryParams);