diff --git a/1CDataBus.sln b/1CDataBus.sln
new file mode 100644
index 0000000..e090786
--- /dev/null
+++ b/1CDataBus.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.9.34728.123
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "1CDataBus", "1CDataBus\1CDataBus.csproj", "{B0551536-536F-4719-96E8-56848F4F4F26}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B0551536-536F-4719-96E8-56848F4F4F26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B0551536-536F-4719-96E8-56848F4F4F26}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B0551536-536F-4719-96E8-56848F4F4F26}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B0551536-536F-4719-96E8-56848F4F4F26}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {19101D4F-6085-422C-A340-C3DFEEDD930D}
+ EndGlobalSection
+EndGlobal
diff --git a/1CDataBus/1CDataBus.csproj b/1CDataBus/1CDataBus.csproj
new file mode 100644
index 0000000..1ae6714
--- /dev/null
+++ b/1CDataBus/1CDataBus.csproj
@@ -0,0 +1,24 @@
+
+
+
+ net8.0
+ enable
+ enable
+ _1CDataBus
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
diff --git a/1CDataBus/1CDataBus.http b/1CDataBus/1CDataBus.http
new file mode 100644
index 0000000..db3904f
--- /dev/null
+++ b/1CDataBus/1CDataBus.http
@@ -0,0 +1,6 @@
+@_1CDataBus_HostAddress = http://localhost:5043
+
+GET {{_1CDataBus_HostAddress}}/weatherforecast/
+Accept: application/json
+
+###
diff --git a/1CDataBus/Controllers/ContractsController.cs b/1CDataBus/Controllers/ContractsController.cs
new file mode 100644
index 0000000..18ca13d
--- /dev/null
+++ b/1CDataBus/Controllers/ContractsController.cs
@@ -0,0 +1,72 @@
+using _1CDataBus.RabbitMQ;
+using _1CDataBus.Structure;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Simple.OData.Client;
+using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace _1CDataBus.Controllers
+{
+ [ApiController]
+ [Route("[controller]")]
+ public class ContractsController : ControllerBase
+ {
+ private readonly IConfiguration _config;
+ private readonly ILogger _logger;
+ private RabbitConnection _rabbit;
+ private ODataClient _client;
+
+ public ContractsController(ILogger logger, IConfiguration config)
+ {
+ _config = config;
+ _logger = logger;
+ _rabbit = new RabbitConnection(_config);
+ var odata = new ODataAccess(_config);
+ _client = odata.Client;
+ }
+
+ [HttpPost("SendContractFromQDoc")]
+ public async Task SendContractFromQDoc(long id)
+ {
+ var contract = await _client
+ .For("IContract")
+ .Key(id)
+ .Expand("BusinessUnit")
+ .Expand("Counterparty")
+ .Expand("Currency")
+ .FindEntryAsync();
+ var sendContract = new Contract(contract);
+ await SendContractTo1C(sendContract);
+ }
+
+ [HttpPost("SendContract")]
+ public async Task SendContractTo1C(Contract contract)
+ {
+ JsonSerializerOptions options = new()
+ {
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ WriteIndented = true
+ };
+ var serialized = JsonSerializer.Serialize(contract, options);
+ await _rabbit.SendMessage(serialized);
+ }
+
+ [HttpGet("GetContract")]
+ public async Task GetContract(long contractId)
+ {
+ var contract = await _client
+ .For("IContracts")
+ .Expand("Versions/Body")
+ .Key(contractId)
+ .FindEntryAsync();
+ var versions = (IEnumerable)contract["Versions"];
+ var lastVersion = versions.OrderByDescending(x => x["Number"]).FirstOrDefault();
+ byte[] bytes = lastVersion["Body"]["Value"];
+ var base64 = Convert.ToBase64String(bytes);
+ return base64;
+ }
+ }
+}
diff --git a/1CDataBus/Controllers/WeatherForecastController.cs b/1CDataBus/Controllers/WeatherForecastController.cs
new file mode 100644
index 0000000..938ce49
--- /dev/null
+++ b/1CDataBus/Controllers/WeatherForecastController.cs
@@ -0,0 +1,33 @@
+using Microsoft.AspNetCore.Mvc;
+
+namespace _1CDataBus.Controllers
+{
+ [ApiController]
+ [Route("[controller]")]
+ public class WeatherForecastController : ControllerBase
+ {
+ private static readonly string[] Summaries = new[]
+ {
+ "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
+ };
+
+ private readonly ILogger _logger;
+
+ public WeatherForecastController(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ [HttpGet(Name = "GetWeatherForecast")]
+ public IEnumerable Get()
+ {
+ return Enumerable.Range(1, 5).Select(index => new WeatherForecast
+ {
+ Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
+ TemperatureC = Random.Shared.Next(-20, 55),
+ Summary = Summaries[Random.Shared.Next(Summaries.Length)]
+ })
+ .ToArray();
+ }
+ }
+}
diff --git a/1CDataBus/Program.cs b/1CDataBus/Program.cs
new file mode 100644
index 0000000..48863a6
--- /dev/null
+++ b/1CDataBus/Program.cs
@@ -0,0 +1,25 @@
+var builder = WebApplication.CreateBuilder(args);
+
+// Add services to the container.
+
+builder.Services.AddControllers();
+// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
+builder.Services.AddEndpointsApiExplorer();
+builder.Services.AddSwaggerGen();
+
+var app = builder.Build();
+
+// Configure the HTTP request pipeline.
+if (app.Environment.IsDevelopment())
+{
+ app.UseSwagger();
+ app.UseSwaggerUI();
+}
+
+app.UseHttpsRedirection();
+
+app.UseAuthorization();
+
+app.MapControllers();
+
+app.Run();
diff --git a/1CDataBus/Properties/launchSettings.json b/1CDataBus/Properties/launchSettings.json
new file mode 100644
index 0000000..de90f50
--- /dev/null
+++ b/1CDataBus/Properties/launchSettings.json
@@ -0,0 +1,41 @@
+{
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:54967",
+ "sslPort": 44384
+ }
+ },
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "http://localhost:5043",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "https://localhost:7130;http://localhost:5043",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/1CDataBus/RabbitMQ/RabbitConnection.cs b/1CDataBus/RabbitMQ/RabbitConnection.cs
new file mode 100644
index 0000000..5259f88
--- /dev/null
+++ b/1CDataBus/RabbitMQ/RabbitConnection.cs
@@ -0,0 +1,48 @@
+using RabbitMQ.Client;
+using System.Text;
+
+namespace _1CDataBus.RabbitMQ
+{
+ public class RabbitConnection : IDisposable
+ {
+ private readonly IConnection _connection;
+ private readonly IChannel _channel;
+ private IConfiguration _configuration;
+ public RabbitConnection(IConfiguration configuration)
+ {
+ _configuration = configuration;
+ var factory = new ConnectionFactory
+ {
+ HostName = _configuration.GetValue("RabbitMqSettings:HostName"),
+ //HostName = "astsrvrabbit1",
+ UserName = _configuration.GetValue("RabbitMqSettings:UserName"),
+ //UserName = "erp",
+ Password = _configuration.GetValue("RabbitMqSettings:Password"),
+ //Password = @"Z!1;Q5#GE4v",
+ VirtualHost = _configuration.GetValue("RabbitMqSettings:VirtualHost")
+ //VirtualHost = "erp"
+ };
+
+ _connection = factory.CreateConnectionAsync().Result;
+ _channel = _connection.CreateChannelAsync().Result;
+ }
+
+ public async Task SendMessage(string body)
+ {
+ await _channel.QueueDeclareAsync(
+ queue: "erp_test",
+ durable: false,
+ exclusive: false,
+ autoDelete: false,
+ arguments: null);
+ var encoded = Encoding.UTF8.GetBytes(body);
+ await _channel.BasicPublishAsync(string.Empty, "erp_test", encoded);
+ }
+
+ public void Dispose()
+ {
+ _channel.Dispose();
+ _connection.Dispose();
+ }
+ }
+}
diff --git a/1CDataBus/Structure/Contract.cs b/1CDataBus/Structure/Contract.cs
new file mode 100644
index 0000000..f81f181
--- /dev/null
+++ b/1CDataBus/Structure/Contract.cs
@@ -0,0 +1,39 @@
+namespace _1CDataBus.Structure
+{
+ public class Contract
+ {
+ private Dictionary _contractKinds = new Dictionary ()
+ {
+ { 1, "Поставщик" },
+ { 2, "Покупатель" },
+ { 3, "Прочее" }
+ };
+
+ public Contract(dynamic contract)
+ {
+ Name = contract["Name"];
+ BusinessUnit = contract["BusinessUnit"]["ExternalId"];
+ BusinessUnitBIN = contract["BusinessUnit"]["BINArmadoc"];
+ Counterparty = contract["Counterparty"]["ExternalId"];
+ CounterpartyBIN = contract["Counterparty"]["BINArmadoc"];
+ CreateDate = contract["Created"].Date;
+ Number = contract["RegistrationNumber"];
+ Date = contract["ContractDateSungero"].Date;
+ Currency = contract["Currency"]["AlphaCode"];
+ QDocID = contract["Id"];
+ }
+
+ public string Name { get; set; }
+ public string BusinessUnit { get; set; }
+ public string BusinessUnitBIN { get; set; }
+ public string Counterparty { get; set; }
+ public string CounterpartyBIN { get; set; }
+ public DateTimeOffset CreateDate { get; set; } = DateTimeOffset.Now;
+ public string Number { get; set; }
+ public DateTimeOffset Date { get; set; }
+ public string ContractKind { get; set; } = "Поставщик";
+ public string Currency { get; set; }
+ public bool Orders { get; set; } = false;
+ public long QDocID { get; set; }
+ }
+}
diff --git a/1CDataBus/Structure/ODataAccess.cs b/1CDataBus/Structure/ODataAccess.cs
new file mode 100644
index 0000000..79eee7d
--- /dev/null
+++ b/1CDataBus/Structure/ODataAccess.cs
@@ -0,0 +1,33 @@
+using Microsoft.Extensions.Configuration;
+using Simple.OData.Client;
+using System.Text;
+
+namespace _1CDataBus.Structure
+{
+ public class ODataAccess
+ {
+ private IConfiguration _configuration;
+
+ public ODataAccess(IConfiguration configuration)
+ {
+ _configuration = configuration;
+ var section = _configuration.GetSection("QDocSettings");
+
+ var IntegrationServiceUrl = section.GetValue("Url");
+ var Login = section.GetValue("Login");
+ var Password = section.GetValue("Password");
+
+ // Настройки Simple OData Client: добавление ко всем запросам URL сервиса и
+ // заголовка с данными аутентификации.
+ var odataClientSettings = new ODataClientSettings(new Uri(IntegrationServiceUrl));
+ odataClientSettings.BeforeRequest += (HttpRequestMessage message) =>
+ {
+ var authenticationHeaderValue = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{Login}:{Password}"));
+ message.Headers.Add("Authorization", "Basic " + authenticationHeaderValue);
+ };
+ Client = new ODataClient(odataClientSettings);
+ }
+
+ public ODataClient? Client { get; set; }
+ }
+}
diff --git a/1CDataBus/WeatherForecast.cs b/1CDataBus/WeatherForecast.cs
new file mode 100644
index 0000000..bc308de
--- /dev/null
+++ b/1CDataBus/WeatherForecast.cs
@@ -0,0 +1,13 @@
+namespace _1CDataBus
+{
+ public class WeatherForecast
+ {
+ public DateOnly Date { get; set; }
+
+ public int TemperatureC { get; set; }
+
+ public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
+
+ public string? Summary { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/1CDataBus/appsettings.Development.json b/1CDataBus/appsettings.Development.json
new file mode 100644
index 0000000..0c208ae
--- /dev/null
+++ b/1CDataBus/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/1CDataBus/appsettings.json b/1CDataBus/appsettings.json
new file mode 100644
index 0000000..4097c27
--- /dev/null
+++ b/1CDataBus/appsettings.json
@@ -0,0 +1,25 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*",
+ "RabbitMqSettings": {
+ "HostName": "astsrvrabbit1",
+ "UserName": "erp",
+ "Password": "Z!1;Q5#GE4v",
+ "VirtualHost": "erp"
+ },
+ "QDocSettings": {
+ "Url": "https://qdoc.solidcore-resources.com/Integration/odata/",
+ "Login": "Administrator",
+ "Password": "MQVuEw9avO"
+ },
+ "QDocTestSettings": {
+ "Url": "https://astsrvqtest.solidcore-resources.com/Integration/odata/",
+ "Login": "Administrator",
+ "Password": "D3cTXol8Se"
+ }
+}
diff --git a/1CDataBus/Справка по сервису 1CDataBus.docx b/1CDataBus/Справка по сервису 1CDataBus.docx
new file mode 100644
index 0000000..adbc223
--- /dev/null
+++ b/1CDataBus/Справка по сервису 1CDataBus.docx
@@ -0,0 +1,25 @@
+Сервис предназначен для обмена договорами с 1С и QDoc.
+POST-запросы:
+https://astsrvqtest02.polymetal.ru:9812/Contracts/SendContractFromQDoc?id=2274
+Отправляет в очередь сериализованный договор из QDoc по идентификатору договора.
+https://astsrvqtest02.polymetal.ru:9812/Contracts/SendContract
+Отправляет в очередь JSON-структуру представляющую договор.
+Образец:
+{
+ "name": "string",
+ "businessUnit": "string",
+ "businessUnitBIN": "string",
+ "counterparty": "string",
+ "counterpartyBIN": "string",
+ "createDate": "2025-02-24T11:27:09.476Z",
+ "number": "string",
+ "date": "2025-02-24T11:27:09.476Z",
+ "contractKind": "string",
+ "currency": "string",
+ "orders": true,
+ "qDocID": 0
+}
+
+GET-запросы:
+https://astsrvqtest02.polymetal.ru:9812/Contracts/GetContract?contractId=2274
+Возвращает из QDoc последнюю версию файла договорного документа по идентификатору договора в виде строки Base64