Inconsistent Template Matching - docusignapi

I'm using the CreateEnvelope() method of the SOAP API to upload a document to DocuSign in the Draft status. When I do this and then request a Sender Token to start an Embedded Sending session, I'm immediately prompted to apply a matching template when the DocuSign interface appears.
If I don't start an Embedded Sending session, however, and the user instead logs into the DocuSign console and opens the Draft envelope, they are NOT automatically prompted to apply the matching template. They have to manually press the "Try automatic template matching on all documents" button, at which point the same template that would've been found automatically in the Embeddded Sending session is found. Is there a reason for this discrepancy?
bool retVal = true;
DocuSignService.DocuSignWS.Envelope envelope = new DocuSignService.DocuSignWS.Envelope();
if (credentials == null)
{
error = ErrorCode.NO_CREDENTIALS;
errorMessage = Strings.GetString("STR_DS_NOCREDS");
return false;
}
envelope.Documents = new DocuSignService.DocuSignWS.Document[itemList.Count];
if (credentials != null)
{
int index = 0;
foreach (Document document in itemList)
{
DocuSignService.DocuSignWS.Document doc = new DocuSignService.DocuSignWS.Document();
doc.ID = document.ID.ToString();
doc.Name = document.Name;
//get document bytes
doc.PDFBytes = document.data;
envelope.Documents[index] = doc;
index++;
}
Tab[] tabs = new DocuSignService.DocuSignWS.Tab[0];
envelope.Tabs = tabs;
envelope.Recipients = new Recipient[0];
//send?
envelope.AccountId = credentials.dsaccountid;
envelope.Subject = "Documents are ready for your signature";
envelope.EmailBlurb = "Documents are awaiting your signature.";
envelope.EnableWetSign = false;
DocuSignService.DocuSignWS.EnvelopeStatus envStatus = new DocuSignService.DocuSignWS.EnvelopeStatus();
using (ServiceWrapper client = new ServiceWrapper())
{
client.UserName = credentials.loginemail;
client.Password = credentials.loginpassword;
client.IntegratorKey = "xxx-xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx";
try
{
envStatus = client.CreateEnvelope(envelope);
}
catch (Exception e)
{
ErrorViewerWriter.Write(e);
error = ErrorCode.UPLOADFAILED;
errorMessage = e.Message;
return false;
}
}
}
return retVal;

Related

Xamarin.iOS Cannot display photo in Push Notification

