---
title: "Руководство по подключению к OData веб-сервисам Microsoft Dynamics 365 for Operations"
canonical: https://ontargit.com/kz/rukovodtvo-po-podklyucheniyu-k-odata/
date: 2021-04-16 14:38:53
description: "Подключение OData веб-сервис в Dynamics 365 for Operations, которые позволяют считывать и модифицировать data entity"
keywords: "OData веб-сервис"
categories: [ERP]
---

# Описание

В D365O, по сравнению с AX2012, была добавлена возможность подключения к развернутым по-умолчанию веб-сервисам OData. Веб-сервисы этого типа позволяют считывать и модифицировать данные всех data entity D365O, настройки которых позволяют это делать. Считывание данных происходит в формате JSON.

Для считывания данных, клиент отправляет на веб-сервисы GET запросы, в которых указывает то, какие данные из data entity он хочет получить. При этом доступна фильтрация по каждому из полей data entity, в том числе и по полям, входящим в другие поля. Подробно о возможных запросах можно прочитать в документации протокола OData , доступной по следующей ссылке: [http://docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/part1-protocol/odata-v4.0-errata03-os-part1-protocol-complete.html#_Toc453752278.](http://docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/part1-protocol/odata-v4.0-errata03-os-part1-protocol-complete.html#_Toc453752278.) Далее из всего функционала по работе с data entity через веб-сервисы приведена информация только по фильтрации и выборке выводимых полей.

Базовым запросом к веб-сервисам OData является запрос вида **«АДРЕС D365O»/data.** Например,[ https://oit-adm-boryseda2b5401719f08ddevaos.cloudax.dynamics.com/data.](https://oit-adm-boryseda2b5401719f08ddevaos.cloudax.dynamics.com/data.) При этом возвращается список названий всех data entity в D365O. При чем, выведенные названия – это не названия файлов data entity в AOT, а PublicCollectionName, указанные в параметрах этих файлов.

При добавлении к базовому запросу названия одной из data entity указаных в списке (запрос вида **«АДРЕС D365O»/data/«НАЗВАНИЕ DATA ENTITY»**), выводятся все значения, содержащиеся в data entity, у которых компания соответствует компании пользователя, под чьей учетной записью был получен доступ к веб-сервису. Например: [https://oit-adm-boryseda2b5401719f08ddevaos.cloudax.dynamics.com/data/ReleasedProductMasters.](https://oit-adm-boryseda2b5401719f08ddevaos.cloudax.dynamics.com/data/ReleasedProductMasters.)

Для получения данных всех компаний, необходимо добавить параметр cross-company=true: **«АДРЕС D365O»/data/«НАЗВАНИЕ DATA ENTITY»?cross-company=true.** Например: [https://oit-adm-boryseda2b5401719f08ddevaos.cloudax.dynamics.com/data/ReleasedProductMasters?cross-company=true.](https://oit-adm-boryseda2b5401719f08ddevaos.cloudax.dynamics.com/data/ReleasedProductMasters?cross-company=true.)

Для получения данных по конкретной компании, помимо указания cross-company=true, необходимо использовать фильтрацию.

Фильтрация в протоколе OData осуществляется с помощью добавления к строке URL параметра $filter=. После знака равенства указываются все условия фильтрации. При построении условий фильтрации могут использоваться следующие операторы:

# Операторы сравнения:

- __

1. eq (равно).

- __

2. ne (не равно).

- __

3. gt (больше).

- __

4. ge (больше или равно).

- __

5. lt (меньше).

- __

6. le (меньше или равно).

- __

7. has (содержит значение (например, Style has Sales.Color’Yellow’)).

# Логические операторы:

- __

1. and (и).

- __

2. or (или).

- __

3. not (не).

# Арифметические операторы:

- __

1. add (суммирование).

- __

2. sub (вычитание).

- __

3. mul (умножение).

- __

4. div (деление).

- __

5. mod (остаток от деления).

# Операторы группировки: ().

Самый простые условия выглядят так: **«НАЗВАНИЕ ПОЛЯ» «ОПЕРАТОР СРАВНЕНИЯ» «ЗНАЧЕНИЕ» «ЛОГИЧЕСКИЙ ОПЕРАТОР» «НАЗВАНИЕ ПОЛЯ» «ОПЕРАТОР СРАВНЕНИЯ» «ЗНАЧЕНИЕ».** Например, добавив условия фильтрации к указанному раннее URL, получаем: https://oit-adm-boryseda2b5401719f08ddevaos.cloudax.dynamics.com/data/ReleasedProductMasters?cross-company=true&amp;$filter=ItemNumber eq ‘M0008’ and BOMLevel eq 0 and dataAreaId eq ‘USMF’. Как видно, последнее из полей в даной строке – dataAreaId, которое отвечает за выбор компании.

При фильтрации по текстовым полям, нужно использовать одинарные кавычки ‘ ’ вокруг значения.

При фильтрации по числовым полям, значение записывается без кавычек.

При фильтрации по значению enum полей, используется синтаксис вида: **«NAMESPACE, К КОТОРОМУ ОТНОСИТСЯ ENUM».«НАЗВАНИЕ ENUM»’«ЗНАЧЕНИЕ ENUM»’.** Например, значение может выглядеть следующим образом: System.Web.OData.TestCommon.Models.Gender’Male’.

Стоит отметить, что при переходе в браузере по URL, в котором указаны условия фильтрации, вид этого URL изменяется, заменяя символы пробела на **%20**, а символы одинарных кавычек на **%27**. Волноваться по этому поводу не нужно, – можно даже комбинировать в запросе указанные выше обозначения этих знаков и их нормальное обозначение, при этом запрос должен отработать корректно.

Описанные выше условия отфильтровывают результат, идущий на выход сервиса, но при этом клиентская программа все еще получает все поля, относящиеся к оставшимся после фильтрации data entity. Чтобы указать, какие конкретно поля нужно возвращать, в запросе URL используется параметр **$select=**. После знака равенства необходимо через запятую, слитно, перечислить необходимые поля. Например: $select=ItemNumber,BOMLevel.

Таким образом, запрос для одновременной фильтрации и выборки выглядит, например, следующим образом: [https://oit-adm-boryseda2b5401719f08ddevaos.cloudax.dynamics.com/data/ReleasedProductMasters?cross-company=true&amp;$select=ItemNumber,BOMLevel,dataAreaId&amp;$filter=ItemNumber eq ’M0008’ and BOMLevel eq 0.](https://oit-adm-boryseda2b5401719f08ddevaos.cloudax.dynamics.com/data/ReleasedProductMasters?cross-company=true&amp;$select=ItemNumber,BOMLevel,dataAreaId&amp;$filter=ItemNumber%20eq%20’M0008’%20and%20BOMLevel%20eq%200.)

При возникновении необходимости в формировании более сложных запросов, рекомендую ознакомиться с [**документацией**](http://docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/part1-protocol/odata-v4.0-errata03-os-part1-protocol-complete.html#_Toc453752278). OData поддерживает большой набор встроенных функций, – от обрезки строк до математических округлений и выделения лет, месяцев, дней и времени из дат.

На выходе OData вебсервисов D365O используется JSON следующей структуры:

- __

1. value – массив, содержащий информацию о каждой возвращенной записи data entity. Данные в массиве содержат все public поля data entity, а также хеш код записи @odata.etag.

- __

2. @odata.context –адрес источника данных.

# Вызов только считывающего данные веб-сервиса D365O из Desktop приложения

# Подготовительный этап

- __

1. Создать новый C# проект.

- __

2. Если нет зарегистрированного приложения в Azure, зарегистрировать его, как описано в статье **[«Регистрация приложения в Azure»](https://ontargit.com/registratsia-prilozheniya-v-azure/).**

- __

3. Добавить ссылку на проект JsonRequestSender, который описан в статье **[«Создание вспомогательных классов JsonRequestSender для отправки запросов JSON»](https://ontargit.com/sozdanie-jsonrequestsender/).**

- __

4. Добавить ссылку на пакет System.Runtime.Serialization, как описано в статье[ **«Добавление пакета System.Runtime.Serialization»**](https://ontargit.com/dobavlenie-paketa-system-runtime-serialization/)**. **

# Основной код

При использовании вспомогательного проекта JsonRequestSender, вызов вебсервиса сводится к одной команде. Для этого сначала нужно создать объект класса RequestSender_OAuth, так как в вебсервисах D365O используется аутентификация OAuth.

Copy to Clipboard
Syntax HighlighterRequestSender_OAuth jsonRequestSender = new RequestSender_OAuth();

Далее, так как считать данные из OData вебсервисов D365O можно с помощью GET запросов, отправляем GET запрос, по правилам запросов OData.

Copy to Clipboard
Syntax HighlighterGoalHeadingsOutputContract goalHeadingsOutputContract = requestSender.sendGETRequest(“data/GoalHeadings?cross-company=true”);

В метод sendGETRequest() передаются один дженерик класс и один аргумент.

Дженерик класс определяет то, объект какого класса должен использоваться в качестве выходного контракта с данными в вебсервисе.

Аргумент определяет относительный адрес вызываемого метода вебсервиса в ресурсе. В данном случае относительный адрес показывает, что нужно вернуть записи из data entity с PublicCollectionName GoalHeadings, по всем компаниям.

# Пример

В качестве примера приведено приложение Microsoft forms для получения данных из data entity HcmGoalHeadingEntity D365O.

_

Copy to Clipboard
Syntax Highlighterusing System;**
using System.Windows.Forms;

namespace GoalHeadingsODataServiceSimple

{

 public partial class GoalHeadingsODataServiceSimpleForm : Form

 {

 public GoalHeadingsODataServiceSimpleForm()

 {

 InitializeComponent();

 }

 private void getGoalHeadingsButton_Click(

 object sender,

 EventArgs e)

 {

 try

 {

 GoalHeadingsODataServiceSimple service = new GoalHeadingsODataServiceSimple();

 GoalHeadingsOutputContract outputContract = service.callService();

 goalHeadingsTextBox.Text = outputContract.ToString();

 }

 catch (Exception ex)

 {

 goalHeadingsTextBox.Text = ex.Message;

 }

 }

 }

}

using System.Runtime.Serialization;

namespace GoalHeadingsODataServiceSimple

{

 [DataContract]

 class GoalHeadingsOutputContract

 {

 [DataMember]

 public GoalHeadingOutputContract[] value {get; set;}

 [DataMember(Name = “@odata.context”)]

 public string context {get; set;}

 public override string ToString()

 {

 string text = “”;

 foreach (GoalHeadingOutputContract outputContract in value)

 {

 text += string.Format(

 “{0}\r\n”,

 outputContract.ToString());

 }

 return text;

 }

 }

}

using System.Runtime.Serialization;

namespace GoalHeadingsODataServiceSimple

{

 [DataContract]

 class GoalHeadingOutputContract

 {

 [DataMember]

 public string goalHeadingId {get; set;}

 [DataMember]

 public NoYes isActive {get; set;}

 [DataMember]

 public string description {get; set;}

 [DataMember(Name = “@odata.etag”)]

 public string etag {get; set;}

 public override string ToString()

 {

 return string.Format(

 “Goal Heading Id: ‘{0}’; Active: ‘{1}’; Description: ‘{2}'”,

 this.goalHeadingId,

 this.isActive,

 this.description);

 }

 }

}

namespace GoalHeadingsODataServiceSimple

{

 enum NoYes

 {

 No = 0,

 Yes = 1

 }

}

using JsonRequestSender;

namespace GoalHeadingsODataServiceSimple

{

 class GoalHeadingsODataServiceSimple

 {

 public GoalHeadingsOutputContract callService()

 {

 RequestSender_OAuth requestSender = new RequestSender_OAuth();

 return requestSender.sendGETRequest(“data/GoalHeadings?cross-company=true”);

 }

 }

}

# Вызов полнофункционального веб-сервиса D365O из Desktop приложения

# Подготовительный этап

- _

1. Создать новый C# проект.

- __

2. Если нет зарегистрированного приложения в Azure, зарегистрировать его, как описано в статье [«Регистрация приложения в Azure»](https://ontargit.com/registratsia-prilozheniya-v-azure/).**

- __

3. Добавить ссылку на проект ODataProxy, который описан в статье **[«Создание вспомогательных классов ODataProxy для подключения к D365O по протоколу OData».](https://ontargit.com/sozdanie-odataproxy-dlya-d3650-po-protokolu-odata/)**

- __

4. Добавить пакет Microsoft.OData.Client, как описано в статье **[«Добавление пакета Microsoft.OData.Client».](https://ontargit.com/dobavlenie-paketa-microsoft-odata-client/)**

# Основной код

# Класс сервиса

Не зависимо от того, нужно ли найти, создать, обновить или удалить запись в data entity, первым делом нужно создать класс, наследующий вспомогательный класс ODataServiceBase из проекта ODataProxy. Например:

Copy to Clipboard
Syntax Highlighterclass PurchTableODataService : ODataServiceBase

Все операции должны быть объявлены в этом классе, так как он настроен на работу со всеми data entity системы, к которой нужно подключиться.

# Создание данных

Для создания новой записи, создается и заполняется значениями объект нужной data entity из прокси класса. При этом нужно заполнить все обязательные поля data entity.

Copy to Clipboard
Syntax HighlighterPurchaseOrderHeaderV2 purchTable = new PurchaseOrderHeaderV2();

 purchTable.PurchaseOrderNumber = purchId;

 purchTable.OrderVendorAccountNumber = orderAccount;

 purchTable.CurrencyCode = currencyCode;

 purchTable.InvoiceVendorAccountNumber = invoiceAccount;

 purchTable.LanguageId = languageId;

 purchTable.DataAreaId = dataAreaId;

 purchTable.DeliveryAddressCountryRegionId = deliveryCountryRegionId;

 purchTable.DeliveryAddressDescription = deliveryAddressDescription;

Далее у OData клиента, созданного на основе прокси класса, вызывается метод AddObject, в котором первым параметром передается PublicCollectionName data entity, а вторым – переменная контракта данных data entity.

Copy to Clipboard
Syntax Highlighterclient.AddObject(

 “PurchaseOrderHeadersV2”,

 purchTable);

После этого запрос отправляется на сервер, желательно в try-catch:

Copy to Clipboard
Syntax Highlightertry

 {

 client.SaveChanges();

 }

 catch (Exception ex)

# Считывание данных

Для считывания данных по data entity нужно выполнить Linq запрос для выбранной в переменной клиента data entity.

При этом результат нужно преобразовать в DataServiceCollection для корректной отработки операции обновления. Это позволит клиенту отправлять PATCH запросы вместо PUT для обновления данных, что предотвратит ошибки при наличии в data entity закрытых на редактирование полей.

Запрос лучше отправлять в try-catch.

Copy to Clipboard
Syntax HighlighterDataServiceCollection purchTables;

 try

 {

 purchTables = new DataServiceCollection(

 client.PurchaseOrderHeadersV2.Where(

 purchTable =&gt;

 (purchTable.PurchaseOrderNumber == purchId

 || purchId == “”)

 &amp;&amp; (purchTable.OrderVendorAccountNumber == orderAccount

 || orderAccount == “”)

 &amp;&amp; (purchTable.CurrencyCode == currencyCode

 || currencyCode == “”)

 &amp;&amp; (purchTable.InvoiceVendorAccountNumber == invoiceAccount

 || invoiceAccount == “”)

 &amp;&amp; (purchTable.LanguageId == languageId

 || languageId == “”)

 &amp;&amp; (purchTable.DataAreaId == dataAreaId

 || dataAreaId == “”)

 &amp;&amp; (purchTable.DeliveryAddressCountryRegionId == deliveryCountryRegionId

 || deliveryCountryRegionId == “”)

 &amp;&amp; (purchTable.DeliveryAddressDescription == deliveryAddressDescription

 || deliveryAddressDescription == “”)));

 }

 catch (Exception ex)

Рассмотрим часть Linq запроса:

Copy to Clipboard
Syntax Highlighterclient.PurchaseOrderHeadersV2.Where(

 purchTable =&gt;

 (purchTable.PurchaseOrderNumber == purchId

 || purchId == “”)

Этот запрос означает, что из клиента OData вызывается считывание данных по data entity с PublicCollectionName PurchaseOrderHeadersV2, и при этом должны вернутся такие записи, у которых поле PurchaseOrderNumber соответствует некоторой переменной purchId, или же записи вне зависимости от PurchaseOrderNumber, если переменная purchId содержит пустую строку.

Таким образом можно отфильтровать результат по любым полям data entity.

Вне зависимости от того, преобразовывался ли результат запроса в рекомендуемый класс DataServiceCollection, по полученным значениям можно пройти с помощью цикла foreach.

Copy to Clipboard
Syntax Highlighterforeach (PurchaseOrderHeaderV2 purchaseTable in purchTables)

# Изменение данных

Для изменения данных data entity, их сначала нужно получить. То есть, сначала необходимо выполнить все шаги описанные в инструкции по считыванию данных.

Далее нужно изменить некоторые поля.

Copy to Clipboard
Syntax HighlighterpurchTable.OrderVendorAccountNumber = orderAccount;

После этого запрос отправляется на сервер, желательно в блоке try-catch:

Copy to Clipboard
Syntax Highlightertry

 {

 client.SaveChanges();

 }

 catch (Exception ex)

# Удаление данных

Для удаления данных data entity, их сначала нужно получить. То есть, сначала необходимо выполнить все шаги описанные в инструкции по считыванию данных.

Далее для указания OData клиенту того, что data entity должна быть удалена при выполнении запроса, вызывается метод DeleteObject, в который передается полученная переменная контракта данных data entity.

Copy to Clipboard
Syntax Highlighterclient.DeleteObject(purchTable);

После этого запрос отправляется на сервер, желательно в блоке try-catch:

Copy to Clipboard
Syntax Highlightertry

 {

 client.SaveChanges();

 }

 catch (Exception ex)

# Пример

В качестве примера приведено приложение Microsoft forms для получения, создания, обновления и удаления данных из data entity PurchPurchaseOrderHeaderV2Entity D365O.

В случае получения данных, поля над кнопками служат фильтрами и не обязательны для заполнения.

Для создания новых записей и для обновления существующих все поля должны быть заполнены. При этом при обновлении будет найдена запись с указанным Purch id и все остальные ее поля будут обновлены.

Для удаления записи достаточно указать ее Purch id.

_

Copy to Clipboard
Syntax Highlighterusing ODataProxy.Microsoft.Dynamics.DataEntities;

using System;

using System.Collections.Generic;

using System.Windows.Forms;

namespace PurchTableODataService

{

 public partial class PurchTableODataServiceForm : Form

 {

 public PurchTableODataServiceForm()

 {

 InitializeComponent();

 }

 private void getPurchTablesButton_Click(

 object sender,

 EventArgs e)

 {

 try

 {

 PurchTableODataService oDataService = new PurchTableODataService();

 IEnumerable purchTables = oDataService.getPurchTables(

 purchIdTextBox.Text,

 orderAccountTextBox.Text,

 currencyCodeTextBox.Text,

 invoiceAccountTextBox.Text,

 languageIdTextBox.Text,

 dataAreaIdTextBox.Text,

 deliveryCountryRegionIdTextBox.Text,

 deliveryAddressDescriptionTextBox.Text);

 string purchTablesText = “”;

 foreach (PurchaseOrderHeaderV2 purchTable in purchTables)

 {

 purchTablesText += string.Format(

 “Purch id: {0}; Order account: {1}; Currency code: {2}; Invoice account: {3}; Language id: {4}; Data area id: {5}; Delivery country region id: {6}; Delivery address description: {7}\r\n”,

 purchTable.PurchaseOrderNumber,

 purchTable.OrderVendorAccountNumber,

 purchTable.CurrencyCode,

 purchTable.InvoiceVendorAccountNumber,

 purchTable.LanguageId,

 purchTable.DataAreaId,

 purchTable.DeliveryAddressCountryRegionId,

 purchTable.DeliveryAddressDescription);

 }

 purchTablesTextBox.Text = purchTablesText;

 }

 catch (Exception ex)

 {

 purchTablesTextBox.Text = ex.Message;

 }

 }

 private void createPurchTableButton_Click(

 object sender,

 EventArgs e)

 {

 try

 {

 PurchTableODataService oDataService = new PurchTableODataService();

 PurchaseOrderHeaderV2 purchTable = oDataService.createPurchTable(

 purchIdTextBox.Text,

 orderAccountTextBox.Text,

 currencyCodeTextBox.Text,

 invoiceAccountTextBox.Text,

 languageIdTextBox.Text,

 dataAreaIdTextBox.Text,

 deliveryCountryRegionIdTextBox.Text,

 deliveryAddressDescriptionTextBox.Text);

 purchTablesTextBox.Text = string.Format(

 “Purchase order has been created. Purch id: {0}; Order account: {1}; Currency code: {2}; Invoice account: {3}; Language id: {4}; Data area id: {5}; Delivery country region id: {6}; Delivery address description: {7}\r\n”,

 purchTable.PurchaseOrderNumber,

 purchTable.OrderVendorAccountNumber,

 purchTable.CurrencyCode,

 purchTable.InvoiceVendorAccountNumber,

 purchTable.LanguageId,

 purchTable.DataAreaId,

 purchTable.DeliveryAddressCountryRegionId,

 purchTable.DeliveryAddressDescription);

 }

 catch (Exception ex)

 {

 purchTablesTextBox.Text = ex.Message;

 }

 }

 private void updatePurchTableButton_Click(

 object sender,

 EventArgs e)

 {

 try

 {

 PurchTableODataService oDataService = new PurchTableODataService();

 PurchaseOrderHeaderV2 purchTable = oDataService.updatePurchTable(

 purchIdTextBox.Text,

 orderAccountTextBox.Text,

 currencyCodeTextBox.Text,

 invoiceAccountTextBox.Text,

 languageIdTextBox.Text,

 dataAreaIdTextBox.Text,

 deliveryCountryRegionIdTextBox.Text,

 deliveryAddressDescriptionTextBox.Text);

 purchTablesTextBox.Text = string.Format(

 “Purchase order has been updated. Purch id: {0}; Order account: {1}; Currency code: {2}; Invoice account: {3}; Language id: {4}; Data area id: {5}; Delivery country region id: {6}; Delivery address description: {7}\r\n”,

 purchTable.PurchaseOrderNumber,

 purchTable.OrderVendorAccountNumber,

 purchTable.CurrencyCode,

 purchTable.InvoiceVendorAccountNumber,

 purchTable.LanguageId,

 purchTable.DataAreaId,

 purchTable.DeliveryAddressCountryRegionId,

 purchTable.DeliveryAddressDescription);

 }

 catch (Exception ex)

 {

 purchTablesTextBox.Text = ex.Message;

 }

 }

 private void deletePurchTableButton_Click(

 object sender,

 EventArgs e)

 {

 try

 {

 PurchTableODataService oDataService = new PurchTableODataService();

 PurchaseOrderHeaderV2 purchTable = oDataService.deletePurchTable(

 purchIdTextBox.Text,

 orderAccountTextBox.Text,

 currencyCodeTextBox.Text,

 invoiceAccountTextBox.Text,

 languageIdTextBox.Text,

 dataAreaIdTextBox.Text,

 deliveryCountryRegionIdTextBox.Text,

 deliveryAddressDescriptionTextBox.Text);

 purchTablesTextBox.Text = string.Format(

 “Purchase order has been deleted. Purch id: {0}; Order account: {1}; Currency code: {2}; Invoice account: {3}; Language id: {4}; Data area id: {5}; Delivery country region id: {6}; Delivery address description: {7}\r\n”,

 purchTable.PurchaseOrderNumber,

 purchTable.OrderVendorAccountNumber,

 purchTable.CurrencyCode,

 purchTable.InvoiceVendorAccountNumber,

 purchTable.LanguageId,

 purchTable.DataAreaId,

 purchTable.DeliveryAddressCountryRegionId,

 purchTable.DeliveryAddressDescription);

 }

 catch (Exception ex)

 {

 purchTablesTextBox.Text = ex.Message;

 }

 }

 }

}

using Microsoft.OData.Client;

using ODataProxy;

using ODataProxy.Microsoft.Dynamics.DataEntities;

using System;

using System.Collections.Generic;

using System.Linq;

namespace PurchTableODataService

{

 class PurchTableODataService : ODataServiceBase

 {

 public PurchaseOrderHeaderV2 createPurchTable(

 string purchId,

 string orderAccount,

 string currencyCode,

 string invoiceAccount,

 string languageId,

 string dataAreaId,

 string deliveryCountryRegionId,

 string deliveryAddressDescription)

 {

 if (!this.validateTextNotEmpty(purchId)

 || !this.validateTextNotEmpty(orderAccount)

 || !this.validateTextNotEmpty(currencyCode)

 || !this.validateTextNotEmpty(invoiceAccount)

 || !this.validateTextNotEmpty(languageId)

 || !this.validateTextNotEmpty(dataAreaId)

 || !this.validateTextNotEmpty(deliveryCountryRegionId)

 || !this.validateTextNotEmpty(deliveryAddressDescription))

 {

 throw new Exception(“All fields must be filled in correctly”);

 }

 PurchaseOrderHeaderV2 purchTable = new PurchaseOrderHeaderV2();

 purchTable.PurchaseOrderNumber = purchId;

 purchTable.OrderVendorAccountNumber = orderAccount;

 purchTable.CurrencyCode = currencyCode;

 purchTable.InvoiceVendorAccountNumber = invoiceAccount;

 purchTable.LanguageId = languageId;

 purchTable.DataAreaId = dataAreaId;

 purchTable.DeliveryAddressCountryRegionId = deliveryCountryRegionId;

 purchTable.DeliveryAddressDescription = deliveryAddressDescription;

 client.AddObject(

 “PurchaseOrderHeadersV2”,

 purchTable);

 try

 {

 client.SaveChanges();

 }

 catch (Exception ex)

 {

 throw new Exception(

 string.Format(

 “Error occured during purchase order creation: {0}”,

 ex.InnerException != null ?

 ex.InnerException.Message :

 ex.Message));

 }

 return purchTable;

 }

 public PurchaseOrderHeaderV2 updatePurchTable(

 string purchId,

 string orderAccount,

 string currencyCode,

 string invoiceAccount,

 string languageId,

 string dataAreaId,

 string deliveryCountryRegionId,

 string deliveryAddressDescription)

 {

 if (!this.validateTextNotEmpty(purchId)

 || !this.validateTextNotEmpty(orderAccount)

 || !this.validateTextNotEmpty(currencyCode)

 || !this.validateTextNotEmpty(invoiceAccount)

 || !this.validateTextNotEmpty(languageId)

 || !this.validateTextNotEmpty(dataAreaId)

 || !this.validateTextNotEmpty(deliveryCountryRegionId)

 || !this.validateTextNotEmpty(deliveryAddressDescription))

 {

 throw new Exception(“All fields must be filled in correctly”);

 }

 PurchaseOrderHeaderV2 purchTable = null;

 IEnumerable purchTables = this.getPurchTables(purchId);

 foreach (PurchaseOrderHeaderV2 purchaseTable in purchTables)

 {

 purchTable = purchaseTable;

 break;

 }

 if (purchTable != null)

 {

 purchTable.OrderVendorAccountNumber = orderAccount;

 purchTable.CurrencyCode = currencyCode;

 purchTable.InvoiceVendorAccountNumber = invoiceAccount;

 purchTable.LanguageId = languageId;

 purchTable.DataAreaId = dataAreaId;

 purchTable.DeliveryAddressCountryRegionId = deliveryCountryRegionId;

 purchTable.DeliveryAddressDescription = deliveryAddressDescription;

 try

 {

 client.SaveChanges();

 }

 catch (Exception ex)

 {

 throw new Exception(

 string.Format(

 “Error occured during purchase order update: {0}”,

 ex.InnerException != null ?

 ex.InnerException.Message :

 ex.Message));

 }

 }

 else

 {

 throw new Exception(“No purchase orders were found”);

 }

 return purchTable;

 }

 public PurchaseOrderHeaderV2 deletePurchTable(

 string purchId,

 string orderAccount,

 string currencyCode,

 string invoiceAccount,

 string languageId,

 string dataAreaId,

 string deliveryCountryRegionId,

 string deliveryAddressDescription)

 {

 if (!this.validateTextNotEmpty(purchId))

 {

 throw new Exception(“Purch id must be filled in”);

 }

 PurchaseOrderHeaderV2 purchTable = null;

 IEnumerable purchTables = this.getPurchTables(

 purchId,

 orderAccount,

 currencyCode,

 invoiceAccount,

 languageId,

 dataAreaId,

 deliveryCountryRegionId,

 deliveryAddressDescription);

 foreach (PurchaseOrderHeaderV2 purchaseTable in purchTables)

 {

 purchTable = purchaseTable;

 break;

 }

 if (purchTable != null)

 {

 client.DeleteObject(purchTable);

 try

 {

 client.SaveChanges();

 }

 catch (Exception ex)

 {

 throw new Exception(

 string.Format(

 “Error occured during purchase order deletion: {0}”,

 ex.InnerException != null ?

 ex.InnerException.Message :

 ex.Message));

 }

 }

 else

 {

 throw new Exception(“No purchase orders were found”);

 }

 return purchTable;

 }

 private bool validateTextNotEmpty(string text)

 {

 bool check = true;

 if (text == “”)

 {

 check = false;

 }

 return check;

 }

 public DataServiceCollection getPurchTables(

 string purchId = “”,

 string orderAccount = “”,

 string currencyCode = “”,

 string invoiceAccount = “”,

 string languageId = “”,

 string dataAreaId = “”,

 string deliveryCountryRegionId = “”,

 string deliveryAddressDescription = “”)

 {

 DataServiceCollection purchTables;

 try

 {

 purchTables = new DataServiceCollection(

 client.PurchaseOrderHeadersV2.Where(

 purchTable =&gt;

 (purchTable.PurchaseOrderNumber == purchId

 || purchId == “”)

 &amp;&amp; (purchTable.OrderVendorAccountNumber == orderAccount

 || orderAccount == “”)

 &amp;&amp; (purchTable.CurrencyCode == currencyCode

 || currencyCode == “”)

 &amp;&amp; (purchTable.InvoiceVendorAccountNumber == invoiceAccount

 || invoiceAccount == “”)

 &amp;&amp; (purchTable.LanguageId == languageId

 || languageId == “”)

 &amp;&amp; (purchTable.DataAreaId == dataAreaId

 || dataAreaId == “”)

 &amp;&amp; (purchTable.DeliveryAddressCountryRegionId == deliveryCountryRegionId

 || deliveryCountryRegionId == “”)

 &amp;&amp; (purchTable.DeliveryAddressDescription == deliveryAddressDescription

 || deliveryAddressDescription == “”)));

 }

 catch (Exception ex)

 {

 throw new Exception(

 string.Format(

 “Error occured during purchase orders receiving: {0}”,

 ex.InnerException != null ?

 ex.InnerException.Message :

 ex.Message));

 }

 return purchTables;

 }

 }

}

_

# Подготовил статью:

ДыкухаБорис Александрович

Senior Software Developer, OntargIT

[](https://www.linkedin.com/in/borys-dykukha-59a7a2a9)