my $TransactionPreviousStatus = $self->TicketObj->Status->OldValue:
I am thinking this should give the old status but I end up getting the current status
For Ex:
Old status: open
Current Status: reply-pls
So when somebody will reply on the ticket, a custom script will execute which should change the status to old value (i.e., open) but again it goes back to reply-pls.
You can not call OldValue on TicketObj, it's a Transaction method. So if I understand your needs correctly, you need to write a scrip which triggers on StatusChange && Correspondence which sets the status back. This is little bit tricky.
AFAIK you need to create a batch scrip which triggers on Correspondence and then find the last transaction of StatusChange and revert it back. Something like this could work:
Description: On correspond don't change the status
Condition: On Correspond
Action: User defined
Template: Blank
Stage: Batch
Custom action commit code:
my $transactions = $self->TicketObj->Transactions;
my $last_status;
while (my $transaction = $transactions->Next) {
if ($transaction->Type eq "Status" ) {
$last_status = $transaction;
}
}
$self->TicketObj->SetStatus($last_status->OldValue);
Related
After clicking the "Return" action on a selected item from a completed Purchase Receipt, we're trying to get the "Open PO Line" value to default to false. Anyone know how customize this?
The field defaulting seems to be overwritten when we press the "Return" button. We've tried several different events in the grid but none of the seem to work.
The desired result is to default the "Open PO Line" to false after a return and once the return is released the Purchase Order line associated with the return should remain completed.
Research
The AllowOpen field on POReceiptLine is a PXBool. This means that the value must be populated via a PXDBScalar, PXFormula, etc. or via some business logic in the graph. To see what is happening, let's look at the DAC for POReceiptLine...
#region AllowOpen
public abstract class allowOpen : PX.Data.BQL.BqlBool.Field<allowOpen> { }
protected Boolean? _AllowOpen;
[PXBool()]
[PXUIField(DisplayName = "Open PO Line", Visibility = PXUIVisibility.Service, Visible = true)]
public virtual Boolean? AllowOpen
{
get
{
return this._AllowOpen;
}
set
{
this._AllowOpen = value;
}
}
#endregion
As you can see, there isn't any logic to this field in the DAC, so we need to turn to the graph to see how it is used. (Even if there were logic in the DAC, we would need to see if the graph does something. However, logic in the DAC might have been an easy override with CacheAttached - unfortunately, not in this case.)
Let's turn to POReceiptEntry where the return is handled. We find AllowComplete and AllowOpen being set in the POReceiptLine_RowSelected event, as we would expect since it must be populated on the graph side of code having no logic in the DAC.
if ((row.AllowComplete == null || row.AllowOpen == null) && fromPO)
{
POLineR source = PXSelect<POLineR,
Where<POLineR.orderType, Equal<Required<POLineR.orderType>>,
And<POLineR.orderNbr, Equal<Required<POLineR.orderNbr>>,
And<POLineR.lineNbr, Equal<Required<POLineR.lineNbr>>>>>>
.Select(this, row.POType, row.PONbr, row.POLineNbr);
// Acuminator disable once PX1047 RowChangesInEventHandlersForbiddenForArgs [Legacy, moved the exception here from PX.Objects.acuminator because the condition was changed]
row.AllowComplete = row.AllowOpen = (row.Released == true) ?
(row.ReceiptType != POReceiptType.POReturn ? source?.Completed == true : source?.Completed != true) :
(source?.AllowComplete ?? false);
The field is populated in the row.AllowComplete = row.AllowOpen = (row.Released == true) ?... section of code.
Subsequently, we see that the CopyFromOrigReceiptLine method sets this value to false on the "destLine" being created.
destLine.AllowOpen = false;
As that isn't "true" then we know this isn't our spot. Continuing on, we see in UpdatePOLineCompletedFlag that AllowComplete and AllowOpen are being set. This could be our spot (or one of them).
row.AllowComplete = row.AllowOpen = poLineCurrent.AllowComplete;
Side note: It is worth noting that this line appears twice in an if then else. In both cases it is going to be executed, therefore it would be better coding practice to place this identical statement AFTER the if then else since both the if and else conditions will execute this same statement regardless of the if.
This particular use appears to be pulling the value from the AllowComplete field of the PO Line being received. At this point, you should consider if you need to look upstream at the PO Line to see if the field there needs to be manipulated as well. I cannot answer that for you as your business case will drive the decision.
There also is a line in the Copy method that sets AllowComplete and AllowOpen.
aDest.AllowComplete = aDest.AllowOpen = (aSrc.CompletePOLine == CompletePOLineTypes.Quantity);
The Copy method is overloaded, and the other signature of the method sets the values to true.
aDest.AllowComplete = true;
aDest.AllowOpen = true;
Both of these cases may need customization as well, but I don't think it's the primary issue.
Next Steps
At this point, we see that either the field is being set in UpdatePOLineCompletedFlag or in methods that seem related to copying records. You will need to investigate further if the copy related methods warrant a change as well. However, I think the initial focus should be on the UpdatePOLineCompletedFlag method.
If we find the other points identified require customization, we likely will handle them all the same way... Override the base method/event, invoke the original method/event in our override, and then force the values to fit our business case. Careful testing will be needed since forcibly altering these values may create unforeseen negative ripples.
Something to try
We want to update (or create) a graph extension for POReceiptEntry to override the UpdatePOLineCompleteFlag method. This compiles, but it is completely untested on my part. We need to create a delegate and specify the PXOverride attribute. Then we want to execute the base method before we override the field(s) in question.
Note the extra code (commented out) as a reminder that you need to be careful of methods (typically events) updating our record in the cache and needing to be located so that we don't use a stale copy of the record. I don't think that's necessary in this case, but it seems to be somewhat obscure in code samples that I see. Of course, that is because I'm always looking at the code repository which rarely has graph extensions overriding event handlers!
#region CreateReceiptEntry Override
public delegate void UpdatePOLineCompleteFlagDelegate(POReceiptLine row, bool isDeleted, POLine aOriginLine);
[PXOverride]
public virtual void UpdatePOLineCompleteFlag(POReceiptLine row, bool isDeleted, POLine aOriginLine, UpdatePOLineCompleteFlagDelegate baseMethod)
{
//Execute original logic
baseMethod(row, isDeleted, aOriginLine);
/* If the base method has updated the cache, then we would need to locate the updated record in the cache to proceed
* This tends to be the case more often with event handlers, so it probably isn't needed in this case.
* This is just for reference as a training reminder
//If row has been updatd in the baseMethod, let's go get the updated cache values
POReceiptLine locateRow = Base.transactions.Locate(row);
if (locateRow != null) row = locateRow;
*/
//Override the fields to false - need to test to see if this creates any issues with breaking existing business logic
row.AllowComplete = row.AllowOpen = false;
}
#endregion
If this doesn't get you the specific answer you need, I hope it at least gives you some insight into how to hunt down "the spot" to change. I suspect you may need to update the POLine for a complete solution as hinted above. (See the event handler POReceiptLine_AllowOpen_FieldUpdated for the code that leads me to that conclusion.)
Good luck with your customization, and happy coding!
Following this sample I know I can write my own this.dataSource.filterPredicate. This works fine as long as I search for a string. I want to filter additionally according to a used state (=myFilterState).
I try to use the predicate like
this.dataSource.filterPredicate =
(data: MyElement, filter: string) => data.myType.useState === this.myFilterState;
The problem I'm facing is that when I change this.myFilterState the filter is not reevaluated until I change the string of the filter. As long as the filter remains empty the filterPredicate is not triggered.
Is there a way to trigger it manually - despite the value of filter?
After investigation of the problem I figured out that the filterpredicate is triggered by the source code only when the filter has a value. Otherwise it won't be triggered.
Therefore I come up with the solution to overwrite the internal _filterData with
this.dataSource._filterData = (data: PropertyCompact[]) => {
this.dataSource.filteredData = data.filter(obj => this.dataSource.filterPredicate(obj, this.dataSource.filter));
return this.dataSource.filteredData;
};
and in case of an change one needs to trigger an update
this.dataSource._updateChangeSubscription();
So imagine the following scenario, using the Parse platform on iOS:
I get a PFObject from the server, let's call it GlassChalice.
Someone else, let's say Bill Blofeld, changes GlassChalice from a different location.
Later, I make some changes to my local GlassChalice, but don't save them to the server.
Still later, I want to update GlassChalice, but I want to update it to the current server values, in other word Bill Blofeld's values. I do not want to replace the server values with my local values, and also do not want to reset my local values to the values GlassChalice was loaded with.
So if I use revert(), will I get what I want?
According to the Parse docs:
- revert Clears any changes to this object made since the last call to save and sets it back to the server state.
...but, as in my example, clearing "changes made since the last call to save" and setting it "back to the server state" aren't always the same thing.
So far this seems like the only way to guarantee the results I want, but it has one obvious problem:
public func updateObjectFromServer(_ objectToUpdate: PFObject, then doThis: (()->Void)? = nil) {
let query = PFObject.query()
query?.whereKey("objectId", equalTo: objectToUpdate.objectId!)
query?.getFirstObjectInBackground (block: {
(serverObject, error) in
if error.isNil() {
objectToUpdate["numberOfLimbs"] = serverObject?["numberOfLimbs"]
objectToUpdate["eyePlacement"] = serverObject?["eyePlacement"]
objectToUpdate["crossStitchingTalentRating"] = serverObject?["crossStitchingTalentRating"]
objectToUpdate["clamsEaten"] = serverObject?["clamsEaten"]
} else {
//handle error...
}
doThis?()
})
}
But the huge problem here is that I have to know all the key names, and type them in explicitly, for this to work.
Is there a better, more generic, way?
I have a data structure like so:
friend_requests_sent:
userId1:
sub_userId1:
status: "request_sent"
sub_userId2:
status: "request_sent"
sub_userId3:
status: "request_failed"
userId2:
sub_userId1:
status: "request_sent"
sub_userId2:
status: "request_failed"
sub_userId3:
status: "request_sent"
Each sub_userId is created at some point in time, but I want to set up a database listener that will be triggered only when the value of status is set to request_sent for a sub_userId.
I.e., I am trying to create an observer on the non-existing (yet) path /friend_requests_sent/userId1/sub_userId4/status that will check if it is indeed request_sent, both when it is created and when the status value changes afterwards.
How can I get back a snapshot of the former? Feels like I got lost in the firebase querying system.
Thanks in advance !
friend_requests_sent:
userId1:
requests:
sub_userId1: "insertstatushere"
sub_userId2: "insertstatushere"
//continue
I think this would be a lot better. Add a child added, removed and changed for that path only (userIDHere/requests/). This way you can check and handle everything.
Here is the code I suggest:
firebase.database().ref("friend_requests_sent").once('value', function(snap){
snap.forEach(function(child){
//For each userID below query looks for subusers whose status are 'request_sent'
child.ref.orderByChild('status').equalTo('request_sent').on('child_added', function(grandChild){
//grandChild.val() is the sub-user of child.val()
//grandChild.val().status is equal to 'request_sent'
alert(grandChild.val().status);
});
});
)};
Hope that works!
We are using zapier to push an invoice from Quickbooks Online (QBO) to ShipStation (SS). Everything is mapped fine except the SKU field (which for some reason QBO doesn't pass). So I created a lookup using a multi-zap and the Formatter Utility to match the description to a table of description/skus I made.
The limitation is that the Formatter Utility will only run once and not iterate through the payload array. Tech support told me this could be done with the code utility, but I'm not sure how to do it. Ideas?
Ok, so the best approach here will be to have 2 different zaps.
Zap A will move have a QBO trigger, go through your filters, and the last step will be a Code action. Zap B will have a "Catch Webhook" trigger and a ShipStation action. Let's go through them.
Zap A has a code step that takes input data. This'll be all of your SKUs separated by commas.
The code step will look something like this:
var skus = inputData.skus.split(',') // now it's an actual array
var otherZapUrl = 'https://zapier.com/catch/12345'
var lookupTable = {
1: 'New York',
2: 'Chicago',
3: 'Los Angeles'
}
skus.map(function(sku){
var payload = {
sku: lookupTable[sku] || 'default',
// other data you want to send along
// name: inputData.name
};
fetch(otherZapUrl, {method: 'POST', body: JSON.stringify(payload)});
})
return [{status: 'ok'}] // this is so the editor doesn't complain
Your second zap will catch the webhook and fill out the SS fields you expect. Hopefully that's straightforward.
As for what you need to do, you'll need to redo your lookup table in javascript (sorry) and replace the otherZapUrl to that of the Zap B endpoint. Also, make sure you specify all of the data you want to pass onto SS in the code's inputData object.
How's that look?