From bddb0b969001ab9e8425f59548ad2aef3a42731d Mon Sep 17 00:00:00 2001 From: Dmitry Mamontov Date: Mon, 3 Nov 2014 13:06:52 +0300 Subject: [PATCH] initial commit --- IntaroCrm.sln | 20 + RetailCrm/ApiException.cs | 21 + RetailCrm/CurlException.cs | 21 + RetailCrm/Properties/AssemblyInfo.cs | 36 ++ RetailCrm/RestApi.cs | 708 +++++++++++++++++++++++++++ RetailCrm/RetailCrm.csproj | 68 +++ RetailCrm/packages.config | 4 + 7 files changed, 878 insertions(+) create mode 100644 IntaroCrm.sln create mode 100644 RetailCrm/ApiException.cs create mode 100644 RetailCrm/CurlException.cs create mode 100644 RetailCrm/Properties/AssemblyInfo.cs create mode 100644 RetailCrm/RestApi.cs create mode 100644 RetailCrm/RetailCrm.csproj create mode 100644 RetailCrm/packages.config diff --git a/IntaroCrm.sln b/IntaroCrm.sln new file mode 100644 index 0000000..9386f3f --- /dev/null +++ b/IntaroCrm.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RetailCrm", "IntaroCrm\RetailCrm.csproj", "{1C407E40-0B79-4593-AC79-03BA8DD76DD1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1C407E40-0B79-4593-AC79-03BA8DD76DD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1C407E40-0B79-4593-AC79-03BA8DD76DD1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1C407E40-0B79-4593-AC79-03BA8DD76DD1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1C407E40-0B79-4593-AC79-03BA8DD76DD1}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/RetailCrm/ApiException.cs b/RetailCrm/ApiException.cs new file mode 100644 index 0000000..3bcf0f5 --- /dev/null +++ b/RetailCrm/ApiException.cs @@ -0,0 +1,21 @@ +using System; + +namespace RetailCrm +{ + public class ApiException : Exception + { + public ApiException() + { + } + + public ApiException(string message) + : base(message) + { + } + + public ApiException(string message, Exception inner) + : base(message, inner) + { + } + } +} diff --git a/RetailCrm/CurlException.cs b/RetailCrm/CurlException.cs new file mode 100644 index 0000000..fff3862 --- /dev/null +++ b/RetailCrm/CurlException.cs @@ -0,0 +1,21 @@ +using System; + +namespace RetailCrm +{ + public class CurlException : Exception + { + public CurlException() + { + } + + public CurlException(string message) + : base(message) + { + } + + public CurlException(string message, Exception inner) + : base(message, inner) + { + } + } +} diff --git a/RetailCrm/Properties/AssemblyInfo.cs b/RetailCrm/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f84f137 --- /dev/null +++ b/RetailCrm/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Управление общими сведениями о сборке осуществляется с помощью +// набора атрибутов. Измените значения этих атрибутов, чтобы изменить сведения, +// связанные со сборкой. +[assembly: AssemblyTitle("IntaroCrm")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("IntaroCrm")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Параметр ComVisible со значением FALSE делает типы в сборке невидимыми +// для COM-компонентов. Если требуется обратиться к типу в этой сборке через +// COM, задайте атрибуту ComVisible значение TRUE для этого типа. +[assembly: ComVisible(false)] + +// Следующий GUID служит для идентификации библиотеки типов, если этот проект будет видимым для COM +[assembly: Guid("9a3f29a5-d878-430c-b7ee-8548d4d03f43")] + +// Сведения о версии сборки состоят из следующих четырех значений: +// +// Основной номер версии +// Дополнительный номер версии +// Номер построения +// Редакция +// +// Можно задать все значения или принять номер построения и номер редакции по умолчанию, +// используя "*", как показано ниже: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/RetailCrm/RestApi.cs b/RetailCrm/RestApi.cs new file mode 100644 index 0000000..092c632 --- /dev/null +++ b/RetailCrm/RestApi.cs @@ -0,0 +1,708 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System.Collections; + +namespace RetailCrm +{ + public class RestApi + { + protected string apiUrl; + protected string apiKey; + protected string apiVersion = "3"; + protected DateTime generatedAt; + protected Dictionary parameters; + + private string userAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"; + private string contentType = "application/x-www-form-urlencoded"; + + /// адрес CRM + /// ключ для работы с api + public RestApi(string crmUrl, string crmKey) + { + apiUrl = crmUrl + "/api/v" + apiVersion + "/"; + apiKey = crmKey; + parameters = new Dictionary(); + parameters.Add("apiKey", apiKey); + } + + /// + /// Получение заказа по id + /// + /// идентификатор заказа + /// поиск заказа по id или externalId + /// информация о заказе + public Dictionary orderGet(int id, string by) + { + Dictionary result = new Dictionary(); + string url = apiUrl + "orders/" + id.ToString(); + if (by.Equals("externalId")) + { + parameters.Add("by", by); + } + result = request(url, "GET"); + + return result; + } + + /// + /// Создание заказа + /// + /// информация о заказе + /// + public Dictionary orderCreate(Dictionary order) + { + Dictionary result = new Dictionary(); + string url = apiUrl + "orders/create"; + string dataJson = JsonConvert.SerializeObject(order); + parameters.Add("order", dataJson); + result = request(url, "POST"); + + return result; + } + + /// + /// Изменение заказа + /// + /// информация о заказе + /// + public Dictionary orderEdit(Dictionary order) + { + Dictionary result = new Dictionary(); + string url = apiUrl + "orders/" + order["externalId"].ToString() + "/edit"; + string dataJson = JsonConvert.SerializeObject(order); + parameters.Add("order", dataJson); + result = request(url, "POST"); + + return result; + } + + /// + /// Пакетная загрузка заказов + /// + /// массив заказов + /// + public Dictionary orderUpload(Dictionary orders) + { + Dictionary result = new Dictionary(); + string url = apiUrl + "orders/upload"; + string dataJson = JsonConvert.SerializeObject(orders); + parameters.Add("orders", dataJson); + result = request(url, "POST"); + + if (result.ContainsKey("uploadedOrders") && result != null) + { + return getDictionary(result["uploadedOrders"]); + } + + return result; + } + + /// + /// Обновление externalId у заказов с переданными id + /// + /// массив, содержащий id и externalId заказа + /// + public Dictionary orderFixExternalIds(Dictionary orders) + { + Dictionary result = new Dictionary(); + string url = apiUrl + "orders/fix-external-ids"; + string dataJson = JsonConvert.SerializeObject(orders); + parameters.Add("orders", dataJson); + result = request(url, "POST"); + + return result; + } + + /// + /// Получение последних измененных заказов + /// + /// начальная дата выборки + /// конечная дата + /// ограничение на размер выборки + /// сдвиг + /// массив заказов + public Dictionary orderHistory(DateTime startDate, DateTime endDate, int limit, int offset) + { + Dictionary result = new Dictionary(); + string url = apiUrl + "orders/history"; + parameters.Add("startDate", startDate.ToString()); + parameters.Add("startDate", endDate.ToString()); + parameters.Add("limit", limit.ToString()); + parameters.Add("offset", offset.ToString()); + result = request(url, "GET"); + + return result; + } + + /// + /// Получение клиента по id + /// + /// идентификатор + /// поиск заказа по id или externalId + /// информация о клиенте + public Dictionary customerGet(string id, string by) + { + Dictionary result = new Dictionary(); + string url = apiUrl + "customers/" + id; + if (by.Equals("externalId")) + { + parameters.Add("by", by); + } + result = request(url, "GET"); + + return result; + } + + /// + /// Получение списка клиентов в соответсвии с запросом + /// + /// телефон + /// почтовый адрес + /// фио пользователя + /// ограничение на размер выборки + /// сдвиг + /// массив клиентов + public Dictionary customers(string phone, string email, string fio, int limit, int offset) + { + Dictionary result = new Dictionary(); + string url = apiUrl + "customers"; + parameters.Add("phone", phone); + parameters.Add("email", email); + parameters.Add("fio", fio); + parameters.Add("limit", limit.ToString()); + parameters.Add("offset", offset.ToString()); + result = request(url, "GET"); + + return result; + } + + /// + /// Создание клиента + /// + /// информация о клиенте + /// + public Dictionary customerCreate(Dictionary customer) + { + Dictionary result = new Dictionary(); + string url = apiUrl + "customers/create"; + string dataJson = JsonConvert.SerializeObject(customer); + parameters.Add("customer", dataJson); + result = request(url, "POST"); + + return result; + } + + /// + /// Редактирование клиента + /// + /// информация о клиенте + /// + public Dictionary customerEdit(Dictionary customer) + { + Dictionary result = new Dictionary(); + string url = apiUrl + "customers/" + customer["externalId"].ToString() + "/edit"; + string dataJson = JsonConvert.SerializeObject(customer); + parameters.Add("customer", dataJson); + result = request(url, "POST"); + + return result; + } + + /// + /// Пакетная загрузка клиентов + /// + /// массив клиентов + /// + public Dictionary customerUpload(Dictionary customers) + { + Dictionary result = new Dictionary(); + string url = apiUrl + "customers/upload"; + string dataJson = JsonConvert.SerializeObject(customers); + parameters.Add("customers", dataJson); + result = request(url, "POST"); + + if (result.ContainsKey("uploaded") && result != null) + { + return getDictionary(result["uploaded"]); + } + + return result; + } + + /// + /// Обновление externalId у клиентов с переданными id + /// + /// массив, содержащий id и externalId заказа + /// + public Dictionary customerFixExternalIds(Dictionary customers) + { + Dictionary result = new Dictionary(); + string url = apiUrl + "customers/fix-external-ids"; + string dataJson = JsonConvert.SerializeObject(customers); + parameters.Add("customers", dataJson); + result = request(url, "POST"); + + return result; + } + + /// + /// Получение списка заказов клиента + /// + /// идентификатор клиента + /// начальная дата выборки + /// конечная дата + /// ограничение на размер выборки + /// сдвиг + /// поиск заказа по id или externalId + /// массив заказов + public Dictionary customerOrdersList(string id, DateTime startDate, DateTime endDate, int limit, int offset, string by) + { + Dictionary result = new Dictionary(); + string url = apiUrl + "customers/" + id + "/orders"; + if (by.Equals("externalId")) + { + parameters.Add("by", by); + } + parameters.Add("startDate", startDate.ToString()); + parameters.Add("endDate", endDate.ToString()); + parameters.Add("limit", limit.ToString()); + parameters.Add("offset", offset.ToString()); + result = request(url, "POST"); + + return result; + } + + /// + /// Получение списка типов доставки + /// + /// массив типов доставки + public Dictionary deliveryTypesList() + { + Dictionary result = new Dictionary(); + string url = apiUrl + "reference/delivery-types"; + result = request(url, "GET"); + + return result; + } + + /// + /// Редактирование типа доставки + /// + /// информация о типе доставки + /// + public Dictionary deliveryTypeEdit(Dictionary deliveryType) + { + Dictionary result = new Dictionary(); + string url = apiUrl + "reference/delivery-types/" + deliveryType["code"].ToString() + "/edit"; + string dataJson = JsonConvert.SerializeObject(deliveryType); + parameters.Add("deliveryType", dataJson); + result = request(url, "POST"); + + return result; + } + + /// + /// Получение списка служб доставки + /// + /// массив служб доставки + public Dictionary deliveryServicesList() + { + Dictionary result = new Dictionary(); + string url = apiUrl + "reference/delivery-services"; + result = request(url, "GET"); + + return result; + } + + /// + /// Редактирование службы доставки + /// + /// информация о службе доставки + /// + public Dictionary deliveryServiceEdit(Dictionary deliveryService) + { + Dictionary result = new Dictionary(); + string url = apiUrl + "reference/delivery-services/" + deliveryService["code"].ToString() + "/edit"; + string dataJson = JsonConvert.SerializeObject(deliveryService); + parameters.Add("deliveryService", dataJson); + result = request(url, "POST"); + + return result; + } + + /// + /// Получение списка типов оплаты + /// + /// массив типов оплаты + public Dictionary paymentTypesList() + { + Dictionary result = new Dictionary(); + string url = apiUrl + "reference/payment-types"; + result = request(url, "GET"); + + return result; + } + + /// + /// Редактирование типа оплаты + /// + /// информация о типе оплаты + /// + public Dictionary paymentTypesEdit(Dictionary paymentType) + { + Dictionary result = new Dictionary(); + string url = apiUrl + "reference/payment-types/" + paymentType["code"].ToString() + "/edit"; + string dataJson = JsonConvert.SerializeObject(paymentType); + parameters.Add("paymentType", dataJson); + result = request(url, "POST"); + + return result; + } + + /// + /// Получение списка статусов оплаты + /// + /// массив статусов оплаты + public Dictionary paymentStatusesList() + { + Dictionary result = new Dictionary(); + string url = apiUrl + "reference/payment-statuses"; + result = request(url, "GET"); + + return result; + } + + /// + /// Редактирование статуса оплаты + /// + /// информация о статусе оплаты + /// + public Dictionary paymentStatusesEdit(Dictionary paymentStatus) + { + Dictionary result = new Dictionary(); + string url = apiUrl + "reference/payment-statuses/" + paymentStatus["code"].ToString() + "/edit"; + string dataJson = JsonConvert.SerializeObject(paymentStatus); + parameters.Add("paymentStatus", dataJson); + result = request(url, "POST"); + + return result; + } + + /// + /// Получение списка типов заказа + /// + /// массив типов заказа + public Dictionary orderTypesList() + { + Dictionary result = new Dictionary(); + string url = apiUrl + "reference/order-types"; + result = request(url, "GET"); + + return result; + } + + /// + /// Редактирование типа заказа + /// + /// информация о типе заказа + /// + public Dictionary orderTypesEdit(Dictionary orderType) + { + Dictionary result = new Dictionary(); + string url = apiUrl + "reference/order-types/" + orderType["code"].ToString() + "/edit"; + string dataJson = JsonConvert.SerializeObject(orderType); + parameters.Add("orderType", dataJson); + result = request(url, "POST"); + + return result; + } + + /// + /// Получение списка способов оформления заказа + /// + /// массив способов оформления заказа + public Dictionary orderMethodsList() + { + Dictionary result = new Dictionary(); + string url = apiUrl + "reference/order-methods"; + result = request(url, "GET"); + + return result; + } + + /// + /// Редактирование способа оформления заказа + /// + /// информация о способе оформления заказа + /// + public Dictionary orderMethodsEdit(Dictionary orderMethod) + { + Dictionary result = new Dictionary(); + string url = apiUrl + "reference/order-methods/" + orderMethod["code"].ToString() + "/edit"; + string dataJson = JsonConvert.SerializeObject(orderMethod); + parameters.Add("orderMethod", dataJson); + result = request(url, "POST"); + + return result; + } + + /// + /// Получение списка статусов заказа + /// + /// массив статусов заказа + public Dictionary orderStatusesList() + { + Dictionary result = new Dictionary(); + string url = apiUrl + "reference/statuses"; + result = request(url, "GET"); + + return result; + } + + /// + /// Редактирование статуса заказа + /// + /// информация о статусе заказа + /// + public Dictionary orderStatusEdit(Dictionary status) + { + Dictionary result = new Dictionary(); + string url = apiUrl + "reference/statuses/" + status["code"].ToString() + "/edit"; + string dataJson = JsonConvert.SerializeObject(status); + parameters.Add("status", dataJson); + result = request(url, "POST"); + + return result; + } + + /// + /// Получение списка групп статусов заказа + /// + /// массив групп статусов заказа + public Dictionary orderStatusGroupsList() + { + Dictionary result = new Dictionary(); + string url = apiUrl + "reference/status-groups"; + result = request(url, "GET"); + + return result; + } + + /// + /// Обновление статистики + /// + /// статус выполненного обновления + public Dictionary statisticUpdate() + { + Dictionary result = new Dictionary(); + string url = apiUrl + "statistic/update"; + result = request(url, "GET"); + + return result; + } + + /// дата генерации + public DateTime getGeneratedAt() + { + return generatedAt; + } + + /// + /// + /// + protected Dictionary request(string url, string method) + { + Dictionary data = new Dictionary(); + string urlParameters = httpBuildQuery(parameters); + + if (method.Equals("GET") && urlParameters.Length > 0) + { + url += "?" + urlParameters; + } + + Exception exception = null; + + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + + request.Method = method; + + if (method.Equals("POST")) + { + UTF8Encoding encoding = new UTF8Encoding(); + byte[] postBytes = encoding.GetBytes(urlParameters); + + request.ContentType = contentType; + request.ContentLength = postBytes.Length; + request.UserAgent = userAgent; + + Stream postStream = request.GetRequestStream(); + postStream.Write(postBytes, 0, postBytes.Length); + postStream.Flush(); + postStream.Close(); + } + + HttpWebResponse response = null; + try + { + response = (HttpWebResponse)request.GetResponse(); + } + catch (WebException ex) + { + response = (HttpWebResponse)ex.Response; + exception = ex; + } + + if (request == null || response == null) + { + throw new CurlException(exception.ToString(), exception); + } + + int statusCode = (int)response.StatusCode; + + Stream dataStream = response.GetResponseStream(); + + StreamReader reader = new StreamReader(dataStream); + string serverResponse = reader.ReadToEnd(); + + parameters.Clear(); + parameters.Add("apiKey", apiKey); + + data = jsonDecode(serverResponse); + + if (data.ContainsKey("generatedAt")) + { + generatedAt = DateTime.ParseExact(data["generatedAt"].ToString(), "Y-m-d H:i:s", + System.Globalization.CultureInfo.InvariantCulture); + data.Remove("generatedAt"); + } + + if (statusCode >= 400 || (data.ContainsKey("success") && !(bool)data["success"])) + { + throw new ApiException(getErrorMessage(data)); + } + + data.Remove("success"); + + if (data.Count == 0) + { + return null; + } + + return data; + } + + /// + /// + protected string getErrorMessage(Dictionary data) + { + string error = ""; + + if (data.ContainsKey("message")) + { + error = data["message"].ToString(); + } + else if (data.ContainsKey("0")) + { + Dictionary sub = getDictionary(data["0"]); + if (sub.ContainsKey("message")) + { + error = sub["message"].ToString(); + } + + } + else if (data.ContainsKey("errorMsg")) + { + error = data["errorMsg"].ToString(); + } + else if (data.ContainsKey("error")) + { + Dictionary sub = getDictionary(data["error"]); + if (sub.ContainsKey("message")) + { + error = sub["message"].ToString(); + } + } + + if (data.ContainsKey("errors")) + { + Dictionary sub = getDictionary(data["errors"]); + foreach (KeyValuePair kvp in data) + { + error += ". " + kvp.Value.ToString(); + } + } + + return error; + } + + /// + /// + public Dictionary getDictionary(object data) + { + Dictionary result = new Dictionary(); + IDictionary idict = (IDictionary)data; + + foreach (object key in idict.Keys) + { + result.Add(key.ToString(), idict[key]); + } + return result; + } + + /// + /// + protected Dictionary jsonDecode(string json) + { + Dictionary data = new Dictionary(); + data = JsonConvert.DeserializeObject>(json); + data = jsonToDictionary(data); + + return data; + } + + /// + /// + protected static Dictionary jsonToDictionary(Dictionary data) + { + Dictionary result = new Dictionary(); + foreach (KeyValuePair kvp in data) + { + string key = kvp.Key; + object value = kvp.Value; + + if (value.GetType() == typeof(JObject)) + { + Dictionary valueJson = JsonConvert.DeserializeObject>(value.ToString()); + value = jsonToDictionary(valueJson); + } + result.Add(key, value); + } + return result; + } + + /// + /// + protected string httpBuildQuery(Dictionary data) + { + string queryString = null; + foreach (KeyValuePair kvp in data) + { + queryString += kvp.Key + "=" + kvp.Value + "&"; + } + + if (queryString.Length > 0) + { + queryString = queryString.Substring(0, queryString.Length - 1); + } + + return queryString; + } + } +} diff --git a/RetailCrm/RetailCrm.csproj b/RetailCrm/RetailCrm.csproj new file mode 100644 index 0000000..739db1f --- /dev/null +++ b/RetailCrm/RetailCrm.csproj @@ -0,0 +1,68 @@ + + + + + Debug + AnyCPU + {1C407E40-0B79-4593-AC79-03BA8DD76DD1} + Library + Properties + RetailCrm + IntaroCrm + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + intaro.pfx + + + + ..\packages\Newtonsoft.Json.6.0.6\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/RetailCrm/packages.config b/RetailCrm/packages.config new file mode 100644 index 0000000..592edee --- /dev/null +++ b/RetailCrm/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file