I have a Notification Service Extension and an AppGroup. I save a photo from camera in the PCL project and copy it to the App Group Container (shared folder).
In the Notification Service Extension I try to download the photo from the App Group container and attach it to the notification but it just does not display in the notification.
I also cannot debug the Service Extension to see what is going. As far as I know that is not possible currently still in Xamarin unless someone can correct me on that please.
Here is the code:
1.in my PCL I save the photo to the AppGroup when a save button is pressed:
private void ButtonSavePhoto_Clicked(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(photoFilePath))
{
Preferences.Set(AppConstants.CUSTOM_PICTURE_FILE_PATH, photoFilePath);
Preferences.Set(AppConstants.CUSTOM_PHOTO_SET_KEY, true);
if (Device.RuntimePlatform == Device.iOS)
{
bool copiedSuccessfully = DependencyService.Get<IPhotoService>().CopiedFileToAppGroupContainer(photoFilePath);
if (copiedSuccessfully)
{
var customPhotoDestPath = DependencyService.Get<IPhotoService>().GetAppContainerCustomPhotoFilePath();
// save the path of the custom photo in the AppGroup container to pass to Notif Extension Service
DependencyService.Get<IGroupUserPrefs>().SetStringValueForKey("imageAbsoluteString", customPhotoDestPath);
// condition whether to use custom photo in push notification
DependencyService.Get<IGroupUserPrefs>().SetBoolValueForKey("isCustomPhotoSet", true);
}
}
buttonSavePhoto.IsEnabled = false;
}
}
2.in my iOS project, Dependency injection calls this method when pressing save button:
public bool CopiedFileToAppGroupContainer(string filePath)
{
bool success = false;
string suiteName = "group.com.company.appName";
var appGroupContainerUrl = NSFileManager.DefaultManager.GetContainerUrl(suiteName);
var appGroupContainerPath = appGroupContainerUrl.Path;
var directoryNameInAppGroupContainer = Path.Combine(appGroupContainerPath, "Pictures");
var filenameDestPath = Path.Combine(directoryNameInAppGroupContainer, AppConstants.CUSTOM_PHOTO_FILENAME);
try
{
Directory.CreateDirectory(directoryNameInAppGroupContainer);
if (File.Exists(filenameDestPath))
{
File.Delete(filenameDestPath);
}
File.Copy(filePath, filenameDestPath);
success = true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return success;
}
Now the path for the photo in the App Group container is:
/private/var/mobile/Containers/Shared/AppGroup/12F209B9-05E9-470C-9C9F-AA959D940302/Pictures/customphoto.jpg
3.Finally in the Notification Service Extension I try to attach the photo to the push notification:
public override void DidReceiveNotificationRequest(UNNotificationRequest request, Action<UNNotificationContent> contentHandler)
{
ContentHandler = contentHandler;
BestAttemptContent = (UNMutableNotificationContent)request.Content.MutableCopy();
string imgPath;
NSUrl imgUrl;
string notificationBody = BestAttemptContent.Body;
string notifBodyInfo = "unknown";
string suiteName = "group.com.company.appname";
NSUserDefaults groupUserDefaults = new NSUserDefaults(suiteName, NSUserDefaultsType.SuiteName);
string pref1_value = groupUserDefaults.StringForKey("user_preference1");
string[] notificationBodySplitAtDelimiterArray = notificationBody.Split(',');
userPrefRegion = notificationBodySplitAtDelimiterArray[0];
bool isCustomAlertSet = groupUserDefaults.BoolForKey("isCustomAlert");
bool isCustomPhotoSet = groupUserDefaults.BoolForKey("isCustomPhotoSet");
string alarmPath = isCustomAlertSet == true ? "customalert.wav" : "default_alert.m4a";
if (isCustomPhotoSet)
{
// this is the App Group url of the custom photo saved in PCL
imgPath = groupUserDefaults.StringForKey("imageAbsoluteString");
}
else
{
imgPath = null;
}
if (imgPath != null )
{
imgUrl = NSUrl.FromString(imgPath);
}
else
{
imgUrl = null;
}
if (!string.IsNullOrEmpty(pref1_value))
{
if (BestAttemptContent.Body.Contains(pref1_value))
{
if (imgUrl != null)
{
// download the image from the AppGroup Container
var task = NSUrlSession.SharedSession.CreateDownloadTask(imgUrl, (tempFile, response, error) =>
{
if (error != null)
{
ContentHandler(BestAttemptContent);
return;
}
if (tempFile == null)
{
ContentHandler(BestAttemptContent);
return;
}
var cache = NSSearchPath.GetDirectories(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomain.User, true);
var cachesFolder = cache[0];
var guid = NSProcessInfo.ProcessInfo.GloballyUniqueString;
var fileName = guid + "customphoto.jpg";
var cacheFile = cachesFolder + fileName;
var attachmentUrl = NSUrl.CreateFileUrl(cacheFile, false, null);
NSError err = null;
NSFileManager.DefaultManager.Copy(tempFile, attachmentUrl, out err);
if (err != null)
{
ContentHandler(BestAttemptContent);
return;
}
UNNotificationAttachmentOptions options = null;
var attachment = UNNotificationAttachment.FromIdentifier("image", attachmentUrl, options, out err);
if (attachment != null)
{
BestAttemptContent.Attachments = new UNNotificationAttachment[] { attachment };
}
});
task.Resume();
}
BestAttemptContent.Title = "My Custom Title";
BestAttemptContent.Subtitle = "My Custom Subtitle";
BestAttemptContent.Body = "Notification Body";
BestAttemptContent.Sound = UNNotificationSound.GetSound(alarmPath);
}
}
else
{
pref1_value = "error getting extracting user pref";
}
// finally display customized notification
ContentHandler(BestAttemptContent);
}
/private/var/mobile/Containers/Shared/AppGroup/12F209B9-05E9-470C-9C9F-AA959D940302/Pictures/customphoto.jpg
From shared code, when image getting from AppGroup .You can check the file path whether work in this project.
imgPath = groupUserDefaults.StringForKey("imageAbsoluteString");
If not getting file from this path. You can get Url from AppGroup directly.Here is a sample as follow:
var FileManager = new NSFileManager();
var appGroupContainer = FileManager.GetContainerUrl("group.com.company.appName");
NSUrl fileURL = appGroupContainer.Append("customphoto.jpg", false);
And if fileURL can not be directly used, also can convert to NSData and save to local file system. This also can be a try.
Here is a sample below that grabs from local file system:
public static void Sendlocalnotification()
{
var localURL = "...";
NSUrl url = NSUrl.FromString(localURL) ;
var attachmentID = "image";
var options = new UNNotificationAttachmentOptions();
NSError error;
var attachment = UNNotificationAttachment.FromIdentifier(attachmentID, url, options,out error);
var content = new UNMutableNotificationContent();
content.Attachments = new UNNotificationAttachment[] { attachment };
content.Title = "Good Morning ~";
content.Subtitle = "Pull this notification ";
content.Body = "reply some message-BY Ann";
content.CategoryIdentifier = "message";
var trigger1 = UNTimeIntervalNotificationTrigger.CreateTrigger(0.1, false);
var requestID = "messageRequest";
var request = UNNotificationRequest.FromIdentifier(requestID, content, trigger1);
UNUserNotificationCenter.Current.AddNotificationRequest(request, (err) =>
{
if (err != null)
{
Console.Write("Notification Error");
}
});
}

Document completion Notification is not sent to each recipient as expected, when signing process is initiated using the DocuSign API

I’m testing document signing via a Docusign template through the Docusign API. Once the document signing is complete, each recipient is supposed to receive a Document completed notification, with the link to view the document. But the Document completion Notification is not sent to each recipient as expected, when signing process is initiated using the DocuSign API. Please direct me to the help link on this.
I also, would like to know how to set up the certificate completion document to be sent to each recipient on completion of signing. Any help on above is appreciated.
Thanks
I am seeing you are setting clientUserId for all types of signers except Vendor roles. Now setting clientUserId for the signer tells DocuSign that you are implementing embedded signing in your app. As its an embedded signing, so by Default and Design, DocuSign does not send any types of email notification to the embedded signer. To make DocuSign send the completion email with Certification of Completion, you need to modify Signing Settings in your DocuSign Account. In your DS Account, Go To Admin -> Select Signing Settings from the Left Hand Side Navigation under SIGNING AND SENDING section. Then, scroll to the bottom on Signing Settings page to Envelope Delivery section as shown in below screenshot, select Send Completion emails to embedded signers and also select Attach Certificate of completion to envelope. This should resolve your issue.
Please find below the code for creating and sending envelope and initiating signing for the Agent and Purchaser roles.
[Authorize]
[HttpPost]
[ValidateAntiForgeryToken]
public JsonResult Create(CreateContractViewModel viewModel)
{
if (!ModelState.IsValid)
return Json(new { success = false, errors=ModelStateExtensions.GetErrors(ViewData.ModelState.Values) }, JsonRequestBehavior.AllowGet);
try
{
#region "Validation"
if (viewModel.Purchasers == null) //only allow max. of 2 purchasers at this point in time.
return Json(new { success = false, errors = "Minimum of one Purchaser is required." },
JsonRequestBehavior.AllowGet);
if (viewModel.Purchasers.Count > 2) //only allow max. of 2 purchasers at this point in time.
return Json(new { success = false, errors = "Number of Purchasers allowed exceed." },
JsonRequestBehavior.AllowGet);
#endregion
// Get template based on drop down selection.
var envTemplate = Templates.Get(viewModel.SelectTemplateId);
// Assign all parties. eg. recipients, signers, and CCs
var docusignRecipients = envTemplate.Recipients;
var signers = docusignRecipients.Signers;
var carbonCopies = docusignRecipients.CarbonCopies;
var templateRolesList = new List<TemplateRole>();
if (viewModel.Purchasers.Count == 1) //remove Purchaser 2 if there's only 1 Purchaser needed.
{
var remove = signers.SingleOrDefault(x => x.RoleName.Equals(RoleNames.Purchaser_2));
if (remove != null)
signers.Remove(remove);
}
// Minimum of 1 Purchaser must exist.
var purchaser1 = viewModel.Purchasers[0];
var purchaser2 = viewModel.Purchasers.Count > 1
? viewModel.Purchasers[1]
: new Purchaser();
#region "Setup Template Roles"
// Recipients
var envelopeRecipients = new Recipients
{
RecipientCount = docusignRecipients.RecipientCount,
Signers = new List<Signer>(),
CarbonCopies = new List<CarbonCopy>()
};
// Signers
TemplateRoles.Signers.AddRoles(viewModel, signers, purchaser1, purchaser2, envelopeRecipients,
docusignRecipients);
// Carbon Copies
TemplateRoles.CarbonCopies.AddRoles(carbonCopies, envelopeRecipients, docusignRecipients);
#endregion
#region "Prepare & Create Envelope"
// Server template
var serverTemplates = new List<ServerTemplate>
{
new ServerTemplate
{
Sequence = "1",
TemplateId = viewModel.SelectTemplateId
}
};
// Server inline template
var serverInlineTemplates = new List<InlineTemplate>
{
new InlineTemplate
{
Sequence = "2",
Recipients = envelopeRecipients
}
};
// refactor this part to a new method.
var documentFactory = new DocumentFactory(viewModel.SelectTemplateId, _documentService);
var type = viewModel.OptionalInserts.GetType();
var optionalInserts = type.GetProperties();
var templateList = new List<CompositeTemplate>();
foreach (var insert in optionalInserts)
{
var value = insert.GetValue(viewModel.OptionalInserts);
var isChecked = (bool)value;
if (!isChecked) continue;
var template = documentFactory.GetTemplate(insert.Name);
var compositeTemplate = template.Compose();
templateList.Add(compositeTemplate);
}
// Prepare composite template, combining the Server and Inline templates
var compositeTemplates = new List<CompositeTemplate>
{
new CompositeTemplate
{
ServerTemplates = serverTemplates,
InlineTemplates = serverInlineTemplates
}
};
templateList.ForEach(t => compositeTemplates.Add(t));
// Create Envelope Definition
var envDef = Envelopes.CreateEnvDef(envTemplate, compositeTemplates);
// Create Envelope
var envSummary = Envelopes.CreateEnvelope(envDef);
#endregion
#region "Generate View Url"
// Generate recipient Url - Agent as 1st recipient by default
var agent = envelopeRecipients.Signers.SingleOrDefault(x => x.RoleName.Equals(RoleNames.Agent));
if (agent == null)
return
Json(
new
{
success = false,
errors = "No Agent role has been found.",
JsonRequestBehavior.AllowGet
});
var returnUrl = Url.Action("Sign", "Contract",
new { envelopeId = envSummary.EnvelopeId, routingOrder = agent.RoutingOrder, selectedTemplateId = viewModel.SelectTemplateId });//modified on 15-6-2017
// Get View Options.
var viewOptions = DocuSign.Recipients.ViewRequest(returnUrl, agent.ClientUserId, agent.Name, agent.Email);
// Create Recipient View
var view = Envelopes.CreateRecipientView(envSummary.EnvelopeId, viewOptions);
#endregion
return Json(new { success = true, returnUrl = view.Url, JsonRequestBehavior.AllowGet });
}
catch (Exception e)
{
return Json(new
{
success = false,
errors = e.Message,
JsonRequestBehavior.AllowGet
});
}
}
public ActionResult Sign(CreateContractViewModel viewModel)
//public ActionResult Sign() //previous code
{
var paramEvent = Request.QueryString["event"];
var paramEnvId = Request.QueryString["envelopeId"];
var paramRoutingOrder = Request.QueryString["routingOrder"];
var selectedTemplateId = Request.QueryString["selectedTemplateId"];
var contractIdQueryString = Request.QueryString["contractId"];
// Validation
if (string.IsNullOrEmpty(paramEvent) || string.IsNullOrEmpty(paramEnvId) ||
string.IsNullOrEmpty(paramRoutingOrder) || (paramEvent != SigningStatus.Signing_Complete))
return View("Error");
// Get next Signer
var recipients = Envelopes.ListRecipients(paramEnvId);
var signers = recipients.Signers;
///////addded on 25/5/2017 to check docusign value extraction
//var cfe = Envelopes.ListRecipientsWithTags(paramEnvId);
//Envelopes.ListCustomFields(paramEnvId);
//List<TextCustomField> tcfList = cfe.TextCustomFields;
//foreach (var tcf in tcfList)
//{
// string dfd = tcf.FieldId;
// string ddfdfd = tcf.Name;
// string sdf = tcf.Value;
// //string str = cfe.TextCustomFields[0].FieldId;
//}
//EnvelopesApi envelopesApi = new EnvelopesApi();
//CustomFieldsEnvelope cfe = envelopesApi.ListCustomFields(accountId, _templateId2);
//TemplateCustomFields cfUpdate = new TemplateCustomFields();
//cfUpdate.TextCustomFields = new System.Collections.Generic.List<TextCustomField>();
//TextCustomField tcf = new TextCustomField();
//tcf.FieldId = cfe.TextCustomFields[0].FieldId;
// Get template based on drop down selection.
//var envTemplate = Templates.Get(viewModel.SelectTemplateId);
//// Assign all parties. eg. recipients, signers, and CCs
//var docusignRecipients = envTemplate.Recipients;
//var signers1 = docusignRecipients.Signers;
int ContractId = 0;
if (contractIdQueryString != null && contractIdQueryString !="")
{
ContractId = int.Parse(contractIdQueryString.Trim());
}
ContractId= GetTabs(signers, selectedTemplateId, paramRoutingOrder, ContractId);
/////
//note:ClientUserId is made null only for the Vendor role in TemplateRoles.Signers.AddRoles method, so that
//signing continues for the Agent and Purchaser roles only
var nextSigner = (from s in signers
where
!string.IsNullOrEmpty(s.ClientUserId) &&
(Convert.ToInt32(s.RoutingOrder) > Convert.ToInt32(paramRoutingOrder))
orderby s.RoutingOrder
select s).FirstOrDefault();
//added following on 06/06/2018 to prevent workflow from proceeding to the next signers. But this will prevent capturing signed dates by signers
//if( paramEvent == "signing_complete" && paramRoutingOrder == "1")
// {
// return View("Completed");
// }
// return View("Completed");
//
#region Code that proceeds workflow to the next signer. Need to have an option to allow agent to go for following approach which allows purchaser to sign via the application or, another option that allows purchaser to receive an email notification for completing signing as done in above commented code
// No next Signer redirect to Completed page.
if (nextSigner == null) return View("Completed");
//var returnUrl = Url.Action("Sign", "Contract",
// new
// {
// envelopeId = paramEnvId,
// routingOrder = nextSigner.RoutingOrder,
// recipientId = nextSigner.RecipientId
// });//original code
var returnUrl = Url.Action("Sign", "Contract",
new
{
envelopeId = paramEnvId,
routingOrder = nextSigner.RoutingOrder,
recipientId = nextSigner.RecipientId,
selectedTemplateId = selectedTemplateId,
contractId = ContractId
});//modified on 19-6-2017
// Get View Options.
var viewOptions = DocuSign.Recipients.ViewRequest(returnUrl, nextSigner.ClientUserId, nextSigner.Name,
nextSigner.Email);
// Create Recipient View
var view = Envelopes.CreateRecipientView(paramEnvId, viewOptions);
return Redirect(view.Url);
#endregion
}
public static class TemplateRoles
{
public static class Signers
{
public static void AddRoles(CreateContractViewModel viewModel, List<Signer> signers, Purchaser purchaser1, Purchaser purchaser2,
global::DocuSign.eSign.Model.Recipients r, global::DocuSign.eSign.Model.Recipients recipients)
{
try
{
foreach (var signer in signers)
{
switch (signer.RoleName)
{
default:
throw new Exception("Unknown Signer role was found on the template.");
region "Role: Agent"
case RoleNames.Agent:
// Fill all Sign tabs for Agent role, which includes Purchaser fields.
// Agent role is the first point of the draft, therefore all the fields need to be prefilled.
var signerTabs = signer.Tabs;
if (signerTabs != null)
{
if (signerTabs.TextTabs != null)
{
var signerTextTabs = signerTabs.TextTabs;
DocuSign.Tabs.Sign(viewModel, signerTextTabs, purchaser1, purchaser2);
}
if (signerTabs.CheckboxTabs != null)
{
var signerCheckboxTabs = signerTabs.CheckboxTabs;
DocuSign.Tabs.SignCheckBoxes(viewModel, signerCheckboxTabs, purchaser1, purchaser2);//Assigning check box values
}
}
var agentSigner = recipients.Signers.Find(x => x.RoleName == "Agent");
if (agentSigner != null)
{
var s = new Signer();
s = agentSigner;
s.RoleName = signer.RoleName;
s.Name = signer.Name;
s.Email = signer.Email;
s.RoutingOrder = signer.RoutingOrder;
s.ClientUserId = Guid.NewGuid().ToString();
s.Tabs = signerTabs;
r.Signers.Add(s);
}
break;
#endregion
#region "Role: Purchaser 1"
case RoleNames.Purchaser_1:
var purchaserSigner = recipients.Signers.Find(x => x.RoleName == "Purchaser 1");
if (purchaserSigner != null)
{
var p1 = new Signer();
p1 = purchaserSigner;
p1.RoleName = RoleNames.Purchaser_1;
p1.Name =
(!string.IsNullOrEmpty(purchaser1.CompanyName)
? purchaser1.CompanyName
: $"{purchaser1.FirstName} {purchaser1.Surname}");
p1.Email = (!string.IsNullOrEmpty(purchaser1.Email) ? purchaser1.Email : null);
p1.RoutingOrder = signer.RoutingOrder;
p1.ClientUserId = Guid.NewGuid().ToString();
p1.Tabs = signer.Tabs;
r.Signers.Add(p1);
}
break;
#endregion
#region "Role: Purchaser 2"
case RoleNames.Purchaser_2:
var purchaser2Signer = recipients.Signers.Find(x => x.RoleName == "Purchaser 2");
if (purchaser2Signer != null)
{
var p2 = new Signer();
p2 = purchaser2Signer;
p2.RoleName = RoleNames.Purchaser_2;
p2.Name =
(!string.IsNullOrEmpty(purchaser2.CompanyName)
? purchaser2.CompanyName
: $"{purchaser2.FirstName} {purchaser2.Surname}");
p2.Email = (!string.IsNullOrEmpty(purchaser2.Email) ? purchaser2.Email : null);
p2.RoutingOrder = signer.RoutingOrder;
p2.ClientUserId = Guid.NewGuid().ToString();
p2.Tabs = signer.Tabs;
r.Signers.Add(p2);
}
break;
#endregion
#region "Role: Vendor"
case RoleNames.Vendor: // No embedded signing.
var vendorSigner = recipients.Signers.Find(x => x.RoleName == "Vendor");
if (vendorSigner != null)
{
var v = new Signer();
v = vendorSigner;
v.RoleName = signer.RoleName;
v.Name = signer.Name;
v.Email = signer.Email;
v.RoutingOrder = signer.RoutingOrder;
v.ClientUserId = null;
r.Signers.Add(v);
}
break;
#endregion
}
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
}
}

Closing Case in Acumatica through API

I want to close the case in Partners Portal remotely using Web API, whenever I close the case in my (client) side. I was able to implement the code but ran into below issue.
It is changing the status and resolution of the case in Partners Portal but Close Case button is enabled and it is visible in My Open Case bucket. Please let me know if I can close the case remotely using Web API or if I am missing anything.
protected virtual void CRCase_RowPersisting(PXCache cache, PXRowPersistingEventArgs e)
{
var caseRow = (CRCase)e.Row;
if (caseRow != null)
{
if (caseRow.Status == "C") // Closed
{
string cloud9CaseCD = null;
cloud9CaseCD = CRCaseForCreate.Current.CaseCD;
string acumaticaCaseCD = string.Empty;
CSAnswers aCCaseNoAttribute = PXSelect<CSAnswers,
Where<CSAnswers.refNoteID, Equal<Required<CSAnswers.refNoteID>>,
And<CSAnswers.attributeID, Equal<Required<CSAnswers.attributeID>>>>>.Select(new PXGraph(), CRCaseForCreate.Current.NoteID, "ACCASENO");
if (aCCaseNoAttribute != null)
{
acumaticaCaseCD = aCCaseNoAttribute.Value;
if(!string.IsNullOrEmpty(acumaticaCaseCD))
{
SP203000WS.Screen context = new SP203000WS.Screen();
context.CookieContainer = new System.Net.CookieContainer();
context.AllowAutoRedirect = true;
context.EnableDecompression = true;
context.Timeout = 1000000;
context.Url = "https://sso.acumatica.com/Soap/SP203000.asmx";
PartnerPortalCreds loginCreds = GetCreds();
string username = loginCreds.PARTPRTUSE;
string password = loginCreds.PARTPRTPAS;
SP203000WS.LoginResult result = context.Login(username, password);
SP203000WS.Content CR306000 = context.GetSchema();
context.Clear();
SP203000WS.Content[] CR306000Content = context.Submit
(
new SP203000WS.Command[]
{
new SP203000WS.Value
{
Value = acumaticaCaseCD,
LinkedCommand = CR306000.Case.CaseID
},
new SP203000WS.Value
{
Value = "C",
LinkedCommand = new SP203000WS.Field { FieldName="Status", ObjectName="Case"}
},
new SP203000WS.Value
{
Value = "RD",
LinkedCommand = new SP203000WS.Field { FieldName="Resolution", ObjectName="Case"}
},
CR306000.Actions.Submit,
CR306000.Case.CaseID
}
);
context.Logout();
}
}
}
}
}
Tried below code using CloseCase Action: -
protected virtual void CRCase_RowPersisting(PXCache cache, PXRowPersistingEventArgs e)
{
var caseRow = (CRCase)e.Row;
if (caseRow != null)
{
if (caseRow.Status == "C") // Closed
{
string cloud9CaseCD = null;
cloud9CaseCD = CRCaseForCreate.Current.CaseCD;
string acumaticaCaseCD = string.Empty;
CSAnswers aCCaseNoAttribute = PXSelect<CSAnswers,
Where<CSAnswers.refNoteID, Equal<Required<CSAnswers.refNoteID>>,
And<CSAnswers.attributeID, Equal<Required<CSAnswers.attributeID>>>>>.Select(new PXGraph(), CRCaseForCreate.Current.NoteID, "ACCASENO");
if (aCCaseNoAttribute != null)
{
acumaticaCaseCD = aCCaseNoAttribute.Value;
if (!string.IsNullOrEmpty(acumaticaCaseCD))
{
SP203010WS.Screen context = new SP203010WS.Screen();
context.CookieContainer = new System.Net.CookieContainer();
context.AllowAutoRedirect = true;
context.EnableDecompression = true;
context.Timeout = 1000000;
context.Url = "https://sso.acumatica.com/Soap/SP203010.asmx";
PartnerPortalCreds loginCreds = GetCreds();
string username = loginCreds.PARTPRTUSE;
string password = loginCreds.PARTPRTPAS;
SP203010WS.LoginResult result = context.Login(username, password);
SP203010WS.Content CR306000 = context.GetSchema();
context.Clear();
var commands1 = new SP203010WS.Command[]
{
new SP203010WS.Value
{
Value = acumaticaCaseCD,
LinkedCommand = CR306000.Case.CaseID
},
new SP203010WS.Value
{
Value = "Yes",
LinkedCommand = CR306000.Case.ServiceCommands.DialogAnswer, Commit = true
},
CR306000.Actions.CloseCase
};
var data = context.Submit(commands1);
context.Logout();
}
}
}
}
}
In the below image you can see that the case is already closed but Close Case menu button is still visible.
Close Case Confirmation Dialogbox on Partners Portal. How should I answer this dialogbox programatically while closing the case using Web API.
Have you tried to invoke Close action via Web API instead of changing values of the Status and Resolution fields? As far as I know, Close button on the Partner Portal updates support case Status to Open and Reason to Pending Closure. Then it's up to support personnel to manually close the case.
Finally found the solution. Updated the code for CloseCase (second code snippet). This will mark the case as Pending Closure in Partners Portal.

Docusign .net send enevlope issue on one server

We've implement docusign api into our application and it works fine in development and on one of our production servers, but on particular production server it fails when running the following line
var scope = new OperationContextScope(client.InnerChannel);
The exception thrown is
Error: InvalidDataContractException: Message: Type 'System.Threading.Tasks.Task`1[DocuSign.DocuSignWeb.SetSharedAccessResult]' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types.
Here is the complete method that is called
public DocuSignResponse SendEnvelope(Envelope envelope, string templateGUID)
{
var response = new DocuSignResponse();
var client = new DSAPIServiceSoapClient("DSAPIServiceSoap", URL);
try
{
var scope = new OperationContextScope(client.InnerChannel);
{
HttpRequestMessageProperty httpRequestProperty = new HttpRequestMessageProperty();
httpRequestProperty.Headers.Add("X-DocuSign-Authentication", AuthHeader);
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestProperty;
if (client.InnerChannel.State != System.ServiceModel.CommunicationState.Faulted)
{
// call service - everything's fine
}
else
{
// channel faulted - re-create your client and then try again
response.Success = false;
response.ErrorMessage = "Channel has faulted!!";
return response;
}
client.Open();
EnvelopeStatus envelopeStatus;
if (!string.IsNullOrEmpty(templateGUID))
{
DocuSignWeb.EnvelopeInformation envelopeInfo = new DocuSignWeb.EnvelopeInformation();
envelopeInfo.AccountId = AccountId;
envelopeInfo.EmailBlurb = envelope.EmailBlurb;
envelopeInfo.Subject = envelope.Subject;
// Configure the inline templates
DocuSignWeb.InlineTemplate inlineTemplate = new DocuSignWeb.InlineTemplate();
inlineTemplate.Sequence = "1";
inlineTemplate.Envelope = new DocuSignWeb.Envelope();
inlineTemplate.Envelope.Recipients = envelope.Recipients;
inlineTemplate.Envelope.AccountId = AccountId;
DocuSignWeb.CompositeTemplate template = new DocuSignWeb.CompositeTemplate();
template.InlineTemplates = new DocuSignWeb.InlineTemplate[] {inlineTemplate};
DocuSignWeb.ServerTemplate serverTemplate = new DocuSignWeb.ServerTemplate();
serverTemplate.Sequence = "1";
serverTemplate.TemplateID = templateGUID;
template.ServerTemplates = new[] {serverTemplate};
template.Document = envelope.Documents[0];
envelopeStatus = client.CreateEnvelopeFromTemplatesAndForms(envelopeInfo, new[] {template}, true);
}
else
{
envelopeStatus = client.CreateAndSendEnvelope(envelope);
}
// An envelope ID indicates that it succeeded
response.Success = true;
response.ResponseRef = envelopeStatus.EnvelopeID;
// No point doing this, as it wouldn't have been signed
// Request the status of that envelope
// response.Status = client.RequestStatus(envelopeStatus.EnvelopeID);
// Used if embed option being used
response.Envelope = envelope;
response.Status = envelopeStatus;
if (client.State != CommunicationState.Faulted)
{
client.Close();
}
else
{
client.Abort();
}
}
}
catch (InvalidDataContractException err)
{
err.LogError();
response.Success = false;
response.ErrorMessage = string.Format("InvalidDataContractException: Message: {0} StackTrace: {1} AuthHeader: {2}", err.Message, err.StackTrace, AuthHeader);
}
catch (CommunicationException err)
{
err.LogError();
response.Success = false;
response.ErrorMessage = string.Format("CommunicationException: Message: {0} StackTrace: {1}", err.Message, err.StackTrace);
client.Abort();
}
catch (TimeoutException err)
{
err.LogError();
response.Success = false;
response.ErrorMessage = string.Format("TimeoutException: Message: {0} StackTrace: {1}", err.Message, err.StackTrace);
client.Abort();
}
catch (Exception err)
{
err.LogError();
response.Success = false;
response.ErrorMessage = string.Format("Exception: Message: {0} StackTrace: {1}", err.Message, err.StackTrace);
client.Abort();
}
finally
{
client = null;
}
return response;
}
At the moment i'm at a loss as to what the issue is, given the same code and database works on another machines, so at present thinking it's environmental.
Any help greatly appreciated.
Looks like this turned out to be server issues on DocuSign side. Occasionally there are bugs and other issues that get rolled back in DocuSign's Demo sandbox environment.

Sharepoint: How to upload files with metadata including Taxonomy fields through web services

Being very new to SharePoint coding I have been assigned the task to create a prototype code to upload a file and setting the field values for that file that will show up when opening the sharepoint page with the file.
This has to be done from a remote machine and not the Sharepoint server itself so using the .Net objects for Sharepoint is out the question.
I quickly found out how to upload a file through the Sharepoint Web Service Copy.asmx:
void UploadTestFile() {
var file = #"C:\Temp\TestFile.doc";
string destinationUrl = "http://mysharepointserver/Documents/"
+ Path.GetFileName(file);
string[] destinationUrls = { destinationUrl };
var CopyWS = new Copy.Copy();
CopyWS.UseDefaultCredentials = true;
CopyWS.Url = "http://mysharepointserver/_vti_bin/copy.asmx";
CopyResult[] result;
byte[] data = File.ReadAllBytes(file);
FieldInformation mf1 = new FieldInformation {
DisplayName = "title",
InternalName = "title",
Type = FieldType.Text,
Value = "Dummy text"
};
FieldInformation mf2 = new FieldInformation {
DisplayName = "MyTermSet",
InternalName = "MyTermSet",
Type = FieldType.Note,
Value = "Test; Unit;"
};
CopyWS.CopyIntoItems(
"+",
destinationUrls,
new FieldInformation[] { mf1, mf2 },
data,
out result);
}
This code easily uploads any file to the target site but only fills the "title" field with info. The field MyTermSet in which I have added 3 terms allready - Test, Unit and Page - will not update with the values "Test;" and "Unit;".
Being very new to Sharepoint and me not grasping all the basics googling has told me that updating "File", "Computed" or "Lookup" fields does not work with the CopyIntoItems method, and MyTermSet being a Taxonomy field is - if I am correct - a Lookup field.
So how do I get MyTermSet updated with the values "Test;" and "Unit;" ?
I would really prefer If someone has a sample code on this. I have followed several hint-links but I am none the wiser. I have found no sample-code on this at all.
Have anyone made one single method that wraps it all? Or another method that takes in the destinationUrl from the file upload and updates the Term Set/Taxonomy field.
Puzzling together what I have found so far, I am now able to do as I wanted. But I would really like to be able to get the Taxonomy field GUIDs dynamically and NOT having to explicitly set them myself:
void UploadTestFile(string FileName, string DocLib, Dictionary<string, string> Fields = null) {
//Upload the file to the target Sharepoint doc lib
string destinationUrl = DocLib + Path.GetFileName(FileName);
string[] destinationUrls = { destinationUrl };
var CopyWS = new Copy.Copy();
CopyWS.UseDefaultCredentials = true;
CopyWS.Url = new Uri(new Uri(DocLib), "/_vti_bin/copy.asmx").ToString();
CopyResult[] result;
var data = File.ReadAllBytes(FileName);
CopyWS.CopyIntoItems(
"+",
destinationUrls,
new FieldInformation[0],
data,
out result);
if (Fields == null) return; //Done uploading
//Get the ID and metadata information of the fields
var list = new ListsWS.Lists();
list.UseDefaultCredentials = true;
var localpath = new Uri(DocLib).LocalPath.TrimEnd('/');
var site = localpath.Substring(0, localpath.LastIndexOf("/")); //Get the site of the URL
list.Url = new Uri(new Uri(DocLib), site + "/_vti_bin/lists.asmx").ToString(); //Lists on the right site
FieldInformation[] fiOut;
byte[] filedata;
var get = CopyWS.GetItem(destinationUrl, out fiOut, out filedata);
if (data.Length != filedata.Length) throw new Exception("Failed on uploading the document.");
//Dictionary on name and display name
var fieldInfos = fiOut.ToDictionary(x => x.InternalName, x => x);
var fieldInfosByName = new Dictionary<string, FieldInformation>();
foreach (var item in fiOut) {
if (!fieldInfosByName.ContainsKey(item.DisplayName)) {
fieldInfosByName.Add(item.DisplayName, item);
}
}
//Update the document with fielddata - this one can be extended for more than Text and Note fields.
if (!fieldInfos.ContainsKey("ID")) throw new Exception("Could not get the ID of the upload.");
var ID = fieldInfos["ID"].Value; //The ID of the document we just uploaded
XDocument doc = new XDocument(); //Creating XML with updates we need
doc.Add(XElement.Parse("<Batch OnError='Continue' ListVersion='1' ViewName=''/>"));
doc.Element("Batch").Add(XElement.Parse("<Method ID='1' Cmd='Update'/>"));
var methNode = doc.Element("Batch").Element("Method");
//Add ID
var fNode = new XElement("Field");
fNode.SetAttributeValue("Name", "ID");
fNode.Value = ID;
methNode.Add(fNode);
//Loop each field and add each Field
foreach (var field in Fields) {
//Get the field object from name or display name
FieldInformation fi = null;
if (fieldInfos.ContainsKey(field.Key)) {
fi = fieldInfos[field.Key];
}
else if (fieldInfosByName.ContainsKey(field.Key)) {
fi = fieldInfosByName[field.Key];
}
if (fi != null) {
//Fix for taxonomy fields - find the correct field to update
if (fi.Type == FieldType.Invalid && fieldInfos.ContainsKey(field.Key + "TaxHTField0")) {
fi = fieldInfos[field.Key + "TaxHTField0"];
}
else if (fi.Type == FieldType.Invalid && fieldInfosByName.ContainsKey(field.Key + "_0")) {
fi = fieldInfosByName[field.Key + "_0"];
}
fNode = new XElement("Field");
fNode.SetAttributeValue("Name", fi.InternalName);
switch (fi.Type) {
case FieldType.Lookup:
fNode.Value = "-1;#" + field.Value;
break;
case FieldType.Choice:
case FieldType.Text:
fNode.Value = field.Value;
break;
case FieldType.Note: //TermSet's
var termsetval = "";
var terms = field.Value.Split(';');
foreach (var term in terms) {
termsetval += "-1;#" + term + ";";
}
fNode.Value = termsetval.TrimEnd(';');
break;
default:
//..Unhandled type. Implement if needed.
break;
}
methNode.Add(fNode); //Adds the field to the XML
}
else {
//Field does not exist. No use in uploading.
}
}
//Gets the listname (not sure if it is the full path or just the folder name)
var listname = new Uri(DocLib).LocalPath;
var listcol = list.GetListCollection(); //Get the lists of the site
listname = (from XmlNode x
in listcol.ChildNodes
where x.Attributes["DefaultViewUrl"].InnerText.StartsWith(listname, StringComparison.InvariantCultureIgnoreCase)
select x.Attributes["ID"].InnerText).DefaultIfEmpty(listname).First();
//Convert the XML to XmlNode and upload the data
var xmldoc = new XmlDocument();
xmldoc.LoadXml(doc.ToString());
list.UpdateListItems(listname, xmldoc.DocumentElement);
}
Then I call it like this:
var fields = new Dictionary<string, string>();
fields.Add("Test", "Dummy Text");
fields.Add("MrTermSet", "Page|a4ba29c1-3ed5-47e9-b43f-36bc59c0ea5c;Unit|4237dfbe-22a2-4d90-bd08-09f4a8dd0ada");
UploadTestFile(#"C:\Temp\TestFile2.doc", #"http://mysharepointserver/Documents/", fields);
I would however prefer to call it like this:
var fields = new Dictionary<string, string>();
fields.Add("Test", "Dummy Text");
fields.Add("MrTermSet", "Page;Unit");
UploadTestFile(#"C:\Temp\TestFile2.doc", #"http://mysharepointserver/Documents/", fields);

Resources