Получить контакт (ы) в Acumatica через RESTful API - PullRequest
0 голосов
/ 10 февраля 2019

@ HB_ACUMATICA и др.,

Я работаю над интеграцией клиентской базы данных FileMaker с Acumatica в течение последних нескольких месяцев.Я могу получить доступ (получить / положить) к различным таблицам (сущностям), таким как PROJECT и CUSTOMER, но сущность CONTACT всегда выдает ошибку.Например,

https://mydomain.acumatica.com/entity/Default/18.200.001/Customer [работает нормально]

https://mydomain.acumatica.com/entity/Default/18.200.001/Contact [всегда ошибка 500]

** [РЕДАКТИРОВАТЬ: Приведенные выше примеры, конечно,неполное, если только вы не пытаетесь найти «все» записи контактов.При тестировании я указывал действительные идентификаторы контактов, например,

https://mydomain.acumatica.com/entity/Default/18.200.001/Customer/Nobody

, где «Никто» не является настоящим идентификатором контакта ... или так меня заставили поверить ... см. Мой ответ ниже] **

Везде, где я просматривал документацию, это означает, что «Контакт» - это правильное имя для сущности.Что я делаю не так?

Большое спасибо.- Эрик

Ответы [ 2 ]

0 голосов
/ 12 февраля 2019

НЕПРАВИЛЬНО: в поле данных указано «Отображаемое имя» - «ContactID» - это 102155, а не «Вегвайзер, Эрик»

Ага!Спасибо всем за потенциально полезную информацию, которая может пригодиться в будущем.Однако эти проблемы и мое беспокойство о том, чтобы что-то делать неправильно, оказались «красными сельдями».Еще раз, странное несоответствие Acumatica в соблюдении своих собственных соглашений озадачило меня.

Я пытался выполнить запрос в том же формате, что и в другом месте, например https://mydomain.acumatica.com/entity/Default/18.200.001/Customer/ACME001 (где «ACME001» - это действительный идентификатор клиента), отлично работает и все хорошо.Принимая во внимание, что https://mydomain.acumatica.com/entity/Default/18.200.001/Contact/Nobody (где «Никто» не является настоящим идентификатором контакта) не работает.

Почему?Несмотря на мой скептицизм и лучшее суждение, что 30-летний опыт программирования баз данных говорит мне, что ContactID Acumatica не является мудрой реализацией для уникального идентификатора записи ... Я верил тому, что мне сказали.Прямо там, в инспекторе свойств элемента, он говорит, что настоящее имя поля, помеченное «Contact ID», действительно, «ContactID».

Когда я в конце концов попробовал другую форму запроса,

https://mydomain.acumatica.com/entity/Default/18.200.001/Contact?$filter=LastName eq 'Wegweiser'&$select=FirstName,ContactID

Я наконец получил полезную нагрузку, которая показала мне правду: к сожалению, то, что в других местах было идентифицировано как «ContactID» и, согласно соглашению, разумно принято как уникальный идентификатор, на самом деле является разумно названным «DisplayName».Настоящий «ContactID», как и следовало ожидать, - это настоящий уникальный идентификатор.

Acumatica - удивительный инструмент.Это просто другое животное со многими головами (или, возможно, с хвостами), iMHO.


РЕДАКТИРОВАТЬ с помощью HB_ACUMATICA

Глядя на Contact.ContactID определение поляв Contact ЦАП он действительно объявлен как поле Integer и не имеет настраиваемого атрибута, который возвращал бы отображаемое значение String на экране:

#region ContactID
public abstract class contactID : IBqlField { }

[PXDBIdentity(IsKey = true)]
[PXUIField(DisplayName = "Contact ID", Visibility = PXUIVisibility.Invisible)]
[PXPersonalDataWarning]
public virtual Int32? ContactID { get; set; }
#endregion

Только с этим определением ЦАПдействительно не соответствует тому, что поле, отображаемое на экране, имеет тип String вместо Integer.Это объясняется тем, что Contact Screen использует график ContactMaint, который переопределяет поле DAC Contact.ContactID с использованием механизма CacheAttached:

[PXUIField(DisplayName = "Contact ID")]
[ContactSelector(true, typeof(ContactTypesAttribute.person), typeof(ContactTypesAttribute.employee))]
[PXMergeAttributes(Method = MergeMethod.Merge)]
public virtual void Contact_ContactID_CacheAttached(PXCache sender) { }

Обратите внимание, что переопределение поля Contact.ContactID добавляетатрибут ContactSelector, которого не было в Contact DAC.Беглый взгляд на этот атрибут показывает, что он использует поле Description, равное PXSelector, для отображения строки DisplayName на экране вместо значения Integer.Эта замена предназначена только для отображения на экранах, ограниченных графиком ContactMaint, все операции с базой данных остаются на основе Integer.Отрывок ContactSelector:

public ContactSelectorAttribute(bool showContactsWithNullEmail, params Type[] contactTypes)
    : base(GetQuery(typeof(Contact.contactID), showContactsWithNullEmail, contactTypes))
{
    if (contactTypes == null || contactTypes.Length == 0)
        throw new ArgumentNullException(nameof(contactTypes));

    DescriptionField = typeof(Contact.displayName);         
}

При вызове веб-службы REST не используется Contact ЦАП вместо ContactMaint графика, поэтому для него требуется Integer вместо String значения.

0 голосов
/ 11 февраля 2019

Это известная проблема, которая еще не исправлена.Сообщение об ошибке, возвращаемое вызовом веб-службы:

{
    "message": "An error has occurred.",
    "exceptionMessage": "Optimization cannot be performed.The following fields cause the error:\r\nAddressValidated: View AddressCurrent has BQL delegate\r\n",
    "exceptionType": "PX.Api.ContractBased.OptimizedExport.CannotOptimizeException",
    "stackTrace": "   at PX.Api.ContractBased.OptimizedExport.NotWorkingOptimizedExportProvider.get_CanOptimize() in C:\\Bld2\\AC-FULL2017R21-JOB1\\sources\\NetTools\\PX.Api.ContractBased\\OptimizedExport\\NotWorkingOptimizedExportProvider.cs:line 84\r\n   at PX.Api.ContractBased.EntityService.GetList(ISystemContract systemContract, String version, String name, EntityImpl entity, Boolean returnFullEntities, CbOperationContext operationContext, Boolean ignoreValueFields, PXGraph graph) in C:\\Bld2\\AC-FULL2017R21-JOB1\\sources\\NetTools\\PX.Api.ContractBased\\EntityService.cs:line 116\r\n   at PX.Api.ContractBased.Soap.SoapFacadeBase.GetListImpl(Entity entity, Boolean returnFullEntities) in C:\\Bld2\\AC-FULL2017R21-JOB1\\sources\\NetTools\\PX.Api.ContractBased\\Soap\\SoapFacadeBase.cs:line 83\r\n   at PX.Api.ContractBased.SystemContracts.V2.RestController.GetList(String objectName, String select, String filter, String expand, String custom, Nullable`1 skip, Nullable`1 top) in C:\\Bld2\\AC-FULL2017R21-JOB1\\sources\\NetTools\\PX.Api.ContractBased\\SystemContracts\\V2\\RestController.cs:line 247\r\n   at lambda_method(Closure , Object , Object[] )\r\n   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.<GetExecutor>b__9(Object instance, Object[] methodParameters)\r\n   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__5.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__5.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Filters.AuthorizationFilterAttribute.<ExecuteAuthorizationFilterAsyncCore>d__2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Controllers.ExceptionFilterResult.<ExecuteAsync>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Web.Http.Controllers.ExceptionFilterResult.<ExecuteAsync>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"
}

Ошибка возникает при получении поля AddressValidated.Однако это поле даже не возвращается запросом. РЕДАКТИРОВАТЬ, он возвращается только тогда, когда активирована функция проверки адреса.

В качестве обходного пути я выбрал один контакт по ContactID, а затем скопировал все возвращенное имя поля.Затем я помещаю эти поля в предложение select запроса, которое указывает, какое поле должно быть возвращено.Похоже, что у этого побочного эффекта не было AddressValidated, и вызов был успешным:

https://mydomain.acumatica.com/entity/Default/18.200.001/?$select=Active,AddressIsSameAsInAccount,BusinessAccount,CompanyName,ContactClass,ContactID,ContactMethod,ConvertedBy,DateOfBirth,DisplayName,DoNotCall,DoNotEmail,DoNotFax,DoNotMail,Duplicate,DuplicateFound,Email,Fax,FaxType,FirstName,Gender,Image,JobTitle,LanguageOrLocale,LastIncomingActivity,LastName,LastOutgoingActivity,MaritalStatus,MiddleName,NoMarketing,NoMassMail,Owner,OwnerEmployeeName,ParentAccount,Phone1,Phone1Type,Phone2,Phone2Type,Phone3,Phone3Type,QualificationDate,Reason,Source,SourceCampaign,SpouseOrPartnerName,Status,Synchronize,Title,Type,WebSite,Workgroup,WorkgroupDescription

Как упоминал Самвел Петросов, вы также можете расширить конечную точку и удалить из нее поле AddressValidated (похоже, это не относится к полям, исходящим из конечной точки по умолчанию, используется только для настраиваемых полей): enter image description here

При таком подходе вам придется изменить конечную точку в URL-адресе нарасширенное имя конечной точки (в данном примере «DefaultPlus»):

https://mydomain.acumatica.com/entity/DefaultPlus/18.200.001/Contact
...