diff --git a/ASC.Web.sln b/ASC.Web.sln index 7b5fd23d0d6..f8af10e8e5f 100644 --- a/ASC.Web.sln +++ b/ASC.Web.sln @@ -87,6 +87,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Calendar", "products\AS EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.SsoAuth.Svc", "common\services\ASC.SsoAuth.Svc\ASC.SsoAuth.Svc.csproj", "{6AD828EA-FBA2-4D30-B969-756B3BE78E4E}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.CRM.BackgroundTasks", "products\ASC.CRM\BackgroundTasks\ASC.CRM.BackgroundTasks.csproj", "{E52C0E35-A05C-437F-8FF2-3E0AC9F81433}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -245,6 +247,10 @@ Global {6AD828EA-FBA2-4D30-B969-756B3BE78E4E}.Debug|Any CPU.Build.0 = Debug|Any CPU {6AD828EA-FBA2-4D30-B969-756B3BE78E4E}.Release|Any CPU.ActiveCfg = Release|Any CPU {6AD828EA-FBA2-4D30-B969-756B3BE78E4E}.Release|Any CPU.Build.0 = Release|Any CPU + {E52C0E35-A05C-437F-8FF2-3E0AC9F81433}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E52C0E35-A05C-437F-8FF2-3E0AC9F81433}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E52C0E35-A05C-437F-8FF2-3E0AC9F81433}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E52C0E35-A05C-437F-8FF2-3E0AC9F81433}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/build/run/CrmBackgroundTasks.bat b/build/run/CrmBackgroundTasks.bat new file mode 100644 index 00000000000..0e43feef227 --- /dev/null +++ b/build/run/CrmBackgroundTasks.bat @@ -0,0 +1,2 @@ +echo "RUN ASC.CRM.BackgroundTasks" +call dotnet run --project ..\..\products\ASC.CRM\BackgroundTasks\ASC.CRM.BackgroundTasks.csproj --no-build --$STORAGE_ROOT=..\..\..\Data --log__dir=..\..\..\Logs --log__name=crm \ No newline at end of file diff --git a/build/run/CrmServer.bat b/build/run/CrmServer.bat index e7b7ed0acd5..ed214856031 100644 --- a/build/run/CrmServer.bat +++ b/build/run/CrmServer.bat @@ -1,2 +1,2 @@ -echo "RUN ASC.CRM" +echo "RUN ASC.CRM.Server" call dotnet run --project ..\..\products\ASC.CRM\Server\ASC.CRM.csproj --no-build --$STORAGE_ROOT=..\..\..\Data --log__dir=..\..\..\Logs --log__name=crm \ No newline at end of file diff --git a/common/ASC.Api.Core/ASC.Api.Core.csproj b/common/ASC.Api.Core/ASC.Api.Core.csproj index a6f4e553ce2..131a6c284bc 100644 --- a/common/ASC.Api.Core/ASC.Api.Core.csproj +++ b/common/ASC.Api.Core/ASC.Api.Core.csproj @@ -9,6 +9,10 @@ false + + + + diff --git a/common/ASC.Api.Core/Convention/ControllerNameAttributeConvention.cs b/common/ASC.Api.Core/Convention/ControllerNameAttributeConvention.cs new file mode 100644 index 00000000000..65af908c8b7 --- /dev/null +++ b/common/ASC.Api.Core/Convention/ControllerNameAttributeConvention.cs @@ -0,0 +1,30 @@ +using System; +using System.Linq; + +using Microsoft.AspNetCore.Mvc.ApplicationModels; + +namespace ASC.Api.Core.Convention +{ + [AttributeUsage(AttributeTargets.Class)] + public class ControllerNameAttribute : Attribute + { + public string Name { get; } + + public ControllerNameAttribute(string name) + { + Name = name; + } + } + + public class ControllerNameAttributeConvention : IControllerModelConvention + { + public void Apply(ControllerModel controller) + { + var controllerNameAttribute = controller.Attributes.OfType().SingleOrDefault(); + if (controllerNameAttribute != null) + { + controller.ControllerName = controllerNameAttribute.Name; + } + } + } +} diff --git a/common/ASC.Api.Core/Core/BaseStartup.cs b/common/ASC.Api.Core/Core/BaseStartup.cs index 1146255bfa9..b81527c2e95 100644 --- a/common/ASC.Api.Core/Core/BaseStartup.cs +++ b/common/ASC.Api.Core/Core/BaseStartup.cs @@ -1,12 +1,15 @@ -using System.Text.Json.Serialization; +using System.Reflection; +using System.Text.Json.Serialization; using ASC.Api.Core.Auth; +using ASC.Api.Core.Convention; using ASC.Api.Core.Core; using ASC.Api.Core.Middleware; using ASC.Common; using ASC.Common.Caching; using ASC.Common.DependencyInjection; using ASC.Common.Logging; +using ASC.Common.Mapping; using Autofac; @@ -81,13 +84,16 @@ public virtual void ConfigureServices(IServiceCollection services) DIHelper.RegisterProducts(Configuration, HostEnvironment.ContentRootPath); var builder = services.AddMvcCore(config => - { - var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build(); - config.Filters.Add(new AuthorizeFilter(policy)); - config.Filters.Add(new TypeFilterAttribute(typeof(TenantStatusFilter))); - config.Filters.Add(new TypeFilterAttribute(typeof(PaymentFilter))); - config.Filters.Add(new TypeFilterAttribute(typeof(IpSecurityFilter))); - config.Filters.Add(new TypeFilterAttribute(typeof(ProductSecurityFilter))); + { + config.Conventions.Add(new ControllerNameAttributeConvention()); + + var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build(); + + config.Filters.Add(new AuthorizeFilter(policy)); + config.Filters.Add(new TypeFilterAttribute(typeof(TenantStatusFilter))); + config.Filters.Add(new TypeFilterAttribute(typeof(PaymentFilter))); + config.Filters.Add(new TypeFilterAttribute(typeof(IpSecurityFilter))); + config.Filters.Add(new TypeFilterAttribute(typeof(ProductSecurityFilter))); config.Filters.Add(new CustomResponseFilterAttribute()); config.Filters.Add(new CustomExceptionFilterAttribute()); config.Filters.Add(new TypeFilterAttribute(typeof(FormatFilter))); @@ -108,7 +114,9 @@ public virtual void ConfigureServices(IServiceCollection services) if (LogParams != null) { LogNLogExtension.ConfigureLog(DIHelper, LogParams); - } + } + + services.AddAutoMapper(Assembly.GetAssembly(typeof(MappingProfile))); } public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env) diff --git a/common/ASC.Common/ASC.Common.csproj b/common/ASC.Common/ASC.Common.csproj index c0330ac2cf2..51385aa1c2c 100644 --- a/common/ASC.Common/ASC.Common.csproj +++ b/common/ASC.Common/ASC.Common.csproj @@ -29,6 +29,7 @@ + diff --git a/common/ASC.Common/Mapping/IMapFrom.cs b/common/ASC.Common/Mapping/IMapFrom.cs new file mode 100644 index 00000000000..3be8426f161 --- /dev/null +++ b/common/ASC.Common/Mapping/IMapFrom.cs @@ -0,0 +1,34 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using AutoMapper; + +namespace ASC.Common.Mapping +{ + public interface IMapFrom + { + void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType()); + } +} diff --git a/common/ASC.Common/Mapping/MappingProfile.cs b/common/ASC.Common/Mapping/MappingProfile.cs new file mode 100644 index 00000000000..3d16f3f2282 --- /dev/null +++ b/common/ASC.Common/Mapping/MappingProfile.cs @@ -0,0 +1,62 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using System; +using System.Linq; +using System.Reflection; + +using AutoMapper; + +namespace ASC.Common.Mapping +{ + public class MappingProfile : Profile + { + public MappingProfile() + { + Array.ForEach(AppDomain.CurrentDomain.GetAssemblies(), a => ApplyMappingsFromAssembly(a)); + } + + private void ApplyMappingsFromAssembly(Assembly assembly) + { + if (!assembly.GetName().Name.StartsWith("ASC.")) return; + + var types = assembly.GetExportedTypes() + .Where(t => t.GetInterfaces().Any(i => + i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom<>))) + .ToList(); + + foreach (var type in types) + { + var instance = Activator.CreateInstance(type); + + var methodInfo = type.GetMethod("Mapping") + ?? type.GetInterface("IMapFrom`1").GetMethod("Mapping"); + + methodInfo?.Invoke(instance, new object[] { this }); + + } + } + } +} \ No newline at end of file diff --git a/config/autofac.products.json b/config/autofac.products.json index a14fc90734a..d1b91e2111d 100644 --- a/config/autofac.products.json +++ b/config/autofac.products.json @@ -28,7 +28,7 @@ "instanceScope": "InstancePerLifetimeScope" }, { - "type": "ASC.CRM.Configuration.ProductEntryPoint, ASC.CRM", + "type": "ASC.Web.CRM.Configuration.ProductEntryPoint, ASC.CRM", "services": [ { "type": "ASC.Web.Core.IWebItem, ASC.Web.Core" diff --git a/config/nlog.config b/config/nlog.config index 28fd9b092e1..d208716642a 100644 --- a/config/nlog.config +++ b/config/nlog.config @@ -20,6 +20,6 @@ - + \ No newline at end of file diff --git a/products/ASC.CRM/BackgroundTasks/ASC.CRM.BackgroundTasks.csproj b/products/ASC.CRM/BackgroundTasks/ASC.CRM.BackgroundTasks.csproj new file mode 100644 index 00000000000..27569b88aea --- /dev/null +++ b/products/ASC.CRM/BackgroundTasks/ASC.CRM.BackgroundTasks.csproj @@ -0,0 +1,8 @@ + + + net5.0 + + + + + diff --git a/products/ASC.CRM/BackgroundTasks/Program.cs b/products/ASC.CRM/BackgroundTasks/Program.cs new file mode 100644 index 00000000000..5445498c51a --- /dev/null +++ b/products/ASC.CRM/BackgroundTasks/Program.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Common.Utils; +using ASC.ElasticSearch; +using ASC.Web.CRM.Core.Search; + +using Autofac; +using Autofac.Extensions.DependencyInjection; + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using ASC.Common.DependencyInjection; + + +namespace ASC.CRM.BackgroundTasks +{ + class Program + { + public async static Task Main(string[] args) + { + var host = CreateHostBuilder(args).Build(); + + await host.RunAsync(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseSystemd() + .UseWindowsService() + .UseServiceProviderFactory(new AutofacServiceProviderFactory()) + .ConfigureAppConfiguration((hostContext, config) => + { + var buided = config.Build(); + var path = buided["pathToConf"]; + if (!Path.IsPathRooted(path)) + { + path = Path.GetFullPath(CrossPlatform.PathCombine(hostContext.HostingEnvironment.ContentRootPath, path)); + } + config.SetBasePath(path); + var env = hostContext.Configuration.GetValue("ENVIRONMENT", "Production"); + config + .AddInMemoryCollection(new Dictionary + { + {"pathToConf", path } + } + ) + .AddJsonFile("appsettings.json") + .AddJsonFile($"appsettings.{env}.json", true) + .AddJsonFile($"appsettings.services.json", true) + .AddJsonFile("storage.json") + .AddJsonFile("notify.json") + .AddJsonFile("kafka.json") + .AddJsonFile($"kafka.{env}.json", true) + .AddEnvironmentVariables() + .AddCommandLine(args); + }) + .ConfigureServices((hostContext, services) => + { + services.AddMemoryCache(); + + var diHelper = new DIHelper(services); + + diHelper.TryAdd(typeof(ICacheNotify<>), typeof(KafkaCache<>)); + + diHelper.RegisterProducts(hostContext.Configuration, hostContext.HostingEnvironment.ContentRootPath); + + services.AddHostedService(); + diHelper.TryAdd(); + + LogNLogExtension.ConfigureLog(diHelper, "ASC.Files", "ASC.Feed.Agregator"); + + diHelper.TryAdd(); + diHelper.TryAdd(); + diHelper.TryAdd(); + diHelper.TryAdd(); + diHelper.TryAdd(); + diHelper.TryAdd(); + diHelper.TryAdd(); + diHelper.TryAdd(); + }) + .ConfigureContainer((context, builder) => + { + builder.Register(context.Configuration, true, false, "search.json"); + }); + } +} diff --git a/products/ASC.CRM/BackgroundTasks/Properties/launchSettings.json b/products/ASC.CRM/BackgroundTasks/Properties/launchSettings.json new file mode 100644 index 00000000000..cad5cdec51b --- /dev/null +++ b/products/ASC.CRM/BackgroundTasks/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "profiles": { + "Kestrel WebServer": { + "commandName": "Project", + "launchBrowser": false, + "environmentVariables": { + "$STORAGE_ROOT": "../../../Data", + "log__dir": "../../../Logs", + "log__name": "crm", + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_URLS": "http://localhost:5026" + } + }, + "WSL 2 : Ubuntu 20.04": { + "commandName": "WSL2", + "launchBrowser": false, + "environmentVariables": { + "$STORAGE_ROOT": "../../../Data", + "log__dir": "../../../Logs", + "log__name": "crm", + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_URLS": "http://localhost:5026" + }, + "distributionName": "Ubuntu-20.04" + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/BackgroundTasks/appsettings.json b/products/ASC.CRM/BackgroundTasks/appsettings.json new file mode 100644 index 00000000000..9bf702e3108 --- /dev/null +++ b/products/ASC.CRM/BackgroundTasks/appsettings.json @@ -0,0 +1,3 @@ +{ + "pathToConf": "..\\..\\..\\config" +} diff --git a/products/ASC.CRM/BackgroundTasks/search.json b/products/ASC.CRM/BackgroundTasks/search.json new file mode 100644 index 00000000000..2138b66a234 --- /dev/null +++ b/products/ASC.CRM/BackgroundTasks/search.json @@ -0,0 +1,76 @@ +{ + "components": [ + { + "type": "ASC.Web.CRM.Core.Search.FactoryIndexerCase, ASC.CRM", + "services": [ + { + "type": "ASC.ElasticSearch.IFactoryIndexer, ASC.ElasticSearch" + } + ], + "instanceScope": "perlifetimescope" + }, + { + "type": "ASC.Web.CRM.Core.Search.FactoryIndexerContact, ASC.CRM", + "services": [ + { + "type": "ASC.ElasticSearch.IFactoryIndexer, ASC.ElasticSearch" + } + ], + "instanceScope": "perlifetimescope" + }, + { + "type": "ASC.Web.CRM.Core.Search.FactoryIndexerContactInfo, ASC.CRM", + "services": [ + { + "type": "ASC.ElasticSearch.IFactoryIndexer, ASC.ElasticSearch" + } + ], + "instanceScope": "perlifetimescope" + }, + { + "type": "ASC.Web.CRM.Core.Search.FactoryIndexerDeal, ASC.CRM", + "services": [ + { + "type": "ASC.ElasticSearch.IFactoryIndexer, ASC.ElasticSearch" + } + ], + "instanceScope": "perlifetimescope" + }, + { + "type": "ASC.Web.CRM.Core.Search.FactoryIndexerEvents, ASC.CRM", + "services": [ + { + "type": "ASC.ElasticSearch.IFactoryIndexer, ASC.ElasticSearch" + } + ], + "instanceScope": "perlifetimescope" + }, + { + "type": "ASC.Web.CRM.Core.Search.FactoryIndexerFieldValue, ASC.CRM", + "services": [ + { + "type": "ASC.ElasticSearch.IFactoryIndexer, ASC.ElasticSearch" + } + ], + "instanceScope": "perlifetimescope" + }, + { + "type": "ASC.Web.CRM.Core.Search.FactoryIndexerInvoice, ASC.CRM", + "services": [ + { + "type": "ASC.ElasticSearch.IFactoryIndexer, ASC.ElasticSearch" + } + ], + "instanceScope": "perlifetimescope" + }, + { + "type": "ASC.Web.CRM.Core.Search.FactoryIndexerTask, ASC.CRM", + "services": [ + { + "type": "ASC.ElasticSearch.IFactoryIndexer, ASC.ElasticSearch" + } + ], + "instanceScope": "perlifetimescope" + } + ] +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/ASC.CRM.csproj b/products/ASC.CRM/Server/ASC.CRM.csproj index 551a8d35579..af1f4be2857 100644 --- a/products/ASC.CRM/Server/ASC.CRM.csproj +++ b/products/ASC.CRM/Server/ASC.CRM.csproj @@ -1,46 +1,192 @@ - - - - net5.0 - - - - - - - - - - - - - - - True - True - CRMCommonResource.resx - - - - - - ResXFileCodeGenerator - CRMCommonResource.Designer.cs - - - CRMCommonResource.resx - - - CRMCommonResource.resx - - - CRMCommonResource.resx - - - CRMCommonResource.resx - - - CRMCommonResource.resx - - - + + + + net5.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + True + True + CRMCasesResource.resx + + + True + True + CRMCommonResource.resx + + + True + True + CRMContactResource.resx + + + True + True + CRMDealResource.resx + + + True + True + CRMEnumResource.resx + + + True + True + CRMErrorsResource.resx + + + True + True + CRMInvoiceResource.resx + + + True + True + CRMJSResource.resx + + + True + True + CRMReportResource.resx + + + True + True + CRMSettingResource.resx + + + True + True + CRMSocialMediaResource.resx + + + True + True + CRMTaskResource.resx + + + True + True + CRMVoipResource.resx + + + True + True + CRMPatternResource.resx + + + + + + PublicResXFileCodeGenerator + CRMCasesResource.Designer.cs + + + PublicResXFileCodeGenerator + CRMCommonResource.Designer.cs + + + PublicResXFileCodeGenerator + CRMContactResource.Designer.cs + + + PublicResXFileCodeGenerator + CRMDealResource.Designer.cs + + + PublicResXFileCodeGenerator + CRMEnumResource.Designer.cs + + + PublicResXFileCodeGenerator + CRMErrorsResource.Designer.cs + + + PublicResXFileCodeGenerator + CRMInvoiceResource.Designer.cs + + + PublicResXFileCodeGenerator + CRMJSResource.Designer.cs + + + PublicResXFileCodeGenerator + CRMReportResource.Designer.cs + + + PublicResXFileCodeGenerator + CRMSettingResource.Designer.cs + + + PublicResXFileCodeGenerator + CRMSocialMediaResource.Designer.cs + + + PublicResXFileCodeGenerator + CRMTaskResource.Designer.cs + + + PublicResXFileCodeGenerator + CRMVoipResource.Designer.cs + + + PublicResXFileCodeGenerator + CRMPatternResource.Designer.cs + + + + + + + + + + + + + diff --git a/products/ASC.CRM/Server/Api/BaseApiController.cs b/products/ASC.CRM/Server/Api/BaseApiController.cs new file mode 100644 index 00000000000..b4130f0f3e4 --- /dev/null +++ b/products/ASC.CRM/Server/Api/BaseApiController.cs @@ -0,0 +1,121 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using System; + +using ASC.Api.Core.Convention; +using ASC.Common; +using ASC.Common.Web; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.Web.Api.Routing; + +using AutoMapper; + +using Microsoft.AspNetCore.Mvc; + +namespace ASC.Api.CRM +{ + [Scope] + [DefaultRoute] + [ApiController] + [ControllerName("crm")] + public abstract class BaseApiController : ControllerBase + { + protected IMapper _mapper; + protected DaoFactory _daoFactory; + protected CrmSecurity _crmSecurity; + + public BaseApiController(DaoFactory daoFactory, + CrmSecurity crmSecurity, + IMapper mapper) + { + _daoFactory = daoFactory; + _crmSecurity = crmSecurity; + _mapper = mapper; + } + + + protected static EntityType ToEntityType(string entityTypeStr) + { + EntityType entityType; + + if (string.IsNullOrEmpty(entityTypeStr)) return EntityType.Any; + + switch (entityTypeStr.ToLower()) + { + case "person": + entityType = EntityType.Person; + break; + case "company": + entityType = EntityType.Company; + break; + case "contact": + entityType = EntityType.Contact; + break; + case "opportunity": + entityType = EntityType.Opportunity; + break; + case "case": + entityType = EntityType.Case; + break; + default: + entityType = EntityType.Any; + break; + } + + return entityType; + } + + protected string GetEntityTitle(EntityType entityType, int entityId, bool checkAccess, out DomainObject entity) + { + switch (entityType) + { + case EntityType.Contact: + case EntityType.Company: + case EntityType.Person: + var contact = (entity = _daoFactory.GetContactDao().GetByID(entityId)) as ASC.CRM.Core.Entities.Contact; + if (contact == null || (checkAccess && !_crmSecurity.CanAccessTo(contact))) + throw new ItemNotFoundException(); + return contact.GetTitle(); + case EntityType.Opportunity: + var deal = (entity = _daoFactory.GetDealDao().GetByID(entityId)) as Deal; + if (deal == null || (checkAccess && !_crmSecurity.CanAccessTo(deal))) + throw new ItemNotFoundException(); + return deal.Title; + case EntityType.Case: + var cases = (entity = _daoFactory.GetCasesDao().GetByID(entityId)) as Cases; + if (cases == null || (checkAccess && !_crmSecurity.CanAccessTo(cases))) + throw new ItemNotFoundException(); + return cases.Title; + default: + throw new ArgumentException("Invalid entityType: " + entityType); + } + } + + } +} diff --git a/products/ASC.CRM/Server/Api/CasesController.cs b/products/ASC.CRM/Server/Api/CasesController.cs new file mode 100644 index 00000000000..a74a02454c6 --- /dev/null +++ b/products/ASC.CRM/Server/Api/CasesController.cs @@ -0,0 +1,781 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Api.Core; +using ASC.Api.CRM; +using ASC.Common.Web; +using ASC.Core; +using ASC.Core.Users; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.ElasticSearch; +using ASC.MessagingSystem; +using ASC.Web.Api.Routing; +using ASC.Web.Core.Users; +using ASC.Web.CRM.Services.NotifyService; +using ASC.Web.Files.Services.WCFService; + +using AutoMapper; + +using Microsoft.AspNetCore.Mvc; + +namespace ASC.CRM.Api +{ + public class CasesController : BaseApiController + { + private readonly DisplayUserSettingsHelper _displayUserSettingsHelper; + private readonly SecurityContext _securityContext; + private readonly NotifyClient _notifyClient; + private readonly ApiContext _apiContext; + private readonly MessageService _messageService; + private readonly MessageTarget _messageTarget; + private readonly UserManager _userManager; + + public CasesController(CrmSecurity crmSecurity, + DaoFactory daoFactory, + ApiContext apiContext, + MessageTarget messageTarget, + MessageService messageService, + NotifyClient notifyClient, + SecurityContext securityContext, + DisplayUserSettingsHelper displayUserSettingsHelper, + UserManager userManager, + IMapper mapper) + : base(daoFactory, crmSecurity, mapper) + { + _apiContext = apiContext; + _messageTarget = messageTarget; + _messageService = messageService; + _notifyClient = notifyClient; + _securityContext = securityContext; + _displayUserSettingsHelper = displayUserSettingsHelper; + _userManager = userManager; + _mapper = mapper; + } + + + /// + /// Close the case with the ID specified in the request + /// + /// Close case + /// Cases + /// Case ID + /// + /// + /// + /// Case + /// + [Update(@"case/{caseid:int}/close")] + public CasesDto CloseCases(int caseid) + { + if (caseid <= 0) throw new ArgumentException(); + + var cases = _daoFactory.GetCasesDao().CloseCases(caseid); + if (cases == null) throw new ItemNotFoundException(); + + _messageService.Send(MessageAction.CaseClosed, _messageTarget.Create(cases.ID), cases.Title); + + + return _mapper.Map(cases); + } + + /// + /// Resume the case with the ID specified in the request + /// + /// Resume case + /// Cases + /// Case ID + /// + /// + /// + /// Case + /// + [Update(@"case/{caseid:int}/reopen")] + public CasesDto ReOpenCases(int caseid) + { + if (caseid <= 0) throw new ArgumentException(); + + var cases = _daoFactory.GetCasesDao().ReOpenCases(caseid); + if (cases == null) throw new ItemNotFoundException(); + + _messageService.Send(MessageAction.CaseOpened, _messageTarget.Create(cases.ID), cases.Title); + + return _mapper.Map(cases); + } + + /// + /// Creates the case with the parameters specified in the request + /// + /// Create case + /// Case title + /// Participants + /// User field list + /// Case privacy: private or not + /// List of users with access to the case + /// Notify users in accessList about the case + /// Case + /// Cases + /// + /// + /// + /// + [Create(@"case")] + public CasesDto CreateCases([FromForm] CreateOrUpdateCasesRequestDto inDto) + { + + var title = inDto.Title; + var customFieldList = inDto.CustomFieldList; + var isPrivate = inDto.isPrivate; + var isNotify = inDto.isNotify; + var members = inDto.Members; + var accessList = inDto.accessList; + + if (string.IsNullOrEmpty(title)) throw new ArgumentException(); + + var casesID = _daoFactory.GetCasesDao().CreateCases(title); + + var cases = new Cases + { + ID = casesID, + Title = title, + CreateBy = _securityContext.CurrentAccount.ID, + CreateOn = DateTime.UtcNow + }; + + SetAccessToCases(cases, isPrivate, accessList, isNotify, false); + + var membersList = members != null ? members.ToList() : new List(); + if (membersList.Any()) + { + var contacts = _daoFactory.GetContactDao().GetContacts(membersList.ToArray()).Where(_crmSecurity.CanAccessTo).ToList(); + membersList = contacts.Select(m => m.ID).ToList(); + _daoFactory.GetCasesDao().SetMembers(cases.ID, membersList.ToArray()); + } + + if (customFieldList != null) + { + var existingCustomFieldList = _daoFactory.GetCustomFieldDao().GetFieldsDescription(EntityType.Case).Select(fd => fd.ID).ToList(); + foreach (var field in customFieldList) + { + if (string.IsNullOrEmpty(field.Value) || !existingCustomFieldList.Contains(field.Key)) continue; + _daoFactory.GetCustomFieldDao().SetFieldValue(EntityType.Case, cases.ID, field.Key, field.Value); + } + } + + var casesToResult = _daoFactory.GetCasesDao().GetByID(casesID); + + return _mapper.Map(casesToResult); + } + + /// + /// Updates the selected case with the parameters specified in the request + /// + /// Update case + /// Case ID + /// Case title + /// Participants + /// User field list + /// Case privacy: private or not + /// List of users with access to the case + /// Notify users in accessList about the case + /// Cases + /// Case + /// + /// + /// + /// + /// + [Update(@"case/{caseid:int}")] + public CasesDto UpdateCases(int caseid, [FromForm] CreateOrUpdateCasesRequestDto inDto) + { + var title = inDto.Title; + var isPrivate = inDto.isPrivate; + var isNotify = inDto.isNotify; + var accessList = inDto.accessList; + var members = inDto.Members; + var customFieldList = inDto.CustomFieldList; + + if ((caseid <= 0) || (string.IsNullOrEmpty(title))) throw new ArgumentException(); + + var cases = _daoFactory.GetCasesDao().GetByID(caseid); + if (cases == null) throw new ItemNotFoundException(); + + cases.Title = title; + + _daoFactory.GetCasesDao().UpdateCases(cases); + + if (_crmSecurity.IsAdmin || cases.CreateBy == _securityContext.CurrentAccount.ID) + { + SetAccessToCases(cases, isPrivate, accessList, isNotify, false); + } + + var membersList = members != null ? members.ToList() : new List(); + if (membersList.Any()) + { + var contacts = _daoFactory.GetContactDao().GetContacts(membersList.ToArray()).Where(_crmSecurity.CanAccessTo).ToList(); + membersList = contacts.Select(m => m.ID).ToList(); + _daoFactory.GetCasesDao().SetMembers(cases.ID, membersList.ToArray()); + } + + if (customFieldList != null) + { + var existingCustomFieldList = _daoFactory.GetCustomFieldDao().GetFieldsDescription(EntityType.Case).Select(fd => fd.ID).ToList(); + foreach (var field in customFieldList) + { + if (string.IsNullOrEmpty(field.Value) || !existingCustomFieldList.Contains(field.Key)) continue; + _daoFactory.GetCustomFieldDao().SetFieldValue(EntityType.Case, cases.ID, field.Key, field.Value); + } + } + + return _mapper.Map(cases); + } + + /// + /// Sets access rights for the selected case with the parameters specified in the request + /// + /// Case ID + /// Case privacy: private or not + /// List of users with access to the case + /// Set rights to case + /// Cases + /// + /// + /// + /// Case + /// + [Update(@"case/{caseid:int}/access")] + public CasesDto SetAccessToCases(int caseid, bool isPrivate, IEnumerable accessList) + { + if (caseid <= 0) throw new ArgumentException(); + + var cases = _daoFactory.GetCasesDao().GetByID(caseid); + if (cases == null) throw new ItemNotFoundException(); + + if (!(_crmSecurity.IsAdmin || cases.CreateBy == _securityContext.CurrentAccount.ID)) throw _crmSecurity.CreateSecurityException(); + + return SetAccessToCases(cases, isPrivate, accessList, false, true); + } + + private CasesDto SetAccessToCases(Cases cases, bool isPrivate, IEnumerable accessList, bool isNotify, bool isMessageServicSende) + { + var accessListLocal = accessList != null ? accessList.Distinct().ToList() : new List(); + if (isPrivate && accessListLocal.Any()) + { + if (isNotify) + { + accessListLocal = accessListLocal.Where(u => u != _securityContext.CurrentAccount.ID).ToList(); + _notifyClient.SendAboutSetAccess(EntityType.Case, cases.ID, _daoFactory, accessListLocal.ToArray()); + } + + if (!accessListLocal.Contains(_securityContext.CurrentAccount.ID)) + { + accessListLocal.Add(_securityContext.CurrentAccount.ID); + } + + _crmSecurity.SetAccessTo(cases, accessListLocal); + if (isMessageServicSende) + { + var users = GetUsersByIdList(accessListLocal); + _messageService.Send(MessageAction.CaseRestrictedAccess, _messageTarget.Create(cases.ID), cases.Title, users.Select(x => x.DisplayUserName(false, _displayUserSettingsHelper))); + } + } + else + { + _crmSecurity.MakePublic(cases); + if (isMessageServicSende) + { + _messageService.Send(MessageAction.CaseOpenedAccess, _messageTarget.Create(cases.ID), cases.Title); + } + } + + return _mapper.Map(cases); + } + + /// + /// Sets access rights for other users to the list of cases with the IDs specified in the request + /// + /// Case ID list + /// Case privacy: private or not + /// List of users with access + /// Set case access rights + /// Cases + /// + /// + /// + /// Case list + /// + [Update(@"case/access")] + public IEnumerable SetAccessToBatchCases([FromForm] SetAccessToBatchCasesRequestDto inDto) + { + var casesid = inDto.CasesId; + var isPrivate = inDto.isPrivate; + var accessList = inDto.AccessList; + + var result = new List(); + + var cases = _daoFactory.GetCasesDao().GetCases(casesid); + + if (!cases.Any()) return new List(); + + foreach (var c in cases) + { + if (c == null) throw new ItemNotFoundException(); + + if (!(_crmSecurity.IsAdmin || c.CreateBy == _securityContext.CurrentAccount.ID)) continue; + + SetAccessToCases(c, isPrivate, accessList, false, true); + result.Add(c); + } + + return ToListCasesDtos(result); + } + + /// + /// Sets access rights for other users to the list of all cases matching the parameters specified in the request + /// + /// Contact ID + /// Case status + /// Tags + /// Case privacy: private or not + /// List of users with access + /// Set case access rights + /// Cases + /// + /// + /// + /// Case list + /// + [Update(@"case/filter/access")] + public IEnumerable SetAccessToBatchCases([FromForm] SetAccessToBatchCasesByFilterInDto inDto) + { + int contactid = inDto.Contactid; + bool? isClosed = inDto.isClosed; + IEnumerable tags = inDto.Tags; + bool isPrivate = inDto.isPrivate; + IEnumerable accessList = inDto.AccessList; + + var result = new List(); + + var caseses = _daoFactory.GetCasesDao().GetCases(_apiContext.FilterValue, contactid, isClosed, tags, 0, 0, null); + + if (!caseses.Any()) return new List(); + + foreach (var casese in caseses) + { + if (casese == null) throw new ItemNotFoundException(); + + if (!(_crmSecurity.IsAdmin || casese.CreateBy == _securityContext.CurrentAccount.ID)) continue; + + SetAccessToCases(casese, isPrivate, accessList, false, true); + result.Add(casese); + } + + return ToListCasesDtos(result); + } + + /// + /// Returns the detailed information about the case with the ID specified in the request + /// + /// Get case by ID + /// Cases + /// Case ID + /// + /// + [Read(@"case/{caseid:int}")] + public CasesDto GetCaseByID(int caseid) + { + if (caseid <= 0) throw new ItemNotFoundException(); + + var cases = _daoFactory.GetCasesDao().GetByID(caseid); + if (cases == null || !_crmSecurity.CanAccessTo(cases)) throw new ItemNotFoundException(); + + return _mapper.Map(cases); + } + + /// + /// Returns the list of all cases matching the parameters specified in the request + /// + /// Get case list + /// Contact ID + /// Case status + /// Tags + /// Cases + /// + /// Case list + /// + [Read(@"case/filter")] + public IEnumerable GetCases(int contactid, bool? isClosed, IEnumerable tags) + { + IEnumerable result; + SortedByType sortBy; + OrderBy casesOrderBy; + + var searchString = _apiContext.FilterValue; + + if (ASC.CRM.Classes.EnumExtension.TryParse(_apiContext.SortBy, true, out sortBy)) + { + casesOrderBy = new OrderBy(sortBy, !_apiContext.SortDescending); + } + else if (string.IsNullOrEmpty(_apiContext.SortBy)) + { + casesOrderBy = new OrderBy(SortedByType.Title, true); + } + else + { + casesOrderBy = null; + } + + var fromIndex = (int)_apiContext.StartIndex; + var count = (int)_apiContext.Count; + + if (casesOrderBy != null) + { + result = ToListCasesDtos( + _daoFactory + .GetCasesDao() + .GetCases( + searchString, + contactid, + isClosed, + tags, + fromIndex, + count, + casesOrderBy)).ToList(); + + _apiContext.SetDataPaginated(); + _apiContext.SetDataFiltered(); + _apiContext.SetDataSorted(); + } + else + { + result = ToListCasesDtos( + _daoFactory + .GetCasesDao() + .GetCases( + searchString, contactid, isClosed, + tags, + 0, + 0, + null)).ToList(); + } + + int totalCount; + + if (result.Count() < count) + { + totalCount = fromIndex + result.Count(); + } + else + { + totalCount = _daoFactory.GetCasesDao().GetCasesCount(searchString, contactid, isClosed, tags); + } + + _apiContext.SetTotalCount(totalCount); + return result; + } + + /// + /// Deletes the case with the ID specified in the request + /// + /// Delete case + /// Case ID + /// Cases + /// + /// + /// + /// Case + /// + [Delete(@"case/{caseid:int}")] + public CasesDto DeleteCase(int caseid) + { + if (caseid <= 0) throw new ArgumentException(); + + var cases = _daoFactory.GetCasesDao().DeleteCases(caseid); + + if (cases == null) throw new ItemNotFoundException(); + + _messageService.Send(MessageAction.CaseDeleted, _messageTarget.Create(cases.ID), cases.Title); + + return _mapper.Map(cases); + } + + /// + /// Deletes the group of cases with the IDs specified in the request + /// + /// Case ID list + /// + /// + /// Delete case group + /// Cases + /// + /// Case list + /// + [Update(@"case")] + public IEnumerable DeleteBatchCases([FromForm] IEnumerable casesids) + { + if (casesids == null) throw new ArgumentException(); + + casesids = casesids.Distinct(); + var caseses = _daoFactory.GetCasesDao().DeleteBatchCases(casesids.ToArray()); + + if (caseses == null || !caseses.Any()) return new List(); + + _messageService.Send(MessageAction.CasesDeleted, _messageTarget.Create(casesids), caseses.Select(c => c.Title)); + + return ToListCasesDtos(caseses); + } + + /// + /// Deletes the list of all cases matching the parameters specified in the request + /// + /// Contact ID + /// Case status + /// Tags + /// + /// + /// Delete case group + /// Cases + /// + /// Case list + /// + [Delete(@"case/filter")] + public IEnumerable DeleteBatchCases(int contactid, bool? isClosed, IEnumerable tags) + { + var caseses = _daoFactory.GetCasesDao().GetCases(_apiContext.FilterValue, contactid, isClosed, tags, 0, 0, null); + if (!caseses.Any()) return new List(); + + caseses = _daoFactory.GetCasesDao().DeleteBatchCases(caseses); + + _messageService.Send(MessageAction.CasesDeleted, _messageTarget.Create(caseses.Select(c => c.ID)), caseses.Select(c => c.Title)); + + return ToListCasesDtos(caseses); + } + + /// + /// Returns the list of all contacts associated with the case with the ID specified in the request + /// + /// Get all case contacts + /// Case ID + /// Cases + /// Contact list + /// + [Read(@"case/{caseid:int}/contact")] + public IEnumerable GetCasesMembers(int caseid) + { + var contactIDs = _daoFactory.GetCasesDao().GetMembers(caseid); + var contacts = _daoFactory.GetContactDao().GetContacts(contactIDs); + + return contactIDs == null + ? new ItemList() + : _mapper.Map, List>(contacts); + } + + /// + /// Adds the selected contact to the case with the ID specified in the request + /// + /// Add case contact + /// Cases + /// Case ID + /// Contact ID + /// + /// + /// + /// Participant + /// + [Create(@"case/{caseid:int}/contact")] + public ContactDto AddMemberToCases([FromRoute] int caseid, [FromForm] int contactid) + { + if ((caseid <= 0) || (contactid <= 0)) throw new ArgumentException(); + + var cases = _daoFactory.GetCasesDao().GetByID(caseid); + if (cases == null || !_crmSecurity.CanAccessTo(cases)) throw new ItemNotFoundException(); + + var contact = _daoFactory.GetContactDao().GetByID(contactid); + if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException(); + + _daoFactory.GetCasesDao().AddMember(caseid, contactid); + + var messageAction = contact is Company ? MessageAction.CaseLinkedCompany : MessageAction.CaseLinkedPerson; + _messageService.Send(messageAction, _messageTarget.Create(cases.ID), cases.Title, contact.GetTitle()); + + return _mapper.Map(contact); + } + + /// + /// Delete the selected contact from the case with the ID specified in the request + /// + /// Delete case contact + /// Cases + /// Case ID + /// Contact ID + /// + /// + /// + /// Participant + /// + [Delete(@"case/{caseid:int}/contact/{contactid:int}")] + public ContactDto DeleteMemberFromCases(int caseid, int contactid) + { + if ((caseid <= 0) || (contactid <= 0)) throw new ArgumentException(); + + var cases = _daoFactory.GetCasesDao().GetByID(caseid); + if (cases == null || !_crmSecurity.CanAccessTo(cases)) throw new ItemNotFoundException(); + + var contact = _daoFactory.GetContactDao().GetByID(contactid); + if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException(); + + var result = _mapper.Map(contact); + + _daoFactory.GetCasesDao().RemoveMember(caseid, contactid); + + var messageAction = contact is Company ? MessageAction.CaseUnlinkedCompany : MessageAction.CaseUnlinkedPerson; + + _messageService.Send(messageAction, _messageTarget.Create(cases.ID), cases.Title, contact.GetTitle()); + + return result; + } + + /// + /// Returns the list of 30 cases in the CRM module with prefix + /// + /// + /// + /// Cases + /// + /// Cases list + /// + /// false + [Read(@"case/byprefix")] + public IEnumerable GetCasesByPrefix(string prefix, int contactID) + { + var result = new List(); + + if (contactID > 0) + { + var findedCases = _daoFactory.GetCasesDao().GetCases(string.Empty, contactID, null, null, 0, 0, null); + + foreach (var item in findedCases) + { + if (item.Title.IndexOf(prefix, StringComparison.Ordinal) != -1) + { + result.Add(_mapper.Map(item)); + } + } + + _apiContext.SetTotalCount(findedCases.Count); + } + else + { + const int maxItemCount = 30; + var findedCases = _daoFactory.GetCasesDao().GetCasesByPrefix(prefix, 0, maxItemCount); + + foreach (var item in findedCases) + { + result.Add(_mapper.Map(item)); + } + } + + return result; + } + + private IEnumerable ToListCasesDtos(ICollection items) + { + if (items == null || items.Count == 0) return new List(); + + var result = new List(); + + var contactIDs = new List(); + var casesIDs = items.Select(item => item.ID).ToArray(); + + var customFields = _daoFactory.GetCustomFieldDao() + .GetEnityFields(EntityType.Case, casesIDs) + .GroupBy(item => item.EntityID) + .ToDictionary(item => item.Key, item => item.Select(x => _mapper.Map(x))); + + var casesMembers = _daoFactory.GetCasesDao().GetMembers(casesIDs); + + foreach (var value in casesMembers.Values) + { + contactIDs.AddRange(value); + } + + var contacts = _daoFactory + .GetContactDao() + .GetContacts(contactIDs.Distinct().ToArray()) + .ToDictionary(item => item.ID, x => _mapper.Map(x)); + + foreach (var cases in items) + { + var casesDto = _mapper.Map(cases); + + casesDto.CustomFields = customFields.ContainsKey(cases.ID) + ? customFields[cases.ID] + : new List(); + + casesDto.Members = casesMembers.ContainsKey(cases.ID) + ? casesMembers[cases.ID].Where(contacts.ContainsKey).Select(item => contacts[item]) + : new List(); + + result.Add(casesDto); + } + + return result; + } + + private IEnumerable GetUsersByIdList(IEnumerable ids) + { + return _userManager.GetUsers().Where(x => ids.Contains(x.ID)); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Api/ContactInfosController.cs b/products/ASC.CRM/Server/Api/ContactInfosController.cs new file mode 100644 index 00000000000..7314d0ab646 --- /dev/null +++ b/products/ASC.CRM/Server/Api/ContactInfosController.cs @@ -0,0 +1,549 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; + +using ASC.Api.CRM; +using ASC.Common.Web; +using ASC.CRM.ApiModels; +using ASC.CRM.Classes; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.MessagingSystem; +using ASC.Web.Api.Routing; + +using AutoMapper; + +using Microsoft.AspNetCore.Mvc; + +namespace ASC.CRM.Api +{ + public class ContactInfosController : BaseApiController + { + private readonly MessageService _messageService; + private readonly MessageTarget _messageTarget; + + public ContactInfosController(CrmSecurity crmSecurity, + DaoFactory daoFactory, + MessageTarget messageTarget, + MessageService messageService, + IMapper mapper) + : base(daoFactory, crmSecurity, mapper) + { + _messageTarget = messageTarget; + _messageService = messageService; + } + + + /// + /// Returns the list of all available contact categories + /// + /// + /// Contact information type + /// + /// Get all categories + /// Contacts + /// + /// List of all available contact categories + /// + [Read(@"contact/data/{infoType}/category")] + public IEnumerable GetContactInfoCategory(ContactInfoType infoType) + { + return Enum.GetNames(ContactInfo.GetCategory(infoType)); + } + + /// + /// Returns the list of all available contact information types + /// + /// Get all contact info types + /// Contacts + /// + [Read(@"contact/data/infoType")] + public IEnumerable GetContactInfoType() + { + return Enum.GetNames(typeof(ContactInfoType)); + } + + /// + /// Returns the detailed information for the contact + /// + /// Contact ID + /// Get contact information + /// Contacts + /// + /// Contact information + /// + [Read(@"contact/{contactid:int}/data")] + public IEnumerable GetContactInfo(int contactid) + { + if (contactid <= 0) throw new ArgumentException(); + + var contact = _daoFactory.GetContactDao().GetByID(contactid); + + if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException(); + + var sourceData = _daoFactory.GetContactInfoDao().GetList(contactid, null, null, null) + .OrderByDescending(info => info.ID) + .ToList(); + + return _mapper.Map, List>(sourceData); + } + + /// + /// Returns the detailed list of all information available for the contact with the ID specified in the request + /// + /// Contact ID + /// Contact information ID + /// Get contact info + /// Contacts + /// Contact information + /// + [Read(@"contact/{contactid:int}/data/{id:int}")] + public ContactInfoDto GetContactInfoByID(int contactid, int id) + { + if (contactid <= 0 || id <= 0) throw new ArgumentException(); + + var contact = _daoFactory.GetContactDao().GetByID(contactid); + if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException(); + + var contactInfo = _daoFactory.GetContactInfoDao().GetByID(id); + + if (contactInfo == null || contactInfo.ContactID != contactid) throw new ArgumentException(); + + return _mapper.Map(contactInfo); + } + + /// + /// Adds the information with the parameters specified in the request to the contact with the selected ID + /// + ///Contact ID + ///Contact information type + ///Data + ///Contact importance: primary or not + ///Category + /// Add contact info + ///Contacts + /// + /// + /// + /// Contact information + /// + /// + [Create(@"contact/{contactid:int}/data")] + public ContactInfoDto CreateContactInfo( + [FromRoute] int contactid, + [FromForm] ContactInfoType infoType, + [FromForm] string data, + [FromForm] bool isPrimary, + [FromForm] string category) + { + if (string.IsNullOrEmpty(data) || contactid <= 0) throw new ArgumentException(); + var contact = _daoFactory.GetContactDao().GetByID(contactid); + if (contact == null) throw new ItemNotFoundException(); + + if (infoType == ContactInfoType.Twitter) + { + if (!_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException(); + } + else + { + if (!_crmSecurity.CanEdit(contact)) throw new ItemNotFoundException(); + } + + var categoryType = ContactInfo.GetCategory(infoType); + if (!Enum.IsDefined(categoryType, category)) throw new ArgumentException(); + + + var contactInfo = new ContactInfo + { + Data = data, + InfoType = infoType, + ContactID = contactid, + IsPrimary = isPrimary, + Category = (int)Enum.Parse(categoryType, category) + }; + + if (contactInfo.InfoType == ContactInfoType.Address) + { + Address res; + if (!Address.TryParse(contactInfo, out res)) + throw new ArgumentException(); + } + + var contactInfoID = _daoFactory.GetContactInfoDao().Save(contactInfo); + + var messageAction = contact is Company ? MessageAction.CompanyUpdatedPrincipalInfo : MessageAction.PersonUpdatedPrincipalInfo; + _messageService.Send(messageAction, _messageTarget.Create(contact.ID), contact.GetTitle()); + + var contactInfoDto = _mapper.Map(contactInfo); + + contactInfoDto.Id = contactInfoID; + + return contactInfoDto; + } + + /// + /// Adds the address information to the contact with the selected ID + /// + /// Contact ID + /// Address data + /// Add address info + /// Contacts + /// + /// + /// + /// Contact information + /// + /// + /// + [Create(@"contact/{contactid:int}/addressdata")] + public ContactInfoDto CreateContactInfoAddress([FromRoute] int contactid, Address address) + { + if (contactid <= 0) throw new ArgumentException("Invalid value", "contactid"); + + var contact = _daoFactory.GetContactDao().GetByID(contactid); + + if (contact == null || !_crmSecurity.CanEdit(contact)) throw new ItemNotFoundException(); + + if (address == null) throw new ArgumentException("Value cannot be null", "address"); + + if (!Enum.IsDefined(typeof(AddressCategory), address.Category)) throw new ArgumentException("Value does not fall within the expected range.", "address.Category"); + + address.CategoryName = ((AddressCategory)address.Category).ToLocalizedString(); + + var settings = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = true + }; + + var contactInfo = new ContactInfo + { + InfoType = ContactInfoType.Address, + ContactID = contactid, + IsPrimary = address.IsPrimary, + Category = address.Category, + Data = JsonSerializer.Serialize(address, settings) + }; + + contactInfo.ID = _daoFactory.GetContactInfoDao().Save(contactInfo); + + var messageAction = contact is Company ? MessageAction.CompanyUpdatedPrincipalInfo : MessageAction.PersonUpdatedPrincipalInfo; + _messageService.Send(messageAction, _messageTarget.Create(contact.ID), contact.GetTitle()); + + return _mapper.Map(contactInfo); + } + + /// + /// Creates contact information (add new information to the old list) with the parameters specified in the request for the contact with the selected ID + /// + ///Group contact info + /// Contact ID + /// Contact information + /// + /// + /// + /// Contacts + /// + /// + /// Contact information + /// + /// false + [Create(@"contact/{contactid:int}/batch")] + public IEnumerable CreateBatchContactInfo([FromRoute] int contactid, [FromForm] IEnumerable items) + { + if (contactid <= 0) throw new ArgumentException(); + + var contact = _daoFactory.GetContactDao().GetByID(contactid); + if (contact == null || !_crmSecurity.CanEdit(contact)) throw new ItemNotFoundException(); + + var itemsList = items != null ? items.ToList() : new List(); + var contactInfoList = itemsList.Select(FromContactInfoDto).ToList(); + + foreach (var contactInfo in contactInfoList) + { + if (contactInfo.InfoType == ContactInfoType.Address) + { + Address res; + if (!Address.TryParse(contactInfo, out res)) + throw new ArgumentException(); + } + contactInfo.ContactID = contactid; + } + + var ids = _daoFactory.GetContactInfoDao().SaveList(contactInfoList, contact); + + for (var index = 0; index < itemsList.Count; index++) + { + var infoDto = itemsList[index]; + infoDto.Id = ids[index]; + } + return itemsList; + } + + /// + /// Updates the information with the parameters specified in the request for the contact with the selected ID + /// + ///Contact information record ID + ///Contact ID + ///Contact information type + ///Data + ///Contact importance: primary or not + ///Contact information category + ///Update contact info + ///Contacts + /// + /// + /// Contact information + /// + [Update(@"contact/{contactid:int}/data/{id:int}")] + public ContactInfoDto UpdateContactInfo(int id, int contactid, ContactInfoType? infoType, string data, bool? isPrimary, string category) + { + if (id <= 0 || string.IsNullOrEmpty(data) || contactid <= 0) throw new ArgumentException(); + + var contact = _daoFactory.GetContactDao().GetByID(contactid); + if (contact == null || !_crmSecurity.CanEdit(contact)) throw new ItemNotFoundException(); + + var contactInfo = _daoFactory.GetContactInfoDao().GetByID(id); + + if (infoType != null) + { + var categoryType = ContactInfo.GetCategory(infoType.Value); + + if (!string.IsNullOrEmpty(category) && Enum.IsDefined(categoryType, category)) + { + contactInfo.Category = (int)Enum.Parse(categoryType, category); + } + + contactInfo.InfoType = infoType.Value; + } + + contactInfo.ContactID = contactid; + + if (isPrimary != null) + { + contactInfo.IsPrimary = isPrimary.Value; + } + + contactInfo.Data = data; + + if (contactInfo.InfoType == ContactInfoType.Address) + { + Address res; + if (!Address.TryParse(contactInfo, out res)) + throw new ArgumentException(); + } + + _daoFactory.GetContactInfoDao().Update(contactInfo); + + var messageAction = contact is Company ? MessageAction.CompanyUpdatedPrincipalInfo : MessageAction.PersonUpdatedPrincipalInfo; + + _messageService.Send(messageAction, _messageTarget.Create(contact.ID), contact.GetTitle()); + + var contactInfoDto = _mapper.Map(contactInfo); + + return contactInfoDto; + } + + /// + /// Updates the address information with the parameters specified in the request for the contact with the selected ID + /// + /// Contact information record ID + /// Contact ID + /// Address data + /// Update address info + /// Contacts + /// + /// + /// + /// Contact information + /// + [Update(@"contact/{contactid:int}/addressdata/{id:int}")] + public ContactInfoDto UpdateContactInfoAddress(int id, int contactid, Address address) + { + if (id <= 0) throw new ArgumentException("Invalid value", "id"); + + var contactInfo = _daoFactory.GetContactInfoDao().GetByID(id); + + if (contactInfo == null || contactInfo.InfoType != ContactInfoType.Address) throw new ItemNotFoundException(); + + if (contactid <= 0) throw new ArgumentException("Invalid value", "contactid"); + + var contact = _daoFactory.GetContactDao().GetByID(contactid); + + if (contact == null || !_crmSecurity.CanEdit(contact) || contactInfo.ContactID != contactid) throw new ItemNotFoundException(); + + if (address == null) throw new ArgumentException("Value cannot be null", "address"); + + if (!Enum.IsDefined(typeof(AddressCategory), address.Category)) throw new ArgumentException("Value does not fall within the expected range.", "address.Category"); + + address.CategoryName = ((AddressCategory)address.Category).ToLocalizedString(); + + var settings = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = true + }; + + contactInfo.IsPrimary = address.IsPrimary; + contactInfo.Category = address.Category; + contactInfo.Data = JsonSerializer.Serialize(address, settings); + + _daoFactory.GetContactInfoDao().Update(contactInfo); + + var messageAction = contact is Company ? MessageAction.CompanyUpdatedPrincipalInfo : MessageAction.PersonUpdatedPrincipalInfo; + + _messageService.Send(messageAction, _messageTarget.Create(contact.ID), contact.GetTitle()); + + return _mapper.Map(contactInfo); + } + + /// + /// Updates contact information (delete old information and add new list) with the parameters specified in the request for the contact with the selected ID + /// + ///Group contact info update + ///Contact ID + ///Contact information + /// + ///Contacts + /// + /// + /// Contact information + /// + /// false + [Update(@"contact/{contactid:int}/batch")] + public IEnumerable UpdateBatchContactInfo(int contactid, IEnumerable items) + { + if (contactid <= 0) throw new ArgumentException(); + + var contact = _daoFactory.GetContactDao().GetByID(contactid); + if (contact == null || !_crmSecurity.CanEdit(contact)) throw new ItemNotFoundException(); + + var itemsList = items != null ? items.ToList() : new List(); + var contactInfoList = itemsList.Select(FromContactInfoDto).ToList(); + + foreach (var contactInfo in contactInfoList) + { + if (contactInfo.InfoType == ContactInfoType.Address) + { + Address res; + if (!Address.TryParse(contactInfo, out res)) + throw new ArgumentException(); + } + contactInfo.ContactID = contactid; + } + + _daoFactory.GetContactInfoDao().DeleteByContact(contactid); + var ids = _daoFactory.GetContactInfoDao().SaveList(contactInfoList, contact); + + for (var index = 0; index < itemsList.Count; index++) + { + var infoDto = itemsList[index]; + infoDto.Id = ids[index]; + } + return itemsList; + } + + /// + /// Returns the detailed information for the contact with the selected ID by the information type specified in the request + /// + /// Contact ID + /// Contact information type + /// Get contact information by type + /// Contacts + /// + /// Contact information + /// + [Read(@"contact/{contactid:int}/data/{infoType}")] + public IEnumerable GetContactInfo(int contactid, ContactInfoType infoType) + { + if (contactid <= 0) throw new ArgumentException(); + + var contact = _daoFactory.GetContactDao().GetByID(contactid); + if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException(); + + return _daoFactory.GetContactInfoDao().GetListData(contactid, infoType); + } + + + /// + /// Deletes the contact information for the contact with the ID specified in the request + /// + /// Contact ID + /// Contact information record ID + /// Delete contact info + /// Contacts + /// + /// + /// + /// Contact information + /// + [Delete(@"contact/{contactid:int}/data/{id:int}")] + public ContactInfoDto DeleteContactInfo(int contactid, int id) + { + if (id <= 0 || contactid <= 0) throw new ArgumentException(); + + var contact = _daoFactory.GetContactDao().GetByID(contactid); + if (contact == null || !_crmSecurity.CanEdit(contact)) throw new ItemNotFoundException(); + + var contactInfo = _daoFactory.GetContactInfoDao().GetByID(id); + if (contactInfo == null) throw new ItemNotFoundException(); + + var wrapper = _mapper.Map(contactInfo); + + _daoFactory.GetContactInfoDao().Delete(id); + + var messageAction = contact is Company ? MessageAction.CompanyUpdatedPrincipalInfo : MessageAction.PersonUpdatedPrincipalInfo; + + _messageService.Send(messageAction, _messageTarget.Create(contact.ID), contact.GetTitle()); + + return wrapper; + } + + private static ContactInfo FromContactInfoDto(ContactInfoDto contactInfoDto) + { + return new ContactInfo + { + ID = contactInfoDto.Id, + Category = contactInfoDto.Category, + Data = contactInfoDto.Data, + InfoType = contactInfoDto.InfoType, + IsPrimary = contactInfoDto.IsPrimary + }; + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Api/ContactsController.cs b/products/ASC.CRM/Server/Api/ContactsController.cs new file mode 100644 index 00000000000..56a722aca99 --- /dev/null +++ b/products/ASC.CRM/Server/Api/ContactsController.cs @@ -0,0 +1,2146 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Api.Collections; +using ASC.Api.Core; +using ASC.Api.CRM; +using ASC.Common.Threading.Progress; +using ASC.Common.Web; +using ASC.Core; +using ASC.Core.Users; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; +using ASC.MessagingSystem; +using ASC.Web.Api.Models; +using ASC.Web.Api.Routing; +using ASC.Web.CRM.Classes; +using ASC.Web.CRM.Services.NotifyService; +using ASC.Web.Studio.Core; + +using Autofac; + +using AutoMapper; + +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +using Contact = ASC.CRM.Core.Entities.Contact; + +namespace ASC.CRM.Api +{ + public class ContactsController : BaseApiController + { + private readonly MailSender _mailSender; + private readonly FileSizeComment _fileSizeComment; + private readonly ContactPhotoManager _contactPhotoManager; + private readonly EmployeeWraperHelper _employeeWraperHelper; + private readonly UserFormatter _userFormatter; + private readonly SetupInfo _setupInfo; + private readonly SecurityContext _securityContext; + private readonly NotifyClient _notifyClient; + private readonly ApiContext _apiContext; + private readonly MessageService _messageService; + private readonly MessageTarget _messageTarget; + + public ContactsController(CrmSecurity crmSecurity, + DaoFactory daoFactory, + ApiContext apiContext, + MessageTarget messageTarget, + MessageService messageService, + NotifyClient notifyClient, + SecurityContext securityContext, + SetupInfo setupInfo, + UserFormatter userFormatter, + EmployeeWraperHelper employeeWraperHelper, + ContactPhotoManager contactPhotoManager, + FileSizeComment fileSizeComment, + MailSender mailSender, + IMapper mapper) + : base(daoFactory, crmSecurity, mapper) + { + _apiContext = apiContext; + _messageTarget = messageTarget; + _messageService = messageService; + _notifyClient = notifyClient; + _securityContext = securityContext; + _setupInfo = setupInfo; + _userFormatter = userFormatter; + _employeeWraperHelper = employeeWraperHelper; + _contactPhotoManager = contactPhotoManager; + _fileSizeComment = fileSizeComment; + _mailSender = mailSender; + _mapper = mapper; + } + + + /// + /// Returns the detailed information about the contact with the ID specified in the request + /// + /// Contact ID + /// Contact + /// Get contact by ID + /// Contacts + /// + /// + [Read(@"contact/{contactid:int}")] + public ContactDto GetContactByID(int contactid) + { + if (contactid <= 0) throw new ArgumentException(); + + var contact = _daoFactory.GetContactDao().GetByID(contactid); + if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException(); + + return _mapper.Map(contact); + } + + public IEnumerable GetContactsByID(IEnumerable contactid) + { + var contacts = _daoFactory.GetContactDao().GetContacts(contactid.ToArray()).Where(r => r != null && _crmSecurity.CanAccessTo(r)).ToList(); + + return _mapper.Map, List>(contacts); + } + + /// + /// Returns the contact list for the project with the ID specified in the request + /// + /// + /// Get contacts by project ID + /// + /// Project ID + /// Contacts + /// + /// Contact list + /// + /// + [Read(@"contact/project/{projectid:int}")] + public IEnumerable GetContactsByProjectID(int projectid) + { + if (projectid <= 0) throw new ArgumentException(); + + var contacts = _daoFactory.GetContactDao().GetContactsByProjectID(projectid); + + return _mapper.Map, List>(contacts.ToList()); + + } + + ///// + ///// Links the selected contact to the project with the ID specified in the request + ///// + ///// Contact ID + ///// Project ID + ///// Contacts + ///// Link contact with project + ///// + ///// + ///// Contact Info + //[Create(@"contact/{contactid:int}/project/{projectid:int}")] + //public ContactDto SetRelativeContactToProject(int contactid, int projectid) + //{ + // if (contactid <= 0 || projectid <= 0) throw new ArgumentException(); + + // var contact = DaoFactory.GetContactDao().GetByID(contactid); + // if (contact == null || !CRMSecurity.CanAccessTo(contact)) throw new ItemNotFoundException(); + + // var project = ProjectsDaoFactory.ProjectDao.GetById(projectid); + // if (project == null) throw new ItemNotFoundException(); + + // using (var scope = DIHelper.Resolve()) + // { + // if (!scope.Resolve().CanLinkContact(project)) throw CRMSecurity.CreateSecurityException(); + // } + + // DaoFactory.GetContactDao().SetRelativeContactProject(new List { contactid }, projectid); + + // var messageAction = contact is Company ? MessageAction.ProjectLinkedCompany : MessageAction.ProjectLinkedPerson; + // MessageService.Send(messageAction, MessageTarget.Create(contact.ID), project.Title, contact.GetTitle()); + + // return ToContactDto(contact); + //} + + ///// + ///// Links the selected contacts to the project with the ID specified in the request + ///// + ///// Contact IDs array + ///// Project ID + ///// Contacts + ///// Link contact list with project + ///// + ///// + ///// + ///// Contact list + ///// + //[Create(@"contact/project/{projectid:int}")] + //public IEnumerable SetRelativeContactListToProject(IEnumerable contactid, int projectid) + //{ + // if (contactid == null) throw new ArgumentException(); + + // var contactIds = contactid.ToList(); + + // if (!contactIds.Any() || projectid <= 0) throw new ArgumentException(); + + // var project = ProjectsDaoFactory.ProjectDao.GetById(projectid); + // if (project == null) throw new ItemNotFoundException(); + + // using (var scope = DIHelper.Resolve()) + // { + // if (!scope.Resolve().CanLinkContact(project)) + // throw CRMSecurity.CreateSecurityException(); + // } + + + // var contacts = DaoFactory.GetContactDao().GetContacts(contactIds.ToArray()).Where(CRMSecurity.CanAccessTo).ToList(); + // contactIds = contacts.Select(c => c.ID).ToList(); + + // DaoFactory.GetContactDao().SetRelativeContactProject(contactIds, projectid); + + // MessageService.Send(MessageAction.ProjectLinkedContacts, MessageTarget.Create(contactIds), project.Title, contacts.Select(x => x.GetTitle())); + + // return contacts.ConvertAll(ToContactDto); + //} + + ///// + ///// Removes the link with the selected project from the contact with the ID specified in the request + ///// + ///// Contact ID + ///// Project ID + ///// Contacts + ///// Remove contact from project + ///// + ///// Contact info + ///// + //[Delete(@"contact/{contactid:int}/project/{projectid:int}")] + //public ContactBaseDto RemoveRelativeContactToProject(int contactid, int projectid) + //{ + // if (contactid <= 0 || projectid <= 0) throw new ArgumentException(); + + // var contact = DaoFactory.GetContactDao().GetByID(contactid); + // if (contact == null || !CRMSecurity.CanAccessTo(contact)) throw new ItemNotFoundException(); + + // var project = ProjectsDaoFactory.ProjectDao.GetById(projectid); + + // using (var scope = DIHelper.Resolve()) + // { + // if (project == null || !scope.Resolve().CanLinkContact(project)) throw new ItemNotFoundException(); + // } + + // DaoFactory.GetContactDao().RemoveRelativeContactProject(contactid, projectid); + + // var action = contact is Company ? MessageAction.ProjectUnlinkedCompany : MessageAction.ProjectUnlinkedPerson; + // MessageService.Send(action, MessageTarget.Create(contact.ID), project.Title, contact.GetTitle()); + + // return ToContactBaseDto(contact); + //} + + /// + /// Adds the selected opportunity to the contact with the ID specified in the request. The same as AddMemberToDeal + /// + /// Opportunity ID + /// Contact ID + /// Add contact opportunity + /// Contacts + /// + /// + /// Opportunity + /// + [Create(@"contact/{contactid:int}/opportunity/{opportunityid:int}")] + public OpportunityDto AddDealToContact([FromRoute] int contactid, [FromRoute] int opportunityid) + { + if ((opportunityid <= 0) || (contactid <= 0)) throw new ArgumentException(); + + var contact = _daoFactory.GetContactDao().GetByID(contactid); + if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException(); + + var opportunity = _daoFactory.GetDealDao().GetByID(opportunityid); + if (opportunity == null || !_crmSecurity.CanAccessTo(opportunity)) throw new ItemNotFoundException(); + + _daoFactory.GetDealDao().AddMember(opportunityid, contactid); + + var messageAction = contact is Company ? MessageAction.OpportunityLinkedCompany : MessageAction.OpportunityLinkedPerson; + + _messageService.Send(messageAction, _messageTarget.Create(contact.ID), opportunity.Title, contact.GetTitle()); + + return _mapper.Map(opportunity); + } + + /// + /// Deletes the selected opportunity from the contact with the ID specified in the request + /// + /// Opportunity ID + /// Contact ID + /// Delete contact opportunity + /// Contacts + /// + /// + /// Opportunity + /// + [Delete(@"contact/{contactid:int}/opportunity/{opportunityid:int}")] + public OpportunityDto DeleteDealFromContact(int contactid, int opportunityid) + { + if ((opportunityid <= 0) || (contactid <= 0)) throw new ArgumentException(); + + var contact = _daoFactory.GetContactDao().GetByID(contactid); + if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException(); + + var opportunity = _daoFactory.GetDealDao().GetByID(opportunityid); + if (opportunity == null || !_crmSecurity.CanAccessTo(opportunity)) throw new ItemNotFoundException(); + + _daoFactory.GetDealDao().RemoveMember(opportunityid, contactid); + + return _mapper.Map(opportunity); + } + + /// + /// Returns the list of all contacts in the CRM module matching the parameters specified in the request + /// + /// Tag + /// Contact stage ID (warmth) + /// Contact type ID + /// + /// Start date + /// End date + /// Responsible ID + /// Responsible ID + /// Get contact list + /// Contacts + /// + /// Contact list + /// + [Read(@"contact/filter")] + public IEnumerable GetContacts( + [FromQuery] IEnumerable tags, + [FromQuery] int? contactStage, + [FromQuery] int? contactType, + [FromQuery] ContactListViewType contactListView, + [FromQuery] Guid? responsibleid, + [FromQuery] bool? isShared, + [FromQuery] ApiDateTime fromDate, + [FromQuery] ApiDateTime toDate) + { + IEnumerable result; + OrderBy contactsOrderBy; + ContactSortedByType sortBy; + + var searchString = _apiContext.FilterValue; + + if (ASC.CRM.Classes.EnumExtension.TryParse(_apiContext.SortBy, true, out sortBy)) + { + contactsOrderBy = new OrderBy(sortBy, !_apiContext.SortDescending); + } + else if (String.IsNullOrEmpty(_apiContext.SortBy)) + { + contactsOrderBy = new OrderBy(ContactSortedByType.Created, false); + } + else + { + contactsOrderBy = null; + } + + + var fromIndex = (int)_apiContext.StartIndex; + var count = (int)_apiContext.Count; + var contactStageInt = contactStage.HasValue ? contactStage.Value : -1; + var contactTypeInt = contactType.HasValue ? contactType.Value : -1; + + if (contactsOrderBy != null) + { + + var contacts = _daoFactory.GetContactDao().GetContacts( + searchString, + tags, + contactStageInt, + contactTypeInt, + contactListView, + fromDate, + toDate, + fromIndex, + count, + contactsOrderBy, + responsibleid, + isShared); + + result = _mapper.Map, List>(contacts); + + _apiContext.SetDataPaginated(); + _apiContext.SetDataFiltered(); + _apiContext.SetDataSorted(); + } + else + { + result = _mapper.Map, List>(_daoFactory.GetContactDao().GetContacts( + searchString, + tags, + contactStageInt, + contactTypeInt, + contactListView, + fromDate, + toDate, + 0, + 0, + null, + responsibleid, + isShared)); + } + + int totalCount; + + if (result.Count() < count) + { + totalCount = fromIndex + result.Count(); + } + else + { + totalCount = _daoFactory.GetContactDao().GetContactsCount( + searchString, + tags, + contactStageInt, + contactTypeInt, + contactListView, + fromDate, + toDate, + responsibleid, + isShared); + } + + _apiContext.SetTotalCount(totalCount); + + return result; + } + + /// + /// Returns the list of the contacts for auto complete feature. + /// + /// String part of contact name, lastname or email. + /// Max result count + /// Search contact list + /// Contacts + /// + /// Contact list + /// + /// false + [Read(@"contact/simple/byEmail")] + public IEnumerable SearchContactsByEmail(string term, int maxCount) + { + var result = ToSimpleListContactDto(_daoFactory.GetContactDao().SearchContactsByEmail( + term, + maxCount)); + + return result; + } + + /// + /// Returns the list of all contacts in the CRM module matching the parameters specified in the request + /// + /// Tag + /// Contact stage ID (warmth) + /// Contact type ID + /// + /// Responsible ID + /// Responsible ID + /// Start date + /// End date + /// Get contact list + /// Contacts + /// + /// Contact list + /// + /// false + [Read(@"contact/simple/filter")] + public IEnumerable GetSimpleContacts( + IEnumerable tags, + int? contactStage, + int? contactType, + ContactListViewType contactListView, + Guid? responsibleid, + bool? isShared, + ApiDateTime fromDate, + ApiDateTime toDate) + { + IEnumerable result; + + OrderBy contactsOrderBy; + + ContactSortedByType sortBy; + + var searchString = _apiContext.FilterValue; + if (ASC.CRM.Classes.EnumExtension.TryParse(_apiContext.SortBy, true, out sortBy)) + { + contactsOrderBy = new OrderBy(sortBy, !_apiContext.SortDescending); + } + else if (String.IsNullOrEmpty(_apiContext.SortBy)) + { + contactsOrderBy = new OrderBy(ContactSortedByType.DisplayName, true); + } + else + { + contactsOrderBy = null; + } + + var fromIndex = (int)_apiContext.StartIndex; + var count = (int)_apiContext.Count; + var contactStageInt = contactStage.HasValue ? contactStage.Value : -1; + var contactTypeInt = contactType.HasValue ? contactType.Value : -1; + + if (contactsOrderBy != null) + { + result = ToSimpleListContactDto(_daoFactory.GetContactDao().GetContacts( + searchString, + tags, + contactStageInt, + contactTypeInt, + contactListView, + fromDate, + toDate, + fromIndex, + count, + contactsOrderBy, + responsibleid, + isShared)); + _apiContext.SetDataPaginated(); + _apiContext.SetDataFiltered(); + _apiContext.SetDataSorted(); + } + else + { + result = ToSimpleListContactDto(_daoFactory.GetContactDao().GetContacts( + searchString, + tags, + contactStageInt, + contactTypeInt, + contactListView, + fromDate, + toDate, + 0, + 0, + null, + responsibleid, + isShared)); + } + + int totalCount; + + if (result.Count() < count) + { + totalCount = fromIndex + result.Count(); + } + else + { + totalCount = _daoFactory.GetContactDao().GetContactsCount( + searchString, + tags, + contactStageInt, + contactTypeInt, + contactListView, + fromDate, + toDate, + responsibleid, + isShared); + } + + _apiContext.SetTotalCount(totalCount); + + return result; + } + + /// + /// Get the group of contacts with the IDs specified in the request + /// + /// Contact ID list + /// + /// + /// Get contact group + /// Contacts + /// + /// Contact list + /// + /// false + [Read(@"contact/mail")] + public IEnumerable GetContactsForMail(IEnumerable contactids) + { + if (contactids == null) throw new ArgumentException(); + + var contacts = _daoFactory.GetContactDao().GetContacts(contactids.ToArray()); + + var result = contacts.Select(x => _mapper.Map(x)); + return result; + } + + /// + /// Deletes the list of all contacts in the CRM module matching the parameters specified in the request + /// + /// Tag + /// Contact stage ID (warmth) + /// Contact type ID + /// + /// Start date + /// End date + /// + /// + /// Delete the list of all contacts + /// Contacts + /// + /// Contact list + /// + [Delete(@"contact/filter")] + public IEnumerable DeleteBatchContacts( + IEnumerable tags, + int? contactStage, + int? contactType, + ContactListViewType contactListView, + ApiDateTime fromDate, + ApiDateTime toDate) + { + int contactStageInt = contactStage.HasValue ? contactStage.Value : -1; + int contactTypeInt = contactType.HasValue ? contactType.Value : -1; + + + var contacts = _daoFactory.GetContactDao().GetContacts( + _apiContext.FilterValue, + tags, + contactStageInt, + contactTypeInt, + contactListView, + fromDate, + toDate, + 0, + 0, + null); + + contacts = _daoFactory.GetContactDao().DeleteBatchContact(contacts); + + _messageService.Send(MessageAction.ContactsDeleted, _messageTarget.Create(contacts.Select(c => c.ID)), contacts.Select(c => c.GetTitle())); + + return contacts.Select(x => _mapper.Map(x)); + } + + + /// + /// Returns the list of all the persons linked to the company with the ID specified in the request + /// + /// Company ID + /// + /// Get company linked persons list + /// Contacts + /// + /// Linked persons + /// + [Read(@"contact/company/{companyid:int}/person")] + public IEnumerable GetPeopleFromCompany(int companyid) + { + if (companyid <= 0) throw new ArgumentException(); + + var company = _daoFactory.GetContactDao().GetByID(companyid); + if (company == null || !_crmSecurity.CanAccessTo(company)) throw new ItemNotFoundException(); + + var contacts = _daoFactory.GetContactDao().GetMembers(companyid).Where(_crmSecurity.CanAccessTo).ToList(); + + return _mapper.Map, List>(contacts); + } + + /// + /// Adds the selected person to the company with the ID specified in the request + /// + /// Company ID + /// Person ID + /// Add person to company + /// Contacts + /// + /// + /// + /// Person + /// + [Create(@"contact/company/{companyid:int}/person")] + public PersonDto AddPeopleToCompany([FromRoute] int companyid, [FromForm] int personid) + { + if ((companyid <= 0) || (personid <= 0)) throw new ArgumentException(); + + var company = _daoFactory.GetContactDao().GetByID(companyid); + var person = _daoFactory.GetContactDao().GetByID(personid); + + if (person == null || company == null || !_crmSecurity.CanAccessTo(person) || !_crmSecurity.CanAccessTo(company)) throw new ItemNotFoundException(); + + _daoFactory.GetContactDao().AddMember(personid, companyid); + + _messageService.Send(MessageAction.CompanyLinkedPerson, _messageTarget.Create(new[] { company.ID, person.ID }), company.GetTitle(), person.GetTitle()); + + return (PersonDto)_mapper.Map(person); + } + + /// + /// Deletes the selected person from the company with the ID specified in the request + /// + /// Company ID + /// Person ID + /// Delete person from company + /// Contacts + /// + /// + /// + /// Person + /// + [Delete(@"contact/company/{companyid:int}/person")] + public PersonDto DeletePeopleFromCompany(int companyid, int personid) + { + if ((companyid <= 0) || (personid <= 0)) throw new ArgumentException(); + + var company = _daoFactory.GetContactDao().GetByID(companyid); + var person = _daoFactory.GetContactDao().GetByID(personid); + if (person == null || company == null || !_crmSecurity.CanAccessTo(person) || !_crmSecurity.CanAccessTo(company)) throw new ItemNotFoundException(); + + _daoFactory.GetContactDao().RemoveMember(personid); + + _messageService.Send(MessageAction.CompanyUnlinkedPerson, _messageTarget.Create(new[] { company.ID, person.ID }), company.GetTitle(), person.GetTitle()); + + return (PersonDto)_mapper.Map(person); + } + + /// + /// Creates the person with the parameters (first name, last name, description, etc.) specified in the request + /// + /// First name + /// Last name + /// Post + /// Company ID + /// Person description text + /// Person privacy: 0 - not shared, 1 - shared for read/write, 2 - shared for read only + /// List of managers for the person + /// User field list + /// Contact photo (upload using multipart/form-data) + /// Create person + /// Contacts + /// Person + /// + [Create(@"contact/person")] + public PersonDto CreatePerson([FromForm] CreateOrUpdatePersonRequestDto intDto) + { + string firstName = intDto.FirstName; + string lastName = intDto.LastName; + string jobTitle = intDto.JobTitle; + int companyId = intDto.CompanyId; + string about = intDto.About; + ShareType shareType = intDto.ShareType; + IEnumerable managerList = intDto.ManagerList; + IEnumerable> customFieldList = intDto.CustomFieldList; + IEnumerable photo = intDto.Photos; + + if (companyId > 0) + { + var company = _daoFactory.GetContactDao().GetByID(companyId); + if (company == null || !_crmSecurity.CanAccessTo(company)) throw new ItemNotFoundException(); + } + + var peopleInst = new Person + { + FirstName = firstName, + LastName = lastName, + JobTitle = jobTitle, + CompanyID = companyId, + About = about, + ShareType = shareType + }; + + peopleInst.ID = _daoFactory.GetContactDao().SaveContact(peopleInst); + peopleInst.CreateBy = _securityContext.CurrentAccount.ID; + peopleInst.CreateOn = DateTime.UtcNow; + + var managerListLocal = managerList != null ? managerList.ToList() : new List(); + if (managerListLocal.Any()) + { + _crmSecurity.SetAccessTo(peopleInst, managerListLocal); + } + + if (customFieldList != null) + { + foreach (var field in customFieldList) + { + if (string.IsNullOrEmpty(field.Value)) continue; + _daoFactory.GetCustomFieldDao().SetFieldValue(EntityType.Person, peopleInst.ID, field.Key, field.Value); + } + } + + var outDto = (PersonDto)_mapper.Map(peopleInst); + + var photoList = photo != null ? photo.ToList() : new List(); + + if (photoList.Any()) + { + outDto.SmallFotoUrl = ChangeContactPhoto(peopleInst.ID, photoList); + } + + _messageService.Send(MessageAction.PersonCreated, _messageTarget.Create(peopleInst.ID), peopleInst.GetTitle()); + + return outDto; + } + + /// + /// Changes the photo for the contact with the ID specified in the request + /// + /// Contact ID + /// Contact photo (upload using multipart/form-data) + /// Change contact photo + /// Contacts + /// + /// + /// Path to contact photo + /// + [Update(@"contact/{contactid:int}/changephoto")] + public string ChangeContactPhoto(int contactid, IEnumerable photo) + { + if (contactid <= 0) + throw new ArgumentException(); + + var contact = _daoFactory.GetContactDao().GetByID(contactid); + + if (contact == null || !_crmSecurity.CanAccessTo(contact)) + throw new ItemNotFoundException(); + + var firstPhoto = photo != null ? photo.FirstOrDefault() : null; + + if (firstPhoto == null) + throw new ArgumentException(); + + var fileStream = firstPhoto.OpenReadStream(); + + if (firstPhoto.Length == 0 || + !firstPhoto.ContentType.StartsWith("image/") || + !fileStream.CanRead) + throw new InvalidOperationException(CRMErrorsResource.InvalidFile); + + if (_setupInfo.MaxImageUploadSize > 0 && + _setupInfo.MaxImageUploadSize < firstPhoto.Length) + throw new Exception(_fileSizeComment.GetFileImageSizeNote(CRMCommonResource.ErrorMessage_UploadFileSize, false)); + + return _contactPhotoManager.UploadPhoto(fileStream, contactid, false).Url; + } + + /// + /// Changes the photo for the contact with the ID specified in the request + /// + /// Contact ID + /// contact photo url + /// Change contact photo + /// Contacts + /// + /// + /// Path to contact photo + /// + [Update(@"contact/{contactid:int}/changephotobyurl")] + public string ChangeContactPhoto(int contactid, string photourl) + { + if (contactid <= 0 || string.IsNullOrEmpty(photourl)) throw new ArgumentException(); + + var contact = _daoFactory.GetContactDao().GetByID(contactid); + if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException(); + + return _contactPhotoManager.UploadPhoto(photourl, contactid, false).Url; + } + + /// + /// Merge two selected contacts + /// + /// the first contact ID for merge + /// the second contact ID for merge + /// Merge contacts + /// Contacts + /// + /// + /// + /// + /// Contact + /// + [Update(@"contact/merge")] + public ContactDto MergeContacts(int fromcontactid, int tocontactid) + { + if (fromcontactid <= 0 || tocontactid <= 0) throw new ArgumentException(); + + var fromContact = _daoFactory.GetContactDao().GetByID(fromcontactid); + var toContact = _daoFactory.GetContactDao().GetByID(tocontactid); + + if (fromContact == null || toContact == null) throw new ItemNotFoundException(); + + if (!_crmSecurity.CanEdit(fromContact) || !_crmSecurity.CanEdit(toContact)) throw _crmSecurity.CreateSecurityException(); + + _daoFactory.GetContactDao().MergeDublicate(fromcontactid, tocontactid); + var resultContact = _daoFactory.GetContactDao().GetByID(tocontactid); + + var messageAction = resultContact is Person ? MessageAction.PersonsMerged : MessageAction.CompaniesMerged; + _messageService.Send(messageAction, _messageTarget.Create(new[] { fromContact.ID, toContact.ID }), fromContact.GetTitle(), toContact.GetTitle()); + + return _mapper.Map(resultContact); + } + + /// + /// Updates the selected person with the parameters (first name, last name, description, etc.) specified in the request + /// + /// Person ID + /// First name + /// Last name + /// Post + /// Company ID + /// Person description text + /// Person privacy: 0 - not shared, 1 - shared for read/write, 2 - shared for read only + /// List of persons managers + /// User field list + /// Contact photo (upload using multipart/form-data) + /// Update person + /// Contacts + /// Person + /// + /// + [Update(@"contact/person/{personid:int}")] + public PersonDto UpdatePerson([FromQuery] int personid, [FromForm] CreateOrUpdatePersonRequestDto inDto) + { + string firstName = inDto.FirstName; + string lastName = inDto.LastName; + string jobTitle = inDto.JobTitle; + int companyId = inDto.CompanyId; + string about = inDto.About; + ShareType shareType = inDto.ShareType; + IEnumerable managerList = inDto.ManagerList; + IEnumerable> customFieldList = inDto.CustomFieldList; + IEnumerable photo = inDto.Photos; + + if (personid <= 0 || string.IsNullOrEmpty(firstName) || string.IsNullOrEmpty(lastName)) throw new ArgumentException(); + + var peopleInst = new Person + { + ID = personid, + FirstName = firstName, + LastName = lastName, + JobTitle = jobTitle, + CompanyID = companyId, + About = about, + ShareType = shareType + }; + + _daoFactory.GetContactDao().UpdateContact(peopleInst); + + peopleInst = (Person)_daoFactory.GetContactDao().GetByID(peopleInst.ID); + + var managerListLocal = managerList != null ? managerList.ToList() : new List(); + if (managerListLocal.Any()) + { + _crmSecurity.SetAccessTo(peopleInst, managerListLocal); + } + + if (customFieldList != null) + { + var existingCustomFieldList = _daoFactory.GetCustomFieldDao().GetFieldsDescription(EntityType.Person).Select(fd => fd.ID).ToList(); + foreach (var field in customFieldList) + { + if (string.IsNullOrEmpty(field.Value) || !existingCustomFieldList.Contains(field.Key)) continue; + _daoFactory.GetCustomFieldDao().SetFieldValue(EntityType.Person, peopleInst.ID, field.Key, field.Value); + } + } + + var outDto = (PersonDto)_mapper.Map(peopleInst); + + var photoList = photo != null ? photo.ToList() : new List(); + + if (photoList.Any()) + { + outDto.SmallFotoUrl = ChangeContactPhoto(peopleInst.ID, photoList); + } + + _messageService.Send(MessageAction.PersonUpdated, _messageTarget.Create(peopleInst.ID), peopleInst.GetTitle()); + + return outDto; + } + + /// + /// Creates the company with the parameters specified in the request + /// + /// Company name + /// Company description text + /// Linked person list + /// Company privacy: 0 - not shared, 1 - shared for read/write, 2 - shared for read only + /// List of managers for the company + /// User field list + /// Contact photo (upload using multipart/form-data) + /// Create company + /// Contacts + /// Company + /// + [Create(@"contact/company")] + public CompanyDto CreateCompany([FromForm] CreateOrUpdateCompanyRequestDto inDto) + { + var personList = inDto.PersonList; + string companyName = inDto.CompanyName; + string about = inDto.About; + ShareType shareType = inDto.ShareType; + IEnumerable managerList = inDto.ManagerList; + IEnumerable> customFieldList = inDto.CustomFieldList; + IEnumerable photo = inDto.Photos; + + var companyInst = new Company + { + CompanyName = companyName, + About = about, + ShareType = shareType + }; + + companyInst.ID = _daoFactory.GetContactDao().SaveContact(companyInst); + companyInst.CreateBy = _securityContext.CurrentAccount.ID; + companyInst.CreateOn = DateTime.UtcNow; + + if (personList != null) + { + foreach (var personID in personList) + { + var person = _daoFactory.GetContactDao().GetByID(personID); + if (person == null || !_crmSecurity.CanAccessTo(person)) continue; + + AddPeopleToCompany(companyInst.ID, personID); + } + } + + var managerListLocal = managerList != null ? managerList.ToList() : new List(); + if (managerListLocal.Any()) + { + _crmSecurity.SetAccessTo(companyInst, managerListLocal); + } + + if (customFieldList != null) + { + var existingCustomFieldList = _daoFactory.GetCustomFieldDao().GetFieldsDescription(EntityType.Company).Select(fd => fd.ID).ToList(); + foreach (var field in customFieldList) + { + if (string.IsNullOrEmpty(field.Value) || !existingCustomFieldList.Contains(field.Key)) continue; + _daoFactory.GetCustomFieldDao().SetFieldValue(EntityType.Company, companyInst.ID, field.Key, field.Value); + } + } + + var wrapper = (CompanyDto)_mapper.Map(companyInst); + + var photoList = photo != null ? photo.ToList() : new List(); + if (photoList.Any()) + { + wrapper.SmallFotoUrl = ChangeContactPhoto(companyInst.ID, photoList); + } + + _messageService.Send(MessageAction.CompanyCreated, _messageTarget.Create(companyInst.ID), companyInst.GetTitle()); + + return wrapper; + } + + /// + /// Quickly creates the list of companies + /// + /// + /// Quick company list creation + /// + /// Company name + /// Contacts + /// Contact list + /// + [Create(@"contact/company/quick")] + public IEnumerable CreateCompany([FromForm] IEnumerable companyName) + { + if (companyName == null) throw new ArgumentException(); + + var contacts = new List(); + var recordIndex = 0; + + foreach (var item in companyName) + { + if (string.IsNullOrEmpty(item)) continue; + + contacts.Add(new Company + { + ID = recordIndex++, + CompanyName = item, + ShareType = ShareType.None + }); + } + + if (contacts.Count == 0) return null; + + _daoFactory.GetContactDao().SaveContactList(contacts); + + var selectedManagers = new List { _securityContext.CurrentAccount.ID }; + + foreach (var ct in contacts) + { + _crmSecurity.SetAccessTo(ct, selectedManagers); + } + + return contacts.ConvertAll(x => _mapper.Map(x)); + } + + /// + /// Quickly creates the list of persons with the first and last names specified in the request + /// + /// + /// Quick person list creation + /// + /// Pairs: user first name, user last name + /// + /// + /// + /// Contacts + /// Contact list + /// + [Create(@"contact/person/quick")] + public IEnumerable CreatePerson([FromForm] IEnumerable> data) + { + if (data == null) return null; + + var contacts = new List(); + var recordIndex = 0; + + foreach (var item in data) + { + if (string.IsNullOrEmpty(item.Key) || string.IsNullOrEmpty(item.Value)) continue; + + contacts.Add(new Person + { + ID = recordIndex++, + FirstName = item.Key, + LastName = item.Value, + ShareType = ShareType.None + }); + } + + if (contacts.Count == 0) return null; + + _daoFactory.GetContactDao().SaveContactList(contacts); + + var selectedManagers = new List { _securityContext.CurrentAccount.ID }; + + foreach (var ct in contacts) + { + _crmSecurity.SetAccessTo(ct, selectedManagers); + } + + _messageService.Send(MessageAction.PersonsCreated, _messageTarget.Create(contacts.Select(x => x.ID)), contacts.Select(x => x.GetTitle())); + + return contacts.ConvertAll(x => _mapper.Map(x)); + } + + /// + /// Updates the selected company with the parameters specified in the request + /// + /// Company ID + /// Company name + /// Company description text + /// Company privacy: 0 - not shared, 1 - shared for read/write, 2 - shared for read only + /// List of company managers + /// User field list + /// Update company + /// Contacts + /// + /// + /// Company + /// + [Update(@"contact/company/{companyid:int}")] + public CompanyDto UpdateCompany( + int companyid, + [FromForm] CreateOrUpdateCompanyRequestDto intDto) + { + string companyName = intDto.CompanyName; + string about = intDto.About; + ShareType shareType = intDto.ShareType; + IEnumerable managerList = intDto.ManagerList; + IEnumerable> customFieldList = intDto.CustomFieldList; + IEnumerable photo = intDto.Photos; + + var companyInst = new Company + { + ID = companyid, + CompanyName = companyName, + About = about, + ShareType = shareType + }; + + _daoFactory.GetContactDao().UpdateContact(companyInst); + + companyInst = (Company)_daoFactory.GetContactDao().GetByID(companyInst.ID); + + var managerListLocal = managerList != null ? managerList.ToList() : new List(); + if (managerListLocal.Any()) + { + _crmSecurity.SetAccessTo(companyInst, managerListLocal); + } + + if (customFieldList != null) + { + var existingCustomFieldList = _daoFactory.GetCustomFieldDao().GetFieldsDescription(EntityType.Company).Select(fd => fd.ID).ToList(); + foreach (var field in customFieldList) + { + if (string.IsNullOrEmpty(field.Value) || !existingCustomFieldList.Contains(field.Key)) continue; + _daoFactory.GetCustomFieldDao().SetFieldValue(EntityType.Company, companyInst.ID, field.Key, field.Value); + } + } + + _messageService.Send(MessageAction.CompanyUpdated, _messageTarget.Create(companyInst.ID), companyInst.GetTitle()); + + return (CompanyDto)_mapper.Map(companyInst); + } + + /// + /// Updates the selected contact status + /// + /// Contact ID + /// Contact status ID + /// Update status in contact by id + /// Contacts + /// + /// + /// + /// Company + /// + [Update(@"contact/{contactid:int}/status")] + public ContactDto UpdateContactStatus(int contactid, int contactStatusid) + { + if (contactid <= 0 || contactStatusid < 0) throw new ArgumentException(); + + var dao = _daoFactory.GetContactDao(); + + if (contactStatusid > 0) + { + var curListItem = _daoFactory.GetListItemDao().GetByID(contactStatusid); + if (curListItem == null) throw new ItemNotFoundException(); + } + + var companyInst = dao.GetByID(contactid); + if (companyInst == null || !_crmSecurity.CanAccessTo(companyInst)) throw new ItemNotFoundException(); + + if (!_crmSecurity.CanEdit(companyInst)) throw _crmSecurity.CreateSecurityException(); + + dao.UpdateContactStatus(new List { companyInst.ID }, contactStatusid); + companyInst.StatusID = contactStatusid; + + var messageAction = companyInst is Company ? MessageAction.CompanyUpdatedTemperatureLevel : MessageAction.PersonUpdatedTemperatureLevel; + _messageService.Send(messageAction, _messageTarget.Create(companyInst.ID), companyInst.GetTitle()); + + return _mapper.Map(companyInst); + } + + /// + /// Updates status of the selected company and all its participants + /// + /// Company ID + /// Contact status ID + /// Update company and participants status + /// Contacts + /// + /// + /// + /// Company + /// + [Update(@"contact/company/{companyid:int}/status")] + public ContactDto UpdateCompanyAndParticipantsStatus(int companyid, int contactStatusid) + { + if (companyid <= 0 || contactStatusid < 0) throw new ArgumentException(); + + var dao = _daoFactory.GetContactDao(); + + if (contactStatusid > 0) + { + var curListItem = _daoFactory.GetListItemDao().GetByID(contactStatusid); + if (curListItem == null) throw new ItemNotFoundException(); + } + + var companyInst = dao.GetByID(companyid); + if (companyInst == null || !_crmSecurity.CanAccessTo(companyInst)) throw new ItemNotFoundException(); + + if (companyInst is Person) throw new Exception(CRMErrorsResource.ContactIsNotCompany); + + var forUpdateStatus = new List(); + forUpdateStatus.Add(companyInst.ID); + + var members = dao.GetMembersIDsAndShareType(companyInst.ID); + foreach (var m in members) + { + if (_crmSecurity.CanAccessTo(m.Key, EntityType.Person, m.Value, 0)) + { + forUpdateStatus.Add(m.Key); + } + } + + dao.UpdateContactStatus(forUpdateStatus, contactStatusid); + + _messageService.Send(MessageAction.CompanyUpdatedTemperatureLevel, _messageTarget.Create(companyInst.ID), companyInst.GetTitle()); + _messageService.Send(MessageAction.CompanyUpdatedPersonsTemperatureLevel, _messageTarget.Create(companyInst.ID), companyInst.GetTitle()); + + return _mapper.Map(companyInst); + } + + /// + /// Updates status of the selected person, related company and all its participants + /// + /// Person ID + /// Contact status ID + /// Update person, related company and participants status + /// Contacts + /// + /// + /// + /// Person + /// + [Update(@"contact/person/{personid:int}/status")] + public ContactDto UpdatePersonAndItsCompanyStatus(int personid, int contactStatusid) + { + if (personid <= 0 || contactStatusid < 0) throw new ArgumentException(); + + if (contactStatusid > 0) + { + var curListItem = _daoFactory.GetListItemDao().GetByID(contactStatusid); + if (curListItem == null) throw new ItemNotFoundException(); + } + + var dao = _daoFactory.GetContactDao(); + + var personInst = dao.GetByID(personid); + if (personInst == null || !_crmSecurity.CanAccessTo(personInst)) throw new ItemNotFoundException(); + + if (personInst is Company) throw new Exception(CRMErrorsResource.ContactIsNotPerson); + + var forUpdateStatus = new List(); + + var companyID = ((Person)personInst).CompanyID; + if (companyID != 0) + { + var companyInst = dao.GetByID(companyID); + if (companyInst == null) throw new ItemNotFoundException(); + + if (!_crmSecurity.CanAccessTo(companyInst)) + { + forUpdateStatus.Add(personInst.ID); + dao.UpdateContactStatus(forUpdateStatus, contactStatusid); + } + else + { + forUpdateStatus.Add(companyInst.ID); + + var members = dao.GetMembersIDsAndShareType(companyInst.ID); + foreach (var m in members) + { + if (_crmSecurity.CanAccessTo(m.Key, EntityType.Person, m.Value, 0)) + { + forUpdateStatus.Add(m.Key); + } + } + dao.UpdateContactStatus(forUpdateStatus, contactStatusid); + } + } + else + { + forUpdateStatus.Add(personInst.ID); + dao.UpdateContactStatus(forUpdateStatus, contactStatusid); + } + + _messageService.Send(MessageAction.PersonUpdatedTemperatureLevel, _messageTarget.Create(personInst.ID), personInst.GetTitle()); + _messageService.Send(MessageAction.PersonUpdatedCompanyTemperatureLevel, _messageTarget.Create(personInst.ID), personInst.GetTitle()); + + personInst = dao.GetByID(personInst.ID); + return _mapper.Map(personInst); + } + + /// + /// Get access rights to the contact with the ID specified in the request + /// + /// Get contact access rights + /// Contacts + /// + /// + /// + /// User list + [Read(@"contact/{contactid:int}/access")] + public IEnumerable GetContactAccessList(int contactid) + { + if (contactid <= 0) throw new ArgumentException(); + + var contact = _daoFactory.GetContactDao().GetByID(contactid); + + if (contact == null) throw new ItemNotFoundException(); + + if (!_crmSecurity.CanAccessTo(contact)) throw _crmSecurity.CreateSecurityException(); + + return _crmSecurity.IsPrivate(contact) + ? _crmSecurity.GetAccessSubjectTo(contact) + .Select(item => _employeeWraperHelper.Get(item.Key)) + : new List(); + } + + /// + /// Sets access rights for other users to the contact with the ID specified in the request + /// + /// Contact ID + /// Contact privacy: private or not + /// List of managers + /// Set contact access rights + /// Contacts + /// + /// + /// + /// + /// Contact + /// + [Update(@"contact/{contactid:int}/access")] + public ContactDto SetAccessToContact(int contactid, bool isShared, IEnumerable managerList) + { + if (contactid <= 0) throw new ArgumentException(); + + var contact = _daoFactory.GetContactDao().GetByID(contactid); + if (contact == null) throw new ItemNotFoundException(); + + if (!_crmSecurity.CanEdit(contact)) throw _crmSecurity.CreateSecurityException(); + + SetAccessToContact(contact, isShared, managerList, false); + + var wrapper = _mapper.Map(contact); + + return wrapper; + } + + private void SetAccessToContact(Contact contact, bool isShared, IEnumerable managerList, bool isNotify) + { + var managerListLocal = managerList != null ? managerList.Distinct().ToList() : new List(); + if (managerListLocal.Any()) + { + if (isNotify) + { + var notifyUsers = managerListLocal.Where(n => n != _securityContext.CurrentAccount.ID).ToArray(); + + if (contact is Person) + _notifyClient.SendAboutSetAccess(EntityType.Person, contact.ID, _daoFactory, notifyUsers); + else + _notifyClient.SendAboutSetAccess(EntityType.Company, contact.ID, _daoFactory, notifyUsers); + + } + + _crmSecurity.SetAccessTo(contact, managerListLocal); + } + else + { + _crmSecurity.MakePublic(contact); + } + + _daoFactory.GetContactDao().MakePublic(contact.ID, isShared); + } + + /// + /// Sets access rights for other users to the list of contacts with the IDs specified in the request + /// + /// Contact ID list + /// Company privacy: shared or not + /// List of managers + /// Set contact access rights + /// Contacts + /// + /// + /// + /// Contact list + /// + [Update(@"contact/access")] + public IEnumerable SetAccessToBatchContact( + [FromBody] SetAccessToBatchContactRequestDto inDto) + { + var contactid = inDto.ContactID; + var isShared = inDto.isShared; + var managerList = inDto.ManagerList; + + if (contactid == null) throw new ArgumentException(); + + var result = new List(); + + foreach (var id in contactid) + { + var contactDto = SetAccessToContact(id, isShared, managerList); + result.Add(contactDto); + } + + return result; + } + + /// + /// Sets access rights for the selected user to the list of contacts with the parameters specified in the request + /// + /// Contact privacy: private or not + /// List of managers + /// Tag + /// Contact stage ID (warmth) + /// Contact type ID + /// + /// Start date + /// End date + /// Set contact access rights + /// Contacts + /// + /// + /// + /// Contact list + /// + [Update(@"contact/filter/access")] + public IEnumerable SetAccessToBatchContact( + [FromForm] SetAccessToBatchContactByFilterRequestDto inDto) + { + IEnumerable tags = inDto.Tags; + int? contactStage = inDto.ContactStage; + int? contactType = inDto.ContactType; + ContactListViewType contactListView = inDto.ContactListView; + ApiDateTime fromDate = inDto.FromDate; + ApiDateTime toDate = inDto.ToDate; + bool isPrivate = inDto.isPrivate; + IEnumerable managerList = inDto.ManagerList; + + int contactStageInt = contactStage.HasValue ? contactStage.Value : -1; + int contactTypeInt = contactType.HasValue ? contactType.Value : -1; + + var result = new List(); + + var contacts = _daoFactory.GetContactDao().GetContacts( + _apiContext.FilterValue, + tags, + contactStageInt, + contactTypeInt, + contactListView, + fromDate, toDate, + 0, 0, null); + + if (!contacts.Any()) + return Enumerable.Empty(); + + foreach (var contact in contacts) + { + if (contact == null) + throw new ItemNotFoundException(); + + if (!_crmSecurity.CanEdit(contact)) continue; + + SetAccessToContact(contact, isPrivate, managerList, false); + + result.Add(contact); + } + return _mapper.Map, List>(result); + } + + /// + /// Deletes the contact with the ID specified in the request from the portal + /// + /// Delete contact + /// Contacts + /// Contact ID + /// + /// + /// + /// Contact + /// + [Delete(@"contact/{contactid:int}")] + public ContactDto DeleteContact(int contactid) + { + if (contactid <= 0) throw new ArgumentException(); + + var contact = _daoFactory.GetContactDao().DeleteContact(contactid); + if (contact == null) throw new ItemNotFoundException(); + + var messageAction = contact is Person ? MessageAction.PersonDeleted : MessageAction.CompanyDeleted; + _messageService.Send(messageAction, _messageTarget.Create(contact.ID), contact.GetTitle()); + + return _mapper.Map(contact); + } + + /// + /// Deletes the group of contacts with the IDs specified in the request + /// + /// Contact ID list + /// + /// + /// Delete contact group + /// Contacts + /// + /// Contact list + /// + [Update(@"contact")] + public IEnumerable DeleteBatchContacts(IEnumerable contactids) + { + if (contactids == null) throw new ArgumentException(); + + var contacts = _daoFactory.GetContactDao().DeleteBatchContact(contactids.ToArray()); + _messageService.Send(MessageAction.ContactsDeleted, _messageTarget.Create(contactids), contacts.Select(c => c.GetTitle())); + + return contacts.Select(x => _mapper.Map(x)); + } + + /// + /// Returns the list of 30 contacts in the CRM module with prefix + /// + /// + /// searchType + /// + /// + /// Contacts + /// + /// Contact list + /// + /// false + [Read(@"contact/byprefix")] + public IEnumerable GetContactsByPrefix(string prefix, int searchType, EntityType entityType, int entityID) + { + var result = new List(); + var allContacts = new List(); + + if (entityID > 0) + { + var findedContacts = new List(); + switch (entityType) + { + case EntityType.Opportunity: + allContacts = _daoFactory.GetContactDao().GetContacts(_daoFactory.GetDealDao().GetMembers(entityID)); + break; + case EntityType.Case: + allContacts = _daoFactory.GetContactDao().GetContacts(_daoFactory.GetCasesDao().GetMembers(entityID)); + break; + } + + foreach (var c in allContacts) + { + var person = c as Person; + if (person != null) + { + var people = person; + + if (_userFormatter.GetUserName(people.FirstName, people.LastName).IndexOf(prefix, StringComparison.Ordinal) != -1) + { + findedContacts.Add(person); + } + } + else + { + var company = (Company)c; + if (company.CompanyName.IndexOf(prefix, StringComparison.Ordinal) != -1) + { + findedContacts.Add(c); + } + } + } + result.AddRange(findedContacts.Select(x => _mapper.Map(x))); + + _apiContext.SetTotalCount(findedContacts.Count); + + } + else + { + const int maxItemCount = 30; + + if (searchType < -1 || searchType > 3) throw new ArgumentException(); + + allContacts = _daoFactory.GetContactDao().GetContactsByPrefix(prefix, searchType, 0, maxItemCount); + + result.AddRange(allContacts.Select(x => _mapper.Map(x))); + + } + + return result; + } + + + /// + /// Returns the list contacts in the CRM module with contact information + /// + /// Contact information type + /// Data + /// Category + /// Contact importance: primary or not + /// Contacts + /// + /// Contact list + /// + [Read(@"contact/bycontactinfo")] + public IEnumerable GetContactsByContactInfo(ContactInfoType? infoType, String data, int? category, bool? isPrimary) + { + if (!infoType.HasValue) throw new ArgumentException(); + + var ids = _daoFactory.GetContactDao().GetContactIDsByContactInfo(infoType.Value, data, category, isPrimary); + + var result = _daoFactory.GetContactDao().GetContacts(ids.ToArray()).ConvertAll(x => _mapper.Map(x)); + + return result; + } + + ///// + ///// + ///// + ///// + ///// + ///// Contacts + ///// + //[Read(@"contact/{contactid:int}/tweets")] + //public List GetUserTweets(int contactid, int count) + //{ + // var MessageCount = 10; + // var twitterAccounts = DaoFactory.GetContactInfoDao().GetList(contactid, ContactInfoType.Twitter, null, null); + + // if (twitterAccounts.Count == 0) + // throw new ResourceNotFoundException( + // Newtonsoft.Json.JsonConvert.SerializeObject( + // new + // { + // message = "", + // description = CRMSocialMediaResource.SocialMediaAccountNotFoundTwitter + // } + // )); + + // var apiInfo = TwitterApiHelper.GetTwitterApiInfoForCurrentUser(); + // TwitterDataProvider twitterProvider = new TwitterDataProvider(apiInfo); + + // List messages = new List(); + + // foreach (var twitterAccount in twitterAccounts) + // { + // try + // { + // messages.AddRange(twitterProvider.GetUserTweets(twitterAccount.ID, twitterAccount.Data, MessageCount)); + // } + // catch (ResourceNotFoundException ex) + // { + // throw new ResourceNotFoundException( + // Newtonsoft.Json.JsonConvert.SerializeObject( + // new + // { + // message = ex.Message, + // description = String.Format("{0}: {1}", CRMSocialMediaResource.ErrorUnknownTwitterAccount, twitterAccount.Data) + // } + // )); + // } + // catch (Exception ex) + // { + // throw new Exception( + // Newtonsoft.Json.JsonConvert.SerializeObject( + // new + // { + // message = ex.Message, + // description = String.Format("{0}: {1}", CRMSocialMediaResource.ErrorUnknownTwitterAccount, twitterAccount.Data) + // } + // )); + // } + // } + + + // return messages.OrderByDescending(m => m.PostedOn).Take(MessageCount).ToList(); + + //} + + + ///// + ///// + ///// + ///// + ///// Contacts + ///// + //[Read(@"contact/twitterprofile")] + //public List FindTwitterProfiles(string searchText) + //{ + // try + // { + // TwitterApiInfo apiInfo = TwitterApiHelper.GetTwitterApiInfoForCurrentUser(); + // if (apiInfo == null) + // throw new SocialMediaAccountNotFound(CRMSocialMediaResource.SocialMediaAccountNotFoundTwitter); + + // TwitterDataProvider provider = new TwitterDataProvider(apiInfo); + // List users = provider.FindUsers(searchText); + // /*List users = new List(); + // users.Add(new TwitterUserInfo { Description = "I'm a cool user", SmallImageUrl = "http://localhost/TeamLab/products/crm/data/0/photos/00/00/10/contact_10_50_50.jpg", UserName = "User", ScreenName = "user", UserID = 1 }); + // users.Add(new TwitterUserInfo { Description = "I'm a cool user", SmallImageUrl = "http://localhost/TeamLab/products/crm/data/0/photos/00/00/10/contact_10_50_50.jpg", UserName = "User", ScreenName = "user", UserID = 1 }); + // users.Add(new TwitterUserInfo { Description = "I'm a cool user", SmallImageUrl = "http://localhost/TeamLab/products/crm/data/0/photos/00/00/10/contact_10_50_50.jpg", UserName = "User", ScreenName = "user", UserID = 1 });*/ + // return users; + // } + // catch (Exception ex) { + // throw new SocialMediaUI(DaoFactory).ProcessError(ex, "ASC.CRM.Api.CRMApi.FindTwitterProfiles"); + // } + //} + + /// + /// + /// + /// + /// + /// + /// Contacts + /// + [Delete(@"contact/{contactid:int}/avatar")] + public string DeleteContactAvatar(int contactId, string contactType, bool uploadOnly) + { + bool isCompany; + + if (contactId != 0) + { + var contact = _daoFactory.GetContactDao().GetByID(contactId); + if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException(); + + if (!_crmSecurity.CanEdit(contact)) throw _crmSecurity.CreateSecurityException(); + + isCompany = contact is Company; + } + else + { + isCompany = contactType != "people"; + } + + if (!uploadOnly) + { + _contactPhotoManager.DeletePhoto(contactId); + return _contactPhotoManager.GetBigSizePhoto(0, isCompany); + } + return ""; + } + + ///// + ///// + ///// + ///// + ///// Contacts + ///// + //[Read(@"contact/{contactid:int}/socialmediaavatar")] + //public List GetContactSMImages(int contactId) + //{ + // return new SocialMediaUI(DaoFactory).GetContactSMImages(contactId); + //} + + ///// + ///// + ///// + ///// + ///// Contacts + ///// + //[Create(@"contact/socialmediaavatar")] + //public List GetContactSMImagesByNetworks(List socialNetworks) + //{ + // if (socialNetworks == null || socialNetworks.Count == 0) + // { + // return new List(); + // } + // var twitter = new List(); + + // foreach (var sn in socialNetworks) + // { + // if (sn.InfoType == ContactInfoType.Twitter) twitter.Add(sn.Data); + // } + + // return new SocialMediaUI(DaoFactory).GetContactSMImages(twitter); + //} + + ///// + ///// + ///// + ///// + ///// + ///// + ///// + ///// + ///// Contacts + ///// + //[Update(@"contact/{contactid:int}/avatar")] + //public ContactPhotoManager.PhotoData UploadUserAvatarFromSocialNetwork(int contactId, SocialNetworks socialNetwork, string userIdentity, bool uploadOnly, string tmpDirName) + //{ + // if (socialNetwork != SocialNetworks.Twitter) + // throw new ArgumentException(); + + // if (contactId != 0) + // { + // var contact = DaoFactory.GetContactDao().GetByID(contactId); + // if (contact == null || !CRMSecurity.CanAccessTo(contact)) throw new ItemNotFoundException(); + + // if (!CRMSecurity.CanEdit(contact)) throw CRMSecurity.CreateSecurityException(); + // } + + // if (socialNetwork == SocialNetworks.Twitter) + // { + // var provider = new TwitterDataProvider(TwitterApiHelper.GetTwitterApiInfoForCurrentUser()); + // var imageUrl = provider.GetUrlOfUserImage(userIdentity, TwitterDataProvider.ImageSize.Original); + // return UploadAvatar(contactId, imageUrl, uploadOnly, tmpDirName, false); + // } + + // return null; + //} + + /// false + [Create(@"contact/mailsmtp/send")] + public IProgressItem SendMailSMTPToContacts( + SendMailSMTPToContactsRequestDto inDto) + { + List fileIDs = inDto.FileIDs; + List contactIds = inDto.ContactIds; + String subject = inDto.Subject; + String body = inDto.Body; + bool storeInHistory = inDto.StoreInHistory; + + if (contactIds == null || contactIds.Count == 0 || String.IsNullOrEmpty(body)) throw new ArgumentException(); + + var contacts = _daoFactory.GetContactDao().GetContacts(contactIds.ToArray()); + + _messageService.Send(MessageAction.CrmSmtpMailSent, _messageTarget.Create(contactIds), contacts.Select(c => c.GetTitle())); + + return _mailSender.Start(fileIDs, contactIds, subject, body, storeInHistory); + } + + /// false + [Create(@"contact/mailsmtp/preview")] + public string GetMailSMTPToContactsPreview([FromForm] string template, [FromForm] int contactId) + { + if (contactId == 0 || String.IsNullOrEmpty(template)) throw new ArgumentException(); + + var manager = new MailTemplateManager(_daoFactory); + + return manager.Apply(template, contactId); + } + + /// false + [Read(@"contact/mailsmtp/status")] + public IProgressItem GetMailSMTPToContactsStatus() + { + return _mailSender.GetStatus(); + } + + /// false + [Update(@"contact/mailsmtp/cancel")] + public IProgressItem CancelMailSMTPToContacts() + { + var progressItem = _mailSender.GetStatus(); + + _mailSender.Cancel(); + + return progressItem; + + } + + /// false + [Update(@"contact/{contactid:int}/creationdate")] + public void SetContactCreationDate(int contactId, ApiDateTime creationDate) + { + var dao = _daoFactory.GetContactDao(); + var contact = dao.GetByID(contactId); + + if (contact == null || !_crmSecurity.CanAccessTo(contact)) + throw new ItemNotFoundException(); + + dao.SetContactCreationDate(contactId, creationDate); + } + + /// false + [Update(@"contact/{contactid:int}/lastmodifeddate")] + public void SetContactLastModifedDate(int contactId, ApiDateTime lastModifedDate) + { + var dao = _daoFactory.GetContactDao(); + var contact = dao.GetByID(contactId); + + if (contact == null || !_crmSecurity.CanAccessTo(contact)) + throw new ItemNotFoundException(); + + dao.SetContactLastModifedDate(contactId, lastModifedDate); + } + + + private ContactPhotoManager.PhotoData UploadAvatar(int contactID, string imageUrl, bool uploadOnly, string tmpDirName, bool checkFormat = true) + { + if (contactID != 0) + { + return _contactPhotoManager.UploadPhoto(imageUrl, contactID, uploadOnly, checkFormat); + } + + if (string.IsNullOrEmpty(tmpDirName) || tmpDirName == "null") tmpDirName = null; + return _contactPhotoManager.UploadPhotoToTemp(imageUrl, tmpDirName, checkFormat); + } + + private IEnumerable ToSimpleListContactDto(IReadOnlyList itemList) + { + if (itemList.Count == 0) return new List(); + + var result = new List(); + + var personsIDs = new List(); + var companyIDs = new List(); + var contactIDs = new int[itemList.Count]; + + var peopleCompanyIDs = new List(); + var peopleCompanyList = new Dictionary(); + + var contactDao = _daoFactory.GetContactDao(); + + for (var index = 0; index < itemList.Count; index++) + { + var contact = itemList[index]; + + if (contact is Company) + { + companyIDs.Add(contact.ID); + } + else + { + var person = contact as Person; + if (person != null) + { + personsIDs.Add(person.ID); + + if (person.CompanyID > 0) + { + peopleCompanyIDs.Add(person.CompanyID); + } + } + } + + contactIDs[index] = itemList[index].ID; + } + + if (peopleCompanyIDs.Count > 0) + { + var tmpList = contactDao.GetContacts(peopleCompanyIDs.ToArray()).ConvertAll(item => _mapper.Map(item)); + var tmpListCanDelete = contactDao.CanDelete(tmpList.Select(item => item.Id).ToArray()); + + foreach (var contactBaseDtoQuick in tmpList) + { + contactBaseDtoQuick.CanDelete = contactBaseDtoQuick.CanEdit && tmpListCanDelete[contactBaseDtoQuick.Id]; + peopleCompanyList.Add(contactBaseDtoQuick.Id, contactBaseDtoQuick); + } + } + + var contactInfos = new Dictionary>(); + + var addresses = new Dictionary>(); + + _daoFactory.GetContactInfoDao().GetAll(contactIDs).ForEach( + item => + { + if (item.InfoType == ContactInfoType.Address) + { + if (!addresses.ContainsKey(item.ContactID)) + { + addresses.Add(item.ContactID, new List
+ { + new Address(item) + }); + } + else + { + addresses[item.ContactID].Add(new Address(item)); + } + } + else + { + if (!contactInfos.ContainsKey(item.ContactID)) + { + contactInfos.Add(item.ContactID, new List { _mapper.Map(item) }); + } + else + { + contactInfos[item.ContactID].Add(_mapper.Map(item)); + } + } + } + ); + + var nearestTasks = _daoFactory.GetTaskDao().GetNearestTask(contactIDs.ToArray()); + + IEnumerable taskCategories = new List(); + + if (nearestTasks.Any()) + { + taskCategories = _daoFactory.GetListItemDao().GetItems(ListType.TaskCategory).ConvertAll(item => _mapper.Map(item)); + } + + foreach (var contact in itemList) + { + ContactDto contactDto; + + var person = contact as Person; + if (person != null) + { + var people = person; + + var peopleDto = _mapper.Map(people); + + if (people.CompanyID > 0 && peopleCompanyList.ContainsKey(people.CompanyID)) + { + peopleDto.Company = peopleCompanyList[people.CompanyID]; + } + + contactDto = peopleDto; + } + else + { + var company = contact as Company; + if (company != null) + { + contactDto = _mapper.Map(company); + } + else + { + throw new ArgumentException(); + } + } + + contactDto.CommonData = contactInfos.ContainsKey(contact.ID) ? contactInfos[contact.ID] : new List(); + + TaskBaseDto taskDto = null; + + if (nearestTasks.ContainsKey(contactDto.Id)) + { + var task = nearestTasks[contactDto.Id]; + + taskDto = _mapper.Map(task); + + if (task.CategoryID > 0) + { + taskDto.Category = taskCategories.First(x => x.Id == task.CategoryID); + } + } + + result.Add(new ContactWithTaskDto + { + Contact = contactDto, + Task = taskDto + }); + } + + + #region CanDelete for main contacts + + if (result.Count > 0) + { + var resultListCanDelete = contactDao.CanDelete(result.Select(item => item.Contact.Id).ToArray()); + foreach (var contactBaseDtoQuick in result) + { + contactBaseDtoQuick.Contact.CanDelete = contactBaseDtoQuick.Contact.CanEdit && resultListCanDelete[contactBaseDtoQuick.Contact.Id]; + } + } + + #endregion + + return result; + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Api/CurrencyRatesController.cs b/products/ASC.CRM/Server/Api/CurrencyRatesController.cs new file mode 100644 index 00000000000..6ac52f9b239 --- /dev/null +++ b/products/ASC.CRM/Server/Api/CurrencyRatesController.cs @@ -0,0 +1,331 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Api.CRM; +using ASC.Core.Common.Settings; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Resources; +using ASC.MessagingSystem; +using ASC.Web.Api.Routing; +using ASC.Web.CRM.Classes; + +using AutoMapper; + +using Microsoft.AspNetCore.Mvc; + +namespace ASC.CRM.Api +{ + public class CurrencyRatesController : BaseApiController + { + private readonly CurrencyProvider _currencyProvider; + private readonly SettingsManager _settingsManager; + private readonly Global _global; + private readonly MessageService _messageService; + + public CurrencyRatesController(CrmSecurity crmSecurity, + DaoFactory daoFactory, + MessageService messageService, + SettingsManager settingsManager, + Global global, + CurrencyProvider currencyProvider, + IMapper mapper) + : base(daoFactory, crmSecurity, mapper) + { + _messageService = messageService; + _settingsManager = settingsManager; + _global = global; + _currencyProvider = currencyProvider; + } + + + //TABLE `crm_currency_rate` column `rate` DECIMAL(10,2) NOT NULL + public const decimal MaxRateValue = (decimal)99999999.99; + + /// + /// Get the list of currency rates + /// + /// Get currency rates list + /// Common + /// + /// List of currency rates + /// + [Read(@"currency/rates")] + public IEnumerable GetCurrencyRates() + { + return _daoFactory.GetCurrencyRateDao().GetAll().ConvertAll(x => _mapper.Map(x)); + } + + /// + /// Get currency rate by id + /// + /// Get currency rate + /// Common + /// + /// Currency rate + /// + /// + [Read(@"currency/rates/{id:int}")] + public CurrencyRateDto GetCurrencyRate(int id) + { + if (id <= 0) throw new ArgumentException(); + + var currencyRate = _daoFactory.GetCurrencyRateDao().GetByID(id); + + return _mapper.Map(currencyRate); + } + + /// + /// Get currency rate by currencies + /// + /// Get currency rate + /// Common + /// + /// Currency rate + /// + /// + [Read(@"currency/rates/{fromCurrency}/{toCurrency}")] + public CurrencyRateDto GetCurrencyRate(string fromCurrency, string toCurrency) + { + if (string.IsNullOrEmpty(fromCurrency) || string.IsNullOrEmpty(toCurrency)) + throw new ArgumentException(); + + var currencyRate = _daoFactory.GetCurrencyRateDao().GetByCurrencies(fromCurrency, toCurrency); + + return _mapper.Map(currencyRate); + } + + /// + /// Create new currency rate object + /// + /// + /// Common + /// + [Create(@"currency/rates")] + public CurrencyRateDto CreateCurrencyRate( + [FromForm] string fromCurrency, + [FromForm] string toCurrency, + [FromForm] decimal rate) + { + ValidateRate(rate); + + ValidateCurrencies(new[] { fromCurrency, toCurrency }); + + var currencyRate = new CurrencyRate + { + FromCurrency = fromCurrency, + ToCurrency = toCurrency, + Rate = rate + }; + + currencyRate.ID = _daoFactory.GetCurrencyRateDao().SaveOrUpdate(currencyRate); + _messageService.Send(MessageAction.CurrencyRateUpdated, fromCurrency, toCurrency); + + return _mapper.Map(currencyRate); + } + + /// + /// Update currency rate object + /// + /// + /// Common + /// + [Update(@"currency/rates/{id:int}")] + public CurrencyRateDto UpdateCurrencyRate(int id, string fromCurrency, string toCurrency, decimal rate) + { + if (id <= 0) + throw new ArgumentException(); + + ValidateRate(rate); + + ValidateCurrencies(new[] { fromCurrency, toCurrency }); + + var currencyRate = _daoFactory.GetCurrencyRateDao().GetByID(id); + + if (currencyRate == null) + throw new ArgumentException(); + + currencyRate.FromCurrency = fromCurrency; + currencyRate.ToCurrency = toCurrency; + currencyRate.Rate = rate; + + currencyRate.ID = _daoFactory.GetCurrencyRateDao().SaveOrUpdate(currencyRate); + _messageService.Send(MessageAction.CurrencyRateUpdated, fromCurrency, toCurrency); + + return _mapper.Map(currencyRate); + } + + /// + /// Set currency rates + /// + /// + /// Common + /// + [Create(@"currency/setrates")] + public List SetCurrencyRates( + [FromForm] String currency, + [FromForm] List rates) + { + if (!_crmSecurity.IsAdmin) + throw _crmSecurity.CreateSecurityException(); + + if (string.IsNullOrEmpty(currency)) + throw new ArgumentException(); + + ValidateCurrencyRates(rates); + + currency = currency.ToUpper(); + + var crmSettings = _settingsManager.Load(); + var defaultCurrency = _currencyProvider.Get(_settingsManager.Load().DefaultCurrency); + + if (defaultCurrency.Abbreviation != currency) + { + var cur = _currencyProvider.Get(currency); + + if (cur == null) + throw new ArgumentException(); + + _global.SaveDefaultCurrencySettings(cur); + + _messageService.Send(MessageAction.CrmDefaultCurrencyUpdated); + } + + rates = _daoFactory.GetCurrencyRateDao().SetCurrencyRates(rates); + + foreach (var rate in rates) + { + _messageService.Send(MessageAction.CurrencyRateUpdated, rate.FromCurrency, rate.ToCurrency); + } + + return rates.Select(x => _mapper.Map(x)).ToList(); + } + + /// + /// Add currency rates + /// + /// + /// Common + /// + [Create(@"currency/addrates")] + public List AddCurrencyRates([FromForm] List rates) + { + if (!_crmSecurity.IsAdmin) + throw _crmSecurity.CreateSecurityException(); + + ValidateCurrencyRates(rates); + + var existingRates = _daoFactory.GetCurrencyRateDao().GetAll(); + + foreach (var rate in rates) + { + var exist = false; + + foreach (var existingRate in existingRates) + { + if (rate.FromCurrency != existingRate.FromCurrency || rate.ToCurrency != existingRate.ToCurrency) + continue; + + existingRate.Rate = rate.Rate; + _daoFactory.GetCurrencyRateDao().SaveOrUpdate(existingRate); + _messageService.Send(MessageAction.CurrencyRateUpdated, rate.FromCurrency, rate.ToCurrency); + exist = true; + break; + } + + if (exist) continue; + + rate.ID = _daoFactory.GetCurrencyRateDao().SaveOrUpdate(rate); + _messageService.Send(MessageAction.CurrencyRateUpdated, rate.FromCurrency, rate.ToCurrency); + existingRates.Add(rate); + } + + return existingRates.Select(x => _mapper.Map(x)).ToList(); + } + + /// + /// Delete currency rate object + /// + /// + /// Common + /// + [Delete(@"currency/rates/{id:int}")] + public CurrencyRateDto DeleteCurrencyRate(int id) + { + if (id <= 0) + throw new ArgumentException(); + + var currencyRate = _daoFactory.GetCurrencyRateDao().GetByID(id); + + if (currencyRate == null) + throw new ArgumentException(); + + _daoFactory.GetCurrencyRateDao().Delete(id); + + return _mapper.Map(currencyRate); + } + + private void ValidateCurrencyRates(IEnumerable rates) + { + var currencies = new List(); + + foreach (var rate in rates) + { + ValidateRate(rate.Rate); + currencies.Add(rate.FromCurrency); + currencies.Add(rate.ToCurrency); + } + + ValidateCurrencies(currencies.ToArray()); + } + + private void ValidateCurrencies(string[] currencies) + { + if (currencies.Any(string.IsNullOrEmpty)) + throw new ArgumentException(); + + var available = _currencyProvider.GetAll().Select(x => x.Abbreviation); + + var unknown = currencies.Where(x => !available.Contains(x)).ToArray(); + + if (!unknown.Any()) return; + + throw new ArgumentException(string.Format(CRMErrorsResource.UnknownCurrency, string.Join(",", unknown))); + } + + private static void ValidateRate(decimal rate) + { + if (rate < 0 || rate > MaxRateValue) + throw new ArgumentException(string.Format(CRMErrorsResource.InvalidCurrencyRate, rate)); + } + + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Api/CustomFieldsController.cs b/products/ASC.CRM/Server/Api/CustomFieldsController.cs new file mode 100644 index 00000000000..dafe225a685 --- /dev/null +++ b/products/ASC.CRM/Server/Api/CustomFieldsController.cs @@ -0,0 +1,428 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Api.CRM; +using ASC.Common.Web; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.MessagingSystem; +using ASC.Web.Api.Routing; + +using AutoMapper; + +using Microsoft.AspNetCore.Mvc; + +namespace ASC.CRM.Api +{ + public class CustomFieldsController : BaseApiController + { + private readonly MessageService _messageService; + private readonly MessageTarget _messageTarget; + + public CustomFieldsController(CrmSecurity crmSecurity, + DaoFactory daoFactory, + MessageTarget messageTarget, + MessageService messageService, + IMapper mapper) + : base(daoFactory, crmSecurity, mapper) + { + _messageTarget = messageTarget; + _messageService = messageService; + } + + + /// + /// Returns the list of descriptions for all existing user fields + /// + /// Type + /// Get user field list + /// User fields + /// + /// User field list + /// + /// + [Read(@"{entityType:regex(contact|person|company|opportunity|case)}/customfield/definitions")] + public IEnumerable GetCustomFieldDefinitions(string entityType) + { + return _mapper.Map, List>(_daoFactory.GetCustomFieldDao().GetFieldsDescription(ToEntityType(entityType))); + } + + /// + /// Returns the list of all user field values using the entity type and entity ID specified in the request + /// + /// Type + /// ID + /// Get user field values + /// User fields + /// + [Read(@"{entityType:regex(contact|person|company|opportunity|case)}/{entityid:int}/customfield")] + public IEnumerable GetCustomFieldForSubject(string entityType, int entityid) + { + return _mapper.Map, List>(_daoFactory.GetCustomFieldDao().GetEnityFields(ToEntityType(entityType), entityid, false)); + } + + /// + /// Sets the new user field value using the entity type, ID, field ID and value specified in the request + /// + /// Type + /// ID + /// Field ID + /// Field Value + /// Set user field value + /// User fields + /// + /// User field + /// + [Create(@"{entityType:regex(contact|person|company|opportunity|case)}/{entityid:int}/customfield/{fieldid:int}")] + public CustomFieldBaseDto SetEntityCustomFieldValue( + [FromRoute] string entityType, + [FromRoute] int entityid, + [FromRoute] int fieldid, + [FromForm] string fieldValue) + { + var customField = _daoFactory.GetCustomFieldDao().GetFieldDescription(fieldid); + + var entityTypeStr = ToEntityType(entityType); + + customField.EntityID = entityid; + customField.Value = fieldValue; + + _daoFactory.GetCustomFieldDao().SetFieldValue(entityTypeStr, entityid, fieldid, fieldValue); + + return _mapper.Map(customField); + } + + /// + /// Creates a new user field with the parameters (entity type, field title, type, etc.) specified in the request + /// + /// Entity type + /// Field title + /// + /// User field value + /// + /// Field position + /// Mask + /// Create user field + /// User fields + /// + /// User field + /// + /// + /// + /// + [Create(@"{entityType:regex(contact|person|company|opportunity|case)}/customfield")] + public CustomFieldDto CreateCustomFieldValue( + [FromRoute] string entityType, + [FromForm] string label, + [FromForm] int fieldType, + [FromForm] int position, + [FromForm] string mask) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + var entityTypeObj = ToEntityType(entityType); + var fieldID = _daoFactory.GetCustomFieldDao().CreateField(entityTypeObj, label, (CustomFieldType)fieldType, mask); + var wrapper = _daoFactory.GetCustomFieldDao().GetFieldDescription(fieldID); + + var messageAction = GetCustomFieldCreatedAction(entityTypeObj); + _messageService.Send(messageAction, _messageTarget.Create(wrapper.ID), wrapper.Label); + + return _mapper.Map(_daoFactory.GetCustomFieldDao().GetFieldDescription(fieldID)); + } + + /// + /// Updates the selected user field with the parameters (entity type, field title, type, etc.) specified in the request + /// + /// User field id + /// Entity type + /// Field title + /// + /// User field value + /// + /// Field position + /// Mask + /// Updates the selected user field + /// User fields + /// + /// User field + /// + /// + /// + /// + /// + /// + [Update(@"{entityType:regex(contact|person|company|opportunity|case)}/customfield/{id:int}")] + public CustomFieldDto UpdateCustomFieldValue(int id, string entityType, string label, int fieldType, int position, string mask) + { + if (id <= 0) throw new ArgumentException(); + if (!_daoFactory.GetCustomFieldDao().IsExist(id)) throw new ItemNotFoundException(); + + var entityTypeObj = ToEntityType(entityType); + + var customField = new CustomField + { + EntityType = entityTypeObj, + Type = (CustomFieldType)fieldType, + ID = id, + Mask = mask, + Label = label, + SortOrder = position + }; + + _daoFactory.GetCustomFieldDao().EditItem(customField); + + customField = _daoFactory.GetCustomFieldDao().GetFieldDescription(id); + + var messageAction = GetCustomFieldUpdatedAction(entityTypeObj); + _messageService.Send(messageAction, _messageTarget.Create(customField.ID), customField.Label); + + return _mapper.Map(customField); + } + + /// + /// Deletes the user field with the ID specified in the request + /// + /// Type + /// Field ID + /// Delete user field + /// User fields + /// + /// + /// + /// User field + /// + [Delete(@"{entityType:regex(contact|person|company|opportunity|case)}/customfield/{fieldid:int}")] + public CustomFieldDto DeleteCustomField(string entityType, int fieldid) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + if (fieldid <= 0) throw new ArgumentException(); + + var customField = _daoFactory.GetCustomFieldDao().GetFieldDescription(fieldid); + if (customField == null) throw new ItemNotFoundException(); + + var result = _mapper.Map(customField); + + _daoFactory.GetCustomFieldDao().DeleteField(fieldid); + + var messageAction = GetCustomFieldDeletedAction(ToEntityType(entityType)); + _messageService.Send(messageAction, _messageTarget.Create(customField.ID), result.Label); + + return result; + } + + /// + /// Updates user fields order + /// + /// User field ID list + /// Entity type + /// User fields + /// + /// User fields + /// + /// + /// + /// + [Update(@"{entityType:regex(contact|person|company|opportunity|case)}/customfield/reorder")] + public IEnumerable UpdateCustomFieldsOrder(IEnumerable fieldids, string entityType) + { + if (fieldids == null) throw new ArgumentException(); + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + var customFields = new List(); + foreach (var id in fieldids) + { + if (!_daoFactory.GetCustomFieldDao().IsExist(id)) throw new ItemNotFoundException(); + customFields.Add(_daoFactory.GetCustomFieldDao().GetFieldDescription(id)); + } + + _daoFactory.GetCustomFieldDao().ReorderFields(fieldids.ToArray()); + + var messageAction = GetCustomFieldsUpdatedOrderAction(ToEntityType(entityType)); + _messageService.Send(messageAction, _messageTarget.Create(fieldids), customFields.Select(x => x.Label)); + + return _mapper.Map, List>(customFields); + } + + private static MessageAction GetCustomFieldCreatedAction(EntityType entityType) + { + switch (entityType) + { + case EntityType.Contact: + return MessageAction.ContactUserFieldCreated; + case EntityType.Person: + return MessageAction.PersonUserFieldCreated; + case EntityType.Company: + return MessageAction.CompanyUserFieldCreated; + case EntityType.Opportunity: + return MessageAction.OpportunityUserFieldCreated; + case EntityType.Case: + return MessageAction.CaseUserFieldCreated; + default: + throw new ArgumentException("Invalid entityType: " + entityType); + } + } + + private static MessageAction GetCustomFieldUpdatedAction(EntityType entityType) + { + switch (entityType) + { + case EntityType.Contact: + return MessageAction.ContactUserFieldUpdated; + case EntityType.Person: + return MessageAction.PersonUserFieldUpdated; + case EntityType.Company: + return MessageAction.CompanyUserFieldUpdated; + case EntityType.Opportunity: + return MessageAction.OpportunityUserFieldUpdated; + case EntityType.Case: + return MessageAction.CaseUserFieldUpdated; + default: + throw new ArgumentException("Invalid entityType: " + entityType); + } + } + + private static MessageAction GetCustomFieldDeletedAction(EntityType entityType) + { + switch (entityType) + { + case EntityType.Contact: + return MessageAction.ContactUserFieldDeleted; + case EntityType.Person: + return MessageAction.PersonUserFieldDeleted; + case EntityType.Company: + return MessageAction.CompanyUserFieldDeleted; + case EntityType.Opportunity: + return MessageAction.OpportunityUserFieldDeleted; + case EntityType.Case: + return MessageAction.CaseUserFieldDeleted; + default: + throw new ArgumentException("Invalid entityType: " + entityType); + } + } + + private static MessageAction GetCustomFieldsUpdatedOrderAction(EntityType entityType) + { + switch (entityType) + { + case EntityType.Contact: + return MessageAction.ContactUserFieldsUpdatedOrder; + case EntityType.Person: + return MessageAction.PersonUserFieldsUpdatedOrder; + case EntityType.Company: + return MessageAction.CompanyUserFieldsUpdatedOrder; + case EntityType.Opportunity: + return MessageAction.OpportunityUserFieldsUpdatedOrder; + case EntityType.Case: + return MessageAction.CaseUserFieldsUpdatedOrder; + default: + throw new ArgumentException("Invalid entityType: " + entityType); + } + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Api/DealsController.cs b/products/ASC.CRM/Server/Api/DealsController.cs new file mode 100644 index 00000000000..35407d9dd72 --- /dev/null +++ b/products/ASC.CRM/Server/Api/DealsController.cs @@ -0,0 +1,983 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Api.Core; +using ASC.Api.CRM; +using ASC.Common.Web; +using ASC.Core; +using ASC.Core.Users; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.MessagingSystem; +using ASC.Web.Api.Models; +using ASC.Web.Api.Routing; +using ASC.Web.Core.Users; +using ASC.Web.CRM.Classes; +using ASC.Web.CRM.Services.NotifyService; + +using AutoMapper; + +using Microsoft.AspNetCore.Mvc; + +namespace ASC.CRM.Api +{ + public class DealsController : BaseApiController + { + private readonly DisplayUserSettingsHelper _displayUserSettingsHelper; + private readonly NotifyClient _notifyClient; + private readonly ApiContext _apiContext; + private readonly MessageService _messageService; + private readonly MessageTarget _messageTarget; + private readonly SecurityContext _securityContext; + private readonly CurrencyProvider _currencyProvider; + private readonly UserManager _userManager; + private readonly EmployeeWraperHelper _employeeWraperHelper; + + public DealsController(CrmSecurity crmSecurity, + DaoFactory daoFactory, + ApiContext apiContext, + MessageTarget messageTarget, + MessageService messageService, + NotifyClient notifyClient, + CurrencyProvider currencyProvider, + UserManager userManager, + EmployeeWraperHelper employeeWraperHelper, + DisplayUserSettingsHelper displayUserSettingsHelper, + SecurityContext securityContext, + IMapper mapper) + : base(daoFactory, crmSecurity, mapper) + { + _apiContext = apiContext; + _messageTarget = messageTarget; + _messageService = messageService; + _notifyClient = notifyClient; + _currencyProvider = currencyProvider; + _userManager = userManager; + _employeeWraperHelper = employeeWraperHelper; + _displayUserSettingsHelper = displayUserSettingsHelper; + _securityContext = securityContext; + } + + + /// + /// Returns the detailed information about the opportunity with the ID specified in the request + /// + /// Opportunity ID + /// + /// Opportunity + /// + /// Get opportunity by ID + /// Opportunities + /// + /// + [Read(@"opportunity/{opportunityid:int}")] + public OpportunityDto GetDealByID(int opportunityid) + { + if (opportunityid <= 0) throw new ArgumentException(); + + var deal = _daoFactory.GetDealDao().GetByID(opportunityid); + if (deal == null || !_crmSecurity.CanAccessTo(deal)) throw new ItemNotFoundException(); + + return _mapper.Map(deal); + } + + /// + /// Updates the selected opportunity to the stage with the ID specified in the request + /// + /// Opportunity ID + /// Opportunity stage ID + /// + /// Opportunity + /// + /// Update opportunity stage + /// Opportunities + /// + /// + [Update(@"opportunity/{opportunityid:int}/stage/{id:int}")] + public OpportunityDto UpdateToDealMilestone(int opportunityid, int stageid) + { + if (opportunityid <= 0 || stageid <= 0) throw new ArgumentException(); + + var deal = _daoFactory.GetDealDao().GetByID(opportunityid); + if (deal == null || !_crmSecurity.CanEdit(deal)) throw new ItemNotFoundException(); + + var stage = _daoFactory.GetDealMilestoneDao().GetByID(stageid); + if (stage == null) throw new ItemNotFoundException(); + + deal.DealMilestoneID = stageid; + deal.DealMilestoneProbability = stage.Probability; + + deal.ActualCloseDate = stage.Status != DealMilestoneStatus.Open ? DateTime.UtcNow : DateTime.MinValue; + _daoFactory.GetDealDao().EditDeal(deal); + _messageService.Send(MessageAction.OpportunityUpdatedStage, _messageTarget.Create(deal.ID), deal.Title); + + return _mapper.Map(deal); + } + + /// + /// Sets access rights for the selected opportunity with the parameters specified in the request + /// + /// Opportunity ID + /// Opportunity privacy: private or not + /// List of users with access + /// Set rights to opportunity + /// Opportunities + /// + /// + /// + /// Opportunity + /// + [Update(@"opportunity/{opportunityid:int}/access")] + public OpportunityDto SetAccessToDeal(int opportunityid, bool isPrivate, IEnumerable accessList) + { + if (opportunityid <= 0) throw new ArgumentException(); + + var deal = _daoFactory.GetDealDao().GetByID(opportunityid); + if (deal == null) throw new ItemNotFoundException(); + + if (!(_crmSecurity.IsAdmin || deal.CreateBy == _securityContext.CurrentAccount.ID)) throw _crmSecurity.CreateSecurityException(); + return SetAccessToDeal(deal, isPrivate, accessList, false, true); + } + + private OpportunityDto SetAccessToDeal(Deal deal, bool isPrivate, IEnumerable accessList, bool isNotify, bool isMessageServicSende) + { + var accessListLocal = accessList != null ? accessList.Distinct().ToList() : new List(); + if (isPrivate && accessListLocal.Count > 0) + { + + if (isNotify) + { + accessListLocal = accessListLocal.Where(u => u != _securityContext.CurrentAccount.ID).ToList(); + _notifyClient.SendAboutSetAccess(EntityType.Opportunity, deal.ID, _daoFactory, accessListLocal.ToArray()); + } + + if (!accessListLocal.Contains(_securityContext.CurrentAccount.ID)) + { + accessListLocal.Add(_securityContext.CurrentAccount.ID); + } + + _crmSecurity.SetAccessTo(deal, accessListLocal); + + if (isMessageServicSende) + { + var users = _userManager.GetUsers().Where(x => accessListLocal.Contains(x.ID)); + _messageService.Send(MessageAction.OpportunityRestrictedAccess, _messageTarget.Create(deal.ID), deal.Title, users.Select(x => x.DisplayUserName(false, _displayUserSettingsHelper))); + } + } + else + { + _crmSecurity.MakePublic(deal); + if (isMessageServicSende) + { + _messageService.Send(MessageAction.OpportunityOpenedAccess, _messageTarget.Create(deal.ID), deal.Title); + } + } + + return _mapper.Map(deal); + } + + /// + /// Sets access rights for other users to the list of all opportunities matching the parameters specified in the request + /// + /// Opportunity responsible + /// Opportunity stage ID + /// Tags + /// Contact ID + /// Participation status: take into account opportunities where the contact is a participant or not + /// Start date + /// End date + /// Opportunity stage type + /// Opportunity privacy: private or not + /// List of users with access + /// Set opportunity access rights + /// Opportunities + /// + /// + /// + /// Opportunity list + /// + [Update(@"opportunity/filter/access")] + public IEnumerable SetAccessToBatchDeal([FromForm] SetAccessToBatchDealByFilterRequestDto inDto) + { + var responsibleid = inDto.Responsibleid; + var opportunityStagesid = inDto.OpportunityStagesid; + var contactid = inDto.Contactid; + var tags = inDto.Tags; + var stageType = inDto.StageType; + var contactAlsoIsParticipant = inDto.ContactAlsoIsParticipant; + var fromDate = inDto.FromDate; + var toDate = inDto.ToDate; + var isPrivate = inDto.isPrivate; + var accessList = inDto.AccessList; + + var result = new List(); + + var deals = _daoFactory.GetDealDao() + .GetDeals(_apiContext.FilterValue, + responsibleid, + opportunityStagesid, + tags, + contactid, + stageType, + contactAlsoIsParticipant, + fromDate, toDate, 0, 0, null); + + if (!deals.Any()) return Enumerable.Empty(); + + foreach (var deal in deals) + { + if (deal == null) throw new ItemNotFoundException(); + + if (!(_crmSecurity.IsAdmin || deal.CreateBy == _securityContext.CurrentAccount.ID)) continue; + + SetAccessToDeal(deal.ID, isPrivate, accessList); + + result.Add(deal); + + } + + return ToListOpportunityDto(result); + } + + /// + /// Sets access rights for other users to the list of opportunities with the IDs specified in the request + /// + /// Opportunity ID list + /// Opportunity privacy: private or not + /// List of users with access + /// Set opportunity access rights + /// Opportunities + /// + /// + /// + /// Opportunity list + /// + [Update(@"opportunity/access")] + public IEnumerable SetAccessToBatchDeal(SetAccessToBatchDealRequestDto inDto) + { + var opportunityid = inDto.Opportunityid; + var isPrivate = inDto.isPrivate; + var accessList = inDto.AccessList; + + if (opportunityid == null) throw new ArgumentException(); + + var result = new List(); + + var deals = _daoFactory.GetDealDao().GetDeals(opportunityid.ToArray()); + + if (!deals.Any()) return new List(); + + foreach (var d in deals) + { + if (d == null) throw new ItemNotFoundException(); + + if (!(_crmSecurity.IsAdmin || d.CreateBy == _securityContext.CurrentAccount.ID)) continue; + + SetAccessToDeal(d, isPrivate, accessList, false, true); + result.Add(d); + } + + return ToListOpportunityDto(result); + } + + + /// + /// Deletes the group of opportunities with the IDs specified in the request + /// + /// Opportunity ID list + /// + /// + /// Delete opportunity group + /// Opportunities + /// + /// Opportunity list + /// + [Update(@"opportunity")] + public IEnumerable DeleteBatchDeals(IEnumerable opportunityids) + { + if (opportunityids == null || !opportunityids.Any()) throw new ArgumentException(); + + var opportunities = _daoFactory.GetDealDao().DeleteBatchDeals(opportunityids.ToArray()); + _messageService.Send(MessageAction.OpportunitiesDeleted, _messageTarget.Create(opportunityids), opportunities.Select(o => o.Title)); + + return ToListOpportunityDto(opportunities); + } + + /// + /// Deletes the list of all opportunities matching the parameters specified in the request + /// + /// Opportunity responsible + /// Opportunity stage ID + /// Tags + /// Contact ID + /// Participation status: take into account opportunities where the contact is a participant or not + /// Start date + /// End date + /// Opportunity stage type + /// + /// + /// Delete opportunity group + /// Opportunities + /// + /// Opportunity list + /// + [Delete(@"opportunity/filter")] + public IEnumerable DeleteBatchDeals( + Guid responsibleid, + int opportunityStagesid, + IEnumerable tags, + int contactid, + DealMilestoneStatus? stageType, + bool? contactAlsoIsParticipant, + ApiDateTime fromDate, + ApiDateTime toDate) + { + var deals = _daoFactory.GetDealDao().GetDeals(_apiContext.FilterValue, + responsibleid, + opportunityStagesid, + tags, + contactid, + stageType, + contactAlsoIsParticipant, + fromDate, toDate, 0, 0, null); + + if (!deals.Any()) return Enumerable.Empty(); + + deals = _daoFactory.GetDealDao().DeleteBatchDeals(deals); + _messageService.Send(MessageAction.OpportunitiesDeleted, _messageTarget.Create(deals.Select(x => x.ID)), deals.Select(d => d.Title)); + + return ToListOpportunityDto(deals); + } + + /// + /// Returns the list of all opportunities matching the parameters specified in the request + /// + /// Opportunity responsible + /// Opportunity stage ID + /// Tags + /// Contact ID + /// Participation status: take into account opportunities where the contact is a participant or not + /// Start date + /// End date + /// Opportunity stage type + /// Get opportunity list + /// Opportunities + /// + /// Opportunity list + /// + [Read(@"opportunity/filter")] + public IEnumerable GetDeals( + [FromQuery] Guid responsibleid, + [FromQuery] int opportunityStagesid, + [FromQuery] IEnumerable tags, + [FromQuery] int contactid, + [FromQuery] DealMilestoneStatus? stageType, + [FromQuery] bool? contactAlsoIsParticipant, + [FromQuery] ApiDateTime fromDate, + [FromQuery] ApiDateTime toDate) + { + DealSortedByType dealSortedByType; + + IEnumerable result; + + var searchString = _apiContext.FilterValue; + + OrderBy dealsOrderBy; + + if (ASC.CRM.Classes.EnumExtension.TryParse(_apiContext.SortBy, true, out dealSortedByType)) + { + dealsOrderBy = new OrderBy(dealSortedByType, !_apiContext.SortDescending); + } + else if (string.IsNullOrEmpty(_apiContext.SortBy)) + { + dealsOrderBy = new OrderBy(DealSortedByType.Stage, true); + } + else + { + dealsOrderBy = null; + } + + var fromIndex = (int)_apiContext.StartIndex; + var count = (int)_apiContext.Count; + + if (dealsOrderBy != null) + { + result = ToListOpportunityDto(_daoFactory.GetDealDao().GetDeals( + searchString, + responsibleid, + opportunityStagesid, + tags, + contactid, + stageType, + contactAlsoIsParticipant, + fromDate, + toDate, + fromIndex, + count, + dealsOrderBy)).ToList(); + + _apiContext.SetDataPaginated(); + _apiContext.SetDataFiltered(); + _apiContext.SetDataSorted(); + } + else + { + result = ToListOpportunityDto(_daoFactory.GetDealDao().GetDeals( + searchString, + responsibleid, + opportunityStagesid, + tags, + contactid, + stageType, + contactAlsoIsParticipant, + fromDate, + toDate, + 0, 0, null)).ToList(); + } + + + int totalCount; + + if (result.Count() < count) + { + totalCount = fromIndex + result.Count(); + } + else + { + totalCount = _daoFactory + .GetDealDao() + .GetDealsCount(searchString, + responsibleid, + opportunityStagesid, + tags, + contactid, + stageType, + contactAlsoIsParticipant, + fromDate, + toDate); + } + + _apiContext.SetTotalCount(totalCount); + + return result; + } + + /// + /// Deletes the opportunity with the ID specified in the request + /// + /// Opportunity ID + /// Delete opportunity + /// Opportunities + /// + /// + /// + /// Opportunity + /// + [Delete(@"opportunity/{opportunityid:int}")] + public OpportunityDto DeleteDeal(int opportunityid) + { + if (opportunityid <= 0) throw new ArgumentException(); + + var deal = _daoFactory.GetDealDao().DeleteDeal(opportunityid); + if (deal == null) throw new ItemNotFoundException(); + + _messageService.Send(MessageAction.OpportunityDeleted, _messageTarget.Create(deal.ID), deal.Title); + + return _mapper.Map(deal); + } + + /// + /// Creates the opportunity with the parameters specified in the request + /// + /// Create opportunity + /// Opportunity primary contact + /// Participants + /// Opportunity title + /// Opportunity description + /// Opportunity responsible + /// Bid + /// Amount of transaction + /// Currency (Abbreviation) + /// Period + /// Stage ID + /// Opportunity success probability + /// Actual opportunity closure date + /// Expected opportunity closure date + /// User field list + /// Opportunity privacy: private or not + /// List of users with access to the opportunity + /// Notify users in accessList about the opportunity + /// Opportunities + /// + /// Opportunity + /// + /// + [Create(@"opportunity")] + public OpportunityDto CreateDeal(CreateOrUpdateDealRequestDto inDto) + { + + var title = inDto.Title; + var description = inDto.Description; + var responsibleid = inDto.Responsibleid; + var bidType = inDto.BidType; + var bidValue = inDto.BidValue; + var perPeriodValue = inDto.PerPeriodValue; + var stageid = inDto.Stageid; + var successProbability = inDto.SuccessProbability; + var contactid = inDto.Contactid; + var actualCloseDate = inDto.ActualCloseDate; + var expectedCloseDate = inDto.ExpectedCloseDate; + var bidCurrencyAbbr = inDto.BidCurrencyAbbr; + var isPrivate = inDto.isPrivate; + var isNotify = inDto.isNotify; + var accessList = inDto.AccessList; + var customFieldList = inDto.CustomFieldList; + var members = inDto.Members; + + var deal = new Deal + { + Title = title, + Description = description, + ResponsibleID = responsibleid, + BidType = bidType, + BidValue = bidValue, + PerPeriodValue = perPeriodValue, + DealMilestoneID = stageid, + DealMilestoneProbability = successProbability < 0 ? 0 : (successProbability > 100 ? 100 : successProbability), + ContactID = contactid, + ActualCloseDate = actualCloseDate, + ExpectedCloseDate = expectedCloseDate, + BidCurrency = !String.IsNullOrEmpty(bidCurrencyAbbr) ? bidCurrencyAbbr.ToUpper() : null, + }; + + _crmSecurity.DemandCreateOrUpdate(deal); + + deal.ID = _daoFactory.GetDealDao().CreateNewDeal(deal); + + deal.CreateBy = _securityContext.CurrentAccount.ID; + deal.CreateOn = DateTime.UtcNow; + + SetAccessToDeal(deal, isPrivate, accessList, isNotify, false); + + var membersList = members != null ? members.ToList() : new List(); + + if (deal.ContactID > 0) + membersList.Add(deal.ContactID); + + if (membersList.Any()) + { + var contacts = _daoFactory.GetContactDao().GetContacts(membersList.ToArray()).Where(_crmSecurity.CanAccessTo).ToList(); + membersList = contacts.Select(m => m.ID).ToList(); + _daoFactory.GetDealDao().SetMembers(deal.ID, membersList.ToArray()); + } + + if (customFieldList != null) + { + var existingCustomFieldList = _daoFactory.GetCustomFieldDao().GetFieldsDescription(EntityType.Opportunity).Select(fd => fd.ID).ToList(); + foreach (var field in customFieldList) + { + if (string.IsNullOrEmpty(field.Value) || !existingCustomFieldList.Contains(field.Key)) continue; + _daoFactory.GetCustomFieldDao().SetFieldValue(EntityType.Opportunity, deal.ID, field.Key, field.Value); + } + } + + return _mapper.Map(deal); + } + + /// + /// Updates the selected opportunity with the parameters specified in the request + /// + /// Update opportunity + ///Opportunity ID + ///Opportunity primary contact + /// Participants + /// Opportunity title + /// Opportunity description + /// Opportunity responsible + /// Bid + /// Amount of transaction + /// Currency (Abbreviation) + /// Period + /// Stage ID + /// Opportunity success probability + /// Actual opportunity closure date + /// Expected opportunity closure date + /// User field list + /// Opportunity privacy: private or not + /// List of users with access to the opportunity + /// Notify users in accessList about the opportunity + /// Opportunities + /// + /// Opportunity + /// + /// + [Update(@"opportunity/{opportunityid:int}")] + public OpportunityDto UpdateDeal( + int opportunityid, CreateOrUpdateDealRequestDto inDto) + { + + var title = inDto.Title; + var description = inDto.Description; + var responsibleid = inDto.Responsibleid; + var bidType = inDto.BidType; + var bidValue = inDto.BidValue; + var perPeriodValue = inDto.PerPeriodValue; + var stageid = inDto.Stageid; + var successProbability = inDto.SuccessProbability; + var contactid = inDto.Contactid; + var actualCloseDate = inDto.ActualCloseDate; + var expectedCloseDate = inDto.ExpectedCloseDate; + var bidCurrencyAbbr = inDto.BidCurrencyAbbr; + var isPrivate = inDto.isPrivate; + var isNotify = inDto.isNotify; + var accessList = inDto.AccessList; + var customFieldList = inDto.CustomFieldList; + var members = inDto.Members; + + var deal = _daoFactory.GetDealDao().GetByID(opportunityid); + if (deal == null) throw new ItemNotFoundException(); + + deal.Title = title; + deal.Description = description; + deal.ResponsibleID = responsibleid; + deal.BidType = bidType; + deal.BidValue = bidValue; + deal.PerPeriodValue = perPeriodValue; + deal.DealMilestoneID = stageid; + deal.DealMilestoneProbability = successProbability < 0 ? 0 : (successProbability > 100 ? 100 : successProbability); + deal.ContactID = contactid; + deal.ActualCloseDate = actualCloseDate; + deal.ExpectedCloseDate = expectedCloseDate; + deal.BidCurrency = !String.IsNullOrEmpty(bidCurrencyAbbr) ? bidCurrencyAbbr.ToUpper() : null; + + _crmSecurity.DemandCreateOrUpdate(deal); + + _daoFactory.GetDealDao().EditDeal(deal); + + deal = _daoFactory.GetDealDao().GetByID(opportunityid); + + var membersList = members != null ? members.ToList() : new List(); + if (membersList.Any()) + { + var contacts = _daoFactory.GetContactDao().GetContacts(membersList.ToArray()).Where(_crmSecurity.CanAccessTo).ToList(); + membersList = contacts.Select(m => m.ID).ToList(); + + _daoFactory.GetDealDao().SetMembers(deal.ID, membersList.ToArray()); + } + + + if (_crmSecurity.IsAdmin || deal.CreateBy == _securityContext.CurrentAccount.ID) + { + SetAccessToDeal(deal, isPrivate, accessList, isNotify, false); + } + + if (customFieldList != null) + { + var existingCustomFieldList = _daoFactory.GetCustomFieldDao().GetFieldsDescription(EntityType.Opportunity).Select(fd => fd.ID).ToList(); + foreach (var field in customFieldList) + { + if (string.IsNullOrEmpty(field.Value) || !existingCustomFieldList.Contains(field.Key)) continue; + _daoFactory.GetCustomFieldDao().SetFieldValue(EntityType.Opportunity, deal.ID, field.Key, field.Value); + } + } + + return _mapper.Map(deal); + } + + /// + /// Returns the list of all contacts associated with the opportunity with the ID specified in the request + /// + /// Opportunity ID + /// Get all opportunity contacts + /// Opportunities + /// Contact list + /// + /// + [Read(@"opportunity/{opportunityid:int}/contact")] + public IEnumerable GetDealMembers(int opportunityid) + { + var opportunity = _daoFactory.GetDealDao().GetByID(opportunityid); + + if (opportunity == null || !_crmSecurity.CanAccessTo(opportunity)) throw new ItemNotFoundException(); + + var contactIDs = _daoFactory.GetDealDao().GetMembers(opportunityid); + + if (contactIDs == null) return new List(); + + var contacts = _daoFactory.GetContactDao().GetContacts(contactIDs); + + var result = _mapper.Map, List>(contacts); + + result.ForEach(item => { if (item.Id == opportunity.ContactID) item.CanEdit = false; }); + + return result; + } + + /// + /// Adds the selected contact to the opportunity with the ID specified in the request + /// + /// Opportunity ID + /// Contact ID + /// Add opportunity contact + /// Opportunities + /// + /// + /// Participant + /// + [Create(@"opportunity/{opportunityid:int}/contact/{contactid:int}")] + public ContactDto AddMemberToDeal([FromRoute] int opportunityid, [FromRoute] int contactid) + { + if (opportunityid <= 0 || contactid <= 0) throw new ArgumentException(); + + var opportunity = _daoFactory.GetDealDao().GetByID(opportunityid); + if (opportunity == null || !_crmSecurity.CanAccessTo(opportunity)) throw new ItemNotFoundException(); + + var contact = _daoFactory.GetContactDao().GetByID(contactid); + if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException(); + + var result = _mapper.Map(contact); + + _daoFactory.GetDealDao().AddMember(opportunityid, contactid); + + var messageAction = contact is Company ? MessageAction.OpportunityLinkedCompany : MessageAction.OpportunityLinkedPerson; + _messageService.Send(messageAction, _messageTarget.Create(opportunity.ID), opportunity.Title, contact.GetTitle()); + + return result; + } + + /// + /// Deletes the selected contact from the opportunity with the ID specified in the request + /// + /// Opportunity ID + /// Contact ID + /// Delete opportunity contact + /// Opportunities + /// + /// + /// + /// Participant + /// + [Delete(@"opportunity/{opportunityid:int}/contact/{contactid:int}")] + public ContactDto DeleteMemberFromDeal(int opportunityid, int contactid) + { + if ((opportunityid <= 0) || (contactid <= 0)) throw new ArgumentException(); + + var opportunity = _daoFactory.GetDealDao().GetByID(opportunityid); + if (opportunity == null || !_crmSecurity.CanAccessTo(opportunity)) throw new ItemNotFoundException(); + + var contact = _daoFactory.GetContactDao().GetByID(contactid); + if (contact == null || !_crmSecurity.CanAccessTo(contact)) throw new ItemNotFoundException(); + + var result = _mapper.Map(contact); + + _daoFactory.GetDealDao().RemoveMember(opportunityid, contactid); + + var messageAction = contact is Company ? MessageAction.OpportunityUnlinkedCompany : MessageAction.OpportunityUnlinkedPerson; + _messageService.Send(messageAction, _messageTarget.Create(opportunity.ID), opportunity.Title, contact.GetTitle()); + + return result; + } + + /// + /// Returns the list of 30 opportunities in the CRM module with prefix + /// + /// + /// + /// + /// Opportunities + /// + /// Opportunities list + /// + /// false + [Read(@"opportunity/byprefix")] + public IEnumerable GetDealsByPrefix(string prefix, int contactID, bool internalSearch = true) + { + var result = new List(); + + if (contactID > 0 && internalSearch) + { + var findedDeals = _daoFactory.GetDealDao().GetDealsByContactID(contactID); + foreach (var item in findedDeals) + { + if (item.Title.IndexOf(prefix, StringComparison.Ordinal) != -1) + { + result.Add(_mapper.Map(item)); + } + } + + _apiContext.SetTotalCount(result.Count); + } + else + { + const int maxItemCount = 30; + var findedDeals = _daoFactory.GetDealDao().GetDealsByPrefix(prefix, 0, maxItemCount, contactID, internalSearch); + foreach (var item in findedDeals) + { + result.Add(_mapper.Map(item)); + } + } + + return result; + } + + /// + /// Returns the list of all contact opportunities + /// + /// Contact ID + /// Get opportunity list + /// Opportunities + /// + /// Opportunity list + /// + [Read(@"opportunity/bycontact/{contactid:int}")] + public IEnumerable GetDeals(int contactid) + { + var deals = _daoFactory.GetDealDao().GetDealsByContactID(contactid); + return ToListOpportunityDto(deals); + } + + /// false + [Update(@"opportunity/{opportunityid:int}/creationdate")] + public void SetDealCreationDate(int opportunityid, ApiDateTime creationDate) + { + var dao = _daoFactory.GetDealDao(); + var opportunity = dao.GetByID(opportunityid); + + if (opportunity == null || !_crmSecurity.CanAccessTo(opportunity)) + throw new ItemNotFoundException(); + + dao.SetDealCreationDate(opportunityid, creationDate); + } + + /// false + [Update(@"opportunity/{opportunityid:int}/lastmodifeddate")] + public void SetDealLastModifedDate(int opportunityid, ApiDateTime lastModifedDate) + { + var dao = _daoFactory.GetDealDao(); + var opportunity = dao.GetByID(opportunityid); + + if (opportunity == null || !_crmSecurity.CanAccessTo(opportunity)) + throw new ItemNotFoundException(); + + dao.SetDealLastModifedDate(opportunityid, lastModifedDate); + } + + + private IEnumerable ToListOpportunityDto(ICollection deals) + { + if (deals == null || deals.Count == 0) return new List(); + + var result = new List(); + + var contactIDs = new List(); + var dealIDs = new List(); + var dealMilestoneIDs = new List(); + + foreach (var deal in deals) + { + contactIDs.Add(deal.ContactID); + dealIDs.Add(deal.ID); + dealMilestoneIDs.Add(deal.DealMilestoneID); + } + + dealMilestoneIDs = dealMilestoneIDs.Distinct().ToList(); + + var contacts = new Dictionary(); + + var customFields = _daoFactory.GetCustomFieldDao().GetEnityFields(EntityType.Opportunity, dealIDs.ToArray()) + .GroupBy(item => item.EntityID) + .ToDictionary(item => item.Key, item => item.Select(x => _mapper.Map(x))); + + var dealMilestones = _daoFactory.GetDealMilestoneDao().GetAll(dealMilestoneIDs.ToArray()) + .ToDictionary(item => item.ID, item => new DealMilestoneBaseDto(item)); + + + var dealMembers = _daoFactory.GetDealDao().GetMembers(dealIDs.ToArray()); + + foreach (var value in dealMembers.Values) + { + contactIDs.AddRange(value); + } + + contactIDs = contactIDs.Distinct().ToList(); + + if (contactIDs.Count > 0) + { + _daoFactory.GetContactDao().GetContacts(contactIDs.ToArray()).ForEach(item => + { + if (item == null) return; + contacts.Add(item.ID, _mapper.Map(item)); + }); + } + + foreach (var deal in deals) + { + var dealDto = _mapper.Map(deal); + + if (contacts.ContainsKey(deal.ContactID)) + { + dealDto.Contact = contacts[deal.ContactID]; + } + + dealDto.CustomFields = customFields.ContainsKey(deal.ID) + ? customFields[deal.ID] + : new List(); + + dealDto.Members = dealMembers.ContainsKey(dealDto.Id) + ? dealMembers[dealDto.Id].Where(contacts.ContainsKey).Select(item => contacts[item]) + : new List(); + + if (dealMilestones.ContainsKey(deal.DealMilestoneID)) + { + dealDto.Stage = dealMilestones[deal.DealMilestoneID]; + } + + dealDto.IsPrivate = _crmSecurity.IsPrivate(deal); + + if (dealDto.IsPrivate) + { + dealDto.AccessList = _crmSecurity.GetAccessSubjectTo(deal).Select(item => _employeeWraperHelper.Get(item.Key)); + } + + if (!string.IsNullOrEmpty(deal.BidCurrency)) + { + dealDto.BidCurrency = _mapper.Map(_currencyProvider.Get(deal.BidCurrency)); + } + + result.Add(dealDto); + } + + return result; + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Controllers/CRMController.cs b/products/ASC.CRM/Server/Api/EntryPointApiController.cs similarity index 65% rename from products/ASC.CRM/Server/Controllers/CRMController.cs rename to products/ASC.CRM/Server/Api/EntryPointApiController.cs index 48abde48265..364185e6a81 100644 --- a/products/ASC.CRM/Server/Controllers/CRMController.cs +++ b/products/ASC.CRM/Server/Api/EntryPointApiController.cs @@ -1,30 +1,32 @@ - -using ASC.Api.Core; -using ASC.Common; -using ASC.CRM.Configuration; -using ASC.Web.Api.Routing; - -using Microsoft.AspNetCore.Mvc; - -namespace ASC.CRM.Controllers -{ - [Scope] - [DefaultRoute] - [ApiController] - public class CRMController : ControllerBase - { - private ProductEntryPoint ProductEntryPoint { get; } - - public CRMController(ProductEntryPoint productEntryPoint) - { - ProductEntryPoint = productEntryPoint; - } - - [Read("info")] - public Module GetModule() - { - ProductEntryPoint.Init(); - return new Module(ProductEntryPoint); - } - } -} + +using ASC.Api.Core; +using ASC.Api.Core.Convention; +using ASC.Common; +using ASC.Web.Api.Routing; +using ASC.Web.CRM.Configuration; + +using Microsoft.AspNetCore.Mvc; + +namespace ASC.CRM.Api +{ + [Scope] + [DefaultRoute] + [ApiController] + [ControllerName("crm")] + public class EntryPointApiController : ControllerBase + { + private ProductEntryPoint ProductEntryPoint { get; } + + public EntryPointApiController(ProductEntryPoint productEntryPoint) + { + ProductEntryPoint = productEntryPoint; + } + + [Read("info")] + public Module GetModule() + { + ProductEntryPoint.Init(); + return new Module(ProductEntryPoint); + } + } +} diff --git a/products/ASC.CRM/Server/Api/InvoicesController.cs b/products/ASC.CRM/Server/Api/InvoicesController.cs new file mode 100644 index 00000000000..f1105ccd0d5 --- /dev/null +++ b/products/ASC.CRM/Server/Api/InvoicesController.cs @@ -0,0 +1,1611 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Api.Core; +using ASC.Api.CRM; +using ASC.Api.Documents; +using ASC.Common.Web; +using ASC.Core.Common.Settings; +using ASC.CRM.ApiModels; +using ASC.CRM.Classes; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; +using ASC.MessagingSystem; +using ASC.Web.Api.Routing; +using ASC.Web.CRM.Classes; + +using AutoMapper; + +using Microsoft.AspNetCore.Mvc; + +namespace ASC.CRM.Api +{ + public class InvoicesController : BaseApiController + { + + private readonly PdfQueueWorker _pdfQueueWorker; + private readonly Global _global; + private readonly FileWrapperHelper _fileWrapperHelper; + private readonly PdfCreator _pdfCreator; + private readonly SettingsManager _settingsManager; + private readonly ApiDateTimeHelper _apiDateTimeHelper; + private readonly ApiContext _apiContext; + private readonly MessageService _messageService; + private readonly MessageTarget _messageTarget; + private readonly CurrencyProvider _currencyProvider; + + public InvoicesController(CrmSecurity crmSecurity, + DaoFactory daoFactory, + ApiContext apiContext, + MessageTarget messageTarget, + MessageService messageService, + ApiDateTimeHelper apiDateTimeHelper, + SettingsManager settingsManager, + FileWrapperHelper fileWrapperHelper, + PdfCreator pdfCreator, + Global global, + PdfQueueWorker pdfQueueWorker, + CurrencyProvider currencyProvider, + IMapper mapper) + : base(daoFactory, crmSecurity, mapper) + { + _apiContext = apiContext; + _messageTarget = messageTarget; + _messageService = messageService; + _apiDateTimeHelper = apiDateTimeHelper; + _settingsManager = settingsManager; + _pdfCreator = pdfCreator; + _fileWrapperHelper = fileWrapperHelper; + _global = global; + _pdfQueueWorker = pdfQueueWorker; + _mapper = mapper; + _currencyProvider = currencyProvider; + } + + + /// + /// Returns the detailed information about the invoice with the ID specified in the request + /// + /// Invoice ID + /// Get invoice by ID + /// Invoices + /// Invoice + [Read(@"invoice/{invoiceid:int}")] + public InvoiceDto GetInvoiceByID(int invoiceid) + { + if (invoiceid <= 0) throw new ArgumentException(); + + var invoice = _daoFactory.GetInvoiceDao().GetByID(invoiceid); + if (invoice == null) throw new ItemNotFoundException(); + + if (!_crmSecurity.CanAccessTo(invoice)) + { + throw _crmSecurity.CreateSecurityException(); + } + + return _mapper.Map(invoice); + } + + /// + /// Returns the detailed information about the invoice sample + /// + /// Get invoice sample + /// Invoices + /// Invoice + [Read(@"invoice/sample")] + public InvoiceDto GetInvoiceSample() + { + var crmSettings = _settingsManager.Load(); + var defaultCurrency = _currencyProvider.Get(crmSettings.DefaultCurrency); + + var sample = InvoiceDto.GetSample(); + + sample.Number = _daoFactory.GetInvoiceDao().GetNewInvoicesNumber(); + sample.Terms = _daoFactory.GetInvoiceDao().GetSettings().Terms ?? string.Empty; + sample.IssueDate = _apiDateTimeHelper.Get(DateTime.UtcNow); + sample.DueDate = _apiDateTimeHelper.Get(DateTime.UtcNow.AddDays(30)); + sample.CreateOn = _apiDateTimeHelper.Get(DateTime.UtcNow); + + sample.Currency = _mapper.Map(defaultCurrency); + + sample.InvoiceLines.First().Quantity = 1; + + return sample; + } + + /// + /// Returns the json data of the invoice with the ID specified in the request + /// + /// Invoice ID + /// Get invoice json data + /// Invoices + /// Json Data + [Read(@"invoice/jsondata/{invoiceid:int}")] + public string GetInvoiceJsonData(int invoiceid) + { + var invoice = _daoFactory.GetInvoiceDao().GetByID(invoiceid); + if (invoice == null) throw new ItemNotFoundException(); + + if (!_crmSecurity.CanAccessTo(invoice)) + { + throw _crmSecurity.CreateSecurityException(); + } + + return invoice.JsonData; + } + + /// + /// Returns the list of invoices matching the creteria specified in the request + /// + /// Invoice status + /// Invoice issue date from + /// Invoice issue date to + /// Invoice due date from + /// Invoice due date to + /// Invoice entity type + /// Invoice entity ID + /// Invoice currency + /// Get invoice list + /// Invoices + /// Invoice list + [Read(@"invoice/filter")] + public IEnumerable GetInvoices( + InvoiceStatus? status, + ApiDateTime issueDateFrom, + ApiDateTime issueDateTo, + ApiDateTime dueDateFrom, + ApiDateTime dueDateTo, + String entityType, + int entityid, + String currency + ) + { + if (!String.IsNullOrEmpty(entityType) && !( + String.Compare(entityType, "contact", true) == 0 || + String.Compare(entityType, "opportunity", true) == 0 || + String.Compare(entityType, "case", true) == 0)) + throw new ArgumentException(); + + IEnumerable result; + + InvoiceSortedByType sortBy; + + OrderBy invoiceOrderBy; + + var searchString = _apiContext.FilterValue; + + if (InvoiceSortedByType.TryParse(_apiContext.SortBy, true, out sortBy)) + { + invoiceOrderBy = new OrderBy(sortBy, !_apiContext.SortDescending); + } + else if (String.IsNullOrEmpty(_apiContext.SortBy)) + { + invoiceOrderBy = new OrderBy(InvoiceSortedByType.Number, true); + } + else + { + invoiceOrderBy = null; + } + + var fromIndex = (int)_apiContext.StartIndex; + var count = (int)_apiContext.Count; + + if (invoiceOrderBy != null) + { + result = ToListInvoiceBaseDtos( + _daoFactory.GetInvoiceDao().GetInvoices( + searchString, + status, + issueDateFrom, issueDateTo, + dueDateFrom, dueDateTo, + ToEntityType(entityType), entityid, + currency, + fromIndex, count, + invoiceOrderBy)); + + _apiContext.SetDataPaginated(); + _apiContext.SetDataFiltered(); + _apiContext.SetDataSorted(); + } + else + { + result = ToListInvoiceBaseDtos( + _daoFactory.GetInvoiceDao().GetInvoices( + searchString, + status, + issueDateFrom, issueDateTo, + dueDateFrom, dueDateTo, + ToEntityType(entityType), entityid, + currency, + 0, + 0, + null)); + } + + int totalCount; + + if (result.Count() < count) + { + totalCount = fromIndex + result.Count(); + } + else + { + totalCount = _daoFactory.GetInvoiceDao().GetInvoicesCount( + searchString, + status, + issueDateFrom, issueDateTo, + dueDateFrom, dueDateTo, + ToEntityType(entityType), entityid, + currency); + } + + _apiContext.SetTotalCount(totalCount); + + return result; + } + + /// + /// Returns the list of all invoices associated with the entity with the ID and type specified in the request + /// + /// Invoice entity type + /// Invoice entity ID + /// Get entity invoices + /// Invoices + /// Invoice list + [Read(@"{entityType:regex(contact|person|company|opportunity)}/invoicelist/{entityid:int}")] + public IEnumerable GetEntityInvoices(String entityType, int entityid) + { + if (String.IsNullOrEmpty(entityType) || entityid <= 0) throw new ArgumentException(); + + return ToListInvoiceBaseDtos(_daoFactory.GetInvoiceDao().GetEntityInvoices(ToEntityType(entityType), entityid)); + } + + /// + /// Updates the status of invoices with the IDs specified in the request + /// + /// Invoice ID list + /// Status + /// Update invoice group status + /// Invoices + /// KeyValuePair of Invoices and InvoiceItems + [Update(@"invoice/status/{status:int}")] + public KeyValuePair, IEnumerable> UpdateInvoiceBatchStatus( + int[] invoiceids, + InvoiceStatus status + ) + { + if (invoiceids == null || !invoiceids.Any()) throw new ArgumentException(); + + var oldInvoices = _daoFactory.GetInvoiceDao().GetByID(invoiceids).Where(_crmSecurity.CanAccessTo).ToList(); + + var updatedInvoices = _daoFactory.GetInvoiceDao().UpdateInvoiceBatchStatus(oldInvoices.ToList().Select(i => i.ID).ToArray(), status); + + // detect what really changed + var realUpdatedInvoices = updatedInvoices + .Select(t => oldInvoices.FirstOrDefault(x => x.ID == t.ID && x.Status != t.Status)) + .Where(inv => inv != null) + .ToList(); + + if (realUpdatedInvoices.Any()) + { + _messageService.Send(MessageAction.InvoicesUpdatedStatus, _messageTarget.Create(realUpdatedInvoices.Select(x => x.ID)), realUpdatedInvoices.Select(x => x.Number), status.ToLocalizedString()); + } + + var invoiceItemsUpdated = new List(); + + if (status == InvoiceStatus.Sent || status == InvoiceStatus.Rejected) + { + var invoiceItemsAll = _daoFactory.GetInvoiceItemDao().GetAll(); + var invoiceItemsWithTrackInventory = invoiceItemsAll.Where(item => item.TrackInventory).ToList(); + + if (status == InvoiceStatus.Sent && invoiceItemsWithTrackInventory != null && invoiceItemsWithTrackInventory.Count != 0) + { + foreach (var inv in updatedInvoices) + { + if (inv.Status == InvoiceStatus.Sent) + { + //could be changed + var oldInv = oldInvoices.FirstOrDefault(i => i.ID == inv.ID); + if (oldInv != null && oldInv.Status == InvoiceStatus.Draft) + { + //was changed to Sent + var invoiceLines = _daoFactory.GetInvoiceLineDao().GetInvoiceLines(inv.ID); + + foreach (var line in invoiceLines) + { + var item = invoiceItemsWithTrackInventory.FirstOrDefault(ii => ii.ID == line.InvoiceItemID); + if (item != null) + { + item.StockQuantity -= line.Quantity; + _daoFactory.GetInvoiceItemDao().SaveOrUpdateInvoiceItem(item); + var oldItem = invoiceItemsUpdated.Find(i => i.ID == item.ID); + if (oldItem != null) + { + invoiceItemsUpdated.Remove(oldItem); + } + invoiceItemsUpdated.Add(item); + } + } + } + } + } + } + + if (status == InvoiceStatus.Rejected && invoiceItemsWithTrackInventory != null && invoiceItemsWithTrackInventory.Count != 0) + { + foreach (var inv in updatedInvoices) + { + if (inv.Status == InvoiceStatus.Rejected) + { + //could be changed + var oldInv = oldInvoices.FirstOrDefault(i => i.ID == inv.ID); + if (oldInv != null && oldInv.Status == InvoiceStatus.Sent) + { + //was changed from Sent to Rejectes + var invoiceLines = _daoFactory.GetInvoiceLineDao().GetInvoiceLines(inv.ID); + + foreach (var line in invoiceLines) + { + var item = invoiceItemsWithTrackInventory.FirstOrDefault(ii => ii.ID == line.InvoiceItemID); + if (item != null) + { + item.StockQuantity += line.Quantity; + + _daoFactory.GetInvoiceItemDao().SaveOrUpdateInvoiceItem(item); + + var oldItem = invoiceItemsUpdated.Find(i => i.ID == item.ID); + + if (oldItem != null) + { + invoiceItemsUpdated.Remove(oldItem); + } + + invoiceItemsUpdated.Add(item); + } + } + } + } + } + } + } + + var listInvoiceBaseDtos = ToListInvoiceBaseDtos(updatedInvoices); + + return new KeyValuePair, IEnumerable>( + listInvoiceBaseDtos, + _mapper.Map, List>(invoiceItemsUpdated)); + } + + /// + /// Delete the invoice with the ID specified in the request + /// + /// Invoice ID + /// Delete invoice + /// Invoices + /// Invoice + [Delete(@"invoice/{invoiceid:int}")] + public InvoiceBaseDto DeleteInvoice(int invoiceid) + { + if (invoiceid <= 0) throw new ArgumentException(); + + var invoice = _daoFactory.GetInvoiceDao().DeleteInvoice(invoiceid); + + if (invoice == null) throw new ItemNotFoundException(); + + _messageService.Send(MessageAction.InvoiceDeleted, _messageTarget.Create(invoice.ID), invoice.Number); + + return _mapper.Map(invoice); + } + + /// + /// Deletes the group of invoices with the IDs specified in the request + /// + /// Invoice ID list + /// Delete invoice group + /// Invoices + /// Invoice list + [Delete(@"invoice")] + public IEnumerable DeleteBatchInvoices(IEnumerable invoiceids) + { + if (invoiceids == null || !invoiceids.Any()) throw new ArgumentException(); + + var invoices = _daoFactory.GetInvoiceDao().DeleteBatchInvoices(invoiceids.ToArray()); + _messageService.Send(MessageAction.InvoicesDeleted, _messageTarget.Create(invoices.Select(x => x.ID)), invoices.Select(x => x.Number)); + + return ToListInvoiceBaseDtos(invoices); + } + + /// + /// Creates the invoice with the parameters (contactId, consigneeId, etc.) specified in the request + /// + /// Invoice number + /// Invoice issue date + /// Invoice template type + /// Invoice contact ID + /// Invoice consignee ID + /// Invoice entity ID + /// Invoice billing address ID + /// Invoice delivery address ID + /// Invoice due date + /// Invoice language + /// Invoice currency + /// Invoice exchange rate + /// Invoice purchase order number + /// Invoice terms + /// Invoice description + /// Invoice lines list + /// Create invoice + /// Invoices + /// Invoice + /// + /// + /// + [Create(@"invoice")] + public InvoiceDto CreateInvoice( + CreateOrUpdateInvoiceRequestDto inDto + ) + { + string number = inDto.Number; + + ApiDateTime issueDate = inDto.IssueDate; + int templateType = inDto.TemplateType; + int contactId = inDto.ContactId; + int consigneeId = inDto.ConsigneeId; + int entityId = inDto.EntityId; + int billingAddressID = inDto.BillingAddressID; + int deliveryAddressID = inDto.DeliveryAddressID; + ApiDateTime dueDate = inDto.DueDate; + string language = inDto.Language; + string currency = inDto.Currency; + decimal exchangeRate = inDto.ExchangeRate; + string purchaseOrderNumber = inDto.PurchaseOrderNumber; + string terms = inDto.Terms; + string description = inDto.Description; + IEnumerable invoiceLines = inDto.InvoiceLines; + + var invoiceLinesList = invoiceLines != null ? invoiceLines.ToList() : new List(); + if (!invoiceLinesList.Any() || !IsLinesForInvoiceCorrect(invoiceLinesList)) throw new ArgumentException(); + + var invoice = new Invoice + { + Status = InvoiceStatus.Draft, + Number = number, + IssueDate = issueDate, + TemplateType = (InvoiceTemplateType)templateType, + ContactID = contactId, + ConsigneeID = consigneeId, + EntityType = EntityType.Opportunity, + EntityID = entityId, + DueDate = dueDate, + Language = language, + Currency = !String.IsNullOrEmpty(currency) ? currency.ToUpper() : null, + ExchangeRate = exchangeRate, + PurchaseOrderNumber = purchaseOrderNumber, + Terms = terms, + Description = description + }; + + _crmSecurity.DemandCreateOrUpdate(invoice); + + if (billingAddressID > 0) + { + var address = _daoFactory.GetContactInfoDao().GetByID(billingAddressID); + if (address == null || address.InfoType != ContactInfoType.Address || address.Category != (int)AddressCategory.Billing || address.ContactID != contactId) + throw new ArgumentException(); + } + + if (deliveryAddressID > 0) + { + var address = _daoFactory.GetContactInfoDao().GetByID(deliveryAddressID); + if (address == null || address.InfoType != ContactInfoType.Address || address.Category != (int)AddressCategory.Postal || address.ContactID != consigneeId) + throw new ArgumentException(); + } + + + invoice.ID = _daoFactory.GetInvoiceDao().SaveOrUpdateInvoice(invoice); + + CreateInvoiceLines(invoiceLinesList, invoice); + + _daoFactory.GetInvoiceDao().UpdateInvoiceJsonData(invoice, billingAddressID, deliveryAddressID); + + return _mapper.Map(invoice); + } + + + private bool IsLinesForInvoiceCorrect(List invoiceLines) + { + foreach (var line in invoiceLines) + { + if (line.InvoiceItemID <= 0 || + line.Quantity < 0 || line.Price < 0 || + line.Discount < 0 || line.Discount > 100 || + line.InvoiceTax1ID < 0 || line.InvoiceTax2ID < 0) + return false; + if (!_daoFactory.GetInvoiceItemDao().IsExist(line.InvoiceItemID)) + return false; + + if (line.InvoiceTax1ID > 0 && !_daoFactory.GetInvoiceTaxDao().IsExist(line.InvoiceTax1ID)) + return false; + + if (line.InvoiceTax2ID > 0 && !_daoFactory.GetInvoiceTaxDao().IsExist(line.InvoiceTax2ID)) + return false; + } + return true; + } + + private List CreateInvoiceLines(List invoiceLines, Invoice invoice) + { + var result = new List(); + for (var i = 0; i < invoiceLines.Count; i++) + { + var line = new InvoiceLine + { + ID = 0, + InvoiceID = invoice.ID, + InvoiceItemID = invoiceLines[i].InvoiceItemID, + InvoiceTax1ID = invoiceLines[i].InvoiceTax1ID, + InvoiceTax2ID = invoiceLines[i].InvoiceTax2ID, + SortOrder = i, + Description = invoiceLines[i].Description, + Quantity = invoiceLines[i].Quantity, + Price = invoiceLines[i].Price, + Discount = Convert.ToInt32(invoiceLines[i].Discount) + }; + + line.ID = _daoFactory.GetInvoiceLineDao().SaveOrUpdateInvoiceLine(line); + result.Add(line); + } + return result; + } + + /// + /// Updates the selected invoice with the parameters (contactId, consigneeId, etc.) specified in the request + /// + /// Invoice ID + /// Invoice issue date + /// Invoice template type + /// Invoice contact ID + /// Invoice consignee ID + /// Invoice entity ID + /// Invoice billing address ID + /// Invoice delivery address ID + /// Invoice due date + /// Invoice language + /// Invoice currency + /// Invoice exchange rate + /// Invoice purchase order number + /// Invoice terms + /// Invoice description + /// Invoice lines list + /// Update invoice + /// Invoices + /// Invoice + /// + /// + /// + [Update(@"invoice/{id:int}")] + public InvoiceDto UpdateInvoice( + int id, + CreateOrUpdateInvoiceRequestDto inDto) + + { + ApiDateTime issueDate = inDto.IssueDate; + int templateType = inDto.TemplateType; + int contactId = inDto.ContactId; + int consigneeId = inDto.ConsigneeId; + int entityId = inDto.EntityId; + int billingAddressID = inDto.BillingAddressID; + int deliveryAddressID = inDto.DeliveryAddressID; + ApiDateTime dueDate = inDto.DueDate; + string language = inDto.Language; + string currency = inDto.Currency; + decimal exchangeRate = inDto.ExchangeRate; + string purchaseOrderNumber = inDto.PurchaseOrderNumber; + string terms = inDto.Terms; + string description = inDto.Description; + IEnumerable invoiceLines = inDto.InvoiceLines; + + var invoiceLinesList = invoiceLines != null ? invoiceLines.ToList() : new List(); + if (!invoiceLinesList.Any() || !IsLinesForInvoiceCorrect(invoiceLinesList)) throw new ArgumentException(); + + var invoice = _daoFactory.GetInvoiceDao().GetByID(id); + if (invoice == null || !_crmSecurity.CanEdit(invoice)) throw new ItemNotFoundException(); + + invoice.IssueDate = issueDate; + invoice.TemplateType = (InvoiceTemplateType)templateType; + invoice.ContactID = contactId; + invoice.ConsigneeID = consigneeId; + invoice.EntityType = EntityType.Opportunity; + invoice.EntityID = entityId; + invoice.DueDate = dueDate; + invoice.Language = language; + invoice.Currency = !String.IsNullOrEmpty(currency) ? currency.ToUpper() : null; ; + invoice.ExchangeRate = exchangeRate; + invoice.PurchaseOrderNumber = purchaseOrderNumber; + invoice.Terms = terms; + invoice.Description = description; + invoice.JsonData = null; + + _crmSecurity.DemandCreateOrUpdate(invoice); + + if (billingAddressID > 0) + { + var address = _daoFactory.GetContactInfoDao().GetByID(billingAddressID); + if (address == null || address.InfoType != ContactInfoType.Address || address.Category != (int)AddressCategory.Billing || address.ContactID != contactId) + throw new ArgumentException(); + } + + if (deliveryAddressID > 0) + { + var address = _daoFactory.GetContactInfoDao().GetByID(deliveryAddressID); + if (address == null || address.InfoType != ContactInfoType.Address || address.Category != (int)AddressCategory.Postal || address.ContactID != consigneeId) + throw new ArgumentException(); + } + + _daoFactory.GetInvoiceDao().SaveOrUpdateInvoice(invoice); + + + _daoFactory.GetInvoiceLineDao().DeleteInvoiceLines(invoice.ID); + CreateInvoiceLines(invoiceLinesList, invoice); + + _daoFactory.GetInvoiceDao().UpdateInvoiceJsonData(invoice, billingAddressID, deliveryAddressID); + + if (_global.CanDownloadInvoices) + { + _pdfQueueWorker.StartTask(invoice.ID); + } + + return _mapper.Map(invoice); + } + + /// + /// Returns the pdf file associated with the invoice with the ID specified in the request + /// + /// Invoice ID + /// Get invoice pdf file + /// Invoices + /// File + [Read(@"invoice/{invoiceid:int}/pdf")] + public FileWrapper GetInvoicePdfExistOrCreate(int invoiceid) + { + if (invoiceid <= 0) throw new ArgumentException(); + + var invoice = _daoFactory.GetInvoiceDao().GetByID(invoiceid); + if (invoice == null) throw new ItemNotFoundException(); + + if (!_crmSecurity.CanAccessTo(invoice)) + { + throw _crmSecurity.CreateSecurityException(); + } + + return _fileWrapperHelper.Get(GetInvoicePdfExistingOrCreate(invoice)); + } + + private ASC.Files.Core.File GetInvoicePdfExistingOrCreate(ASC.CRM.Core.Entities.Invoice invoice) + { + var existingFile = invoice.GetInvoiceFile(_daoFactory); + + if (existingFile != null) + { + return existingFile; + } + else + { + var newFile = _pdfCreator.CreateFile(invoice, _daoFactory); + + invoice.FileID = Int32.Parse(newFile.ID.ToString()); + + _daoFactory.GetInvoiceDao().UpdateInvoiceFileID(invoice.ID, invoice.FileID); + + _daoFactory.GetRelationshipEventDao().AttachFiles(invoice.ContactID, invoice.EntityType, invoice.EntityID, new[] { invoice.FileID }); + + return newFile; + } + } + + + /// + /// Returns information about the generation of the pdf file of the invoice + /// + /// Invoice ID + /// Storage Url + /// Revision ID + /// Check invoice pdf file + /// Invoices + /// ConverterData + [Create(@"invoice/converter/data")] + public ConverterData GetInvoiceConverterData( + [FromForm] int invoiceId, + [FromForm] string storageUrl, + [FromForm] string revisionId) + { + if (invoiceId <= 0) throw new ArgumentException(); + + var invoice = _daoFactory.GetInvoiceDao().GetByID(invoiceId); + if (invoice == null) throw new ItemNotFoundException(); + + if (!_crmSecurity.CanAccessTo(invoice)) + { + throw _crmSecurity.CreateSecurityException(); + } + + var converterData = new ConverterData + { + StorageUrl = storageUrl, + RevisionId = revisionId, + InvoiceId = invoiceId + }; + + var existingFile = invoice.GetInvoiceFile(_daoFactory); + if (existingFile != null) + { + converterData.FileId = invoice.FileID; + return converterData; + } + + if (string.IsNullOrEmpty(storageUrl) || string.IsNullOrEmpty(revisionId)) + { + return _pdfCreator.StartCreationFileAsync(invoice); + } + else + { + var convertedFile = _pdfCreator.GetConvertedFile(converterData, _daoFactory); + if (convertedFile != null) + { + invoice.FileID = Int32.Parse(convertedFile.ID.ToString()); + _daoFactory.GetInvoiceDao().UpdateInvoiceFileID(invoice.ID, invoice.FileID); + _daoFactory.GetRelationshipEventDao().AttachFiles(invoice.ContactID, invoice.EntityType, invoice.EntityID, new[] { invoice.FileID }); + + converterData.FileId = invoice.FileID; + return converterData; + } + else + { + return converterData; + } + } + } + + /// + /// Returns the existence of the invoice with the Number specified in the request + /// + /// Invoice number + /// Check invoice existence by number + /// Invoices + /// IsExist + [Read(@"invoice/bynumber/exist")] + public Boolean GetInvoiceByNumberExistence(string number) + { + if (String.IsNullOrEmpty(number)) throw new ArgumentException(); + + return _daoFactory.GetInvoiceDao().IsExist(number); + + } + + /// + /// Returns the detailed information about the invoice with the Number specified in the request + /// + /// Invoice number + /// Get invoice by number + /// Invoices + /// Invoice + [Read(@"invoice/bynumber")] + public InvoiceDto GetInvoiceByNumber(string number) + { + if (String.IsNullOrEmpty(number)) throw new ArgumentException(); + + var invoice = _daoFactory.GetInvoiceDao().GetByNumber(number); + if (invoice == null) throw new ItemNotFoundException(); + + if (!_crmSecurity.CanAccessTo(invoice)) + { + throw _crmSecurity.CreateSecurityException(); + } + + return _mapper.Map(invoice); + } + + /// + /// Returns the list of invoice items matching the creteria specified in the request + /// + /// Status + /// InventoryStock + /// Get invoice item list + /// Invoices + /// InvoiceItem list + [Read(@"invoiceitem/filter")] + public IEnumerable GetInvoiceItems(int status, bool? inventoryStock) + { + IEnumerable result; + + InvoiceItemSortedByType sortBy; + + OrderBy invoiceOrderBy; + + var searchString = _apiContext.FilterValue; + + if (InvoiceItemSortedByType.TryParse(_apiContext.SortBy, true, out sortBy)) + { + invoiceOrderBy = new OrderBy(sortBy, !_apiContext.SortDescending); + } + else if (String.IsNullOrEmpty(_apiContext.SortBy)) + { + invoiceOrderBy = new OrderBy(InvoiceItemSortedByType.Name, true); + } + else + { + invoiceOrderBy = null; + } + + var fromIndex = (int)_apiContext.StartIndex; + var count = (int)_apiContext.Count; + + if (invoiceOrderBy != null) + { + var resultFromDao = _daoFactory.GetInvoiceItemDao().GetInvoiceItems( + searchString, + status, + inventoryStock, + fromIndex, count, + invoiceOrderBy); + + result = _mapper.Map, List>(resultFromDao); + + _apiContext.SetDataPaginated(); + _apiContext.SetDataFiltered(); + _apiContext.SetDataSorted(); + } + else + { + var resultFromDao = _daoFactory.GetInvoiceItemDao().GetInvoiceItems( + searchString, + status, + inventoryStock, + 0, 0, + null); + + result = _mapper.Map, List>(resultFromDao); + + } + + int totalCount; + + if (result.Count() < count) + { + totalCount = fromIndex + result.Count(); + } + else + { + totalCount = _daoFactory.GetInvoiceItemDao().GetInvoiceItemsCount( + searchString, + status, + inventoryStock); + } + + _apiContext.SetTotalCount(totalCount); + + return result; + } + + /// + /// Returns the detailed information about the invoice item with the ID specified in the request + /// + /// Invoice Item ID + /// Get invoice item by ID + /// Invoices + /// Invoice Item + [Read(@"invoiceitem/{invoiceitemid:int}")] + public InvoiceItemDto GetInvoiceItemByID(int invoiceitemid) + { + if (invoiceitemid <= 0) throw new ArgumentException(); + + var invoiceItem = _daoFactory.GetInvoiceItemDao().GetByID(invoiceitemid); + if (invoiceItem == null) throw new ItemNotFoundException(); + + return _mapper.Map(invoiceItem); + } + + /// + /// Creates the invoice line with the parameters (invoiceId, invoiceItemId, etc.) specified in the request + /// + /// Invoice ID + /// Invoice item ID + /// First invoice tax ID + /// Second invoice tax ID + /// Sort Order + /// Description + /// Quantity + /// Price + /// Discount + /// Create invoice line + /// Invoices + /// InvoiceLine + [Create(@"invoiceline")] + public InvoiceLineDto CreateInvoiceLine( + CreateOrUpdateInvoiceLineRequestDto inDto + ) + { + int invoiceId = inDto.InvoiceId; + int invoiceItemId = inDto.InvoiceItemId; + int invoiceTax1Id = inDto.InvoiceTax1Id; + int invoiceTax2Id = inDto.InvoiceTax2Id; + int sortOrder = inDto.SortOrder; + string description = inDto.Description; + int quantity = inDto.Quantity; + decimal price = inDto.Price; + int discount = inDto.Discount; + + var invoiceLine = new InvoiceLine + { + InvoiceID = invoiceId, + InvoiceItemID = invoiceItemId, + InvoiceTax1ID = invoiceTax1Id, + InvoiceTax2ID = invoiceTax2Id, + SortOrder = sortOrder, + Description = description, + Quantity = quantity, + Price = price, + Discount = discount + }; + + if (invoiceId <= 0) + throw new ArgumentException(); + + var invoice = _daoFactory.GetInvoiceDao().GetByID(invoiceId); + + _crmSecurity.DemandCreateOrUpdate(invoiceLine, invoice); + + invoiceLine.ID = _daoFactory.GetInvoiceLineDao().SaveOrUpdateInvoiceLine(invoiceLine); + + _daoFactory.GetInvoiceDao().UpdateInvoiceJsonDataAfterLinesUpdated(invoice); + + if (_global.CanDownloadInvoices) + { + _pdfQueueWorker.StartTask(invoice.ID); + } + + return _mapper.Map(invoiceLine); + } + + /// + /// Updates the selected invoice line with the parameters (invoiceId, invoiceItemId, etc.) specified in the request + /// + /// Line ID + /// Invoice ID + /// Invoice item ID + /// First invoice tax ID + /// Second invoice tax ID + /// Sort Order + /// Description + /// Quantity + /// Price + /// Discount + /// Update invoice line + /// Invoices + /// InvoiceLine + [Update(@"invoiceline/{id:int}")] + public InvoiceLineDto UpdateInvoiceLine(int id, CreateOrUpdateInvoiceLineRequestDto inDto) + { + int invoiceId = inDto.InvoiceId; + int invoiceItemId = inDto.InvoiceItemId; + int invoiceTax1Id = inDto.InvoiceTax1Id; + int invoiceTax2Id = inDto.InvoiceTax2Id; + int sortOrder = inDto.SortOrder; + string description = inDto.Description; + int quantity = inDto.Quantity; + decimal price = inDto.Price; + int discount = inDto.Discount; + + if (invoiceId <= 0) + throw new ArgumentException(); + + var invoiceLine = _daoFactory.GetInvoiceLineDao().GetByID(id); + + if (invoiceLine == null || invoiceLine.InvoiceID != invoiceId) throw new ItemNotFoundException(); + + + invoiceLine.InvoiceID = invoiceId; + invoiceLine.InvoiceItemID = invoiceItemId; + invoiceLine.InvoiceTax1ID = invoiceTax1Id; + invoiceLine.InvoiceTax2ID = invoiceTax2Id; + invoiceLine.SortOrder = sortOrder; + invoiceLine.Description = description; + invoiceLine.Quantity = quantity; + invoiceLine.Price = price; + invoiceLine.Discount = discount; + + var invoice = _daoFactory.GetInvoiceDao().GetByID(invoiceId); + _crmSecurity.DemandCreateOrUpdate(invoiceLine, invoice); + + _daoFactory.GetInvoiceLineDao().SaveOrUpdateInvoiceLine(invoiceLine); + + _daoFactory.GetInvoiceDao().UpdateInvoiceJsonDataAfterLinesUpdated(invoice); + + if (_global.CanDownloadInvoices) + { + _pdfQueueWorker.StartTask(invoice.ID); + } + + return _mapper.Map(invoiceLine); + } + + /// + /// Deletes the invoice line with the ID specified in the request + /// + /// Line ID + /// Delete invoice line + /// Invoices + /// Line ID + [Delete(@"invoiceline/{id:int}")] + public int DeleteInvoiceLine(int id) + { + var invoiceLine = _daoFactory.GetInvoiceLineDao().GetByID(id); + if (invoiceLine == null) throw new ItemNotFoundException(); + if (!_daoFactory.GetInvoiceLineDao().CanDelete(invoiceLine.ID)) throw new Exception("Can't delete invoice line"); + + var invoice = _daoFactory.GetInvoiceDao().GetByID(invoiceLine.InvoiceID); + if (invoice == null) throw new ItemNotFoundException(); + if (!_crmSecurity.CanEdit(invoice)) throw _crmSecurity.CreateSecurityException(); + + _daoFactory.GetInvoiceLineDao().DeleteInvoiceLine(id); + + _daoFactory.GetInvoiceDao().UpdateInvoiceJsonDataAfterLinesUpdated(invoice); + + if (_global.CanDownloadInvoices) + { + _pdfQueueWorker.StartTask(invoice.ID); + } + + return id; + } + + /// + /// Creates the invoice item with the parameters (title, description, price, etc.) specified in the request + /// + /// Item title + /// Item description + /// Item price + /// Item stock keeping unit + /// Item quantity + /// Item stock quantity + /// Track inventory + /// Item first invoice tax ID + /// Item second invoice tax ID + /// Create invoice item + /// Invoices + /// InvoiceItem + [Create(@"invoiceitem")] + public InvoiceItemDto CreateInvoiceItem( + CreateOrUpdateInvoiceItemRequestDto inDto + ) + { + string title = inDto.Title; + string description = inDto.Description; + decimal price = inDto.Price; + string sku = inDto.Sku; + int quantity = inDto.Quantity; + int stockQuantity = inDto.StockQuantity; + bool trackInventory = inDto.TrackInventory; + int invoiceTax1id = inDto.InvoiceTax1id; + int invoiceTax2id = inDto.InvoiceTax2id; + + if (!_crmSecurity.IsAdmin) + { + throw _crmSecurity.CreateSecurityException(); + } + + if (String.IsNullOrEmpty(title) || price <= 0) throw new ArgumentException(); + + var invoiceItem = new InvoiceItem + { + Title = title, + Description = description, + Price = price, + StockKeepingUnit = sku, + StockQuantity = stockQuantity, + TrackInventory = trackInventory, + InvoiceTax1ID = invoiceTax1id, + InvoiceTax2ID = invoiceTax2id + }; + + invoiceItem = _daoFactory.GetInvoiceItemDao().SaveOrUpdateInvoiceItem(invoiceItem); + + _messageService.Send(MessageAction.InvoiceItemCreated, _messageTarget.Create(invoiceItem.ID), invoiceItem.Title); + + return _mapper.Map(invoiceItem); + } + + /// + /// Updates the selected invoice item with the parameters (title, description, price, etc.) specified in the request + /// + /// Item ID + /// Item title + /// Item description + /// Item price + /// Item stock keeping unit + /// Item quantity + /// Item stock quantity + /// Track inventory + /// Item first invoice tax ID + /// Item second invoice tax ID + /// Update invoice item + /// Invoices + /// InvoiceItem + [Update(@"invoiceitem/{id:int}")] + public InvoiceItemDto UpdateInvoiceItem(int id, + CreateOrUpdateInvoiceItemRequestDto inDto + ) + { + string title = inDto.Title; + string description = inDto.Description; + decimal price = inDto.Price; + string sku = inDto.Sku; + int quantity = inDto.Quantity; + int stockQuantity = inDto.StockQuantity; + bool trackInventory = inDto.TrackInventory; + int invoiceTax1id = inDto.InvoiceTax1id; + int invoiceTax2id = inDto.InvoiceTax2id; + + if (!_crmSecurity.IsAdmin) + { + throw _crmSecurity.CreateSecurityException(); + } + + if (id <= 0 || String.IsNullOrEmpty(title) || price <= 0) throw new ArgumentException(); + + if (!_daoFactory.GetInvoiceItemDao().IsExist(id)) throw new ItemNotFoundException(); + + var invoiceItem = new InvoiceItem + { + ID = id, + Title = title, + Description = description, + Price = price, + StockKeepingUnit = sku, + StockQuantity = stockQuantity, + TrackInventory = trackInventory, + InvoiceTax1ID = invoiceTax1id, + InvoiceTax2ID = invoiceTax2id + }; + + invoiceItem = _daoFactory.GetInvoiceItemDao().SaveOrUpdateInvoiceItem(invoiceItem); + _messageService.Send(MessageAction.InvoiceItemUpdated, _messageTarget.Create(invoiceItem.ID), invoiceItem.Title); + + return _mapper.Map(invoiceItem); + } + + /// + /// Deletes the invoice item with the ID specified in the request + /// + /// Item ID + /// Delete invoice item + /// Invoices + /// InvoiceItem + [Delete(@"invoiceitem/{id:int}")] + public InvoiceItemDto DeleteInvoiceItem(int id) + { + if (!_crmSecurity.IsAdmin) + { + throw _crmSecurity.CreateSecurityException(); + } + + if (id <= 0) throw new ArgumentException(); + + var invoiceItem = _daoFactory.GetInvoiceItemDao().DeleteInvoiceItem(id); + if (invoiceItem == null) throw new ItemNotFoundException(); + + _messageService.Send(MessageAction.InvoiceItemDeleted, _messageTarget.Create(invoiceItem.ID), invoiceItem.Title); + + return _mapper.Map(invoiceItem); + + } + + /// + /// Deletes the group of invoice items with the IDs specified in the request + /// + /// Item ID list + /// Delete Invoice item group + /// Invoices + /// InvoiceItem list + [Delete(@"invoiceitem")] + public IEnumerable DeleteBatchItems(IEnumerable ids) + { + if (!_crmSecurity.IsAdmin) + { + throw _crmSecurity.CreateSecurityException(); + } + + if (ids == null) throw new ArgumentException(); + ids = ids.Distinct(); + + var items = _daoFactory.GetInvoiceItemDao().DeleteBatchInvoiceItems(ids.ToArray()); + + _messageService.Send(MessageAction.InvoiceItemsDeleted, _messageTarget.Create(ids), items.Select(x => x.Title)); + + return _mapper.Map, List>(items); + } + + /// + /// Returns the list of invoice taxes + /// + /// Get invoice taxes list + /// Invoices + /// InvoiceTax list + [Read(@"invoice/tax")] + public IEnumerable GetInvoiceTaxes() + { + var responceFromDao = _daoFactory.GetInvoiceTaxDao().GetAll(); + + return _mapper.Map, List>(responceFromDao); + } + + /// + /// Creates the invoice tax with the parameters (name, description, rate) specified in the request + /// + /// Tax name + /// Tax description + /// Tax rate + /// Create invoice tax + /// Invoices + /// InvoiceTax + [Create(@"invoice/tax")] + public InvoiceTaxDto CreateInvoiceTax( + [FromBody] CreateOrUpdateInvoiceTaxRequestDto inDto) + { + string name = inDto.Name; + string description = inDto.Description; + decimal rate = inDto.Rate; + + if (!_crmSecurity.IsAdmin) + { + throw _crmSecurity.CreateSecurityException(); + } + + if (String.IsNullOrEmpty(name)) throw new ArgumentException(CRMInvoiceResource.EmptyTaxNameError); + if (_daoFactory.GetInvoiceTaxDao().IsExist(name)) throw new ArgumentException(CRMInvoiceResource.ExistTaxNameError); + + var invoiceTax = new InvoiceTax + { + Name = name, + Description = description, + Rate = rate + }; + + invoiceTax = _daoFactory.GetInvoiceTaxDao().SaveOrUpdateInvoiceTax(invoiceTax); + _messageService.Send(MessageAction.InvoiceTaxCreated, _messageTarget.Create(invoiceTax.ID), invoiceTax.Name); + + return _mapper.Map(invoiceTax); + } + + /// + /// Updates the selected invoice tax with the parameters (name, description, rate) specified in the request + /// + /// Tax ID + /// Tax name + /// Tax description + /// Tax rate + /// Update invoice tax + /// Invoices + /// InvoiceTax + [Update(@"invoice/tax/{id:int}")] + public InvoiceTaxDto UpdateInvoiceTax( + int id, + CreateOrUpdateInvoiceTaxRequestDto inDto) + { + string name = inDto.Name; + string description = inDto.Description; + decimal rate = inDto.Rate; + + if (!_crmSecurity.IsAdmin) + { + throw _crmSecurity.CreateSecurityException(); + } + + if (id <= 0 || String.IsNullOrEmpty(name)) throw new ArgumentException(CRMInvoiceResource.EmptyTaxNameError); + + if (!_daoFactory.GetInvoiceTaxDao().IsExist(id)) throw new ItemNotFoundException(); + + var invoiceTax = new InvoiceTax + { + ID = id, + Name = name, + Description = description, + Rate = rate + }; + + invoiceTax = _daoFactory.GetInvoiceTaxDao().SaveOrUpdateInvoiceTax(invoiceTax); + _messageService.Send(MessageAction.InvoiceTaxUpdated, _messageTarget.Create(invoiceTax.ID), invoiceTax.Name); + + return _mapper.Map(invoiceTax); + } + + /// + /// Delete the invoice tax with the ID specified in the request + /// + /// Tax ID + /// Delete invoice tax + /// Invoices + /// InvoiceTax + [Delete(@"invoice/tax/{id:int}")] + public InvoiceTaxDto DeleteInvoiceTax(int id) + { + if (!_crmSecurity.IsAdmin) + { + throw _crmSecurity.CreateSecurityException(); + } + + if (id <= 0) throw new ArgumentException(); + + var invoiceTax = _daoFactory.GetInvoiceTaxDao().DeleteInvoiceTax(id); + if (invoiceTax == null) throw new ItemNotFoundException(); + + _messageService.Send(MessageAction.InvoiceTaxDeleted, _messageTarget.Create(invoiceTax.ID), invoiceTax.Name); + + return _mapper.Map(invoiceTax); + } + + /// + /// Get default invoice settings + /// + /// Get default invoice settings + /// Invoices + /// InvoiceSetting + [Read(@"invoice/settings")] + public InvoiceSetting GetSettings() + { + return _daoFactory.GetInvoiceDao().GetSettings(); + } + + /// + /// Save default invoice number + /// + /// Is autogenerated + /// Prefix + /// Number + /// Save default invoice number + /// Invoices + /// InvoiceSetting + [Update(@"invoice/settings/name")] + public InvoiceSetting SaveNumberSettings( + SaveNumberSettingsRequestDto inDto + ) + { + var autogenerated = inDto.AutoGenerated; + var number = inDto.Number; + var prefix = inDto.Prefix; + + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + if (autogenerated && string.IsNullOrEmpty(number)) + throw new ArgumentException(); + + if (autogenerated && _daoFactory.GetInvoiceDao().IsExist(prefix + number)) + throw new ArgumentException(); + + var invoiceSetting = GetSettings(); + + invoiceSetting.Autogenerated = autogenerated; + invoiceSetting.Prefix = prefix; + invoiceSetting.Number = number; + + var settings = _daoFactory.GetInvoiceDao().SaveInvoiceSettings(invoiceSetting); + _messageService.Send(MessageAction.InvoiceNumberFormatUpdated); + + return settings; + } + + /// + /// Save default invoice terms + /// + /// Terms + /// Save default invoice terms + /// Invoices + /// InvoiceSetting + [Update(@"invoice/settings/terms")] + public InvoiceSetting SaveTermsSettings(string terms) + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + var invoiceSetting = GetSettings(); + + invoiceSetting.Terms = terms; + + var result = _daoFactory.GetInvoiceDao().SaveInvoiceSettings(invoiceSetting); + _messageService.Send(MessageAction.InvoiceDefaultTermsUpdated); + + return result; + } + + /// false + [Update(@"invoice/{invoiceid:int}/creationdate")] + public void SetInvoiceCreationDate(int invoiceid, ApiDateTime creationDate) + { + var dao = _daoFactory.GetInvoiceDao(); + var invoice = dao.GetByID(invoiceid); + + if (invoice == null || !_crmSecurity.CanAccessTo(invoice)) + throw new ItemNotFoundException(); + + dao.SetInvoiceCreationDate(invoiceid, creationDate); + } + + /// false + [Update(@"invoice/{invoiceid:int}/lastmodifeddate")] + public void SetInvoiceLastModifedDate(int invoiceid, ApiDateTime lastModifedDate) + { + var dao = _daoFactory.GetInvoiceDao(); + var invoice = dao.GetByID(invoiceid); + + if (invoice == null || !_crmSecurity.CanAccessTo(invoice)) + { + throw new ItemNotFoundException(); + } + + dao.SetInvoiceLastModifedDate(invoiceid, lastModifedDate); + } + + private IEnumerable ToListInvoiceBaseDtos(ICollection items) + { + if (items == null || items.Count == 0) return new List(); + + var result = new List(); + + var contactIDs = items.Select(item => item.ContactID); + + contactIDs.ToList().AddRange(items.Select(item => item.ConsigneeID)); + + var contacts = _daoFactory.GetContactDao().GetContacts(contactIDs.Distinct().ToArray()) + .ToDictionary(item => item.ID, x => _mapper.Map(x)); + + foreach (var invoice in items) + { + var invoiceDto = _mapper.Map(invoice); + + if (contacts.ContainsKey(invoice.ContactID)) + { + invoiceDto.Contact = contacts[invoice.ContactID]; + } + + if (contacts.ContainsKey(invoice.ConsigneeID)) + { + invoiceDto.Consignee = contacts[invoice.ContactID]; + } + + if (invoice.EntityID > 0) + { + invoiceDto.Entity = ToEntityDto(invoice.EntityType, invoice.EntityID); //Need to optimize + } + + invoiceDto.Cost = invoice.GetInvoiceCost(_daoFactory); + + result.Add(invoiceDto); + } + + return result; + } + + private EntityDto ToEntityDto(EntityType entityType, int entityID) + { + if (entityID == 0) return null; + + var result = new EntityDto + { + EntityId = entityID + }; + + switch (entityType) + { + case EntityType.Case: + var caseObj = _daoFactory.GetCasesDao().GetByID(entityID); + if (caseObj == null) + return null; + + result.EntityType = "case"; + result.EntityTitle = caseObj.Title; + + break; + case EntityType.Opportunity: + var dealObj = _daoFactory.GetDealDao().GetByID(entityID); + + if (dealObj == null) + return null; + + result.EntityType = "opportunity"; + result.EntityTitle = dealObj.Title; + + break; + default: + return null; + } + + return result; + } + + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Api/ListItemsController.cs b/products/ASC.CRM/Server/Api/ListItemsController.cs new file mode 100644 index 00000000000..9cf985deee7 --- /dev/null +++ b/products/ASC.CRM/Server/Api/ListItemsController.cs @@ -0,0 +1,1099 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Api.CRM; +using ASC.Common.Web; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.MessagingSystem; +using ASC.Web.Api.Routing; + +using AutoMapper; + +using Microsoft.AspNetCore.Mvc; + +namespace ASC.CRM.Api +{ + public class ListItemsController : BaseApiController + { + private readonly MessageService _messageService; + private readonly MessageTarget _messageTarget; + + public ListItemsController(CrmSecurity crmSecurity, + DaoFactory daoFactory, + MessageTarget messageTarget, + MessageService messageService, + IMapper mapper) + : base(daoFactory, crmSecurity, mapper) + { + _messageTarget = messageTarget; + _messageService = messageService; + } + + + /// + /// Creates an opportunity stage with the parameters (title, description, success probability, etc.) specified in the request + /// + /// Title + /// Description + /// Color + /// Success probability + /// Stage type + /// Create opportunity stage + /// Opportunities + /// + /// + /// Opportunity stage + /// + [Create(@"opportunity/stage")] + public DealMilestoneDto CreateDealMilestone( + [FromForm] string title, + [FromForm] string description, + [FromForm] string color, + [FromForm] int successProbability, + [FromForm] DealMilestoneStatus stageType) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (string.IsNullOrEmpty(title)) throw new ArgumentException(); + + if (successProbability < 0) successProbability = 0; + + var dealMilestone = new DealMilestone + { + Title = title, + Color = color, + Description = description, + Probability = successProbability, + Status = stageType + }; + + dealMilestone.ID = _daoFactory.GetDealMilestoneDao().Create(dealMilestone); + _messageService.Send(MessageAction.OpportunityStageCreated, _messageTarget.Create(dealMilestone.ID), dealMilestone.Title); + + return ToDealMilestoneDto(dealMilestone); + } + + /// + /// Updates the selected opportunity stage with the parameters (title, description, success probability, etc.) specified in the request + /// + /// Opportunity stage ID + /// Title + /// Description + /// Color + /// Success probability + /// Stage type + /// Update opportunity stage + /// Opportunities + /// + /// + /// + /// Opportunity stage + /// + [Update(@"opportunity/stage/{id:int}")] + public DealMilestoneDto UpdateDealMilestone( + int id, + string title, + string description, + string color, + int successProbability, + DealMilestoneStatus stageType) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (id <= 0 || string.IsNullOrEmpty(title)) throw new ArgumentException(); + + if (successProbability < 0) successProbability = 0; + + var curDealMilestoneExist = _daoFactory.GetDealMilestoneDao().IsExist(id); + if (!curDealMilestoneExist) throw new ItemNotFoundException(); + + var dealMilestone = new DealMilestone + { + Title = title, + Color = color, + Description = description, + Probability = successProbability, + Status = stageType, + ID = id + }; + + _daoFactory.GetDealMilestoneDao().Edit(dealMilestone); + _messageService.Send(MessageAction.OpportunityStageUpdated, _messageTarget.Create(dealMilestone.ID), dealMilestone.Title); + + return ToDealMilestoneDto(dealMilestone); + } + + /// + /// Updates the selected opportunity stage with the color specified in the request + /// + /// Opportunity stage ID + /// Color + /// Update opportunity stage color + /// Opportunities + /// + /// + /// + /// Opportunity stage + /// + [Update(@"opportunity/stage/{id:int}/color")] + public DealMilestoneDto UpdateDealMilestoneColor(int id, string color) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (id <= 0) throw new ArgumentException(); + + var dealMilestone = _daoFactory.GetDealMilestoneDao().GetByID(id); + if (dealMilestone == null) throw new ItemNotFoundException(); + + dealMilestone.Color = color; + + _daoFactory.GetDealMilestoneDao().ChangeColor(id, color); + _messageService.Send(MessageAction.OpportunityStageUpdatedColor, _messageTarget.Create(dealMilestone.ID), dealMilestone.Title); + + return ToDealMilestoneDto(dealMilestone); + } + + /// + /// Updates the available opportunity stages order with the list specified in the request + /// + /// + /// Update opportunity stages order + /// + /// Opportunity stage ID list + /// Opportunities + /// + /// Opportunity stages + /// + /// + /// + /// + [Update(@"opportunity/stage/reorder")] + public IEnumerable UpdateDealMilestonesOrder(IEnumerable ids) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (ids == null) throw new ArgumentException(); + + var idsList = ids.ToList(); + + var result = idsList.Select(id => _daoFactory.GetDealMilestoneDao().GetByID(id)).ToList(); + + _daoFactory.GetDealMilestoneDao().Reorder(idsList.ToArray()); + _messageService.Send(MessageAction.OpportunityStagesUpdatedOrder, _messageTarget.Create(idsList), result.Select(x => x.Title)); + + return result.Select(ToDealMilestoneDto); + } + + /// + /// Deletes the opportunity stage with the ID specified in the request + /// + /// Delete opportunity stage + /// Opportunities + /// Opportunity stage ID + /// + /// + /// + /// Opportunity stage + /// + [Delete(@"opportunity/stage/{id:int}")] + public DealMilestoneDto DeleteDealMilestone(int id) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (id <= 0) throw new ArgumentException(); + + var dealMilestone = _daoFactory.GetDealMilestoneDao().GetByID(id); + if (dealMilestone == null) throw new ItemNotFoundException(); + + var result = ToDealMilestoneDto(dealMilestone); + + _daoFactory.GetDealMilestoneDao().Delete(id); + _messageService.Send(MessageAction.OpportunityStageDeleted, _messageTarget.Create(dealMilestone.ID), dealMilestone.Title); + + return result; + } + + /// + /// Creates a new history category with the parameters (title, description, etc.) specified in the request + /// + ///Title + ///Description + ///Order + ///Image name + ///Create history category + /// History + ///History category + /// + [Create(@"history/category")] + public HistoryCategoryDto CreateHistoryCategory( + [FromForm] string title, + [FromForm] string description, + [FromForm] string imageName, + [FromForm] int sortOrder) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (string.IsNullOrEmpty(title)) throw new ArgumentException(); + + var listItem = new ListItem + { + Title = title, + Description = description, + SortOrder = sortOrder, + AdditionalParams = imageName + }; + + listItem.ID = _daoFactory.GetListItemDao().CreateItem(ListType.HistoryCategory, listItem); + _messageService.Send(MessageAction.HistoryEventCategoryCreated, _messageTarget.Create(listItem.ID), listItem.Title); + + return _mapper.Map(listItem); + } + + /// + /// Updates the selected history category with the parameters (title, description, etc.) specified in the request + /// + ///History category ID + ///Title + ///Description + ///Order + ///Image name + ///Update history category + ///History + ///History category + /// + /// + [Update(@"history/category/{id:int}")] + public HistoryCategoryDto UpdateHistoryCategory(int id, string title, string description, string imageName, int sortOrder) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (id <= 0 || string.IsNullOrEmpty(title)) throw new ArgumentException(); + + var curHistoryCategoryExist = _daoFactory.GetListItemDao().IsExist(id); + if (!curHistoryCategoryExist) throw new ItemNotFoundException(); + + var listItem = new ListItem + { + Title = title, + Description = description, + SortOrder = sortOrder, + AdditionalParams = imageName, + ID = id + }; + + _daoFactory.GetListItemDao().EditItem(ListType.HistoryCategory, listItem); + _messageService.Send(MessageAction.HistoryEventCategoryUpdated, _messageTarget.Create(listItem.ID), listItem.Title); + + return _mapper.Map(listItem); + } + + /// + /// Updates the icon of the selected history category + /// + /// History category ID + /// icon name + /// Update history category icon + /// History + /// + /// + /// + /// History category + /// + [Update(@"history/category/{id:int}/icon")] + public HistoryCategoryDto UpdateHistoryCategoryIcon(int id, string imageName) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (id <= 0) throw new ArgumentException(); + + var historyCategory = _daoFactory.GetListItemDao().GetByID(id); + if (historyCategory == null) throw new ItemNotFoundException(); + + historyCategory.AdditionalParams = imageName; + + _daoFactory.GetListItemDao().ChangePicture(id, imageName); + _messageService.Send(MessageAction.HistoryEventCategoryUpdatedIcon, _messageTarget.Create(historyCategory.ID), historyCategory.Title); + + return _mapper.Map(historyCategory); + } + + /// + /// Updates the history categories order with the list specified in the request + /// + /// + /// Update history categories order + /// + /// History category title list + /// History + /// + /// History categories + /// + /// + /// + /// + [Update(@"history/category/reorder")] + public IEnumerable UpdateHistoryCategoriesOrder(IEnumerable titles) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (titles == null) throw new ArgumentException(); + + var result = titles.Select(title => _daoFactory.GetListItemDao().GetByTitle(ListType.HistoryCategory, title)).ToList(); + + _daoFactory.GetListItemDao().ReorderItems(ListType.HistoryCategory, titles.ToArray()); + _messageService.Send(MessageAction.HistoryEventCategoriesUpdatedOrder, _messageTarget.Create(result.Select(x => x.ID)), result.Select(x => x.Title)); + + return _mapper.Map, List>(result); + } + + /// + /// Deletes the selected history category with the ID specified in the request + /// + /// Delete history category + /// History + /// History category ID + /// + /// + /// + /// History category + [Delete(@"history/category/{id:int}")] + public HistoryCategoryDto DeleteHistoryCategory(int id) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (id <= 0) throw new ArgumentException(); + + var dao = _daoFactory.GetListItemDao(); + var listItem = dao.GetByID(id); + if (listItem == null) throw new ItemNotFoundException(); + + if (dao.GetItemsCount(ListType.HistoryCategory) < 2) + { + throw new ArgumentException("The last history category cannot be deleted"); + } + + var result = _mapper.Map(listItem); + + dao.DeleteItem(ListType.HistoryCategory, id, 0); + + _messageService.Send(MessageAction.HistoryEventCategoryDeleted, _messageTarget.Create(listItem.ID), listItem.Title); + + return result; + } + + /// + /// Creates a new task category with the parameters (title, description, etc.) specified in the request + /// + ///Title + ///Description + ///Order + ///Image name + ///Create task category + ///Tasks + ///Task category + /// + /// + /// Task category + /// + [Create(@"task/category")] + public TaskCategoryDto CreateTaskCategory( + [FromForm] string title, + [FromForm] string description, + [FromForm] string imageName, + [FromForm] int sortOrder) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + var listItem = new ListItem + { + Title = title, + Description = description, + SortOrder = sortOrder, + AdditionalParams = imageName + }; + + listItem.ID = _daoFactory.GetListItemDao().CreateItem(ListType.TaskCategory, listItem); + _messageService.Send(MessageAction.CrmTaskCategoryCreated, _messageTarget.Create(listItem.ID), listItem.Title); + + return _mapper.Map(listItem); + } + + /// + /// Updates the selected task category with the parameters (title, description, etc.) specified in the request + /// + ///Task category ID + ///Title + ///Description + ///Order + ///Image name + ///Update task category + ///Tasks + ///Task category + /// + /// + /// + /// + /// Task category + /// + [Update(@"task/category/{id:int}")] + public TaskCategoryDto UpdateTaskCategory(int id, string title, string description, string imageName, int sortOrder) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (id <= 0 || string.IsNullOrEmpty(title)) throw new ArgumentException(); + + var curTaskCategoryExist = _daoFactory.GetListItemDao().IsExist(id); + if (!curTaskCategoryExist) throw new ItemNotFoundException(); + + var listItem = new ListItem + { + Title = title, + Description = description, + SortOrder = sortOrder, + AdditionalParams = imageName, + ID = id + }; + + _daoFactory.GetListItemDao().EditItem(ListType.TaskCategory, listItem); + _messageService.Send(MessageAction.CrmTaskCategoryUpdated, _messageTarget.Create(listItem.ID), listItem.Title); + + return _mapper.Map(listItem); + } + + /// + /// Updates the icon of the task category with the ID specified in the request + /// + /// Task category ID + /// icon name + /// Update task category icon + /// Tasks + /// + /// + /// + /// Task category + /// + [Update(@"task/category/{id:int}/icon")] + public TaskCategoryDto UpdateTaskCategoryIcon(int id, string imageName) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (id <= 0) throw new ArgumentException(); + + var taskCategory = _daoFactory.GetListItemDao().GetByID(id); + if (taskCategory == null) throw new ItemNotFoundException(); + + taskCategory.AdditionalParams = imageName; + + _daoFactory.GetListItemDao().ChangePicture(id, imageName); + _messageService.Send(MessageAction.CrmTaskCategoryUpdatedIcon, _messageTarget.Create(taskCategory.ID), taskCategory.Title); + + return _mapper.Map(taskCategory); + } + + /// + /// Updates the task categories order with the list specified in the request + /// + /// + /// Update task categories order + /// + /// Task category title list + /// Tasks + /// + /// Task categories + /// + /// + /// + /// + [Update(@"task/category/reorder")] + public IEnumerable UpdateTaskCategoriesOrder(IEnumerable titles) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (titles == null) throw new ArgumentException(); + + var result = titles.Select(title => _daoFactory.GetListItemDao().GetByTitle(ListType.TaskCategory, title)).ToList(); + + _daoFactory.GetListItemDao().ReorderItems(ListType.TaskCategory, titles.ToArray()); + + _messageService.Send(MessageAction.CrmTaskCategoriesUpdatedOrder, _messageTarget.Create(result.Select(x => x.ID)), result.Select(x => x.Title)); + + return _mapper.Map, List>(result); + } + + /// + /// Deletes the task category with the ID specified in the request + /// + /// Delete task category + /// Tasks + /// Task category ID + /// Task category ID for replace in task with current category stage + /// + /// + /// + [Delete(@"task/category/{categoryid:int}")] + public TaskCategoryDto DeleteTaskCategory(int categoryid, int newcategoryid) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (categoryid <= 0 || newcategoryid < 0) throw new ArgumentException(); + + var dao = _daoFactory.GetListItemDao(); + var listItem = dao.GetByID(categoryid); + if (listItem == null) throw new ItemNotFoundException(); + + if (dao.GetItemsCount(ListType.TaskCategory) < 2) + { + throw new ArgumentException("The last task category cannot be deleted"); + } + + dao.DeleteItem(ListType.TaskCategory, categoryid, newcategoryid); + _messageService.Send(MessageAction.CrmTaskCategoryDeleted, _messageTarget.Create(listItem.ID), listItem.Title); + + return _mapper.Map(listItem); + } + + /// + /// Creates a new contact status with the parameters (title, description, etc.) specified in the request + /// + ///Title + ///Description + ///Color + ///Order + ///Contact status + /// Create contact status + /// Contacts + /// + /// + /// Contact status + /// + [Create(@"contact/status")] + public ContactStatusDto CreateContactStatus( + [FromForm] string title, + [FromForm] string description, + [FromForm] string color, + [FromForm] int sortOrder) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + var listItem = new ListItem + { + Title = title, + Description = description, + Color = color, + SortOrder = sortOrder + }; + + listItem.ID = _daoFactory.GetListItemDao().CreateItem(ListType.ContactStatus, listItem); + _messageService.Send(MessageAction.ContactTemperatureLevelCreated, _messageTarget.Create(listItem.ID), listItem.Title); + + return _mapper.Map(listItem); + } + + /// + /// Updates the selected contact status with the parameters (title, description, etc.) specified in the request + /// + ///Contact status ID + ///Title + ///Description + ///Color + ///Order + ///Contact status + /// Update contact status + /// Contacts + /// + /// + /// + /// + /// Contact status + /// + [Update(@"contact/status/{id:int}")] + public ContactStatusDto UpdateContactStatus(int id, string title, string description, string color, int sortOrder) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (id <= 0 || string.IsNullOrEmpty(title)) throw new ArgumentException(); + + var curListItemExist = _daoFactory.GetListItemDao().IsExist(id); + if (!curListItemExist) throw new ItemNotFoundException(); + + var listItem = new ListItem + { + ID = id, + Title = title, + Description = description, + Color = color, + SortOrder = sortOrder + }; + + _daoFactory.GetListItemDao().EditItem(ListType.ContactStatus, listItem); + _messageService.Send(MessageAction.ContactTemperatureLevelUpdated, _messageTarget.Create(listItem.ID), listItem.Title); + + return _mapper.Map(listItem); + } + + /// + /// Updates the color of the selected contact status with the new color specified in the request + /// + /// Contact status ID + /// Color + /// Update contact status color + /// Contacts + /// + /// + /// + /// Contact status + /// + [Update(@"contact/status/{id:int}/color")] + public ContactStatusDto UpdateContactStatusColor(int id, string color) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (id <= 0) throw new ArgumentException(); + + var contactStatus = _daoFactory.GetListItemDao().GetByID(id); + if (contactStatus == null) throw new ItemNotFoundException(); + + contactStatus.Color = color; + + _daoFactory.GetListItemDao().ChangeColor(id, color); + _messageService.Send(MessageAction.ContactTemperatureLevelUpdatedColor, _messageTarget.Create(contactStatus.ID), contactStatus.Title); + + return _mapper.Map(contactStatus); + } + + /// + /// Updates the contact statuses order with the list specified in the request + /// + /// + /// Update contact statuses order + /// + /// Contact status title list + /// Contacts + /// + /// Contact statuses + /// + /// + /// + /// + [Update(@"contact/status/reorder")] + public IEnumerable UpdateContactStatusesOrder(IEnumerable titles) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (titles == null) throw new ArgumentException(); + + var result = titles.Select(title => _daoFactory.GetListItemDao().GetByTitle(ListType.ContactStatus, title)).ToList(); + + _daoFactory.GetListItemDao().ReorderItems(ListType.ContactStatus, titles.ToArray()); + _messageService.Send(MessageAction.ContactTemperatureLevelsUpdatedOrder, _messageTarget.Create(result.Select(x => x.ID)), result.Select(x => x.Title)); + + return _mapper.Map, List>(result); + } + + /// + /// Deletes the contact status with the ID specified in the request + /// + /// Delete contact status + /// Contacts + /// Contact status ID + /// + /// + /// + /// + /// Contact status + /// + [Delete(@"contact/status/{contactStatusid:int}")] + public ContactStatusDto DeleteContactStatus(int contactStatusid) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (contactStatusid <= 0) throw new ArgumentException(); + + var dao = _daoFactory.GetListItemDao(); + var listItem = dao.GetByID(contactStatusid); + if (listItem == null) throw new ItemNotFoundException(); + + if (dao.GetItemsCount(ListType.ContactStatus) < 2) + { + throw new ArgumentException("The last contact status cannot be deleted"); + } + + var contactStatus = _mapper.Map(listItem); + + dao.DeleteItem(ListType.ContactStatus, contactStatusid, 0); + _messageService.Send(MessageAction.ContactTemperatureLevelDeleted, _messageTarget.Create(contactStatus.Id), contactStatus.Title); + + return contactStatus; + } + + /// + /// Returns the status of the contact for the ID specified in the request + /// + /// Contact status ID + /// Contact status + /// Get contact status + /// Contacts + /// + /// + [Read(@"contact/status/{contactStatusid:int}")] + public ContactStatusDto GetContactStatusByID(int contactStatusid) + { + if (contactStatusid <= 0) throw new ArgumentException(); + + var listItem = _daoFactory.GetListItemDao().GetByID(contactStatusid); + if (listItem == null) throw new ItemNotFoundException(); + + return _mapper.Map(listItem); + } + + /// + /// Creates a new contact type with the parameters (title, etc.) specified in the request + /// + ///Title + ///Order + ///Contact type + /// Create contact type + /// Contacts + /// + /// + /// Contact type + /// + [Create(@"contact/type")] + public ContactTypeDto CreateContactType([FromForm] string title, [FromForm] int sortOrder) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + var listItem = new ListItem + { + Title = title, + Description = string.Empty, + SortOrder = sortOrder + }; + + listItem.ID = _daoFactory.GetListItemDao().CreateItem(ListType.ContactType, listItem); + _messageService.Send(MessageAction.ContactTypeCreated, _messageTarget.Create(listItem.ID), listItem.Title); + + return _mapper.Map(listItem); + } + + /// + /// Updates the selected contact type with the parameters (title, description, etc.) specified in the request + /// + ///Contact type ID + ///Title + ///Order + ///Contact type + /// Update contact type + /// Contacts + /// + /// + /// + /// + /// Contact type + /// + [Update(@"contact/type/{id:int}")] + public ContactTypeDto UpdateContactType(int id, string title, int sortOrder) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (id <= 0 || string.IsNullOrEmpty(title)) throw new ArgumentException(); + + var curListItemExist = _daoFactory.GetListItemDao().IsExist(id); + if (!curListItemExist) throw new ItemNotFoundException(); + + var listItem = new ListItem + { + ID = id, + Title = title, + SortOrder = sortOrder + }; + + _daoFactory.GetListItemDao().EditItem(ListType.ContactType, listItem); + _messageService.Send(MessageAction.ContactTypeUpdated, _messageTarget.Create(listItem.ID), listItem.Title); + + return _mapper.Map(listItem); + } + + /// + /// Updates the contact types order with the list specified in the request + /// + /// + /// Update contact types order + /// + /// Contact type title list + /// Contacts + /// + /// Contact types + /// + /// + /// + /// + [Update(@"contact/type/reorder")] + public IEnumerable UpdateContactTypesOrder(IEnumerable titles) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (titles == null) throw new ArgumentException(); + + var result = titles.Select(title => _daoFactory.GetListItemDao().GetByTitle(ListType.ContactType, title)).ToList(); + + _daoFactory.GetListItemDao().ReorderItems(ListType.ContactType, titles.ToArray()); + _messageService.Send(MessageAction.ContactTypesUpdatedOrder, _messageTarget.Create(result.Select(x => x.ID)), result.Select(x => x.Title)); + + return _mapper.Map, List>(result); + } + + /// + /// Deletes the contact type with the ID specified in the request + /// + /// Delete contact type + /// Contacts + /// Contact type ID + /// + /// + /// + /// + /// Contact type + /// + [Delete(@"contact/type/{contactTypeid:int}")] + public ContactTypeDto DeleteContactType(int contactTypeid) + { + if (!(_crmSecurity.IsAdmin)) throw _crmSecurity.CreateSecurityException(); + + if (contactTypeid <= 0) throw new ArgumentException(); + var dao = _daoFactory.GetListItemDao(); + + var listItem = dao.GetByID(contactTypeid); + if (listItem == null) throw new ItemNotFoundException(); + + if (dao.GetItemsCount(ListType.ContactType) < 2) + { + throw new ArgumentException("The last contact type cannot be deleted"); + } + + var contactType = _mapper.Map(listItem); + + dao.DeleteItem(ListType.ContactType, contactTypeid, 0); + _messageService.Send(MessageAction.ContactTypeDeleted, _messageTarget.Create(listItem.ID), listItem.Title); + + return contactType; + } + + /// + /// Returns the type of the contact for the ID specified in the request + /// + /// Contact type ID + /// Contact type + /// Get contact type + /// Contacts + /// + /// + [Read(@"contact/type/{contactTypeid:int}")] + public ContactTypeDto GetContactTypeByID(int contactTypeid) + { + if (contactTypeid <= 0) throw new ArgumentException(); + + var listItem = _daoFactory.GetListItemDao().GetByID(contactTypeid); + if (listItem == null) throw new ItemNotFoundException(); + + return _mapper.Map(listItem); + } + + /// + /// Returns the stage of the opportunity with the ID specified in the request + /// + /// Opportunity stage ID + /// Opportunity stage + /// Get opportunity stage + /// Opportunities + /// + /// + [Read(@"opportunity/stage/{stageid:int}")] + public DealMilestoneDto GetDealMilestoneByID(int stageid) + { + if (stageid <= 0) throw new ArgumentException(); + + var dealMilestone = _daoFactory.GetDealMilestoneDao().GetByID(stageid); + if (dealMilestone == null) throw new ItemNotFoundException(); + + return ToDealMilestoneDto(dealMilestone); + } + + /// + /// Returns the category of the task with the ID specified in the request + /// + /// Task category ID + /// Task category + /// Get task category + /// Tasks + /// + /// + [Read(@"task/category/{categoryid:int}")] + public TaskCategoryDto GetTaskCategoryByID(int categoryid) + { + if (categoryid <= 0) throw new ArgumentException(); + + var listItem = _daoFactory.GetListItemDao().GetByID(categoryid); + if (listItem == null) throw new ItemNotFoundException(); + + return _mapper.Map(listItem); + } + + /// + /// Returns the list of all history categories available on the portal + /// + /// Get all history categories + /// History + /// + /// List of all history categories + /// + [Read(@"history/category")] + public IEnumerable GetHistoryCategoryDto() + { + var result = _daoFactory.GetListItemDao().GetItems(ListType.HistoryCategory).ConvertAll(item => new HistoryCategoryDto(item)); + + var relativeItemsCount = _daoFactory.GetListItemDao().GetRelativeItemsCount(ListType.HistoryCategory); + + result.ForEach(x => + { + if (relativeItemsCount.ContainsKey(x.Id)) + x.RelativeItemsCount = relativeItemsCount[x.Id]; + }); + return result; + } + + /// + /// Returns the list of all task categories available on the portal + /// + /// Get all task categories + /// Tasks + /// + /// List of all task categories + /// + [Read(@"task/category")] + public IEnumerable GetTaskCategories() + { + var result = _daoFactory.GetListItemDao().GetItems(ListType.TaskCategory).ConvertAll(item => (TaskCategoryDto)_mapper.Map(item)); + + var relativeItemsCount = _daoFactory.GetListItemDao().GetRelativeItemsCount(ListType.TaskCategory); + + result.ForEach(x => + { + if (relativeItemsCount.ContainsKey(x.Id)) + x.RelativeItemsCount = relativeItemsCount[x.Id]; + }); + return result; + } + + /// + /// Returns the list of all contact statuses available on the portal + /// + /// Get all contact statuses + /// Contacts + /// + /// List of all contact statuses + /// + [Read(@"contact/status")] + public IEnumerable GetContactStatuses() + { + var result = _daoFactory.GetListItemDao().GetItems(ListType.ContactStatus).ConvertAll(item => new ContactStatusDto(item)); + + var relativeItemsCount = _daoFactory.GetListItemDao().GetRelativeItemsCount(ListType.ContactStatus); + + result.ForEach(x => + { + if (relativeItemsCount.ContainsKey(x.Id)) + x.RelativeItemsCount = relativeItemsCount[x.Id]; + }); + return result; + } + + /// + /// Returns the list of all contact types available on the portal + /// + /// Get all contact types + /// Contacts + /// + /// List of all contact types + /// + [Read(@"contact/type")] + public IEnumerable GetContactTypes() + { + var result = _daoFactory.GetListItemDao().GetItems(ListType.ContactType).ConvertAll(item => new ContactTypeDto(item)); + + var relativeItemsCount = _daoFactory.GetListItemDao().GetRelativeItemsCount(ListType.ContactType); + + result.ForEach(x => + { + if (relativeItemsCount.ContainsKey(x.Id)) + x.RelativeItemsCount = relativeItemsCount[x.Id]; + }); + + return result; + } + + /// + /// Returns the list of all opportunity stages available on the portal + /// + /// Get all opportunity stages + /// Opportunities + /// + /// List of all opportunity stages + /// + [Read(@"opportunity/stage")] + public IEnumerable GetDealMilestones() + { + var result = _daoFactory.GetDealMilestoneDao().GetAll().ConvertAll(item => new DealMilestoneDto(item)); + + var relativeItemsCount = _daoFactory.GetDealMilestoneDao().GetRelativeItemsCount(); + + result.ForEach(x => + { + if (relativeItemsCount.ContainsKey(x.Id)) + x.RelativeItemsCount = relativeItemsCount[x.Id]; + }); + + return result; + } + + private DealMilestoneDto ToDealMilestoneDto(DealMilestone dealMilestone) + { + var result = new DealMilestoneDto(dealMilestone) + { + RelativeItemsCount = _daoFactory.GetDealMilestoneDao().GetRelativeItemsCount(dealMilestone.ID) + }; + return result; + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Api/RelationshipEventsController.cs b/products/ASC.CRM/Server/Api/RelationshipEventsController.cs new file mode 100644 index 00000000000..a90e35e22fa --- /dev/null +++ b/products/ASC.CRM/Server/Api/RelationshipEventsController.cs @@ -0,0 +1,842 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Mime; +using System.Text; +using System.Text.RegularExpressions; + +using ASC.Api.Core; +using ASC.Api.CRM; +using ASC.Api.Documents; +using ASC.Common.Web; +using ASC.Core; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.MessagingSystem; +using ASC.Web.Api.Routing; +using ASC.Web.CRM.Services.NotifyService; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Utils; + +using AutoMapper; + +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +using OrderBy = ASC.CRM.Core.Entities.OrderBy; + +namespace ASC.CRM.Api +{ + public class RelationshipEventsController : BaseApiController + { + private readonly FileUploader _fileUploader; + private readonly ASC.Files.Core.Data.DaoFactory _filesDaoFactory; + private readonly FileWrapperHelper _fileWrapperHelper; + private readonly FilesSettingsHelper _filesSettingsHelper; + private readonly ApiContext _apiContext; + private readonly MessageService _messageService; + private readonly MessageTarget _messageTarget; + private readonly SecurityContext _securityContext; + private readonly NotifyClient _notifyClient; + + public RelationshipEventsController( + CrmSecurity crmSecurity, + DaoFactory daoFactory, + ApiContext apiContext, + MessageTarget messageTarget, + MessageService messageService, + FileWrapperHelper fileWrapperHelper, + ASC.Files.Core.Data.DaoFactory filesDaoFactory, + FileUploader fileUploader, + SecurityContext securityContext, + NotifyClient notifyClient, + FilesSettingsHelper filesSettingsHelper, + IMapper mapper) + : base(daoFactory, crmSecurity, mapper) + { + _apiContext = apiContext; + _messageTarget = messageTarget; + _messageService = messageService; + _fileWrapperHelper = fileWrapperHelper; + _filesDaoFactory = filesDaoFactory; + _fileUploader = fileUploader; + _securityContext = securityContext; + _notifyClient = notifyClient; + _filesSettingsHelper = filesSettingsHelper; + } + + + /// + /// Returns the list of all events matching the parameters specified in the request + /// + /// + /// Get event list + /// + /// History + /// Related entity type + /// Related entity ID + /// Task category ID + /// Event author + /// Earliest task due date + /// Latest task due date + /// + /// Event list + /// + [Read(@"history/filter")] + public IEnumerable GetHistory( + string entityType, + int entityId, + int categoryId, + Guid createBy, + ApiDateTime fromDate, + ApiDateTime toDate) + { + var entityTypeObj = ToEntityType(entityType); + + switch (entityTypeObj) + { + case EntityType.Contact: + var contact = _daoFactory.GetContactDao().GetByID(entityId); + if (contact == null || !_crmSecurity.CanAccessTo(contact)) + throw new ItemNotFoundException(); + break; + case EntityType.Case: + var cases = _daoFactory.GetCasesDao().GetByID(entityId); + if (cases == null || !_crmSecurity.CanAccessTo(cases)) + throw new ItemNotFoundException(); + break; + case EntityType.Opportunity: + var deal = _daoFactory.GetDealDao().GetByID(entityId); + if (deal == null || !_crmSecurity.CanAccessTo(deal)) + throw new ItemNotFoundException(); + break; + default: + if (entityId != 0) + { + throw new ArgumentException(); + } + break; + } + + RelationshipEventByType eventByType; + + IEnumerable result; + + OrderBy eventOrderBy; + + if (ASC.CRM.Classes.EnumExtension.TryParse(_apiContext.SortBy, true, out eventByType)) + { + eventOrderBy = new OrderBy(eventByType, !_apiContext.SortDescending); + } + else if (string.IsNullOrEmpty(_apiContext.SortBy)) + { + eventOrderBy = new OrderBy(RelationshipEventByType.Created, false); + } + else + { + eventOrderBy = null; + } + + if (eventOrderBy != null) + { + result = ToListRelationshipEventDto(_daoFactory.GetRelationshipEventDao().GetItems( + _apiContext.FilterValue, + entityTypeObj, + entityId, + createBy, + categoryId, + fromDate, + toDate, + (int)_apiContext.StartIndex, + (int)_apiContext.Count, + eventOrderBy)); + + _apiContext.SetDataPaginated(); + _apiContext.SetDataFiltered(); + _apiContext.SetDataSorted(); + } + else + { + result = ToListRelationshipEventDto(_daoFactory.GetRelationshipEventDao().GetItems( + _apiContext.FilterValue, + entityTypeObj, + entityId, + createBy, + categoryId, + fromDate, + toDate, + 0, + 0, + null)); + } + + return result; + } + + /// + /// Deletes the event with the ID specified in the request and all the files associated with this event + /// + /// + /// Delete event and related files + /// + /// History + /// Event ID + /// + /// + /// + /// Event + /// + [Delete(@"history/{id:int}")] + public RelationshipEventDto DeleteHistory(int id) + { + if (id <= 0) throw new ArgumentException(); + + var item = _daoFactory.GetRelationshipEventDao().GetByID(id); + if (item == null) throw new ItemNotFoundException(); + var wrapper = _mapper.Map(item); + + _daoFactory.GetRelationshipEventDao().DeleteItem(id); + + var messageAction = GetHistoryDeletedAction(item.EntityType, item.ContactID); + var entityTitle = wrapper.Contact == null ? wrapper.Entity.EntityTitle : wrapper.Contact.DisplayName; + _messageService.Send(messageAction, _messageTarget.Create(item.ID), entityTitle, wrapper.Category.Title); + + return wrapper; + } + + /// + /// Creates a text (.txt) file in the selected folder with the title and contents sent in the request + /// + /// Create txt + /// Files + /// Entity type + /// Entity ID + /// File title + /// File contents + /// + /// File info + /// + [Create(@"{entityType:regex(contact|opportunity|case)}/{entityid:int}/files/text")] + public FileWrapper CreateTextFile( + [FromRoute] string entityType, + [FromRoute] int entityid, + [FromForm] string title, + [FromForm] string content) + { + if (title == null) throw new ArgumentNullException("title"); + if (content == null) throw new ArgumentNullException("content"); + + var folderid = GetRootFolderID(); + + FileWrapper result; + + var extension = ".txt"; + if (!string.IsNullOrEmpty(content)) + { + if (Regex.IsMatch(content, @"<([^\s>]*)(\s[^<]*)>")) + { + extension = ".html"; + } + } + + using (var memStream = new MemoryStream(Encoding.UTF8.GetBytes(content))) + { + title = title.EndsWith(extension, StringComparison.OrdinalIgnoreCase) ? title : (title + extension); + result = SaveFile(folderid, memStream, title); + } + + AttachFiles(entityType, entityid, new List { (int)result.Id }); + + return result; + } + + /// + /// Upload file + /// + /// Upload file + /// Files + /// + /// + ///
  • Single file upload. You should set Content-Type & Content-Disposition header to specify filename and content type, and send file in request body
  • + ///
  • Using standart multipart/form-data method
  • + /// ]]> + ///
    + /// Entity type + /// Entity ID + /// Request Input stream + /// Content-Type Header + /// Content-Disposition Header + /// List of files when posted as multipart/form-data + /// If True, upload documents in original formats as well + /// + /// File info + /// + [Create(@"{entityType:regex(contact|opportunity|case)}/{entityid:int}/files/upload")] + public FileWrapper UploadFileInCRM([FromForm] UploadFileInCRMRequestDto inDto) + { + string entityType = inDto.EntityType; + int entityid = inDto.Entityid; + Stream file = inDto.File; + ContentType contentType = inDto.ContentType; + ContentDisposition contentDisposition = inDto.ContentDisposition; + IEnumerable files = inDto.Files; + bool storeOriginalFileFlag = inDto.StoreOriginalFileFlag; + + _filesSettingsHelper.StoreOriginalFiles = storeOriginalFileFlag; + + var folderid = GetRootFolderID(); + + var fileNames = new List(); + + FileWrapper uploadedFile = null; + if (files != null && files.Any()) + { + //For case with multiple files + foreach (var postedFile in files) + { + using var fileStream = postedFile.OpenReadStream(); + uploadedFile = SaveFile(folderid, fileStream, postedFile.FileName); + fileNames.Add(uploadedFile.Title); + } + } + else if (file != null) + { + uploadedFile = SaveFile(folderid, file, contentDisposition.FileName); + fileNames.Add(uploadedFile.Title); + } + + return uploadedFile; + } + + private FileWrapper SaveFile(int folderid, Stream file, string fileName) + { + var resultFile = _fileUploader.Exec(folderid, fileName, file.Length, file); + + return _fileWrapperHelper.Get(resultFile); + } + + /// + /// Creates the event with the parameters specified in the request + /// + /// + /// Create event + /// + /// History + /// Contact ID + /// Related entity type + /// Related entity ID + /// + /// + /// + /// Contents + /// Category ID + /// Event creation date + /// List of IDs of the files associated with the event + /// User field list + /// + /// Created event + /// + [Create(@"history")] + public RelationshipEventDto AddHistoryTo([FromForm] AddHistoryToRequestDto inDto) + { + string entityType = inDto.EntityType; + int entityId = inDto.EntityId; + int contactId = inDto.ContactId; + string content = inDto.Content; + int categoryId = inDto.CategoryId; + ApiDateTime created = inDto.Created; + IEnumerable fileId = inDto.FileId; + IEnumerable notifyUserList = inDto.NotifyUserList; + + if (!string.IsNullOrEmpty(entityType) && + !( + string.Compare(entityType, "opportunity", StringComparison.OrdinalIgnoreCase) == 0 || + string.Compare(entityType, "case", StringComparison.OrdinalIgnoreCase) == 0) + ) + throw new ArgumentException(); + + var entityTypeObj = ToEntityType(entityType); + + var entityTitle = ""; + if (contactId > 0) + { + var contact = _daoFactory.GetContactDao().GetByID(contactId); + if (contact == null || !_crmSecurity.CanAccessTo(contact)) + throw new ArgumentException(); + entityTitle = contact.GetTitle(); + } + + if (entityTypeObj == EntityType.Case) + { + var cases = _daoFactory.GetCasesDao().GetByID(entityId); + if (cases == null || !_crmSecurity.CanAccessTo(cases)) + throw new ArgumentException(); + if (contactId <= 0) + { + entityTitle = cases.Title; + } + } + if (entityTypeObj == EntityType.Opportunity) + { + var deal = _daoFactory.GetDealDao().GetByID(entityId); + if (deal == null || !_crmSecurity.CanAccessTo(deal)) + throw new ArgumentException(); + if (contactId <= 0) + { + entityTitle = deal.Title; + } + } + + var relationshipEvent = new RelationshipEvent + { + CategoryID = categoryId, + EntityType = entityTypeObj, + EntityID = entityId, + Content = content, + ContactID = contactId, + CreateOn = created, + CreateBy = _securityContext.CurrentAccount.ID + }; + + var category = _daoFactory.GetListItemDao().GetByID(categoryId); + if (category == null) throw new ArgumentException(); + + var item = _daoFactory.GetRelationshipEventDao().CreateItem(relationshipEvent); + + + notifyUserList = notifyUserList != null ? notifyUserList.ToList() : new List(); + var needNotify = notifyUserList.Any(); + + var fileListInfoHashtable = new Hashtable(); + + if (fileId != null) + { + var fileIds = fileId.ToList(); + var files = _filesDaoFactory.GetFileDao().GetFiles(fileIds.ToArray()); + + if (needNotify) + { + foreach (var file in files) + { + var extension = Path.GetExtension(file.Title); + if (extension == null) continue; + + var fileInfo = string.Format("{0} ({1})", file.Title, extension.ToUpper()); + if (!fileListInfoHashtable.ContainsKey(fileInfo)) + { + fileListInfoHashtable.Add(fileInfo, file.DownloadUrl); + } + else + { + fileInfo = string.Format("{0} ({1}, {2})", file.Title, extension.ToUpper(), file.UniqID); + fileListInfoHashtable.Add(fileInfo, file.DownloadUrl); + } + } + } + + _daoFactory.GetRelationshipEventDao().AttachFiles(item.ID, fileIds.ToArray()); + + if (files.Any()) + { + var fileAttachAction = GetFilesAttachAction(entityTypeObj, contactId); + + _messageService.Send(fileAttachAction, _messageTarget.Create(item.ID), entityTitle, files.Select(x => x.Title)); + } + } + + if (needNotify) + { + _notifyClient.SendAboutAddRelationshipEventAdd(item, fileListInfoHashtable, _daoFactory, notifyUserList.ToArray()); + } + + var historyCreatedAction = GetHistoryCreatedAction(entityTypeObj, contactId); + + _messageService.Send(historyCreatedAction, _messageTarget.Create(item.ID), entityTitle, category.Title); + + return _mapper.Map(item); + } + + /// + /// Associates the selected file(s) with the entity with the ID or type specified in the request + /// + /// + /// Associate file with entity + /// + /// Entity type + /// Entity ID + /// List of IDs of the files + /// Files + /// Entity with the file attached + [Create(@"{entityType:regex(contact|opportunity|case)}/{entityid:int}/files")] + public RelationshipEventDto AttachFiles( + [FromRoute] string entityType, + [FromRoute] int entityid, + [FromForm] IEnumerable fileids) + { + if (entityid <= 0 || fileids == null) throw new ArgumentException(); + + var files = _filesDaoFactory.GetFileDao().GetFiles(fileids.ToArray()); + + var folderid = GetRootFolderID(); + + if (files.Exists(file => file.FolderID.ToString() != folderid.ToString())) + throw new ArgumentException("invalid file folder"); + + var entityTypeObj = ToEntityType(entityType); + + DomainObject entityObj; + + var entityTitle = GetEntityTitle(entityTypeObj, entityid, true, out entityObj); + + switch (entityTypeObj) + { + case EntityType.Contact: + var relationshipEvent1 = _daoFactory.GetRelationshipEventDao().AttachFiles(entityid, EntityType.Any, 0, fileids.ToArray()); + var messageAction = entityObj is Company ? MessageAction.CompanyAttachedFiles : MessageAction.PersonAttachedFiles; + _messageService.Send(messageAction, _messageTarget.Create(entityid), entityTitle, files.Select(x => x.Title)); + return _mapper.Map(relationshipEvent1); + case EntityType.Opportunity: + var relationshipEvent2 = _daoFactory.GetRelationshipEventDao().AttachFiles(0, entityTypeObj, entityid, fileids.ToArray()); + _messageService.Send(MessageAction.OpportunityAttachedFiles, _messageTarget.Create(entityid), entityTitle, files.Select(x => x.Title)); + return _mapper.Map(relationshipEvent2); + case EntityType.Case: + var relationshipEvent3 = _daoFactory.GetRelationshipEventDao().AttachFiles(0, entityTypeObj, entityid, fileids.ToArray()); + _messageService.Send(MessageAction.CaseAttachedFiles, _messageTarget.Create(entityid), entityTitle, files.Select(x => x.Title)); + return _mapper.Map(relationshipEvent3); + default: + throw new ArgumentException(); + } + } + + /// + /// Returns the ID for the root folder used to store the files for the CRM module + /// + /// Get root folder ID + /// Files + /// + /// Root folder ID + /// + [Read(@"files/root")] + public int GetRootFolderID() + { + return _daoFactory.GetFileDao().GetRoot(); + } + + /// + /// Returns the list of all files for the entity with the ID or type specified in the request + /// + /// Entity type + /// Entity ID + /// Get file list + /// Files + /// + /// File list + /// + [Read(@"{entityType:regex(contact|opportunity|case)}/{entityid:int}/files")] + public IEnumerable> GetFiles(string entityType, int entityid) + { + if (entityid <= 0) throw new ArgumentException(); + + var entityTypeObj = ToEntityType(entityType); + + switch (entityTypeObj) + { + case EntityType.Contact: + return _daoFactory.GetRelationshipEventDao().GetAllFiles(new[] { entityid }, EntityType.Any, 0).ConvertAll(file => _fileWrapperHelper.Get(file)); + case EntityType.Opportunity: + case EntityType.Case: + return _daoFactory.GetRelationshipEventDao().GetAllFiles(null, entityTypeObj, entityid).ConvertAll(file => _fileWrapperHelper.Get(file)); + default: + throw new ArgumentException(); + } + } + + /// + /// Deletes the file with the ID specified in the request + /// + /// Delete file + /// Files + /// File ID + /// + /// + /// + /// File Info + /// + [Delete(@"files/{fileid:int}")] + public FileWrapper DeleteCRMFile(int fileid) + { + if (fileid < 0) throw new ArgumentException(); + + var file = _filesDaoFactory.GetFileDao().GetFile(fileid); + if (file == null) throw new ItemNotFoundException(); + var result = _fileWrapperHelper.Get(file); + + var _eventsDao = _daoFactory.GetRelationshipEventDao(); + var eventIDs = _eventsDao.RemoveFile(file); + var events = new List(); + + eventIDs.ForEach(id => events.Add(_eventsDao.GetByID(id))); + + foreach (var evt in events) + { + DomainObject entityObj; + var entityTitle = evt.ContactID > 0 + ? GetEntityTitle(EntityType.Contact, evt.ContactID, false, out entityObj) + : GetEntityTitle(evt.EntityType, evt.EntityID, false, out entityObj); + var messageAction = GetFilesDetachAction(evt.EntityType, evt.ContactID); + + _messageService.Send(messageAction, _messageTarget.Create(file.ID), entityTitle, file.Title); + } + + return result; + } + + private IEnumerable ToListRelationshipEventDto(List itemList) + { + if (itemList.Count == 0) return new List(); + + var result = new List(); + + var contactIDs = new List(); + var eventIDs = new List(); + var categoryIDs = new List(); + var entityDtosIDs = new Dictionary>(); + + + foreach (var item in itemList) + { + eventIDs.Add(item.ID); + + if (!categoryIDs.Contains(item.CategoryID)) + { + categoryIDs.Add(item.CategoryID); + } + + if (item.ContactID > 0 && !contactIDs.Contains(item.ContactID)) + { + contactIDs.Add(item.ContactID); + } + + if (item.EntityID <= 0) continue; + + if (!entityDtosIDs.ContainsKey(item.EntityType)) + { + entityDtosIDs.Add(item.EntityType, new List + { + item.EntityID + }); + } + else if (!entityDtosIDs[item.EntityType].Contains(item.EntityID)) + { + entityDtosIDs[item.EntityType].Add(item.EntityID); + } + } + + var entityDtos = new Dictionary(); + + foreach (var entityType in entityDtosIDs.Keys) + { + switch (entityType) + { + case EntityType.Opportunity: + _daoFactory.GetDealDao().GetDeals(entityDtosIDs[entityType].Distinct().ToArray()) + .ForEach(item => + { + if (item == null) return; + + entityDtos.Add( + string.Format("{0}_{1}", (int)entityType, item.ID), + new EntityDto + { + EntityId = item.ID, + EntityTitle = item.Title, + EntityType = "opportunity" + }); + }); + break; + case EntityType.Case: + _daoFactory.GetCasesDao().GetByID(entityDtosIDs[entityType].ToArray()) + .ForEach(item => + { + if (item == null) return; + + entityDtos.Add( + string.Format("{0}_{1}", (int)entityType, item.ID), + new EntityDto + { + EntityId = item.ID, + EntityTitle = item.Title, + EntityType = "case" + }); + }); + break; + default: + throw new ArgumentException(); + } + } + + var categories = _daoFactory.GetListItemDao().GetItems(categoryIDs.ToArray()).ToDictionary(x => x.ID, x => _mapper.Map(x)); + + var files = _daoFactory.GetRelationshipEventDao().GetFiles(eventIDs.ToArray()); + + var contacts = _daoFactory.GetContactDao().GetContacts(contactIDs.ToArray()).ToDictionary(item => item.ID, x => _mapper.Map(x)); + + foreach (var item in itemList) + { + var eventObjWrap = _mapper.Map(item); + + if (contacts.ContainsKey(item.ContactID)) + { + eventObjWrap.Contact = contacts[item.ContactID]; + } + + if (item.EntityID > 0) + { + var entityStrKey = string.Format("{0}_{1}", (int)item.EntityType, item.EntityID); + + if (entityDtos.ContainsKey(entityStrKey)) + { + eventObjWrap.Entity = entityDtos[entityStrKey]; + } + } + + eventObjWrap.Files = files.ContainsKey(item.ID) ? files[item.ID].ConvertAll(file => + _fileWrapperHelper.Get(file)) : new List>(); + + if (categories.ContainsKey(item.CategoryID)) + { + eventObjWrap.Category = categories[item.CategoryID]; + } + + result.Add(eventObjWrap); + } + + return result; + } + + + + private MessageAction GetHistoryCreatedAction(EntityType entityType, int contactId) + { + if (contactId > 0) + { + var contact = _daoFactory.GetContactDao().GetByID(contactId); + return contact is Company ? MessageAction.CompanyCreatedHistoryEvent : MessageAction.PersonCreatedHistoryEvent; + } + + switch (entityType) + { + case EntityType.Opportunity: + return MessageAction.OpportunityCreatedHistoryEvent; + case EntityType.Case: + return MessageAction.CaseCreatedHistoryEvent; + case EntityType.Any: + var contact = _daoFactory.GetContactDao().GetByID(contactId); + return contact is Company ? MessageAction.CompanyCreatedHistoryEvent : MessageAction.PersonCreatedHistoryEvent; + default: + throw new ArgumentException("Invalid entityType: " + entityType); + } + } + + private MessageAction GetHistoryDeletedAction(EntityType entityType, int contactId) + { + if (contactId > 0) + { + var contact = _daoFactory.GetContactDao().GetByID(contactId); + return contact is Company ? MessageAction.CompanyDeletedHistoryEvent : MessageAction.PersonDeletedHistoryEvent; + } + + switch (entityType) + { + case EntityType.Opportunity: + return MessageAction.OpportunityDeletedHistoryEvent; + case EntityType.Case: + return MessageAction.CaseDeletedHistoryEvent; + case EntityType.Any: + var contact = _daoFactory.GetContactDao().GetByID(contactId); + return contact is Company ? MessageAction.CompanyDeletedHistoryEvent : MessageAction.PersonDeletedHistoryEvent; + default: + throw new ArgumentException("Invalid entityType: " + entityType); + } + } + + private MessageAction GetFilesAttachAction(EntityType entityType, int contactId) + { + if (contactId > 0) + { + var contact = _daoFactory.GetContactDao().GetByID(contactId); + return contact is Company ? MessageAction.CompanyAttachedFiles : MessageAction.PersonAttachedFiles; + } + + switch (entityType) + { + case EntityType.Opportunity: + return MessageAction.OpportunityAttachedFiles; + case EntityType.Case: + return MessageAction.CaseAttachedFiles; + case EntityType.Any: + var contact = _daoFactory.GetContactDao().GetByID(contactId); + return contact is Company ? MessageAction.CompanyAttachedFiles : MessageAction.PersonAttachedFiles; + default: + throw new ArgumentException("Invalid entityType: " + entityType); + } + } + + private MessageAction GetFilesDetachAction(EntityType entityType, int contactId) + { + if (contactId > 0) + { + var contact = _daoFactory.GetContactDao().GetByID(contactId); + return contact is Company ? MessageAction.CompanyDetachedFile : MessageAction.PersonDetachedFile; + } + + switch (entityType) + { + case EntityType.Opportunity: + return MessageAction.OpportunityDetachedFile; + case EntityType.Case: + return MessageAction.CaseDetachedFile; + case EntityType.Any: + var contact = _daoFactory.GetContactDao().GetByID(contactId); + return contact is Company ? MessageAction.CompanyDetachedFile : MessageAction.PersonAttachedFiles; + default: + throw new ArgumentException("Invalid entityType: " + entityType); + } + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Api/ReportsController.cs b/products/ASC.CRM/Server/Api/ReportsController.cs new file mode 100644 index 00000000000..2f16efd3e58 --- /dev/null +++ b/products/ASC.CRM/Server/Api/ReportsController.cs @@ -0,0 +1,198 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Api.CRM; +using ASC.Api.Documents; +using ASC.Common.Web; +using ASC.Core.Common.Settings; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Enums; +using ASC.Web.Api.Routing; +using ASC.Web.CRM.Classes; +using ASC.Web.Files.Services.DocumentService; + +using AutoMapper; + +using Microsoft.AspNetCore.Mvc; + +namespace ASC.CRM.Api +{ + public class ReportsController : BaseApiController + { + private readonly DocbuilderReportsUtilityHelper _docbuilderReportsUtilityHelper; + private readonly FileWrapperHelper _fileWrapperHelper; + private readonly ReportHelper _reportHelper; + private readonly Global _global; + private readonly SettingsManager _settingsManager; + + public ReportsController(CrmSecurity crmSecurity, + DaoFactory daoFactory, + SettingsManager settingsManager, + Global global, + ReportHelper reportHelper, + FileWrapperHelper fileWrapperHelper, + DocbuilderReportsUtilityHelper docbuilderReportsUtilityHelper, + IMapper mapper + ) + : base(daoFactory, crmSecurity, mapper) + { + _settingsManager = settingsManager; + _global = global; + _reportHelper = reportHelper; + _fileWrapperHelper = fileWrapperHelper; + _docbuilderReportsUtilityHelper = docbuilderReportsUtilityHelper; + } + + + /// Returns a list of all user report files + /// Get report files + /// Reports + /// Report files + /// if user can't create reports + [Read(@"report/files")] + public IEnumerable> GetFiles() + { + if (!_global.CanCreateReports) + throw _crmSecurity.CreateSecurityException(); + + var reportDao = _daoFactory.GetReportDao(); + + var files = reportDao.GetFiles(); + + if (!files.Any()) + { + var settings = _settingsManager.Load(); + + if (settings.NeedToGenerate) + { + files = reportDao.SaveSampleReportFiles(); + + settings.NeedToGenerate = false; + + _settingsManager.Save(settings); + } + } + + return files.ConvertAll(file => _fileWrapperHelper.Get(file)).OrderByDescending(file => file.Id); + } + + /// Delete the report file with the ID specified in the request + /// File ID + /// Delete report file + /// Reports + /// if user can't create reports + /// if fileid les than 0 + /// if file not found + [Delete(@"report/file/{fileid:int}")] + public void DeleteFile(int fileid) + { + if (!_global.CanCreateReports) + throw _crmSecurity.CreateSecurityException(); + + if (fileid < 0) throw new ArgumentException(); + + var file = _daoFactory.GetReportDao().GetFile(fileid); + + if (file == null) throw new ItemNotFoundException("File not found"); + + _daoFactory.GetReportDao().DeleteFile(fileid); + } + + /// Get the state of the report generation task + /// Get report generation state + /// Reports + /// Report state + /// if user can't create reports + [Read(@"report/status")] + public ReportState GetStatus() + { + if (!_global.CanCreateReports) + throw _crmSecurity.CreateSecurityException(); + + return _docbuilderReportsUtilityHelper.Status(ReportOrigin.CRM); + + } + + /// Terminate the report generation task + /// Terminate report generation + /// Reports + /// if user can't create reports + [Read(@"report/terminate")] + public void Terminate() + { + if (!_global.CanCreateReports) + throw _crmSecurity.CreateSecurityException(); + + _docbuilderReportsUtilityHelper.Terminate(ReportOrigin.CRM); + } + + /// Check data availability for a report + /// Report type + /// Time period + /// Managers + /// Check report data + /// Reports + /// Object + /// if user can't create reports + [Create(@"report/check")] + public object CheckReportData( + [FromForm] ReportType type, + [FromForm] ReportTimePeriod timePeriod, + [FromForm] Guid[] managers) + { + if (!_global.CanCreateReports) + throw _crmSecurity.CreateSecurityException(); + + return new + { + hasData = _reportHelper.CheckReportData(type, timePeriod, managers), + missingRates = _reportHelper.GetMissingRates(type) + }; + } + + /// Run the report generation task + /// Report type + /// Time period + /// Managers + /// Generate report + /// Reports + /// Report state + /// if user can't create reports + [Create(@"report/generate")] + public ReportState GenerateReport([FromForm] ReportType type, [FromForm] ReportTimePeriod timePeriod, [FromForm] Guid[] managers) + { + if (!_global.CanCreateReports) + throw _crmSecurity.CreateSecurityException(); + + return _reportHelper.RunGenareteReport(type, timePeriod, managers); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Api/TagsController.cs b/products/ASC.CRM/Server/Api/TagsController.cs new file mode 100644 index 00000000000..86f685e3826 --- /dev/null +++ b/products/ASC.CRM/Server/Api/TagsController.cs @@ -0,0 +1,710 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Api.Core; +using ASC.Api.CRM; +using ASC.Common.Web; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; +using ASC.MessagingSystem; +using ASC.Web.Api.Routing; + +using AutoMapper; + +using Microsoft.AspNetCore.Mvc; + +namespace ASC.CRM.Api +{ + public class TagsController : BaseApiController + { + private readonly ApiContext _apiContext; + private readonly MessageService _messageService; + private readonly MessageTarget _messageTarget; + + public TagsController(CrmSecurity crmSecurity, + DaoFactory daoFactory, + ApiContext apiContext, + MessageTarget messageTarget, + MessageService messageService, + IMapper mapper) + : base(daoFactory, crmSecurity, mapper) + { + _apiContext = apiContext; + _messageTarget = messageTarget; + _messageService = messageService; + } + + + + /// + /// Returns the list of all tags associated with the entity with the ID and type specified in the request + /// + /// Entity type + /// Entity ID + /// Get entity tags + /// Tags + /// + /// Tag + /// + /// + [Read(@"{entityType:regex(contact|opportunity|case)}/tag/{entityid:int}")] + public IEnumerable GetEntityTags(string entityType, int entityid) + { + if (string.IsNullOrEmpty(entityType) || entityid <= 0) throw new ArgumentException(); + + var entityTypeObj = ToEntityType(entityType); + + switch (entityTypeObj) + { + case EntityType.Contact: + case EntityType.Person: + case EntityType.Company: + var contact = _daoFactory.GetContactDao().GetByID(entityid); + if (contact == null || !_crmSecurity.CanAccessTo(contact)) + throw new ItemNotFoundException(); + break; + case EntityType.Case: + var cases = _daoFactory.GetCasesDao().GetByID(entityid); + if (cases == null || !_crmSecurity.CanAccessTo(cases)) + throw new ItemNotFoundException(); + break; + case EntityType.Opportunity: + var deal = _daoFactory.GetDealDao().GetByID(entityid); + if (deal == null || !_crmSecurity.CanAccessTo(deal)) + throw new ItemNotFoundException(); + break; + } + + return _daoFactory.GetTagDao().GetEntityTags(entityTypeObj, entityid); + } + + /// + /// Returns the list of all tags for the contact with the ID specified in the request + /// + /// Contact ID + /// Get all contact tags + /// Tags + /// + /// List of contact tags + /// + /// + [Read(@"contact/{contactid:int}/tag")] + public IEnumerable GetContactTags(int contactid) + { + if (contactid <= 0) throw new ArgumentException(); + var contact = _daoFactory.GetContactDao().GetByID(contactid); + if (contact == null || !_crmSecurity.CanAccessTo(contact)) + throw new ItemNotFoundException(); + return _daoFactory.GetTagDao().GetEntityTags(EntityType.Contact, contactid); + } + + /// + /// Creates the tag for the selected entity with the tag name specified in the request + /// + /// Entity type + /// Tag name + /// Create tag + /// Tags + /// + /// Tag + /// + /// + [Create(@"{entityType:regex(contact|opportunity|case)}/tag")] + public string CreateTag([FromRoute] string entityType, [FromForm] string tagName) + { + if (string.IsNullOrEmpty(tagName)) throw new ArgumentException(); + + var entityTypeObj = ToEntityType(entityType); + + var messageAction = GetEntityTagCreatedAction(entityTypeObj); + _daoFactory.GetTagDao().AddTag(entityTypeObj, tagName); + + _messageService.Send(messageAction, tagName); + + return tagName; + } + + /// + /// Returns the list of all tags associated with the entity type specified in the request + /// + /// Entity type + /// Get tags for entity type + /// Tags + /// + /// Tag + /// + /// + [Read(@"{entityType:regex(contact|opportunity|case)}/tag")] + public IEnumerable GetAllTags(string entityType) + { + if (string.IsNullOrEmpty(entityType)) throw new ArgumentException(); + var entType = ToEntityType(entityType); + + var tagTitles = _daoFactory.GetTagDao().GetAllTags(entType).ToList(); + var relativeItemsCountArrayJSON = _daoFactory.GetTagDao().GetTagsLinkCount(entType).ToList(); + if (tagTitles.Count != relativeItemsCountArrayJSON.Count) throw new ArgumentException(); + + var result = new List(); + for (var i = 0; i < tagTitles.Count; i++) + { + result.Add(new TagDto(tagTitles[i], relativeItemsCountArrayJSON[i])); + } + return result.OrderBy(x => x.Title.Trim(), StringComparer.OrdinalIgnoreCase).ToList(); + } + + /// + /// Adds a group of tags to the entity with the ID specified in the request + /// + /// Add tag group to entity + /// Tags + /// Tag type + /// Entity ID + /// Tag name + /// + /// + /// Tag + /// + [Create(@"{entityType:regex(contact|opportunity|case)}/taglist")] + public string AddTagToBatch([FromRoute] string entityType, [FromForm] IEnumerable entityid, [FromForm] string tagName) + { + var ids = entityid.ToList(); + if (entityid == null || !ids.Any()) throw new ArgumentException(); + + foreach (var entityID in ids) + { + AddTagTo(entityType, entityID, tagName); + } + return tagName; + } + + /// + /// Adds the selected tag to the group of contacts with the parameters specified in the request + /// + /// Add tag to contact group + /// Tags + /// Tag + /// Contact stage ID (warmth) + /// Contact type ID + /// + /// Start date + /// End date + /// Tag name + /// + /// + /// Tag + /// + [Create(@"contact/filter/taglist")] + public string AddTagToBatchContacts( + [FromForm] IEnumerable tags, + [FromForm] int contactStage, + [FromForm] int contactType, + [FromForm] ContactListViewType contactListView, + [FromForm] ApiDateTime fromDate, + [FromForm] ApiDateTime toDate, + [FromForm] string tagName) + { + var contacts = _daoFactory + .GetContactDao() + .GetContacts(_apiContext.FilterValue, + tags, + contactStage, + contactType, + contactListView, + fromDate, + toDate, + 0, + 0, + null).Where(_crmSecurity.CanEdit).ToList(); + + foreach (var contact in contacts) + { + AddTagTo("contact", contact.ID, tagName); + } + + return tagName; + } + + /// + /// Adds the selected tag to the group of opportunities with the parameters specified in the request + /// + /// Add tag to opportunity group + /// Tags + /// Opportunity responsible + /// Opportunity stage ID + /// Tags + /// Contact ID + /// Participation status: take into account opportunities where the contact is a participant or not + /// Start date + /// End date + /// Opportunity stage type + /// Tag name + /// + /// + /// Tag + /// + [Create(@"opportunity/filter/taglist")] + public string AddTagToBatchDeals( + [FromForm] Guid responsibleid, + [FromForm] int opportunityStagesid, + [FromForm] IEnumerable tags, + [FromForm] int contactid, + [FromForm] DealMilestoneStatus? stageType, + [FromForm] bool? contactAlsoIsParticipant, + [FromForm] ApiDateTime fromDate, + [FromForm] ApiDateTime toDate, + [FromForm] string tagName) + { + var deals = _daoFactory + .GetDealDao() + .GetDeals( + _apiContext.FilterValue, + responsibleid, + opportunityStagesid, + tags, + contactid, + stageType, + contactAlsoIsParticipant, + fromDate, toDate, 0, 0, null).Where(_crmSecurity.CanAccessTo).ToList(); + + foreach (var deal in deals) + { + AddTagTo("opportunity", deal.ID, tagName); + } + return tagName; + } + + /// + /// Adds the selected tag to the group of cases with the parameters specified in the request + /// + /// Add tag to case group + /// Tags + /// Contact ID + /// Case status + /// Tags + /// Tag name + /// + /// + /// Tag + /// + [Create(@"case/filter/taglist")] + public string AddTagToBatchCases( + [FromForm] int contactid, + [FromForm] bool? isClosed, + [FromForm] IEnumerable tags, + [FromForm] string tagName) + { + var caseses = _daoFactory.GetCasesDao().GetCases(_apiContext.FilterValue, contactid, isClosed, tags, 0, 0, null) + .Where(_crmSecurity.CanAccessTo).ToList(); + + if (!caseses.Any()) return tagName; + + foreach (var casese in caseses) + { + AddTagTo("case", casese.ID, tagName); + } + + return tagName; + } + + /// + /// Deletes all the unused tags from the entities with the type specified in the request + /// + /// Delete unused tags + /// Tags + /// Entity type + /// Tags + [Delete(@"{entityType:regex(contact|opportunity|case)}/tag/unused")] + public IEnumerable DeleteUnusedTag(string entityType) + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + var entityTypeObj = ToEntityType(entityType); + + var result = _daoFactory.GetTagDao().GetUnusedTags(entityTypeObj); + + _daoFactory.GetTagDao().DeleteUnusedTags(entityTypeObj); + + return new List(result); + } + + /// + /// Adds the selected tag to the entity with the type and ID specified in the request + /// + /// Add tag + /// Tags + /// Entity type + /// Entity ID + /// Tag name + /// + /// + /// Tag + /// + [Create(@"{entityType:regex(contact|opportunity|case)}/{entityid:int}/tag")] + public string AddTagTo([FromRoute] string entityType, [FromRoute] int entityid, [FromForm] string tagName) + { + if (entityid <= 0 || string.IsNullOrEmpty(tagName)) throw new ArgumentException(); + + var entityTypeObj = ToEntityType(entityType); + DomainObject entityObj; + var entityTitle = GetEntityTitle(entityTypeObj, entityid, true, out entityObj); + + if (entityTypeObj == EntityType.Contact && !_crmSecurity.CanEdit(entityObj as Contact)) throw _crmSecurity.CreateSecurityException(); + + _daoFactory.GetTagDao().AddTagToEntity(entityTypeObj, entityid, tagName); + + var messageAction = GetTagCreatedAction(entityTypeObj, entityid); + _messageService.Send(messageAction, _messageTarget.Create(entityid), entityTitle, tagName); + + return tagName; + } + + /// + /// Adds the selected tag to the entity (company or person) specified in the request and to all related contacts + /// + /// Add tag + /// Entity type + /// Entity ID + /// Tag name + /// Tags + /// + /// + /// + /// + /// Tag + /// + [Create(@"{entityType:regex(company|person)}/{entityid:int}/tag/group")] + public string AddContactTagToGroup([FromRoute] string entityType, [FromRoute] int entityid, [FromForm] string tagName) + { + if (entityid <= 0 || string.IsNullOrEmpty(tagName)) throw new ArgumentException(); + + var entityTypeObj = ToEntityType(entityType); + if (entityTypeObj != EntityType.Company && entityTypeObj != EntityType.Person) throw new ArgumentException(); + + var contactInst = _daoFactory.GetContactDao().GetByID(entityid); + if (contactInst == null || !_crmSecurity.CanAccessTo(contactInst)) throw new ItemNotFoundException(); + + if (contactInst is Person && entityTypeObj == EntityType.Company) throw new Exception(CRMErrorsResource.ContactIsNotCompany); + if (contactInst is Company && entityTypeObj == EntityType.Person) throw new Exception(CRMErrorsResource.ContactIsNotPerson); + + + var contactIDsToAddTag = new List(); + + + if (contactInst is Company) + { + contactIDsToAddTag.Add(contactInst.ID); + + var members = _daoFactory.GetContactDao().GetMembersIDsAndShareType(contactInst.ID); + + foreach (var m in members) + { + if (_crmSecurity.CanAccessTo(m.Key, EntityType.Person, m.Value, 0)) + { + contactIDsToAddTag.Add(m.Key); + } + } + } + else + { + var CompanyID = ((Person)contactInst).CompanyID; + if (CompanyID != 0) + { + var cnt = _daoFactory.GetContactDao().GetByID(CompanyID); + if (cnt != null && cnt is Company && _crmSecurity.CanAccessTo(cnt)) + { + contactIDsToAddTag.Add(CompanyID); + + var members = _daoFactory.GetContactDao().GetMembersIDsAndShareType(CompanyID); + + foreach (var m in members) + { + if (_crmSecurity.CanAccessTo(m.Key, EntityType.Person, m.Value, 0)) + { + contactIDsToAddTag.Add(m.Key); + } + } + } + else + { + contactIDsToAddTag.Add(contactInst.ID); + } + } + else + { + contactIDsToAddTag.Add(contactInst.ID); + } + } + + _daoFactory.GetTagDao().AddTagToContacts(contactIDsToAddTag.ToArray(), tagName); + + + var entityTitle = contactInst.GetTitle(); + var messageActions = GetTagCreatedGroupAction(entityTypeObj); + foreach (var messageAction in messageActions) + { + _messageService.Send(messageAction, _messageTarget.Create(contactInst.ID), entityTitle, tagName); + } + + return tagName; + } + + /// + /// Deletes the selected tag from the entity with the type specified in the request + /// + /// Delete tag + /// Entity type + /// Tag name + /// Tags + /// + /// + /// + /// Tag + /// + [Delete(@"{entityType:regex(contact|opportunity|case)}/tag")] + public string DeleteTag(string entityType, string tagName) + { + if (string.IsNullOrEmpty(entityType) || string.IsNullOrEmpty(tagName)) throw new ArgumentException(); + + + var entityTypeObj = ToEntityType(entityType); + + if (!_daoFactory.GetTagDao().IsExist(entityTypeObj, tagName)) throw new ItemNotFoundException(); + + _daoFactory.GetTagDao().DeleteTag(entityTypeObj, tagName); + + var messageAction = GetEntityTagDeletedAction(entityTypeObj); + _messageService.Send(messageAction, tagName); + + return tagName; + } + + /// + /// Deletes the selected tag from the entity with the type and ID specified in the request + /// + /// Remove tag + /// Tags + /// Entity type + /// Entity ID + /// Tag name + /// + /// + /// Tag + /// + [Delete(@"{entityType:regex(contact|opportunity|case)}/{entityid:int}/tag")] + public string DeleteTagFrom(string entityType, int entityid, string tagName) + { + if (string.IsNullOrEmpty(entityType) || entityid <= 0 || string.IsNullOrEmpty(tagName)) throw new ArgumentException(); + + var entityTypeObj = ToEntityType(entityType); + DomainObject entityObj; + var entityTitle = GetEntityTitle(entityTypeObj, entityid, true, out entityObj); + + if (entityTypeObj == EntityType.Contact && !_crmSecurity.CanEdit(entityObj as Contact)) throw _crmSecurity.CreateSecurityException(); + + if (!_daoFactory.GetTagDao().IsExist(entityTypeObj, tagName)) throw new ItemNotFoundException(); + + _daoFactory.GetTagDao().DeleteTagFromEntity(entityTypeObj, entityid, tagName); + + var messageAction = GetTagDeletedAction(entityTypeObj, entityid); + + _messageService.Send(messageAction, _messageTarget.Create(entityid), entityTitle, tagName); + + return tagName; + } + + /// + /// Deletes the selected tag from the entity (company or person) specified in the request and from all related contacts + /// + /// Delete tag + /// Entity type + /// Entity ID + /// Tag name + /// Tags + /// + /// + /// + /// + /// Tag + /// + [Delete(@"{entityType:regex(company|person)}/{entityid:int}/tag/group")] + public string DeleteContactTagFromGroup(string entityType, int entityid, string tagName) + { + if (entityid <= 0 || string.IsNullOrEmpty(tagName)) throw new ArgumentException(); + + var entityTypeObj = ToEntityType(entityType); + if (entityTypeObj != EntityType.Company && entityTypeObj != EntityType.Person) throw new ArgumentException(); + + var contactInst = _daoFactory.GetContactDao().GetByID(entityid); + if (contactInst == null) throw new ItemNotFoundException(); + + if (contactInst is Person && entityTypeObj == EntityType.Company) throw new Exception(CRMErrorsResource.ContactIsNotCompany); + if (contactInst is Company && entityTypeObj == EntityType.Person) throw new Exception(CRMErrorsResource.ContactIsNotPerson); + + var contactIDsForDeleteTag = new List(); + + if (contactInst is Company) + { + contactIDsForDeleteTag.Add(contactInst.ID); + + var members = _daoFactory.GetContactDao().GetMembersIDsAndShareType(contactInst.ID); + + foreach (var m in members) + { + if (_crmSecurity.CanAccessTo(m.Key, EntityType.Person, m.Value, 0)) + { + contactIDsForDeleteTag.Add(m.Key); + } + } + } + else + { + var CompanyID = ((Person)contactInst).CompanyID; + if (CompanyID != 0) + { + var cnt = _daoFactory.GetContactDao().GetByID(CompanyID); + if (cnt != null && cnt is Company && _crmSecurity.CanAccessTo(cnt)) + { + contactIDsForDeleteTag.Add(CompanyID); + + var members = _daoFactory.GetContactDao().GetMembersIDsAndShareType(CompanyID); + + foreach (var m in members) + { + if (_crmSecurity.CanAccessTo(m.Key, EntityType.Person, m.Value, 0)) + { + contactIDsForDeleteTag.Add(m.Key); + } + } + } + else + { + contactIDsForDeleteTag.Add(contactInst.ID); + } + } + else + { + contactIDsForDeleteTag.Add(contactInst.ID); + } + } + + _daoFactory.GetTagDao().DeleteTagFromContacts(contactIDsForDeleteTag.ToArray(), tagName); + + return tagName; + } + + + private static MessageAction GetEntityTagCreatedAction(EntityType entityType) + { + switch (entityType) + { + case EntityType.Contact: + return MessageAction.ContactsCreatedTag; + case EntityType.Opportunity: + return MessageAction.OpportunitiesCreatedTag; + case EntityType.Case: + return MessageAction.CasesCreatedTag; + default: + throw new ArgumentException("Invalid entityType: " + entityType); + } + } + + private static MessageAction GetEntityTagDeletedAction(EntityType entityType) + { + switch (entityType) + { + case EntityType.Contact: + return MessageAction.ContactsDeletedTag; + case EntityType.Opportunity: + return MessageAction.OpportunitiesDeletedTag; + case EntityType.Case: + return MessageAction.CasesDeletedTag; + default: + throw new ArgumentException("Invalid entityType: " + entityType); + } + } + + private MessageAction GetTagCreatedAction(EntityType entityType, int entityId) + { + switch (entityType) + { + case EntityType.Contact: + var entity = _daoFactory.GetContactDao().GetByID(entityId); + return entity is Company ? MessageAction.CompanyCreatedTag : MessageAction.PersonCreatedTag; + case EntityType.Company: + return MessageAction.CompanyCreatedTag; + case EntityType.Person: + return MessageAction.PersonCreatedTag; + case EntityType.Opportunity: + return MessageAction.OpportunityCreatedTag; + case EntityType.Case: + return MessageAction.CaseCreatedTag; + default: + throw new ArgumentException("Invalid entityType: " + entityType); + } + } + + private static IEnumerable GetTagCreatedGroupAction(EntityType entityType) + { + switch (entityType) + { + case EntityType.Company: + return new List { MessageAction.CompanyCreatedTag, MessageAction.CompanyCreatedPersonsTag }; + case EntityType.Person: + return new List { MessageAction.PersonCreatedTag, MessageAction.PersonCreatedCompanyTag }; + default: + throw new ArgumentException("Invalid entityType: " + entityType); + } + } + + private MessageAction GetTagDeletedAction(EntityType entityType, int entityId) + { + switch (entityType) + { + case EntityType.Contact: + var entity = _daoFactory.GetContactDao().GetByID(entityId); + return entity is Company ? MessageAction.CompanyDeletedTag : MessageAction.PersonDeletedTag; + case EntityType.Company: + return MessageAction.CompanyDeletedTag; + case EntityType.Person: + return MessageAction.PersonDeletedTag; + case EntityType.Opportunity: + return MessageAction.OpportunityDeletedTag; + case EntityType.Case: + return MessageAction.CaseDeletedTag; + default: + throw new ArgumentException("Invalid entityType: " + entityType); + } + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Api/TaskTemplateController.cs b/products/ASC.CRM/Server/Api/TaskTemplateController.cs new file mode 100644 index 00000000000..79a179e2170 --- /dev/null +++ b/products/ASC.CRM/Server/Api/TaskTemplateController.cs @@ -0,0 +1,411 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Api.CRM; +using ASC.Common.Web; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.Web.Api.Models; +using ASC.Web.Api.Routing; + +using AutoMapper; + +using Microsoft.AspNetCore.Mvc; + +namespace ASC.CRM.Api +{ + public class TaskTemplateController : BaseApiController + { + private readonly EmployeeWraperHelper _employeeWraperHelper; + + public TaskTemplateController(CrmSecurity crmSecurity, + DaoFactory daoFactory, + EmployeeWraperHelper employeeWraperHelper, + IMapper mapper) + : base(daoFactory, crmSecurity, mapper) + + { + _employeeWraperHelper = employeeWraperHelper; + } + + + + /// + /// Creates a new task template container with the type and title specified in the request + /// + /// Type + /// Title + /// Create task template container + /// Task Templates + /// + /// Task template container + /// + /// + /// false + [Create(@"{entityType:regex(contact|person|company|opportunity|case)}/tasktemplatecontainer")] + public TaskTemplateContainerDto CreateTaskTemplateContainer([FromRoute] string entityType, [FromForm] string title) + { + if (string.IsNullOrEmpty(title)) throw new ArgumentException(); + + var taskTemplateContainer = new TaskTemplateContainer + { + EntityType = ToEntityType(entityType), + Title = title + }; + + taskTemplateContainer.ID = _daoFactory.GetTaskTemplateContainerDao().SaveOrUpdate(taskTemplateContainer); + return ToTaskTemplateContainerDto(taskTemplateContainer); + } + + /// + /// Returns the complete list of all the task template containers available on the portal + /// + /// Type + /// Get task template container list + /// Task Templates + /// + /// Task template container list + /// + /// false + [Read(@"{entityType:regex(contact|person|company|opportunity|case)}/tasktemplatecontainer")] + public IEnumerable GetTaskTemplateContainers(string entityType) + { + return ToTaskListTemplateContainerDto(_daoFactory.GetTaskTemplateContainerDao().GetItems(ToEntityType(entityType))); + } + + /// + /// Deletes the task template container with the ID specified in the request + /// + /// Task template container ID + /// Delete task template container + /// Task Templates + /// + /// Deleted task template container + /// + /// + /// + /// false + [Delete(@"tasktemplatecontainer/{containerid:int}")] + public TaskTemplateContainerDto DeleteTaskTemplateContainer(int containerid) + { + if (containerid <= 0) throw new ArgumentException(); + + var result = ToTaskTemplateContainerDto(_daoFactory.GetTaskTemplateContainerDao().GetByID(containerid)); + if (result == null) throw new ItemNotFoundException(); + + _daoFactory.GetTaskTemplateContainerDao().Delete(containerid); + + return result; + } + + /// + /// Updates the task template container with the ID specified in the request + /// + /// Task template container ID + /// Title + /// Update task template container + /// Task Templates + /// + /// Task template container + /// + /// + /// + /// false + [Update(@"tasktemplatecontainer/{containerid:int}")] + public TaskTemplateContainerDto UpdateTaskTemplateContainer(int containerid, string title) + { + if (containerid <= 0 || string.IsNullOrEmpty(title)) throw new ArgumentException(); + + var result = _daoFactory.GetTaskTemplateContainerDao().GetByID(containerid); + if (result == null) throw new ItemNotFoundException(); + + result.Title = title; + + _daoFactory.GetTaskTemplateContainerDao().SaveOrUpdate(result); + + return ToTaskTemplateContainerDto(result); + } + + /// + /// Returns the detailed information on the task template container with the ID specified in the request + /// + /// Task template container ID + /// Get task template container by ID + /// Task Templates + /// + /// Task template container + /// + /// + /// + /// false + [Read(@"tasktemplatecontainer/{containerid:int}")] + public TaskTemplateContainerDto GetTaskTemplateContainerByID(int containerid) + { + if (containerid <= 0) throw new ArgumentException(); + + var item = _daoFactory.GetTaskTemplateContainerDao().GetByID(containerid); + if (item == null) throw new ItemNotFoundException(); + + return ToTaskTemplateContainerDto(item); + } + + /// + /// Returns the list of all tasks in the container with the ID specified in the request + /// + /// Task template container ID + /// Get task template list by contaier ID + /// Task Templates + /// + /// Task template list + /// + /// + /// + /// false + [Read(@"tasktemplatecontainer/{containerid:int}/tasktemplate")] + public IEnumerable GetTaskTemplates(int containerid) + { + if (containerid <= 0) throw new ArgumentException(); + + var container = _daoFactory.GetTaskTemplateContainerDao().GetByID(containerid); + if (container == null) throw new ItemNotFoundException(); + + return _daoFactory.GetTaskTemplateDao().GetList(containerid).ConvertAll(ToTaskTemplateDto); + } + + /// + /// Creates a new task template with the parameters specified in the request in the container with the selected ID + /// + /// Task template container ID + /// Title + /// Description + /// Responsible ID + /// Category ID + /// Responsible notification: notify or not + /// Ticks offset + /// + /// Create task template + /// Task Templates + /// Task template + /// + /// + /// false + [Create(@"tasktemplatecontainer/{containerid:int}/tasktemplate")] + public TaskTemplateDto CreateTaskTemplate( + [FromRoute] int containerid, + [FromForm] string title, + [FromForm] string description, + [FromForm] Guid responsibleid, + [FromForm] int categoryid, + [FromForm] bool isNotify, + [FromForm] long offsetTicks, + [FromForm] bool deadLineIsFixed + ) + { + if (containerid <= 0 || string.IsNullOrEmpty(title) || categoryid <= 0) throw new ArgumentException(); + + var container = _daoFactory.GetTaskTemplateContainerDao().GetByID(containerid); + if (container == null) throw new ItemNotFoundException(); + + var item = new TaskTemplate + { + CategoryID = categoryid, + ContainerID = containerid, + DeadLineIsFixed = deadLineIsFixed, + Description = description, + isNotify = isNotify, + ResponsibleID = responsibleid, + Title = title, + Offset = TimeSpan.FromTicks(offsetTicks) + }; + + item.ID = _daoFactory.GetTaskTemplateDao().SaveOrUpdate(item); + + return ToTaskTemplateDto(item); + } + + /// + /// Updates the selected task template with the parameters specified in the request in the container with the selected ID + /// + /// Task template ID + /// Task template container ID + /// Title + /// Description + /// Responsible ID + /// Category ID + /// Responsible notification: notify or not + /// Ticks offset + /// + /// Update task template + /// Task Templates + /// Task template + /// + /// + /// false + [Update(@"tasktemplatecontainer/{containerid:int}/tasktemplate")] + public TaskTemplateDto UpdateTaskTemplate( + int id, + int containerid, + string title, + string description, + Guid responsibleid, + int categoryid, + bool isNotify, + long offsetTicks, + bool deadLineIsFixed + ) + { + if (containerid <= 0 || string.IsNullOrEmpty(title) || categoryid <= 0) throw new ArgumentException(); + + var updatingItem = _daoFactory.GetTaskTemplateDao().GetByID(id); + if (updatingItem == null) throw new ItemNotFoundException(); + + var container = _daoFactory.GetTaskTemplateContainerDao().GetByID(containerid); + if (container == null) throw new ItemNotFoundException(); + + var item = new TaskTemplate + { + CategoryID = categoryid, + ContainerID = containerid, + DeadLineIsFixed = deadLineIsFixed, + Description = description, + isNotify = isNotify, + ResponsibleID = responsibleid, + Title = title, + ID = id, + Offset = TimeSpan.FromTicks(offsetTicks) + }; + + item.ID = _daoFactory.GetTaskTemplateDao().SaveOrUpdate(item); + + return ToTaskTemplateDto(item); + } + + /// + /// Deletes the task template with the ID specified in the request + /// + /// Task template ID + /// Delete task template + /// Task Templates + /// Task template + /// + /// + /// false + [Delete(@"tasktemplatecontainer/tasktemplate/{id:int}")] + public TaskTemplateDto DeleteTaskTemplate(int id) + { + if (id <= 0) throw new ArgumentException(); + + var taskTemplate = _daoFactory.GetTaskTemplateDao().GetByID(id); + if (taskTemplate == null) throw new ItemNotFoundException(); + + var result = ToTaskTemplateDto(taskTemplate); + + _daoFactory.GetTaskTemplateDao().Delete(id); + + return result; + } + + /// + /// Return the task template with the ID specified in the request + /// + /// Task template ID + /// Get task template by ID + /// Task Templates + /// Task template + /// + /// + /// false + [Read(@"tasktemplatecontainer/tasktemplate/{id:int}")] + public TaskTemplateDto GetTaskTemplateByID(int id) + { + if (id <= 0) throw new ArgumentException(); + + var taskTemplate = _daoFactory.GetTaskTemplateDao().GetByID(id); + if (taskTemplate == null) throw new ItemNotFoundException(); + + return ToTaskTemplateDto(taskTemplate); + } + + protected TaskTemplateDto ToTaskTemplateDto(TaskTemplate taskTemplate) + { + // TODO: set task template category + return new TaskTemplateDto + { + // Category = GetTaskCategoryByID(taskTemplate.CategoryID), + ContainerID = taskTemplate.ContainerID, + DeadLineIsFixed = taskTemplate.DeadLineIsFixed, + Description = taskTemplate.Description, + Id = taskTemplate.ID, + isNotify = taskTemplate.isNotify, + Title = taskTemplate.Title, + OffsetTicks = taskTemplate.Offset.Ticks, + Responsible = _employeeWraperHelper.Get(taskTemplate.ResponsibleID) + }; + } + + protected IEnumerable ToTaskListTemplateContainerDto(IEnumerable items) + { + var result = new List(); + + var taskTemplateDictionary = _daoFactory.GetTaskTemplateDao().GetAll() + .GroupBy(item => item.ContainerID) + .ToDictionary(x => x.Key, y => y.Select(ToTaskTemplateDto)); + + foreach (var item in items) + { + var taskTemplateContainer = new TaskTemplateContainerDto + { + Title = item.Title, + EntityType = item.EntityType.ToString(), + Id = item.ID + }; + + if (taskTemplateDictionary.ContainsKey(taskTemplateContainer.Id)) + { + taskTemplateContainer.Items = taskTemplateDictionary[taskTemplateContainer.Id]; + } + + result.Add(taskTemplateContainer); + } + + return result; + } + + protected TaskTemplateContainerDto ToTaskTemplateContainerDto(TaskTemplateContainer item) + { + return ToTaskListTemplateContainerDto(new List + { + item + }).FirstOrDefault(); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Api/TasksController.cs b/products/ASC.CRM/Server/Api/TasksController.cs new file mode 100644 index 00000000000..a957c6aaa62 --- /dev/null +++ b/products/ASC.CRM/Server/Api/TasksController.cs @@ -0,0 +1,715 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Api.Core; +using ASC.Api.CRM; +using ASC.Common.Web; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; +using ASC.MessagingSystem; +using ASC.Web.Api.Routing; +using ASC.Web.CRM.Core.Search; +using ASC.Web.CRM.Services.NotifyService; + +using AutoMapper; + +using Microsoft.AspNetCore.Mvc; + +namespace ASC.CRM.Api +{ + public class TasksController : BaseApiController + { + private readonly NotifyClient _notifyClient; + private readonly ApiContext _apiContext; + private readonly MessageService _messageService; + private readonly MessageTarget _messageTarget; + + public TasksController(CrmSecurity crmSecurity, + DaoFactory daoFactory, + ApiContext apiContext, + MessageTarget messageTarget, + MessageService messageService, + NotifyClient notifyClient, + IMapper mapper, + FactoryIndexerCase factoryIndexerCase) + : base(daoFactory, crmSecurity, mapper) + { + _apiContext = apiContext; + _messageTarget = messageTarget; + _messageService = messageService; + _notifyClient = notifyClient; + _mapper = mapper; + } + + /// + /// Returns the detailed information about the task with the ID specified in the request + /// + /// Task ID + /// Task + /// Get task by ID + /// Tasks + /// + /// + [Read(@"task/{taskid:int}")] + public TaskDto GetTaskByID(int taskid) + { + if (taskid <= 0) throw new ArgumentException(); + + var task = _daoFactory.GetTaskDao().GetByID(taskid); + + if (task == null) throw new ItemNotFoundException(); + + if (!_crmSecurity.CanAccessTo(task)) + { + throw _crmSecurity.CreateSecurityException(); + } + + return _mapper.Map(task); + } + + /// + /// Returns the list of tasks matching the creteria specified in the request + /// + /// Task responsible + /// Task category ID + /// Show open or closed tasks only + /// Earliest task due date + /// Latest task due date + /// Related entity type + /// Related entity ID + /// + /// Get task list + /// Tasks + /// + /// Task list + /// + [Read(@"task/filter")] + public IEnumerable GetAllTasks( + Guid responsibleid, + int categoryid, + bool? isClosed, + ApiDateTime fromDate, + ApiDateTime toDate, + string entityType, + int entityid) + { + TaskSortedByType taskSortedByType; + + if (!string.IsNullOrEmpty(entityType) && + !( + string.Compare(entityType, "contact", StringComparison.OrdinalIgnoreCase) == 0 || + string.Compare(entityType, "opportunity", StringComparison.OrdinalIgnoreCase) == 0 || + string.Compare(entityType, "case", StringComparison.OrdinalIgnoreCase) == 0) + ) + throw new ArgumentException(); + + var searchText = _apiContext.FilterValue; + + IEnumerable result; + + OrderBy taskOrderBy; + + if (ASC.CRM.Classes.EnumExtension.TryParse(_apiContext.SortBy, true, out taskSortedByType)) + { + taskOrderBy = new OrderBy(taskSortedByType, !_apiContext.SortDescending); + } + else if (string.IsNullOrEmpty(_apiContext.SortBy)) + { + taskOrderBy = new OrderBy(TaskSortedByType.DeadLine, true); + } + else + { + taskOrderBy = null; + } + + var fromIndex = (int)_apiContext.StartIndex; + var count = (int)_apiContext.Count; + + if (taskOrderBy != null) + { + result = ToTaskListDto( + _daoFactory.GetTaskDao() + .GetTasks( + searchText, + responsibleid, + categoryid, + isClosed, + fromDate, + toDate, + ToEntityType(entityType), + entityid, + fromIndex, + count, + taskOrderBy)).ToList(); + + _apiContext.SetDataPaginated(); + _apiContext.SetDataFiltered(); + _apiContext.SetDataSorted(); + } + else + result = ToTaskListDto( + _daoFactory + .GetTaskDao() + .GetTasks( + searchText, + responsibleid, + categoryid, + isClosed, + fromDate, + toDate, + ToEntityType(entityType), + entityid, + 0, + 0, null)).ToList(); + + + int totalCount; + + if (result.Count() < count) + { + totalCount = fromIndex + result.Count(); + } + else + { + totalCount = _daoFactory + .GetTaskDao() + .GetTasksCount( + searchText, + responsibleid, + categoryid, + isClosed, + fromDate, + toDate, + ToEntityType(entityType), + entityid); + } + + _apiContext.SetTotalCount(totalCount); + + return result; + } + + /// + /// Open anew the task with the ID specified in the request + /// + /// Resume task + /// Tasks + /// Task ID + /// + /// + /// Task + /// + [Update(@"task/{taskid:int}/reopen")] + public TaskDto ReOpenTask(int taskid) + { + if (taskid <= 0) throw new ArgumentException(); + + _daoFactory.GetTaskDao().OpenTask(taskid); + + var task = _daoFactory.GetTaskDao().GetByID(taskid); + + _messageService.Send(MessageAction.CrmTaskOpened, _messageTarget.Create(task.ID), task.Title); + + return _mapper.Map(task); + } + + /// + /// Close the task with the ID specified in the request + /// + /// Close task + /// Tasks + /// Task ID + /// + /// + /// Task + /// + [Update(@"task/{taskid:int}/close")] + public TaskDto CloseTask(int taskid) + { + if (taskid <= 0) throw new ArgumentException(); + + _daoFactory.GetTaskDao().CloseTask(taskid); + + var task = _daoFactory.GetTaskDao().GetByID(taskid); + _messageService.Send(MessageAction.CrmTaskClosed, _messageTarget.Create(task.ID), task.Title); + + return _mapper.Map(task); + + } + + /// + /// Delete the task with the ID specified in the request + /// + /// Delete task + /// Tasks + /// Task ID + /// + /// + /// + /// Deleted task + /// + [Delete(@"task/{taskid:int}")] + public TaskDto DeleteTask(int taskid) + { + if (taskid <= 0) throw new ArgumentException(); + + var task = _daoFactory.GetTaskDao().GetByID(taskid); + if (task == null) throw new ItemNotFoundException(); + + _daoFactory.GetTaskDao().DeleteTask(taskid); + _messageService.Send(MessageAction.CrmTaskDeleted, _messageTarget.Create(task.ID), task.Title); + + return _mapper.Map(task); + + } + + /// + /// Creates the task with the parameters (title, description, due date, etc.) specified in the request + /// + /// Task title + /// Task description + /// Task due date + /// Task responsible ID + /// Task category ID + /// Contact ID + /// Related entity type + /// Related entity ID + /// Notify the responsible about the task + /// Time period in minutes for reminder to the responsible about the task + /// + /// Create task + /// Tasks + /// Task + [Create(@"task")] + public TaskDto CreateTask( + [FromForm] string title, + [FromForm] string description, + [FromForm] ApiDateTime deadline, + [FromForm] Guid responsibleId, + [FromForm] int categoryId, + [FromForm] int contactId, + [FromForm] string entityType, + [FromForm] int entityId, + [FromForm] bool isNotify, + [FromForm] int alertValue + ) + { + if (!string.IsNullOrEmpty(entityType) && + !( + string.Compare(entityType, "opportunity", StringComparison.OrdinalIgnoreCase) == 0 || + string.Compare(entityType, "case", StringComparison.OrdinalIgnoreCase) == 0 + ) + || categoryId <= 0) + throw new ArgumentException(); + + var listItem = _daoFactory.GetListItemDao().GetByID(categoryId); + if (listItem == null) throw new ItemNotFoundException(CRMErrorsResource.TaskCategoryNotFound); + + var task = new Task + { + Title = title, + Description = description, + ResponsibleID = responsibleId, + CategoryID = categoryId, + DeadLine = deadline, + ContactID = contactId, + EntityType = ToEntityType(entityType), + EntityID = entityId, + IsClosed = false, + AlertValue = alertValue + }; + + task = _daoFactory.GetTaskDao().SaveOrUpdateTask(task); + + if (isNotify) + { + Contact taskContact = null; + Cases taskCase = null; + Deal taskDeal = null; + + if (task.ContactID > 0) + { + taskContact = _daoFactory.GetContactDao().GetByID(task.ContactID); + } + + if (task.EntityID > 0) + { + switch (task.EntityType) + { + case EntityType.Case: + taskCase = _daoFactory.GetCasesDao().GetByID(task.EntityID); + break; + case EntityType.Opportunity: + taskDeal = _daoFactory.GetDealDao().GetByID(task.EntityID); + break; + } + } + + _notifyClient.SendAboutResponsibleByTask(task, listItem.Title, taskContact, taskCase, taskDeal, null); + } + + _messageService.Send(MessageAction.CrmTaskCreated, _messageTarget.Create(task.ID), task.Title); + + return _mapper.Map(task); + } + + /// + /// Creates the group of the same task with the parameters (title, description, due date, etc.) specified in the request for several contacts + /// + /// Task title + /// Task description + /// Task due date + /// Task responsible ID + /// Task category ID + /// contact ID list + /// Related entity type + /// Related entity ID + /// Notify the responsible about the task + /// Time period in minutes for reminder to the responsible about the task + /// + /// Create task list + /// Tasks + /// Tasks + /// false + [Create(@"contact/task/group")] + public IEnumerable CreateTaskGroup( + [FromForm] string title, + [FromForm] string description, + [FromForm] ApiDateTime deadline, + [FromForm] Guid responsibleId, + [FromForm] int categoryId, + [FromForm] int[] contactId, + [FromForm] string entityType, + [FromForm] int entityId, + [FromForm] bool isNotify, + [FromForm] int alertValue) + { + var tasks = new List(); + + if ( + !string.IsNullOrEmpty(entityType) && + !(string.Compare(entityType, "opportunity", StringComparison.OrdinalIgnoreCase) == 0 || + string.Compare(entityType, "case", StringComparison.OrdinalIgnoreCase) == 0) + ) + throw new ArgumentException(); + + foreach (var cid in contactId) + { + tasks.Add(new Task + { + Title = title, + Description = description, + ResponsibleID = responsibleId, + CategoryID = categoryId, + DeadLine = deadline, + ContactID = cid, + EntityType = ToEntityType(entityType), + EntityID = entityId, + IsClosed = false, + AlertValue = alertValue + }); + } + + tasks = _daoFactory.GetTaskDao().SaveOrUpdateTaskList(tasks).ToList(); + + string taskCategory = null; + if (isNotify) + { + if (categoryId > 0) + { + var listItem = _daoFactory.GetListItemDao().GetByID(categoryId); + if (listItem == null) throw new ItemNotFoundException(); + + taskCategory = listItem.Title; + } + } + + for (var i = 0; i < tasks.Count; i++) + { + if (!isNotify) continue; + + Contact taskContact = null; + Cases taskCase = null; + Deal taskDeal = null; + + if (tasks[i].ContactID > 0) + { + taskContact = _daoFactory.GetContactDao().GetByID(tasks[i].ContactID); + } + + if (tasks[i].EntityID > 0) + { + switch (tasks[i].EntityType) + { + case EntityType.Case: + taskCase = _daoFactory.GetCasesDao().GetByID(tasks[i].EntityID); + break; + case EntityType.Opportunity: + taskDeal = _daoFactory.GetDealDao().GetByID(tasks[i].EntityID); + break; + } + } + + _notifyClient.SendAboutResponsibleByTask(tasks[i], taskCategory, taskContact, taskCase, taskDeal, null); + } + + if (tasks.Any()) + { + var contacts = _daoFactory.GetContactDao().GetContacts(contactId); + var task = tasks.First(); + _messageService.Send(MessageAction.ContactsCreatedCrmTasks, _messageTarget.Create(tasks.Select(x => x.ID)), contacts.Select(x => x.GetTitle()), task.Title); + } + + return ToTaskListDto(tasks); + } + + + /// + /// Updates the selected task with the parameters (title, description, due date, etc.) specified in the request + /// + /// Task ID + /// Task title + /// Task description + /// Task due date + /// Task responsible ID + /// Task category ID + /// Contact ID + /// Related entity type + /// Related entity ID + /// Notify or not + /// Time period in minutes for reminder to the responsible about the task + /// Update task + /// Tasks + /// Task + [Update(@"task/{taskid:int}")] + public TaskDto UpdateTask( + int taskid, + string title, + string description, + ApiDateTime deadline, + Guid responsibleid, + int categoryid, + int contactid, + string entityType, + int entityid, + bool isNotify, + int alertValue) + { + if (!string.IsNullOrEmpty(entityType) && + !(string.Compare(entityType, "opportunity", StringComparison.OrdinalIgnoreCase) == 0 || + string.Compare(entityType, "case", StringComparison.OrdinalIgnoreCase) == 0 + ) || categoryid <= 0) + throw new ArgumentException(); + + var listItem = _daoFactory.GetListItemDao().GetByID(categoryid); + if (listItem == null) throw new ItemNotFoundException(CRMErrorsResource.TaskCategoryNotFound); + + var task = new Task + { + ID = taskid, + Title = title, + Description = description, + DeadLine = deadline, + AlertValue = alertValue, + ResponsibleID = responsibleid, + CategoryID = categoryid, + ContactID = contactid, + EntityID = entityid, + EntityType = ToEntityType(entityType) + }; + + + task = _daoFactory.GetTaskDao().SaveOrUpdateTask(task); + + if (isNotify) + { + Contact taskContact = null; + Cases taskCase = null; + Deal taskDeal = null; + + if (task.ContactID > 0) + { + taskContact = _daoFactory.GetContactDao().GetByID(task.ContactID); + } + + if (task.EntityID > 0) + { + switch (task.EntityType) + { + case EntityType.Case: + taskCase = _daoFactory.GetCasesDao().GetByID(task.EntityID); + break; + case EntityType.Opportunity: + taskDeal = _daoFactory.GetDealDao().GetByID(task.EntityID); + break; + } + } + + _notifyClient.SendAboutResponsibleByTask(task, listItem.Title, taskContact, taskCase, taskDeal, null); + } + + _messageService.Send(MessageAction.CrmTaskUpdated, _messageTarget.Create(task.ID), task.Title); + + return _mapper.Map(task); + } + + /// false + [Update(@"task/{taskid:int}/creationdate")] + public void SetTaskCreationDate(int taskId, ApiDateTime creationDate) + { + var dao = _daoFactory.GetTaskDao(); + var task = dao.GetByID(taskId); + + if (task == null || !_crmSecurity.CanAccessTo(task)) + throw new ItemNotFoundException(); + + dao.SetTaskCreationDate(taskId, creationDate); + } + + /// false + [Update(@"task/{taskid:int}/lastmodifeddate")] + public void SetTaskLastModifedDate(int taskId, ApiDateTime lastModifedDate) + { + var dao = _daoFactory.GetTaskDao(); + var task = dao.GetByID(taskId); + + if (task == null || !_crmSecurity.CanAccessTo(task)) + throw new ItemNotFoundException(); + + dao.SetTaskLastModifedDate(taskId, lastModifedDate); + } + + private IEnumerable ToTaskListDto(IEnumerable itemList) + { + var result = new List(); + + var contactIDs = new List(); + var taskIDs = new List(); + var categoryIDs = new List(); + var entityDtosIDs = new Dictionary>(); + + foreach (var item in itemList) + { + taskIDs.Add(item.ID); + + if (!categoryIDs.Contains(item.CategoryID)) + { + categoryIDs.Add(item.CategoryID); + } + + if (item.ContactID > 0 && !contactIDs.Contains(item.ContactID)) + { + contactIDs.Add(item.ContactID); + } + + if (item.EntityID > 0) + { + if (item.EntityType != EntityType.Opportunity && item.EntityType != EntityType.Case) continue; + + if (!entityDtosIDs.ContainsKey(item.EntityType)) + { + entityDtosIDs.Add(item.EntityType, new List + { + item.EntityID + }); + } + else if (!entityDtosIDs[item.EntityType].Contains(item.EntityID)) + { + entityDtosIDs[item.EntityType].Add(item.EntityID); + } + } + } + + var entityDtos = new Dictionary(); + + foreach (var entityType in entityDtosIDs.Keys) + { + switch (entityType) + { + case EntityType.Opportunity: + _daoFactory.GetDealDao().GetDeals(entityDtosIDs[entityType].Distinct().ToArray()) + .ForEach(item => + { + if (item == null) return; + + entityDtos.Add( + string.Format("{0}_{1}", (int)entityType, item.ID), + new EntityDto + { + EntityId = item.ID, + EntityTitle = item.Title, + EntityType = "opportunity" + }); + }); + break; + case EntityType.Case: + _daoFactory.GetCasesDao().GetByID(entityDtosIDs[entityType].ToArray()) + .ForEach(item => + { + if (item == null) return; + + entityDtos.Add( + string.Format("{0}_{1}", (int)entityType, item.ID), + new EntityDto + { + EntityId = item.ID, + EntityTitle = item.Title, + EntityType = "case" + }); + }); + break; + } + } + + var categories = _daoFactory.GetListItemDao().GetItems(categoryIDs.ToArray()).ToDictionary(x => x.ID, x => _mapper.Map(x)); + var contacts = _daoFactory.GetContactDao().GetContacts(contactIDs.ToArray()).ToDictionary(item => item.ID, x => _mapper.Map(x)); + var restrictedContacts = _daoFactory.GetContactDao().GetRestrictedContacts(contactIDs.ToArray()).ToDictionary(item => item.ID, x => _mapper.Map(x)); + + foreach (var item in itemList) + { + var taskDto = _mapper.Map(item); + + taskDto.CanEdit = _crmSecurity.CanEdit(item); + + if (contacts.ContainsKey(item.ContactID)) + { + taskDto.Contact = contacts[item.ContactID]; + } + + if (restrictedContacts.ContainsKey(item.ContactID)) + { + taskDto.Contact = restrictedContacts[item.ContactID]; + /*Hide some fields. Should be refactored! */ + taskDto.Contact.Currency = null; + taskDto.Contact.Email = null; + taskDto.Contact.AccessList = null; + } + + if (item.EntityID > 0) + { + var entityStrKey = string.Format("{0}_{1}", (int)item.EntityType, item.EntityID); + + if (entityDtos.ContainsKey(entityStrKey)) + { + taskDto.Entity = entityDtos[entityStrKey]; + } + } + + if (categories.ContainsKey(item.CategoryID)) + { + taskDto.Category = categories[item.CategoryID]; + } + + result.Add(taskDto); + } + + return result; + } + } + +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Api/UtilsController.cs b/products/ASC.CRM/Server/Api/UtilsController.cs new file mode 100644 index 00000000000..df390d9df77 --- /dev/null +++ b/products/ASC.CRM/Server/Api/UtilsController.cs @@ -0,0 +1,620 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security; +using System.Text.Json; + +using ASC.Api.CRM; +using ASC.Common.Threading.Progress; +using ASC.Core.Common.Settings; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; +using ASC.MessagingSystem; +using ASC.Web.Api.Routing; +using ASC.Web.Core.Utility; +using ASC.Web.CRM.Classes; + +using AutoMapper; + +using Microsoft.AspNetCore.Mvc; + +namespace ASC.CRM.Api +{ + public class UtilsController : BaseApiController + { + private readonly ExportToCsv _exportToCsv; + private readonly ImportFromCSV _importFromCSV; + private readonly Global _global; + private readonly OrganisationLogoManager _organisationLogoManager; + private readonly ImportFromCSVManager _importFromCSVManager; + private readonly InvoiceSetting _invoiceSetting; + private readonly SettingsManager _settingsManager; + private readonly CurrencyProvider _currencyProvider; + private readonly MessageService _messageService; + + public UtilsController(CrmSecurity crmSecurity, + DaoFactory daoFactory, + MessageService messageService, + SettingsManager settingsManager, + CurrencyProvider currencyProvider, + InvoiceSetting invoiceSetting, + ImportFromCSVManager importFromCSVManager, + OrganisationLogoManager organisationLogoManager, + Global global, + ImportFromCSV importFromCSV, + ExportToCsv exportToCsv, + IMapper mapper) + : base(daoFactory, crmSecurity, mapper) + { + _messageService = messageService; + _currencyProvider = currencyProvider; + _settingsManager = settingsManager; + _invoiceSetting = invoiceSetting; + _importFromCSVManager = importFromCSVManager; + _organisationLogoManager = organisationLogoManager; + _global = global; + _importFromCSV = importFromCSV; + _exportToCsv = exportToCsv; + } + + /// + /// Returns the list of all currencies currently available on the portal + /// + /// Get currency list + /// Common + /// + /// List of available currencies + /// + [Read(@"settings/currency")] + public IEnumerable GetAvaliableCurrency() + { + return _currencyProvider.GetAll().ConvertAll(item => _mapper.Map(item)); + } + + /// + /// Returns the result of convertation from one currency to another + /// + /// Amount to convert + /// Old currency key + /// New currency key + /// Get the result of convertation + /// Common + /// + /// Decimal result of convertation + /// + [Read(@"settings/currency/convert")] + public Decimal ConvertAmount(Decimal amount, String fromcurrency, String tocurrency) + { + return _currencyProvider.MoneyConvert(amount, fromcurrency, tocurrency); + } + + /// + /// Returns the summary table with rates for selected currency + /// + /// Currency (Abbreviation) + /// Get the summary table + /// Common + /// + /// Dictionary of currencies and rates + /// + /// + [Read(@"settings/currency/summarytable")] + public IEnumerable GetSummaryTable(String currency) + { + var result = new List(); + + if (string.IsNullOrEmpty(currency)) + { + throw new ArgumentException(); + } + + var cur = _currencyProvider.Get(currency.ToUpper()); + + if (cur == null) throw new ArgumentException(); + + var table = _currencyProvider.MoneyConvert(cur).ToList(); + + foreach (var row in table) + { + var currencyInfoDto = _mapper.Map(row.Key); + + currencyInfoDto.Rate = row.Value; + + result.Add(currencyInfoDto); + } + + return result; + } + + /// + /// + /// + /// Change contact status group auto + /// + /// Contacts + /// + /// ChangeContactStatusGroupAuto setting value (true, false or null) + /// + /// + [Update(@"contact/status/settings")] + public Boolean? UpdateCRMContactStatusSettings(Boolean? changeContactStatusGroupAuto) + { + var tenantSettings = _settingsManager.Load(); + + tenantSettings.ChangeContactStatusGroupAuto = changeContactStatusGroupAuto; + + _settingsManager.Save(tenantSettings); + + _messageService.Send(MessageAction.ContactTemperatureLevelSettingsUpdated); + + return changeContactStatusGroupAuto; + } + + /// + /// + /// + /// Write mail to history auto + /// + /// Contacts + /// + /// WriteMailToHistoryAuto setting value (true or false) + /// + /// + [Update(@"contact/mailtohistory/settings")] + public Boolean UpdateCRMWriteMailToHistorySettings(Boolean writeMailToHistoryAuto) + { + var tenantSettings = _settingsManager.Load(); + + tenantSettings.WriteMailToHistoryAuto = writeMailToHistoryAuto; + + _settingsManager.Save(tenantSettings); + //MessageService.Send( MessageAction.ContactTemperatureLevelSettingsUpdated); + + return writeMailToHistoryAuto; + } + + /// + /// + /// + /// add tag to contact group auto + /// + /// Contacts + /// + /// AddTagToContactGroupAuto setting value (true, false or null) + /// + /// + [Update(@"contact/tag/settings")] + public Boolean? UpdateCRMContactTagSettings(Boolean? addTagToContactGroupAuto) + { + var tenantSettings = _settingsManager.Load(); + tenantSettings.AddTagToContactGroupAuto = addTagToContactGroupAuto; + + _settingsManager.Save(tenantSettings); + + _messageService.Send(MessageAction.ContactsTagSettingsUpdated); + + return addTagToContactGroupAuto; + } + + + /// + /// Set IsConfiguredPortal tenant setting and website contact form key specified in the request + /// + /// Set tenant settings + /// Common + /// + /// IsConfiguredPortal setting value (true or false) + /// + [Update(@"settings")] + public Boolean SetIsPortalConfigured(Boolean? configured, Guid? webFormKey) + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + var tenantSettings = _settingsManager.Load(); + + tenantSettings.IsConfiguredPortal = configured ?? true; + tenantSettings.WebFormKey = webFormKey ?? Guid.NewGuid(); + + _settingsManager.Save(tenantSettings); + + return tenantSettings.IsConfiguredPortal; + } + + /// + /// Save organisation company name + /// + /// Organisation company name + /// Save organisation company name + /// Organisation + /// Organisation company name + /// + [Update(@"settings/organisation/base")] + public String UpdateOrganisationSettingsCompanyName(String companyName) + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + var tenantSettings = _settingsManager.Load(); + + if (tenantSettings.InvoiceSetting == null) + { + tenantSettings.InvoiceSetting = _invoiceSetting.DefaultSettings; + } + tenantSettings.InvoiceSetting.CompanyName = companyName; + + _settingsManager.Save(tenantSettings); + + _messageService.Send(MessageAction.OrganizationProfileUpdatedCompanyName, companyName); + + return companyName; + } + + /// + /// Save organisation company address + /// + /// Organisation company street/building/apartment address + /// City + /// State + /// Zip + /// Country + /// Save organisation company address + /// Organisation + /// Returns a JSON object with the organization company address details + /// + [Update(@"settings/organisation/address")] + public String UpdateOrganisationSettingsCompanyAddress(String street, String city, String state, String zip, String country) + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + var tenantSettings = _settingsManager.Load(); + + if (tenantSettings.InvoiceSetting == null) + { + tenantSettings.InvoiceSetting = _invoiceSetting.DefaultSettings; + } + + var companyAddress = JsonSerializer.Serialize(new + { + type = AddressCategory.Billing.ToString(), + street, + city, + state, + zip, + country + }); + + tenantSettings.InvoiceSetting.CompanyAddress = companyAddress; + + _settingsManager.Save(tenantSettings); + + _messageService.Send(MessageAction.OrganizationProfileUpdatedAddress); + + return companyAddress; + } + + /// + /// Save organisation logo + /// + /// Reset organisation logo + /// Save organisation logo + /// Organisation + /// Organisation logo ID + /// + /// + [Update(@"settings/organisation/logo")] + public Int32 UpdateOrganisationSettingsLogo(bool reset) + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + int companyLogoID; + + if (!reset) + { + companyLogoID = _organisationLogoManager.TryUploadOrganisationLogoFromTmp(_daoFactory); + if (companyLogoID == 0) + { + throw new Exception("Downloaded image not found"); + } + } + else + { + companyLogoID = 0; + } + + var tenantSettings = _settingsManager.Load(); + + if (tenantSettings.InvoiceSetting == null) + { + tenantSettings.InvoiceSetting = _invoiceSetting.DefaultSettings; + } + tenantSettings.InvoiceSetting.CompanyLogoID = companyLogoID; + + _settingsManager.Save(tenantSettings); + + _messageService.Send(MessageAction.OrganizationProfileUpdatedInvoiceLogo); + + return companyLogoID; + } + + /// + /// Get organisation logo in base64 format (if 'id' is 0 then take current logo) + /// + /// organisation logo id + /// Get organisation logo + /// Organisation + /// Organisation logo content in base64 + /// + [Read(@"settings/organisation/logo")] + public String GetOrganisationSettingsLogo(int id) + { + if (id != 0) + { + return _organisationLogoManager.GetOrganisationLogoBase64(id); + } + else + { + var tenantSettings = _settingsManager.Load(); + + if (tenantSettings.InvoiceSetting == null) + { + return string.Empty; + } + + return _organisationLogoManager.GetOrganisationLogoBase64(tenantSettings.InvoiceSetting.CompanyLogoID); + } + } + + /// + /// Change Website Contact Form key + /// + /// Change web form key + /// Common + /// Web form key + /// + [Update(@"settings/webformkey/change")] + public string ChangeWebToLeadFormKey() + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + var tenantSettings = _settingsManager.Load(); + + tenantSettings.WebFormKey = Guid.NewGuid(); + + _settingsManager.Save(tenantSettings); + + _messageService.Send(MessageAction.WebsiteContactFormUpdatedKey); + + return tenantSettings.WebFormKey.ToString(); + } + + /// + /// Change default CRM currency + /// + /// Currency (Abbreviation) + /// Change currency + /// Common + /// currency + /// + /// + [Update(@"settings/currency")] + public CurrencyInfoDto UpdateCRMCurrency(String currency) + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + if (string.IsNullOrEmpty(currency)) + { + throw new ArgumentException(); + } + currency = currency.ToUpper(); + var cur = _currencyProvider.Get(currency); + if (cur == null) throw new ArgumentException(); + + _global.SaveDefaultCurrencySettings(cur); + _messageService.Send(MessageAction.CrmDefaultCurrencyUpdated); + + return _mapper.Map(cur); + } + + /// false + [Create(@"{entityType:regex(contact|opportunity|case|task)}/import/start")] + public string StartImportFromCSV([FromRoute] string entityType, [FromForm] string csvFileURI, [FromForm] string jsonSettings) + { + EntityType entityTypeObj; + + if (string.IsNullOrEmpty(entityType) || string.IsNullOrEmpty(csvFileURI) || string.IsNullOrEmpty(jsonSettings)) throw new ArgumentException(); + switch (entityType.ToLower()) + { + case "contact": + entityTypeObj = EntityType.Contact; + break; + case "opportunity": + entityTypeObj = EntityType.Opportunity; + break; + case "case": + entityTypeObj = EntityType.Case; + break; + case "task": + entityTypeObj = EntityType.Task; + break; + default: + throw new ArgumentException(); + } + + _importFromCSVManager.StartImport(entityTypeObj, csvFileURI, jsonSettings); + + return ""; + + } + + /// false + [Read(@"{entityType:regex(contact|opportunity|case|task)}/import/status")] + public IProgressItem GetImportFromCSVStatus(string entityType) + { + EntityType entityTypeObj; + + if (string.IsNullOrEmpty(entityType)) throw new ArgumentException(); + switch (entityType.ToLower()) + { + case "contact": + entityTypeObj = EntityType.Contact; + break; + case "opportunity": + entityTypeObj = EntityType.Opportunity; + break; + case "case": + entityTypeObj = EntityType.Case; + break; + case "task": + entityTypeObj = EntityType.Task; + break; + default: + throw new ArgumentException(); + } + + return _importFromCSV.GetStatus(entityTypeObj); + } + + /// false + [Read(@"import/samplerow")] + public String GetImportFromCSVSampleRow(string csvFileURI, int indexRow, string jsonSettings) + { + if (String.IsNullOrEmpty(csvFileURI) || indexRow < 0) throw new ArgumentException(); + + if (!_global.GetStore().IsFile("temp", csvFileURI)) throw new ArgumentException(); + + var CSVFileStream = _global.GetStore().GetReadStream("temp", csvFileURI); + + return _importFromCSV.GetRow(CSVFileStream, indexRow, jsonSettings); + } + + /// false + [Create(@"import/uploadfake")] + public FileUploadResult ProcessUploadFake([FromForm] string csvFileURI, [FromForm] string jsonSettings) + { + return _importFromCSVManager.ProcessUploadFake(csvFileURI, jsonSettings); + } + + /// false + [Read(@"export/status")] + public IProgressItem GetExportStatus() + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + return _exportToCsv.GetStatus(false); + + } + + /// false + [Update(@"export/cancel")] + public IProgressItem CancelExport() + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + _exportToCsv.Cancel(false); + + return _exportToCsv.GetStatus(false); + + } + + /// false + [Create(@"export/start")] + public IProgressItem StartExport() + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + _messageService.Send(MessageAction.CrmAllDataExported); + + return _exportToCsv.Start(null, CRMSettingResource.Export + ".zip"); + } + + /// false + [Read(@"export/partial/status")] + public IProgressItem GetPartialExportStatus() + { + return _exportToCsv.GetStatus(true); + } + + /// false + [Update(@"export/partial/cancel")] + public IProgressItem CancelPartialExport() + { + + _exportToCsv.Cancel(true); + + return _exportToCsv.GetStatus(true); + + } + + /// false + [Create(@"export/partial/{entityType:regex(contact|opportunity|case|task|invoiceitem)}/start")] + public IProgressItem StartPartialExport([FromRoute] string entityType, [FromForm] string base64FilterString) + { + if (string.IsNullOrEmpty(base64FilterString)) throw new ArgumentException(); + + FilterObject filterObject; + String fileName; + + switch (entityType.ToLower()) + { + case "contact": + filterObject = new ContactFilterObject(base64FilterString); + fileName = CRMContactResource.Contacts + ".csv"; + _messageService.Send(MessageAction.ContactsExportedToCsv); + break; + case "opportunity": + filterObject = new DealFilterObject(base64FilterString); + fileName = CRMCommonResource.DealModuleName + ".csv"; + _messageService.Send(MessageAction.OpportunitiesExportedToCsv); + break; + case "case": + filterObject = new CasesFilterObject(base64FilterString); + fileName = CRMCommonResource.CasesModuleName + ".csv"; + _messageService.Send(MessageAction.CasesExportedToCsv); + break; + case "task": + filterObject = new TaskFilterObject(base64FilterString); + fileName = CRMCommonResource.TaskModuleName + ".csv"; + _messageService.Send(MessageAction.CrmTasksExportedToCsv); + break; + case "invoiceitem": + fileName = CRMCommonResource.ProductsAndServices + ".csv"; + filterObject = new InvoiceItemFilterObject(base64FilterString); + break; + default: + throw new ArgumentException(); + } + + return _exportToCsv.Start(filterObject, fileName); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Api/VoipController.cs b/products/ASC.CRM/Server/Api/VoipController.cs new file mode 100644 index 00000000000..f5766d2f992 --- /dev/null +++ b/products/ASC.CRM/Server/Api/VoipController.cs @@ -0,0 +1,980 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Security; + +using ASC.Api.Core; +using ASC.Api.CRM; +using ASC.Api.Utils; +using ASC.Common.Web; +using ASC.Core.Notify.Signalr; +using ASC.Core.Tenants; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; +using ASC.Data.Storage; +using ASC.VoipService; +using ASC.VoipService.Dao; +using ASC.VoipService.Twilio; +using ASC.Web.Api.Routing; +using ASC.Web.CRM.Classes; +using ASC.Web.Studio.Utility; + +using AutoMapper; + +using Microsoft.AspNetCore.Mvc; + +using SecurityContext = ASC.Core.SecurityContext; + +namespace ASC.CRM.Api +{ + public class VoIPController : BaseApiController + { + private readonly SignalrServiceClient _signalrServiceClient; + private readonly ApiContext _apiContext; + private readonly VoipEngine _voipEngine; + private readonly TenantUtil _tenantUtil; + private readonly SecurityContext _securityContext; + private readonly CommonLinkUtility _commonLinkUtility; + private readonly StorageFactory _storageFactory; + private readonly ContactPhotoManager _contactPhotoManager; + private readonly Global _global; + + public VoIPController(CrmSecurity crmSecurity, + DaoFactory daoFactory, + Global global, + ContactPhotoManager contactPhotoManager, + StorageFactory storageFactory, + CommonLinkUtility commonLinkUtility, + SecurityContext securityContext, + TenantUtil tenantUtil, + VoipEngine voipEngine, + ApiContext apiContext, + SignalrServiceClient signalrServiceClient, + IMapper mapper) + : base(daoFactory, crmSecurity, mapper) + { + _global = global; + _contactPhotoManager = contactPhotoManager; + _storageFactory = storageFactory; + _commonLinkUtility = commonLinkUtility; + _securityContext = securityContext; + _tenantUtil = tenantUtil; + _voipEngine = voipEngine; + _apiContext = apiContext; + _signalrServiceClient = signalrServiceClient; + } + + /// + /// + /// + /// + /// Voip + /// + /// + /// + [Read(@"voip/numbers/available")] + public IEnumerable GetAvailablePhoneNumbers(PhoneNumberType numberType, string isoCountryCode) + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + if (string.IsNullOrEmpty(isoCountryCode)) throw new ArgumentException(); + + return _daoFactory.GetVoipDao().GetProvider().GetAvailablePhoneNumbers(numberType, isoCountryCode); + + } + + /// + /// + /// + /// + /// Voip + /// + /// + [Read(@"voip/numbers/unlinked")] + public IEnumerable GetUnlinkedPhoneNumbers() + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + var listPhones = _daoFactory.GetVoipDao().GetProvider().GetExistingPhoneNumbers(); + var buyedPhones = _daoFactory.GetVoipDao().GetNumbers(); + + return listPhones.Where(r => buyedPhones.All(b => r.Id != b.Id)).ToList(); + } + + /// + /// + /// + /// + /// Voip + /// + /// + [Read(@"voip/numbers/existing")] + public IEnumerable GetExistingPhoneNumbers() + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + return _daoFactory.GetVoipDao().GetNumbers(); + } + /// + /// + /// + /// + /// Voip + /// + /// + [Create(@"voip/numbers")] + public VoipPhone BuyNumber([FromForm] string number) + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + var newPhone = _daoFactory.GetVoipDao().GetProvider().BuyNumber(number); + + _daoFactory.GetVoipDao().GetProvider().CreateQueue(newPhone); + SetDefaultAudio(newPhone); + + _daoFactory.GetVoipDao().GetProvider().UpdateSettings(newPhone); + return _daoFactory.GetVoipDao().SaveOrUpdateNumber(newPhone); + } + + /// + /// + /// + /// + /// Voip + /// + /// + [Create(@"voip/numbers/link")] + public VoipPhone LinkNumber([FromForm] string id) + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + var newPhone = _daoFactory.GetVoipDao().GetProvider().GetPhone(id); + + _daoFactory.GetVoipDao().GetProvider().CreateQueue(newPhone); + SetDefaultAudio(newPhone); + + _daoFactory.GetVoipDao().GetProvider().UpdateSettings(newPhone); + + return _daoFactory.GetVoipDao().SaveOrUpdateNumber(newPhone); + } + + public void SetDefaultAudio(VoipPhone newPhone) + { + var storage = _storageFactory.GetStorage("", "crm"); + const string path = "default/"; + var files = storage.ListFilesRelative("voip", path, "*.*", true) + .Select(filePath => new + { + path = _commonLinkUtility.GetFullAbsolutePath(storage.GetUri("voip", Path.Combine(path, filePath)).ToString()), + audioType = (AudioType)Enum.Parse(typeof(AudioType), Directory.GetParent(filePath).Name, true) + }).ToList(); + + var audio = files.Find(r => r.audioType == AudioType.Greeting); + newPhone.Settings.GreetingAudio = audio != null ? audio.path : ""; + + audio = files.Find(r => r.audioType == AudioType.HoldUp); + newPhone.Settings.HoldAudio = audio != null ? audio.path : ""; + + audio = files.Find(r => r.audioType == AudioType.VoiceMail); + newPhone.Settings.VoiceMail = audio != null ? audio.path : ""; + + audio = files.Find(r => r.audioType == AudioType.Queue); + newPhone.Settings.Queue.WaitUrl = audio != null ? audio.path : ""; + } + + /// + /// + /// + /// + /// Voip + /// + /// + [Delete(@"voip/numbers/{numberId:regex(\w+)}")] + public VoipPhone DeleteNumber(string numberId) + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + var dao = _daoFactory.GetVoipDao(); + var phone = dao.GetNumber(numberId).NotFoundIfNull(); + + _daoFactory.GetVoipDao().GetProvider().DisablePhone(phone); + dao.DeleteNumber(numberId); + + new SignalRHelper(phone.Number, _signalrServiceClient).Reload(); + + return phone; + } + + /// + /// + /// + /// + /// Voip + /// + /// + [Read(@"voip/numbers/{numberId:regex(\w+)}")] + public VoipPhone GetNumber(string numberId) + { + return _daoFactory.GetVoipDao().GetNumber(numberId).NotFoundIfNull(); + } + + /// + /// + /// + /// + /// Voip + /// + [Read(@"voip/numbers/current")] + public VoipPhone GetCurrentNumber() + { + return _daoFactory.GetVoipDao().GetCurrentNumber().NotFoundIfNull(); + } + + /// + /// + /// + /// + /// Voip + /// + [Read(@"voip/token")] + public string GetToken() + { + return _daoFactory.GetVoipDao().GetProvider().GetToken(GetCurrentNumber().Caller); + } + + /// + /// + /// + /// + /// Voip + /// + /// + [Update(@"voip/numbers/{numberId:regex(\w+)}/settings")] + public VoipPhone UpdateSettings(string numberId, string greeting, string holdUp, string wait, string voiceMail, WorkingHours workingHours, bool? allowOutgoingCalls, bool? record, string alias) + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + var dao = _daoFactory.GetVoipDao(); + var number = dao.GetNumber(numberId).NotFoundIfNull(); + + number.Alias = Update.IfNotEmptyAndNotEquals(number.Alias, alias); + number.Settings.GreetingAudio = Update.IfNotEmptyAndNotEquals(number.Settings.GreetingAudio, greeting); + number.Settings.HoldAudio = Update.IfNotEmptyAndNotEquals(number.Settings.HoldAudio, holdUp); + number.Settings.VoiceMail = Update.IfNotEmptyAndNotEquals(number.Settings.VoiceMail, voiceMail); + number.Settings.WorkingHours = Update.IfNotEmptyAndNotEquals(number.Settings.WorkingHours, workingHours); + + if (!string.IsNullOrEmpty(wait)) + { + number.Settings.Queue.WaitUrl = wait; + } + + if (allowOutgoingCalls.HasValue) + { + number.Settings.AllowOutgoingCalls = allowOutgoingCalls.Value; + if (!number.Settings.AllowOutgoingCalls) + { + number.Settings.Operators.ForEach(r => r.AllowOutgoingCalls = false); + } + } + + if (record.HasValue) + { + number.Settings.Record = record.Value; + if (!number.Settings.Record) + { + number.Settings.Operators.ForEach(r => r.Record = false); + } + } + + dao.SaveOrUpdateNumber(number); + + return number; + } + + /// + /// + /// + /// + /// Voip + /// + /// + [Update(@"voip/numbers/settings")] + public object UpdateSettings(Queue queue, bool pause) + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + var dao = _daoFactory.GetVoipDao(); + var numbers = dao.GetNumbers(); + + if (queue != null) + { + foreach (var number in numbers) + { + if (number.Settings.Queue == null || string.IsNullOrEmpty(number.Settings.Queue.Id)) + { + var phone = number as TwilioPhone; + if (phone != null) + { + queue = phone.CreateQueue(phone.Number, queue.Size, queue.WaitUrl, queue.WaitTime * 60); + } + + queue.Name = number.Number; + number.Settings.Queue = queue; + } + else + { + var oldQueue = number.Settings.Queue; + oldQueue.Size = Update.IfNotEmptyAndNotEquals(oldQueue.Size, queue.Size); + oldQueue.WaitTime = Update.IfNotEmptyAndNotEquals(oldQueue.WaitTime, queue.WaitTime * 60); + oldQueue.WaitUrl = Update.IfNotEmptyAndNotEquals(oldQueue.WaitUrl, queue.WaitUrl); + } + + number.Settings.Pause = pause; + + dao.SaveOrUpdateNumber(number); + } + } + + return new { queue, pause }; + } + + /// + /// + /// + /// + /// Voip + /// + /// + + [Read(@"voip/numbers/settings")] + public object GetVoipSettings() + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + var dao = _daoFactory.GetVoipDao(); + var number = dao.GetNumbers().FirstOrDefault(r => r.Settings.Queue != null); + if (number != null) + { + return new { queue = number.Settings.Queue, pause = number.Settings.Pause }; + } + + var files = _storageFactory.GetStorage("", "crm").ListFiles("voip", "default/" + AudioType.Queue.ToString().ToLower(), "*.*", true); + var file = files.FirstOrDefault(); + return new { queue = new Queue(null, "Default", 5, file != null ? _commonLinkUtility.GetFullAbsolutePath(file.ToString()) : "", 5), pause = false }; + } + + /// + /// + /// + /// + /// Voip + /// + /// + [Read(@"voip/uploads")] + public IEnumerable GetUploadedFilesUri() + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + var result = new List(); + + foreach (var audioType in Enum.GetNames(typeof(AudioType))) + { + var type = (AudioType)Enum.Parse(typeof(AudioType), audioType); + + var path = audioType.ToLower(); + var store = _global.GetStore(); + var filePaths = store.ListFilesRelative("voip", path, "*", true); + result.AddRange( + filePaths.Select(filePath => + GetVoipUpload(store.GetUri("voip", Path.Combine(path, filePath)), Path.GetFileName(filePath), type))); + + path = "default/" + audioType.ToLower(); + store = _storageFactory.GetStorage("", "crm"); + filePaths = store.ListFilesRelative("voip", path, "*.*", true); + result.AddRange( + filePaths.Select(filePath => + GetVoipUpload(store.GetUri("voip", Path.Combine(path, filePath)), Path.GetFileName(filePath), type, true))); + } + + return result; + } + + private VoipUpload GetVoipUpload(Uri link, string fileName, AudioType audioType, bool isDefault = false) + { + return new VoipUpload + { + Path = _commonLinkUtility.GetFullAbsolutePath(link.ToString()), + Name = fileName, + AudioType = audioType, + IsDefault = isDefault + }; + } + + /// + /// + /// + /// + /// Voip + /// + /// + /// + [Delete(@"voip/uploads")] + public VoipUpload DeleteUploadedFile(AudioType audioType, string fileName) + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + var store = _global.GetStore(); + var path = Path.Combine(audioType.ToString().ToLower(), fileName); + var result = new VoipUpload + { + AudioType = audioType, + Name = fileName, + Path = _commonLinkUtility.GetFullAbsolutePath(store.GetUri(path).ToString()) + }; + + if (!store.IsFile("voip", path)) throw new ItemNotFoundException(); + store.Delete("voip", path); + + var dao = _daoFactory.GetVoipDao(); + var numbers = dao.GetNumbers(); + + var defAudio = _storageFactory.GetStorage("", "crm").ListFiles("voip", "default/" + audioType.ToString().ToLower(), "*.*", true).FirstOrDefault(); + if (defAudio == null) return result; + + foreach (var number in numbers) + { + switch (audioType) + { + case AudioType.Greeting: + if (number.Settings.GreetingAudio == result.Path) + { + number.Settings.GreetingAudio = _commonLinkUtility.GetFullAbsolutePath(defAudio.ToString()); + } + break; + case AudioType.HoldUp: + if (number.Settings.HoldAudio == result.Path) + { + number.Settings.HoldAudio = _commonLinkUtility.GetFullAbsolutePath(defAudio.ToString()); + } + break; + case AudioType.Queue: + var queue = number.Settings.Queue; + if (queue != null && queue.WaitUrl == result.Path) + { + queue.WaitUrl = _commonLinkUtility.GetFullAbsolutePath(defAudio.ToString()); + } + break; + case AudioType.VoiceMail: + if (number.Settings.VoiceMail == result.Path) + { + number.Settings.VoiceMail = _commonLinkUtility.GetFullAbsolutePath(defAudio.ToString()); + } + break; + } + + dao.SaveOrUpdateNumber(number); + } + + return result; + } + + /// + /// + /// + /// + /// Voip + /// + /// + [Read(@"voip/numbers/{numberId:regex(\w+)}/oper")] + public IEnumerable GetOperators(string numberId) + { + return _daoFactory.GetVoipDao().GetNumber(numberId).Settings.Operators.Select(r => r.Id); + } + + /// + /// + /// + /// + /// Voip + /// + /// + /// + [Update(@"voip/numbers/{numberId:regex(\w+)}/oper")] + public IEnumerable AddOperators(string numberId, IEnumerable operators) + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + if (_daoFactory.GetVoipDao().GetNumbers().SelectMany(r => r.Settings.Operators).Any(r => operators.Contains(r.Id))) + { + throw new ArgumentException("Duplicate", "operators"); + } + + var dao = _daoFactory.GetVoipDao(); + var phone = dao.GetNumber(numberId); + var lastOper = phone.Settings.Operators.LastOrDefault(); + var startOperId = lastOper != null ? Convert.ToInt32(lastOper.PostFix) + 1 : 100; + + var addedOperators = operators.Select(o => new Agent(o, AnswerType.Client, phone, (startOperId++).ToString(CultureInfo.InvariantCulture))).ToList(); + phone.Settings.Operators.AddRange(addedOperators); + + dao.SaveOrUpdateNumber(phone); + return addedOperators; + } + + /// + /// + /// + /// + /// Voip + /// + /// + [Delete(@"voip/numbers/{numberId:regex(\w+)}/oper")] + public Guid DeleteOperator(string numberId, Guid oper) + { + if (!_crmSecurity.IsAdmin) throw _crmSecurity.CreateSecurityException(); + + var dao = _daoFactory.GetVoipDao(); + var phone = dao.GetNumber(numberId); + var startOperId = 100; + + phone.Settings.Operators.RemoveAll(r => r.Id == oper); + phone.Settings.Operators.ToList() + .ForEach(r => + { + r.PhoneNumber = phone.Number; + r.PostFix = startOperId.ToString(CultureInfo.InvariantCulture); + startOperId++; + }); + + dao.SaveOrUpdateNumber(phone); + return oper; + } + + /// + /// + /// + /// + /// Voip + /// + /// + [Update(@"voip/opers/{operatorId}")] + public Agent UpdateOperator(Guid operatorId, AgentStatus? status, bool? allowOutgoingCalls, bool? record, AnswerType? answerType, string redirectToNumber) + { + if (!_crmSecurity.IsAdmin && !operatorId.Equals(_securityContext.CurrentAccount.ID)) throw _crmSecurity.CreateSecurityException(); + + var dao = _daoFactory.GetVoipDao(); + var phone = dao.GetNumbers().FirstOrDefault(r => r.Settings.Operators.Exists(a => a.Id == operatorId)).NotFoundIfNull(); + + var oper = phone.Settings.Operators.Find(r => r.Id == operatorId); + + if (status.HasValue) + { + oper.Status = status.Value; + } + + if (allowOutgoingCalls.HasValue) + { + oper.AllowOutgoingCalls = phone.Settings.AllowOutgoingCalls && allowOutgoingCalls.Value; + } + + if (record.HasValue) + { + oper.Record = phone.Settings.Record && record.Value; + } + + if (answerType.HasValue) + { + oper.Answer = answerType.Value; + } + + if (!string.IsNullOrEmpty(redirectToNumber)) + { + oper.RedirectToNumber = redirectToNumber; + } + + dao.SaveOrUpdateNumber(phone); + + if (allowOutgoingCalls.HasValue) + { + new SignalRHelper(phone.Number, _signalrServiceClient).Reload(operatorId.ToString()); + } + + return oper; + } + + + /// + /// + /// + /// + /// Voip + /// + /// + [Create(@"voip/call")] + public VoipCallDto MakeCall([FromForm] string to, [FromForm] string contactId) + { + var number = _daoFactory.GetVoipDao().GetCurrentNumber().NotFoundIfNull(); + + if (!number.Settings.Caller.AllowOutgoingCalls) throw new SecurityException(CRMErrorsResource.AccessDenied); + + var contactPhone = to.TrimStart('+'); + + ContactDto contact; + + if (String.IsNullOrEmpty(contactId)) + { + var ids = _daoFactory.GetContactDao().GetContactIDsByContactInfo(ContactInfoType.Phone, contactPhone, null, null); + + contact = _daoFactory.GetContactDao().GetContacts(ids.ToArray()).ConvertAll(x => _mapper.Map(x)).FirstOrDefault(); + + } + else + { + contact = _mapper.Map(_daoFactory.GetContactDao().GetByID(Convert.ToInt32(contactId))); + } + + if (contact == null) + { + contact = _mapper.Map(_voipEngine.CreateContact(contactPhone)); + } + + contact = GetContactWithFotos(contact); + + var call = number.Call(to, contact.Id.ToString(CultureInfo.InvariantCulture)); + + return _mapper.Map(call); + + } + + /// + /// + /// + /// + /// Voip + /// + [Create(@"voip/call/{callId:regex(\w+)}/answer")] + public VoipCallDto AnswerCall([FromRoute] string callId) + { + var dao = _daoFactory.GetVoipDao(); + var call = dao.GetCall(callId).NotFoundIfNull(); + var number = dao.GetCurrentNumber().NotFoundIfNull(); + + number.AnswerQueueCall(call.Id); + + return _mapper.Map(call); + + } + + /// + /// + /// + /// + /// Voip + /// + [Create(@"voip/call/{callId:regex(\w+)}/reject")] + public VoipCallDto RejectCall([FromRoute] string callId) + { + var dao = _daoFactory.GetVoipDao(); + var call = dao.GetCall(callId).NotFoundIfNull(); + var number = dao.GetCurrentNumber().NotFoundIfNull(); + + number.RejectQueueCall(call.Id); + + return _mapper.Map(call); + } + + /// + /// + /// + /// + /// Voip + /// + [Create(@"voip/call/{callId:regex(\w+)}/redirect")] + public VoipCallDto ReditectCall([FromRoute] string callId, [FromForm] string to) + { + var dao = _daoFactory.GetVoipDao(); + var call = dao.GetCall(callId).NotFoundIfNull(); + var number = dao.GetCurrentNumber().NotFoundIfNull(); + + if (call.ContactId != 0) + { + var contact = _daoFactory.GetContactDao().GetByID(call.ContactId); + var managers = _crmSecurity.GetAccessSubjectGuidsTo(contact); + + if (!managers.Contains(Guid.Parse(to))) + { + managers.Add(Guid.Parse(to)); + _crmSecurity.SetAccessTo(contact, managers); + } + } + + number.RedirectCall(call.Id, to); + + return _mapper.Map(call); + } + + /// + /// + /// + /// + /// Voip + /// + [Create(@"voip/call/{callId:regex(\w+)}")] + public VoipCallDto SaveCall( + [FromRoute] string callId, + [FromForm] string from, + [FromForm] string to, + [FromForm] Guid answeredBy, + [FromForm] VoipCallStatus? status, + [FromForm] string contactId, + [FromForm] decimal? price) + { + var dao = _daoFactory.GetVoipDao(); + + var call = dao.GetCall(callId) ?? new VoipCall(); + + call.Id = callId; + call.From = Update.IfNotEmptyAndNotEquals(call.From, from); + call.To = Update.IfNotEmptyAndNotEquals(call.To, to); + call.AnsweredBy = Update.IfNotEmptyAndNotEquals(call.AnsweredBy, answeredBy); + + try + { + if (call.ContactId == 0) + { + var contactPhone = call.Status == VoipCallStatus.Incoming || call.Status == VoipCallStatus.Answered ? call.From : call.To; + if (!string.IsNullOrEmpty(contactId)) + { + call.ContactId = Convert.ToInt32(contactId); + } + else + { + _voipEngine.GetContact(call); + } + + if (call.ContactId == 0) + { + contactPhone = contactPhone.TrimStart('+'); + + var peopleInst = new Person + { + FirstName = contactPhone, + LastName = _tenantUtil.DateTimeFromUtc(DateTime.UtcNow).ToString("yyyy-MM-dd hh:mm"), + ShareType = ShareType.None + }; + + peopleInst.ID = _daoFactory.GetContactDao().SaveContact(peopleInst); + + _crmSecurity.SetAccessTo(peopleInst, new List { _securityContext.CurrentAccount.ID }); + + var person = (PersonDto)_mapper.Map(peopleInst); + + _daoFactory.GetContactInfoDao().Save(new ContactInfo { ContactID = person.Id, IsPrimary = true, InfoType = ContactInfoType.Phone, Data = contactPhone }); + + call.ContactId = person.Id; + } + } + } + catch (Exception) + { + + } + + if (status.HasValue) + { + call.Status = status.Value; + } + + if (call.Price == 0 && price.HasValue) + { + call.Price = price.Value; + } + + call = dao.SaveOrUpdateCall(call); + + return _mapper.Map(call); + + } + + /// + /// + /// + /// + /// Voip + /// + [Create(@"voip/price/{callId:regex(\w+)}")] + public void SavePrice([FromRoute] string callId) + { + _voipEngine.SaveAdditionalInfo(callId); + } + + /// + /// + /// + /// + /// Voip + /// + [Read(@"voip/call")] + public IEnumerable GetCalls(string callType, ApiDateTime from, ApiDateTime to, Guid? agent, int? client, int? contactID) + { + var voipDao = _daoFactory.GetVoipDao(); + + var filter = new VoipCallFilter + { + Type = callType, + FromDate = from != null ? from.UtcTime : (DateTime?)null, + ToDate = to != null ? to.UtcTime.AddDays(1).AddMilliseconds(-1) : (DateTime?)null, + Agent = agent, + Client = client, + ContactID = contactID, + SortBy = _apiContext.SortBy, + SortOrder = !_apiContext.SortDescending, + SearchText = _apiContext.FilterValue, + Offset = _apiContext.StartIndex, + Max = _apiContext.Count, + }; + + _apiContext.SetDataPaginated(); + _apiContext.SetDataFiltered(); + _apiContext.SetDataSorted(); + _apiContext.TotalCount = voipDao.GetCallsCount(filter); + + var defaultSmallPhoto = _contactPhotoManager.GetSmallSizePhoto(-1, false); + var calls = voipDao.GetCalls(filter).Select( + r => + { + ContactDto contact; + + if (r.ContactId != 0) + { + contact = r.ContactIsCompany + ? (ContactDto)new CompanyDto() { DisplayName = r.ContactTitle, Id = r.ContactId } + : new PersonDto() + { + DisplayName = r.ContactTitle, + Id = r.ContactId + }; + + contact.SmallFotoUrl = _contactPhotoManager.GetSmallSizePhoto(contact.Id, contact.IsCompany); + + } + else + { + contact = new PersonDto() { SmallFotoUrl = defaultSmallPhoto, Id = -1 }; + } + + var item = _mapper.Map(r); + + item.Contact = contact; + + return item; + + }).ToList(); + + return calls; + } + + /// + /// + /// + /// + /// Voip + /// + [Read(@"voip/call/missed")] + public IEnumerable GetMissedCalls() + { + var voipDao = _daoFactory.GetVoipDao(); + var defaultSmallPhoto = _contactPhotoManager.GetSmallSizePhoto(-1, false); + + var calls = voipDao.GetMissedCalls(_securityContext.CurrentAccount.ID, 10, DateTime.UtcNow.AddDays(-7)).Select( + r => + { + ContactDto contact; + + if (r.ContactId != 0) + { + contact = r.ContactIsCompany + ? (ContactDto)new CompanyDto() { DisplayName = r.ContactTitle, Id = r.ContactId } + : new PersonDto() { DisplayName = r.ContactTitle, Id = r.ContactId }; + + contact.SmallFotoUrl = _contactPhotoManager.GetSmallSizePhoto(contact.Id, contact.IsCompany); + + } + else + { + contact = new PersonDto() { SmallFotoUrl = defaultSmallPhoto, Id = -1 }; + } + + var item = _mapper.Map(r); + + item.Contact = contact; + + return item; + + }).ToList(); + + _apiContext.SetDataPaginated(); + _apiContext.SetDataFiltered(); + _apiContext.SetDataSorted(); + _apiContext.TotalCount = calls.Count; + + return calls; + } + + /// + /// + /// + /// + /// Voip + /// + [Read(@"voip/call/{callId:regex(\w+)}")] + public VoipCallDto GetCall(string callId) + { + var call = _daoFactory.GetVoipDao().GetCall(callId); + + _voipEngine.GetContact(call); + + return _mapper.Map(call); + } + + private ContactDto GetContactWithFotos(ContactDto contact) + { + contact.SmallFotoUrl = _contactPhotoManager.GetSmallSizePhoto(contact.Id, contact.IsCompany); + contact.MediumFotoUrl = _contactPhotoManager.GetMediumSizePhoto(contact.Id, contact.IsCompany); + + return contact; + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/ApiModels/RequestsDto/AddHistoryToRequestDto.cs b/products/ASC.CRM/Server/ApiModels/RequestsDto/AddHistoryToRequestDto.cs new file mode 100644 index 00000000000..fb2031da167 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/RequestsDto/AddHistoryToRequestDto.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; + +using ASC.Api.Core; + +namespace ASC.CRM.ApiModels +{ + public class AddHistoryToRequestDto + { + public String EntityType { get; set; } + public int EntityId { get; set; } + public int ContactId { get; set; } + public string Content { get; set; } + public int CategoryId { get; set; } + public ApiDateTime Created { get; set; } + public IEnumerable FileId { get; set; } + public IEnumerable NotifyUserList { get; set; } + } +} diff --git a/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdateCasesRequestDto.cs b/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdateCasesRequestDto.cs new file mode 100644 index 00000000000..0741f74ed79 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdateCasesRequestDto.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; + +using ASC.Api.Collections; + +namespace ASC.CRM.ApiModels +{ + public class CreateOrUpdateCasesRequestDto + { + public string Title { get; set; } + public IEnumerable Members { get; set; } + public IEnumerable> CustomFieldList { get; set; } + public bool isPrivate { get; set; } + public IEnumerable accessList { get; set; } + public bool isNotify { get; set; } + } +} diff --git a/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdateDealRequestDto.cs b/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdateDealRequestDto.cs new file mode 100644 index 00000000000..09e58547f62 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdateDealRequestDto.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; + +using ASC.Api.Collections; +using ASC.Api.Core; +using ASC.CRM.Core.Enums; + +namespace ASC.CRM.ApiModels +{ + public class CreateOrUpdateDealRequestDto + { + public int Contactid { get; set; } + public IEnumerable Members { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public Guid Responsibleid { get; set; } + public BidType BidType { get; set; } + public decimal BidValue { get; set; } + public string BidCurrencyAbbr { get; set; } + public int PerPeriodValue { get; set; } + public int Stageid { get; set; } + public int SuccessProbability { get; set; } + public ApiDateTime ActualCloseDate { get; set; } + public ApiDateTime ExpectedCloseDate { get; set; } + public IEnumerable> CustomFieldList { get; set; } + public bool isPrivate { get; set; } + public IEnumerable AccessList { get; set; } + public bool isNotify { get; set; } + } +} diff --git a/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdateInvoiceItemRequestDto.cs b/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdateInvoiceItemRequestDto.cs new file mode 100644 index 00000000000..19af5d62a8c --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdateInvoiceItemRequestDto.cs @@ -0,0 +1,16 @@ +namespace ASC.CRM.ApiModels +{ + public class CreateOrUpdateInvoiceItemRequestDto + { + public string Title { get; set; } + public string Description { get; set; } + public decimal Price { get; set; } + public string Sku { get; set; } + public int Quantity { get; set; } + public int StockQuantity { get; set; } + public bool TrackInventory { get; set; } + public int InvoiceTax1id { get; set; } + public int InvoiceTax2id { get; set; } + + } +} diff --git a/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdateInvoiceLineRequestDto.cs b/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdateInvoiceLineRequestDto.cs new file mode 100644 index 00000000000..24f8db3a82d --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdateInvoiceLineRequestDto.cs @@ -0,0 +1,15 @@ +namespace ASC.CRM.ApiModels +{ + public class CreateOrUpdateInvoiceLineRequestDto + { + public int InvoiceId { get; set; } + public int InvoiceItemId { get; set; } + public int InvoiceTax1Id { get; set; } + public int InvoiceTax2Id { get; set; } + public int SortOrder { get; set; } + public string Description { get; set; } + public int Quantity { get; set; } + public decimal Price { get; set; } + public int Discount { get; set; } + } +} diff --git a/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdateInvoiceRequestDto.cs b/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdateInvoiceRequestDto.cs new file mode 100644 index 00000000000..11d84ecc231 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdateInvoiceRequestDto.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; + +using ASC.Api.Core; +using ASC.CRM.Core.Entities; + +namespace ASC.CRM.ApiModels +{ + public class CreateOrUpdateInvoiceRequestDto + { + public string Number { get; set; } + public ApiDateTime IssueDate { get; set; } + public int TemplateType { get; set; } + public int ContactId { get; set; } + public int ConsigneeId { get; set; } + public int EntityId { get; set; } + public int BillingAddressID { get; set; } + public int DeliveryAddressID { get; set; } + public ApiDateTime DueDate { get; set; } + public string Language { get; set; } + public string Currency { get; set; } + public decimal ExchangeRate { get; set; } + public string PurchaseOrderNumber { get; set; } + public string Terms { get; set; } + public string Description { get; set; } + public IEnumerable InvoiceLines { get; set; } + } +} diff --git a/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdateInvoiceTaxRequestDto.cs b/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdateInvoiceTaxRequestDto.cs new file mode 100644 index 00000000000..9b340d10fa5 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdateInvoiceTaxRequestDto.cs @@ -0,0 +1,9 @@ +namespace ASC.CRM.ApiModels +{ + public class CreateOrUpdateInvoiceTaxRequestDto + { + public string Name { get; set; } + public string Description { get; set; } + public decimal Rate { get; set; } + } +} diff --git a/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdatePersonRequestDto.cs b/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdatePersonRequestDto.cs new file mode 100644 index 00000000000..7565b89a567 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/RequestsDto/CreateOrUpdatePersonRequestDto.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; + +using ASC.Api.Collections; +using ASC.CRM.Core.Enums; + +using Microsoft.AspNetCore.Http; + +namespace ASC.CRM.ApiModels +{ + public class CreateOrUpdateContactRequestDto + { + public string About { get; set; } + public ShareType ShareType { get; set; } + public IEnumerable ManagerList { get; set; } + public IEnumerable> CustomFieldList { get; set; } + public IEnumerable Photos { get; set; } + } + + public class CreateOrUpdateCompanyRequestDto : CreateOrUpdateContactRequestDto + { + public String CompanyName { get; set; } + + public IEnumerable PersonList { get; set; } + + } + + public class CreateOrUpdatePersonRequestDto : CreateOrUpdateContactRequestDto + { + public String FirstName { get; set; } + public String LastName { get; set; } + public String JobTitle { get; set; } + public int CompanyId { get; set; } + } +} + + diff --git a/products/ASC.CRM/Server/ApiModels/RequestsDto/SaveNumberSettingsRequestDto.cs b/products/ASC.CRM/Server/ApiModels/RequestsDto/SaveNumberSettingsRequestDto.cs new file mode 100644 index 00000000000..03fb76eff19 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/RequestsDto/SaveNumberSettingsRequestDto.cs @@ -0,0 +1,9 @@ +namespace ASC.CRM.ApiModels +{ + public class SaveNumberSettingsRequestDto + { + public bool AutoGenerated { get; set; } + public string Prefix { get; set; } + public string Number { get; set; } + } +} diff --git a/products/ASC.CRM/Server/ApiModels/RequestsDto/SendMailSMTPToContactsRequestDto.cs b/products/ASC.CRM/Server/ApiModels/RequestsDto/SendMailSMTPToContactsRequestDto.cs new file mode 100644 index 00000000000..b77a3a2f406 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/RequestsDto/SendMailSMTPToContactsRequestDto.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +namespace ASC.CRM.ApiModels +{ + public class SendMailSMTPToContactsRequestDto + { + public List FileIDs { get; set; } + public List ContactIds { get; set; } + public String Subject { get; set; } + public String Body { get; set; } + public bool StoreInHistory { get; set; } + } +} diff --git a/products/ASC.CRM/Server/ApiModels/RequestsDto/SetAccessToBatchCasesRequestDto.cs b/products/ASC.CRM/Server/ApiModels/RequestsDto/SetAccessToBatchCasesRequestDto.cs new file mode 100644 index 00000000000..f24a87f62a4 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/RequestsDto/SetAccessToBatchCasesRequestDto.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; + +namespace ASC.CRM.ApiModels +{ + public class SetAccessToBatchCasesByFilterInDto + { + public int Contactid { get; set; } + public bool? isClosed { get; set; } + public IEnumerable Tags { get; set; } + public bool isPrivate { get; set; } + public IEnumerable AccessList { get; set; } + + } + + public class SetAccessToBatchCasesRequestDto + { + public IEnumerable CasesId { get; set; } + public bool isPrivate { get; set; } + public IEnumerable AccessList { get; set; } + } +} diff --git a/products/ASC.CRM/Server/ApiModels/RequestsDto/SetAccessToBatchContactRequestDto.cs b/products/ASC.CRM/Server/ApiModels/RequestsDto/SetAccessToBatchContactRequestDto.cs new file mode 100644 index 00000000000..0874b950b86 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/RequestsDto/SetAccessToBatchContactRequestDto.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; + +using ASC.Api.Core; +using ASC.CRM.Core.Enums; + +namespace ASC.CRM.ApiModels +{ + public class SetAccessToBatchContactByFilterRequestDto + { + public IEnumerable Tags { get; set; } + public int? ContactStage { get; set; } + public int? ContactType { get; set; } + public ContactListViewType ContactListView { get; set; } + public ApiDateTime FromDate { get; set; } + public ApiDateTime ToDate { get; set; } + public bool isPrivate { get; set; } + public IEnumerable ManagerList { get; set; } + } + + public class SetAccessToBatchContactRequestDto + { + public IEnumerable ContactID { get; set; } + + public bool isShared { get; set; } + + public IEnumerable ManagerList { get; set; } + } +} diff --git a/products/ASC.CRM/Server/ApiModels/RequestsDto/SetAccessToBatchDealByFilterRequestDto.cs b/products/ASC.CRM/Server/ApiModels/RequestsDto/SetAccessToBatchDealByFilterRequestDto.cs new file mode 100644 index 00000000000..6d60c671807 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/RequestsDto/SetAccessToBatchDealByFilterRequestDto.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; + +using ASC.Api.Core; +using ASC.CRM.Core.Enums; + +namespace ASC.CRM.ApiModels +{ + public class SetAccessToBatchDealByFilterRequestDto + { + public Guid Responsibleid { get; set; } + public int OpportunityStagesid { get; set; } + public IEnumerable Tags { get; set; } + public int Contactid { get; set; } + public DealMilestoneStatus? StageType { get; set; } + public bool? ContactAlsoIsParticipant { get; set; } + public ApiDateTime FromDate { get; set; } + public ApiDateTime ToDate { get; set; } + public bool isPrivate { get; set; } + public IEnumerable AccessList { get; set; } + } + + public class SetAccessToBatchDealRequestDto + { + public IEnumerable Opportunityid { get; set; } + public bool isPrivate { get; set; } + public IEnumerable AccessList { get; set; } + } +} diff --git a/products/ASC.CRM/Server/ApiModels/RequestsDto/UploadFileInCRMRequestDto.cs b/products/ASC.CRM/Server/ApiModels/RequestsDto/UploadFileInCRMRequestDto.cs new file mode 100644 index 00000000000..7d82b6a420e --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/RequestsDto/UploadFileInCRMRequestDto.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using System.IO; +using System.Net.Mime; + +using Microsoft.AspNetCore.Http; + +namespace ASC.CRM.ApiModels +{ + public class UploadFileInCRMRequestDto + { + public string EntityType { get; set; } + public int Entityid { get; set; } + public Stream File { get; set; } + public ContentType ContentType { get; set; } + public ContentDisposition ContentDisposition { get; set; } + public IEnumerable Files { get; set; } + public bool StoreOriginalFileFlag { get; set; } + } +} diff --git a/products/ASC.CRM/Server/ApiModels/ResponsesDto/CasesDto.cs b/products/ASC.CRM/Server/ApiModels/ResponsesDto/CasesDto.cs new file mode 100644 index 00000000000..e284215a227 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/ResponsesDto/CasesDto.cs @@ -0,0 +1,78 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; + +using ASC.Api.Core; +using ASC.Common.Mapping; +using ASC.CRM.Core.Entities; +using ASC.CRM.Mapping; +using ASC.Web.Api.Models; + +using AutoMapper; + +namespace ASC.CRM.ApiModels +{ + public class CasesDto : IMapFrom + { + public CasesDto() + { + } + + + public int Id { get; set; } + public IEnumerable Members { get; set; } + public EmployeeWraper CreateBy { get; set; } + public ApiDateTime Created { get; set; } + public String Title { get; set; } + public bool IsClosed { get; set; } + public bool IsPrivate { get; set; } + public IEnumerable AccessList { get; set; } + public bool CanEdit { get; set; } + public IEnumerable CustomFields { get; set; } + public static CasesDto GetSample() + { + return new CasesDto + { + IsClosed = false, + Title = "Exhibition organization", + Created = ApiDateTime.GetSample(), + CreateBy = EmployeeWraper.GetSample(), + IsPrivate = false, + CustomFields = new[] { CustomFieldBaseDto.GetSample() }, + CanEdit = true + }; + } + + public void Mapping(Profile profile) + { + profile.CreateMap().ConvertUsing(); + } + } +} + + diff --git a/products/ASC.CRM/Server/ApiModels/ResponsesDto/ContactDto.cs b/products/ASC.CRM/Server/ApiModels/ResponsesDto/ContactDto.cs new file mode 100644 index 00000000000..ffa3dc7f065 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/ResponsesDto/ContactDto.cs @@ -0,0 +1,254 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; + +using System.Text.Json; +using System.Text.Json.Serialization; + +using ASC.Api.Core; +using ASC.Common; +using ASC.Common.Mapping; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Mapping; +using ASC.Web.Api.Models; + +using AutoMapper; + +namespace ASC.CRM.ApiModels +{ + public class PersonDto : ContactDto + { + public String FirstName { get; set; } + public String LastName { get; set; } + public ContactBaseDto Company { get; set; } + public String Title { get; set; } + public new static PersonDto GetSample() + { + return new PersonDto + { + IsPrivate = true, + IsShared = false, + IsCompany = false, + FirstName = "Tadjeddine", + LastName = "Bachir", + Company = CompanyDto.GetSample(), + Title = "Programmer", + About = "", + Created = ApiDateTime.GetSample(), + CreateBy = EmployeeWraper.GetSample(), + ShareType = ShareType.None + }; + } + } + + [Scope] + public class PersonDtoHelper + { + public PersonDto Get(Person person) + { + return new PersonDto + { + FirstName = person.FirstName, + LastName = person.LastName, + Title = person.JobTitle + }; + } + + public PersonDto GetQuick(Person person) + { + return new PersonDto + { + FirstName = person.FirstName, + LastName = person.LastName, + Title = person.JobTitle + }; + } + } + + public class CompanyDto : ContactDto + { + public String CompanyName { get; set; } + public IEnumerable Persons { get; set; } + public int PersonsCount { get; set; } + public new static CompanyDto GetSample() + { + return new CompanyDto + { + IsPrivate = true, + IsShared = false, + IsCompany = true, + About = "", + CompanyName = "Food and Culture Project", + PersonsCount = 0 + }; + } + } + + public class ContactDto : ContactBaseDto, IMapFrom + { + public ContactDto() + { + + } + + public IEnumerable
    Addresses { get; set; } + public EmployeeWraper CreateBy { get; set; } + public ApiDateTime Created { get; set; } + public String About { get; set; } + public String Industry { get; set; } + public ContactStatusBaseDto ContactStatus { get; set; } + public ContactTypeBaseDto ContactType { get; set; } + public IEnumerable CommonData { get; set; } + public IEnumerable CustomFields { get; set; } + public IEnumerable Tags { get; set; } + public int TaskCount { get; set; } + public bool HaveLateTasks { get; set; } + public new static ContactDto GetSample() + { + return new PersonDto + { + IsPrivate = true, + IsShared = false, + IsCompany = false, + FirstName = "Tadjeddine", + LastName = "Bachir", + Company = CompanyDto.GetSample(), + Title = "Programmer", + About = "", + Created = ApiDateTime.GetSample(), + CreateBy = EmployeeWraper.GetSample(), + CommonData = new List() { ContactInfoDto.GetSample() }, + CustomFields = new List() { CustomFieldBaseDto.GetSample() }, + ShareType = ShareType.None, + CanDelete = true, + CanEdit = true, + TaskCount = 0, + HaveLateTasks = false + }; + } + + public void Mapping(Profile profile) + { + + profile.CreateMap().ConvertUsing(); + profile.CreateMap, List>().ConvertUsing(); + profile.CreateMap().ConvertUsing(); + profile.CreateMap().ConvertUsing(); + profile.CreateMap().ConvertUsing(); + + profile.CreateMap().ConvertUsing(); + profile.CreateMap().ConvertUsing(); + } + } + + public class ContactBaseWithEmailDto : ContactBaseDto + { + public ContactInfoDto Email { get; set; } + } + + public class ContactBaseWithPhoneDto : ContactBaseDto + { + public ContactInfoDto Phone { get; set; } + } + + public class ContactDtoJsonConverter : JsonConverter + { + public override ContactDto Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + throw new NotImplementedException(); + } + + public override void Write(Utf8JsonWriter writer, ContactDto value, JsonSerializerOptions options) + { + if (value is PersonDto) + { + JsonSerializer.Serialize(writer, (PersonDto)value!, options); + + return; + } + + if (value is CompanyDto) + { + JsonSerializer.Serialize(writer, (CompanyDto)value!, options); + + return; + } + + if (value is ContactDto) + { + JsonSerializer.Serialize(writer, value!, options); + + return; + } + + throw new NotImplementedException(); + } + } + + /// + /// Contact base information + /// + public class ContactBaseDto + { + public ContactBaseDto() + { + + } + + public int Id { get; set; } + public String SmallFotoUrl { get; set; } + public String MediumFotoUrl { get; set; } + public String DisplayName { get; set; } + public bool IsCompany { get; set; } + public IEnumerable AccessList { get; set; } + public bool IsPrivate { get; set; } + public bool IsShared { get; set; } + public ShareType ShareType { get; set; } + public CurrencyInfoDto Currency { get; set; } + public bool CanEdit { get; set; } + public bool CanDelete { get; set; } + public static ContactBaseDto GetSample() + { + return new ContactBaseDto + { + IsPrivate = true, + IsShared = false, + IsCompany = false, + DisplayName = "Tadjeddine Bachir", + SmallFotoUrl = "url to foto" + }; + } + } + + public class ContactWithTaskDto + { + public TaskBaseDto Task { get; set; } + public ContactDto Contact { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/ApiModels/ResponsesDto/ContactInfoDto.cs b/products/ASC.CRM/Server/ApiModels/ResponsesDto/ContactInfoDto.cs new file mode 100644 index 00000000000..339a78bd6b9 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/ResponsesDto/ContactInfoDto.cs @@ -0,0 +1,146 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +using System.Text.Json; + +using ASC.Common.Mapping; +using ASC.CRM.Classes; +using ASC.CRM.Core; +using ASC.CRM.Core.Enums; + +using AutoMapper; + +namespace ASC.CRM.ApiModels +{ + /// + /// Address + /// + public class Address + { + public Address() + { + } + + public Address(ContactInfo contactInfo) + { + if (contactInfo.InfoType != ContactInfoType.Address) throw new ArgumentException(); + + var jsonElement = JsonDocument.Parse(contactInfo.Data).RootElement; + + City = jsonElement.GetProperty("city").GetString(); + Country = jsonElement.GetProperty("country").GetString(); + State = jsonElement.GetProperty("state").GetString(); + Street = jsonElement.GetProperty("street").GetString(); + Zip = jsonElement.GetProperty("zip").GetString(); + Category = contactInfo.Category; + CategoryName = contactInfo.CategoryToString(); + IsPrimary = contactInfo.IsPrimary; + } + + public static bool TryParse(ContactInfo contactInfo, out Address res) + { + if (contactInfo.InfoType != ContactInfoType.Address) + { + res = null; + return false; + } + + try + { + res = JsonSerializer.Deserialize
    (contactInfo.Data); + res.Category = contactInfo.Category; + res.CategoryName = contactInfo.CategoryToString(); + res.IsPrimary = contactInfo.IsPrimary; + return true; + } + catch (Exception) + { + res = null; + return false; + } + } + + public String Street { get; set; } + public String City { get; set; } + public String State { get; set; } + public String Zip { get; set; } + public String Country { get; set; } + public int Category { get; set; } + public String CategoryName { get; set; } + public Boolean IsPrimary { get; set; } + + public static Address GetSample() + { + return new Address + { + Country = "Latvia", + Zip = "LV-1021", + Street = "Lubanas st. 125a-25", + State = "", + City = "Riga", + IsPrimary = true, + Category = (int)ContactInfoBaseCategory.Work, + CategoryName = ((AddressCategory)ContactInfoBaseCategory.Work).ToLocalizedString() + }; + } + } + + /// + /// Contact information + /// + public class ContactInfoDto : IMapFrom + { + public ContactInfoDto() + { + } + + public int Id { get; set; } + public ContactInfoType InfoType { get; set; } + public int Category { get; set; } + public String Data { get; set; } + public String CategoryName { get; set; } + public bool IsPrimary { get; set; } + + public static ContactInfoDto GetSample() + { + return new ContactInfoDto + { + IsPrimary = true, + Category = (int)ContactInfoBaseCategory.Home, + CategoryName = ContactInfoBaseCategory.Home.ToLocalizedString(), + Data = "support@onlyoffice.com", + InfoType = ContactInfoType.Email + }; + } + + public void Mapping(Profile profile) + { + profile.CreateMap(); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/ApiModels/ResponsesDto/CurrencyInfoDto.cs b/products/ASC.CRM/Server/ApiModels/ResponsesDto/CurrencyInfoDto.cs new file mode 100644 index 00000000000..10bbaaf3ba8 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/ResponsesDto/CurrencyInfoDto.cs @@ -0,0 +1,87 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + + +using ASC.Common.Mapping; +using ASC.Core.Common.EF; +using ASC.CRM.Core; +using ASC.Web.Core.Calendars; + +using AutoMapper; + +namespace ASC.CRM.ApiModels +{ + /// + /// Currency information + /// + public class CurrencyInfoDto : IMapFrom + { + public CurrencyInfoDto() + { + + + } + public String Title { get; set; } + public String Symbol { get; set; } + public String Abbreviation { get; set; } + public String CultureName { get; set; } + public bool IsConvertable { get; set; } + public bool IsBasic { get; set; } + public static CurrencyInfoDto GetSample() + { + return new CurrencyInfoDto + { + Title = "Chinese Yuan", + Abbreviation = "CNY", + Symbol = "¥", + CultureName = "CN", + IsConvertable = true, + IsBasic = false + }; + } + + } + + /// + /// Currency rate information + /// + public class CurrencyRateInfoDto : CurrencyInfoDto, IMapFrom + { + public CurrencyRateInfoDto() + { + } + + //public CurrencyRateInfoDto(CurrencyInfo currencyInfo, Decimal rate) + // : base(currencyInfo) + //{ + // Rate = rate; + //} + + public decimal Rate { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/ApiModels/ResponsesDto/CurrencyRateDto.cs b/products/ASC.CRM/Server/ApiModels/ResponsesDto/CurrencyRateDto.cs new file mode 100644 index 00000000000..a747a8c8804 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/ResponsesDto/CurrencyRateDto.cs @@ -0,0 +1,67 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + + +using ASC.Common.Mapping; +using ASC.CRM.Core; + +using AutoMapper; + +namespace ASC.CRM.ApiModels +{ + /// + /// Currency rate + /// + public class CurrencyRateDto : IMapFrom + { + public CurrencyRateDto() + { + } + + public int Id { get; set; } + public String FromCurrency { get; set; } + public String ToCurrency { get; set; } + public decimal Rate { get; set; } + public static CurrencyRateDto GetSample() + { + return new CurrencyRateDto + { + Id = 1, + FromCurrency = "EUR", + ToCurrency = "USD", + Rate = (decimal)1.1 + }; + } + + public void Mapping(Profile profile) + { + profile.CreateMap(); + } + + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/ApiModels/ResponsesDto/CustomFieldDto.cs b/products/ASC.CRM/Server/ApiModels/ResponsesDto/CustomFieldDto.cs new file mode 100644 index 00000000000..38c43b5c16e --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/ResponsesDto/CustomFieldDto.cs @@ -0,0 +1,99 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + + +using ASC.Api.Core; +using ASC.Common.Mapping; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Mapping; + +using AutoMapper; + +namespace ASC.CRM.ApiModels +{ + public class CustomFieldDto : CustomFieldBaseDto, IMapFrom + { + public CustomFieldDto() + { + + } + + public int RelativeItemsCount { get; set; } + + public new static CustomFieldDto GetSample() + { + return new CustomFieldDto + { + Position = 10, + EntityId = 14523423, + FieldType = CustomFieldType.Date, + FieldValue = ApiDateTime.GetSample().ToString(), + Label = "Birthdate", + Mask = "", + RelativeItemsCount = 0 + }; + } + + public void Mapping(Profile profile) + { + profile.CreateMap() + .ConvertUsing(); + } + } + + /// + /// User custom fields + /// + public class CustomFieldBaseDto : IMapFrom + { + public CustomFieldBaseDto() + { + + } + public int Id { get; set; } + public int EntityId { get; set; } + public String Label { get; set; } + public String FieldValue { get; set; } + public CustomFieldType FieldType { get; set; } + public int Position { get; set; } + public String Mask { get; set; } + public static CustomFieldBaseDto GetSample() + { + return new CustomFieldBaseDto + { + Position = 10, + EntityId = 14523423, + FieldType = CustomFieldType.Date, + FieldValue = ApiDateTime.GetSample().ToString(), + Label = "Birthdate", + Mask = "" + }; + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/ApiModels/ResponsesDto/InvoiceDto.cs b/products/ASC.CRM/Server/ApiModels/ResponsesDto/InvoiceDto.cs new file mode 100644 index 00000000000..f8c9a029835 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/ResponsesDto/InvoiceDto.cs @@ -0,0 +1,105 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using System.Collections.Generic; + + +using ASC.Api.Core; +using ASC.Common.Mapping; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Mapping; +using ASC.Web.Api.Models; + +using AutoMapper; + +namespace ASC.CRM.ApiModels +{ + /// + /// Invoice + /// + public class InvoiceBaseDto : IMapFrom + { + public int Id { get; set; } + public InvoiceStatusDto Status { get; set; } + public string Number { get; set; } + public ApiDateTime IssueDate { get; set; } + public InvoiceTemplateType TemplateType { get; set; } + public ContactBaseWithEmailDto Contact { get; set; } + public ContactBaseWithEmailDto Consignee { get; set; } + public EntityDto Entity { get; set; } + public ApiDateTime DueDate { get; set; } + public string Language { get; set; } + public CurrencyInfoDto Currency { get; set; } + public decimal ExchangeRate { get; set; } + public string PurchaseOrderNumber { get; set; } + public string Terms { get; set; } + public string Description { get; set; } + public int FileID { get; set; } + public ApiDateTime CreateOn { get; set; } + public EmployeeWraper CreateBy { get; set; } + public decimal Cost { get; set; } + public bool CanEdit { get; set; } + public bool CanDelete { get; set; } + + public void Mapping(Profile profile) + { + profile.CreateMap().ConvertUsing(); + } + } + + /// + /// Invoice + /// + public class InvoiceDto : InvoiceBaseDto + { + public List InvoiceLines { get; set; } + + public static InvoiceDto GetSample() + { + return new InvoiceDto + { + Status = InvoiceStatusDto.GetSample(), + Number = string.Empty, + IssueDate = ApiDateTime.GetSample(), + TemplateType = InvoiceTemplateType.Eur, + Language = string.Empty, + DueDate = ApiDateTime.GetSample(), + Currency = CurrencyInfoDto.GetSample(), + ExchangeRate = (decimal)1.00, + PurchaseOrderNumber = string.Empty, + Terms = string.Empty, + Description = string.Empty, + FileID = -1, + CreateOn = ApiDateTime.GetSample(), + CreateBy = EmployeeWraper.GetSample(), + CanEdit = true, + CanDelete = true, + Cost = 0, + InvoiceLines = new List { InvoiceLineDto.GetSample() } + }; + } + } +} diff --git a/products/ASC.CRM/Server/ApiModels/ResponsesDto/InvoiceItemDto.cs b/products/ASC.CRM/Server/ApiModels/ResponsesDto/InvoiceItemDto.cs new file mode 100644 index 00000000000..fadf9a6fcba --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/ResponsesDto/InvoiceItemDto.cs @@ -0,0 +1,62 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + + +using ASC.Api.Core; +using ASC.Common.Mapping; +using ASC.CRM.Core.Entities; +using ASC.CRM.Mapping; +using ASC.Web.Api.Models; + +using AutoMapper; + +namespace ASC.CRM.ApiModels +{ + /// + /// Invoice Item + /// + public class InvoiceItemDto : IMapFrom + { + public string Title { get; set; } + public string StockKeepingUnit { get; set; } + public string Description { get; set; } + public decimal Price { get; set; } + public CurrencyInfoDto Currency { get; set; } + public decimal StockQuantity { get; set; } + public bool TrackInvenory { get; set; } + public InvoiceTaxDto InvoiceTax1 { get; set; } + public InvoiceTaxDto InvoiceTax2 { get; set; } + public ApiDateTime CreateOn { get; set; } + public EmployeeWraper CreateBy { get; set; } + public bool CanEdit { get; set; } + public bool CanDelete { get; set; } + + public void Mapping(Profile profile) + { + profile.CreateMap().ConvertUsing(); + } + } +} diff --git a/products/ASC.CRM/Server/ApiModels/ResponsesDto/InvoiceLineDto.cs b/products/ASC.CRM/Server/ApiModels/ResponsesDto/InvoiceLineDto.cs new file mode 100644 index 00000000000..88d49ef8e36 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/ResponsesDto/InvoiceLineDto.cs @@ -0,0 +1,63 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + + +using ASC.Common.Mapping; +using ASC.CRM.Core.Entities; + +namespace ASC.CRM.ApiModels +{ + /// + /// Invoice Line + /// + public class InvoiceLineDto : IMapFrom + { + public int Id { get; set; } + public int InvoiceID { get; set; } + public int InvoiceItemID { get; set; } + public int InvoiceTax1ID { get; set; } + public int InvoiceTax2ID { get; set; } + public int SortOrder { get; set; } + public string Description { get; set; } + public decimal Quantity { get; set; } + public decimal Price { get; set; } + public decimal Discount { get; set; } + public static InvoiceLineDto GetSample() + { + return new InvoiceLineDto + { + Description = string.Empty, + Discount = (decimal)0.00, + InvoiceID = 0, + InvoiceItemID = 0, + InvoiceTax1ID = 0, + InvoiceTax2ID = 0, + Price = (decimal)0.00, + Quantity = 0 + }; + } + } +} diff --git a/products/ASC.CRM/Server/ApiModels/ResponsesDto/InvoiceStatusDto.cs b/products/ASC.CRM/Server/ApiModels/ResponsesDto/InvoiceStatusDto.cs new file mode 100644 index 00000000000..7ac778973ed --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/ResponsesDto/InvoiceStatusDto.cs @@ -0,0 +1,59 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + + +using ASC.Common.Mapping; +using ASC.CRM.Classes; +using ASC.CRM.Core.Enums; + +using AutoMapper; + +namespace ASC.CRM.ApiModels +{ + /// + /// Invoice Status + /// + public class InvoiceStatusDto : IMapFrom + { + public int Id { get; set; } + public string Title { get; set; } + public static InvoiceStatusDto GetSample() + { + return new InvoiceStatusDto + { + Id = (int)InvoiceStatus.Draft, + Title = InvoiceStatus.Draft.ToLocalizedString() + }; + } + + public void Mapping(Profile profile) + { + profile.CreateMap() + .ForMember(x => x.Title, x => x.MapFrom(y => y.ToLocalizedString())); + + } + } +} diff --git a/products/ASC.CRM/Server/ApiModels/ResponsesDto/InvoiceTaxDto.cs b/products/ASC.CRM/Server/ApiModels/ResponsesDto/InvoiceTaxDto.cs new file mode 100644 index 00000000000..c76812501b6 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/ResponsesDto/InvoiceTaxDto.cs @@ -0,0 +1,72 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + + +using ASC.Api.Core; +using ASC.Common.Mapping; +using ASC.CRM.Core.Entities; +using ASC.CRM.Mapping; +using ASC.Web.Api.Models; + +using AutoMapper; + +namespace ASC.CRM.ApiModels +{ + //public static class InvoiceItemDtoHelperExtension + //{ + // public static DIHelper AddInvoiceItemDtoHelperService(this DIHelper services) + // { + // services.TryAddTransient(); + // return services.AddCurrencyProviderService() + // .AddSettingsManagerService() + // .AddApiDateTimeHelper() + // .AddEmployeeWraper() + // .AddCRMSecurityService(); + // } + //} + + /// + /// Invoice Tax + /// + public class InvoiceTaxDto : IMapFrom + { + public int Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public decimal Rate { get; set; } + public ApiDateTime CreateOn { get; set; } + public EmployeeWraper CreateBy { get; set; } + public bool CanEdit { get; set; } + public bool CanDelete { get; set; } + + public void Mapping(Profile profile) + { + profile.CreateMap().ConvertUsing(); + } + + + } +} diff --git a/products/ASC.CRM/Server/ApiModels/ResponsesDto/ListItemDto.cs b/products/ASC.CRM/Server/ApiModels/ResponsesDto/ListItemDto.cs new file mode 100644 index 00000000000..e343943b1b9 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/ResponsesDto/ListItemDto.cs @@ -0,0 +1,398 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + + +using ASC.Common.Mapping; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Mapping; + +using AutoMapper; + +namespace ASC.CRM.ApiModels +{ + + #region History Category + public class HistoryCategoryBaseDto : ListItemDto + { + public HistoryCategoryBaseDto() + { + + } + + public HistoryCategoryBaseDto(ListItem listItem) + : base(listItem) + { + } + + public String ImagePath { get; set; } + + public static HistoryCategoryBaseDto GetSample() + { + return new HistoryCategoryBaseDto + { + Title = "Lunch", + SortOrder = 10, + Color = String.Empty, + Description = "", + ImagePath = "path to image" + }; + } + } + + public class HistoryCategoryDto : HistoryCategoryBaseDto + { + public HistoryCategoryDto() + { + } + + public HistoryCategoryDto(ListItem listItem) + : base(listItem) + { + } + + + public int RelativeItemsCount { get; set; } + + public new static HistoryCategoryDto GetSample() + { + return new HistoryCategoryDto + { + Title = "Lunch", + SortOrder = 10, + Color = String.Empty, + Description = "", + ImagePath = "path to image", + RelativeItemsCount = 1 + }; + } + } + + #endregion + + #region Deal Milestone + + public class DealMilestoneBaseDto : ListItemDto + { + public DealMilestoneBaseDto() + { + } + + public DealMilestoneBaseDto(DealMilestone dealMilestone) + { + SuccessProbability = dealMilestone.Probability; + StageType = dealMilestone.Status; + Color = dealMilestone.Color; + Description = dealMilestone.Description; + Title = dealMilestone.Title; + } + + public int SuccessProbability { get; set; } + public DealMilestoneStatus StageType { get; set; } + public static DealMilestoneBaseDto GetSample() + { + return new DealMilestoneBaseDto + { + Title = "Discussion", + SortOrder = 2, + Color = "#B9AFD3", + Description = "The potential buyer showed his/her interest and sees how your offering meets his/her goal", + StageType = DealMilestoneStatus.Open, + SuccessProbability = 20 + }; + } + } + + public class DealMilestoneDto : DealMilestoneBaseDto, IMapFrom + { + public DealMilestoneDto() + { + } + + public DealMilestoneDto(DealMilestone dealMilestone) + : base(dealMilestone) + { + } + public int RelativeItemsCount { get; set; } + + public new static DealMilestoneDto GetSample() + { + return new DealMilestoneDto + { + Title = "Discussion", + SortOrder = 2, + Color = "#B9AFD3", + Description = "The potential buyer showed his/her interest and sees how your offering meets his/her goal", + StageType = DealMilestoneStatus.Open, + SuccessProbability = 20, + RelativeItemsCount = 1 + }; + } + + public override void Mapping(Profile profile) + { + profile.CreateMap().ConvertUsing(); + } + } + + #endregion + + #region Task Category + + public class TaskCategoryBaseDto : ListItemDto + { + public TaskCategoryBaseDto() + { + } + + public TaskCategoryBaseDto(ListItem listItem) : base(listItem) + { + + } + + public String ImagePath { get; set; } + + public static TaskCategoryBaseDto GetSample() + { + return new TaskCategoryBaseDto + { + Title = "Appointment", + SortOrder = 2, + Description = "", + ImagePath = "path to image" + }; + } + + + + } + + public class TaskCategoryDto : TaskCategoryBaseDto + { + public TaskCategoryDto() + { + } + + public TaskCategoryDto(ListItem listItem) : base(listItem) + { + } + + + + public int RelativeItemsCount { get; set; } + + public new static TaskCategoryDto GetSample() + { + return new TaskCategoryDto + { + Id = 30, + Title = "Appointment", + SortOrder = 2, + Description = "", + ImagePath = "path to image", + RelativeItemsCount = 1 + }; + } + } + + + + + + + + #endregion + + #region Contact Status + + public class ContactStatusBaseDto : ListItemDto + { + public ContactStatusBaseDto() + { + } + + public ContactStatusBaseDto(ListItem listItem) + : base(listItem) + { + } + + public static ContactStatusBaseDto GetSample() + { + return new ContactStatusBaseDto + { + Title = "Cold", + SortOrder = 2, + Description = "" + }; + } + } + + public class ContactStatusDto : ContactStatusBaseDto + { + public ContactStatusDto() + { + } + + public ContactStatusDto(ListItem listItem) + : base(listItem) + { + } + + + public int RelativeItemsCount { get; set; } + + public new static ContactStatusDto GetSample() + { + return new ContactStatusDto + { + Title = "Cold", + SortOrder = 2, + Description = "", + RelativeItemsCount = 1 + }; + } + } + + #endregion + + #region Contact Type + + public class ContactTypeBaseDto : ListItemDto + { + public ContactTypeBaseDto() + { + + } + + public ContactTypeBaseDto(ListItem listItem) + : base(listItem) + { + } + + public static ContactTypeBaseDto GetSample() + { + return new ContactTypeBaseDto + { + Id = 30, + Title = "Client", + SortOrder = 2, + Description = "" + }; + } + } + + public class ContactTypeDto : ContactTypeBaseDto + { + public ContactTypeDto() + { + } + + public ContactTypeDto(ListItem listItem) + : base(listItem) + { + } + + + public int RelativeItemsCount { get; set; } + + public new static ContactTypeDto GetSample() + { + return new ContactTypeDto + { + Id = 30, + Title = "Client", + SortOrder = 2, + Description = "", + RelativeItemsCount = 1 + }; + } + } + + #endregion + + #region Tags + + public class TagDto + { + public TagDto() + { + Title = String.Empty; + RelativeItemsCount = 0; + } + + public TagDto(String tag, int relativeItemsCount = 0) + { + Title = tag; + RelativeItemsCount = relativeItemsCount; + } + + public String Title { get; set; } + public int RelativeItemsCount { get; set; } + public static TagDto GetSample() + { + return new TagDto + { + Title = "Tag", + RelativeItemsCount = 1 + }; + } + } + + #endregion + + public class ListItemDto : IMapFrom + { + public ListItemDto() + { + + } + + protected ListItemDto(ListItem listItem) + { + Title = listItem.Title; + Description = listItem.Description; + Color = listItem.Color; + SortOrder = listItem.SortOrder; + } + + public int Id { get; set; } + public String Title { get; set; } + public String Description { get; set; } + public String Color { get; set; } + public int SortOrder { get; set; } + + public virtual void Mapping(Profile profile) + { + profile.CreateMap().ConvertUsing(); + profile.CreateMap().ConvertUsing(); + profile.CreateMap().ConvertUsing(); + profile.CreateMap().ConvertUsing(); + profile.CreateMap().ConvertUsing(); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/ApiModels/ResponsesDto/OpportunityDto.cs b/products/ASC.CRM/Server/ApiModels/ResponsesDto/OpportunityDto.cs new file mode 100644 index 00000000000..f04d8c47f18 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/ResponsesDto/OpportunityDto.cs @@ -0,0 +1,98 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; + + +using ASC.Api.Core; +using ASC.Common.Mapping; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Mapping; +using ASC.Web.Api.Models; + +using AutoMapper; + +namespace ASC.CRM.ApiModels +{ + /// + /// Opportunity + /// + public class OpportunityDto : IMapFrom + { + + public OpportunityDto() + { + } + + public int Id { get; set; } + public EmployeeWraper CreateBy { get; set; } + public ApiDateTime Created { get; set; } + public IEnumerable Members { get; set; } + public ContactBaseDto Contact { get; set; } + public String Title { get; set; } + public String Description { get; set; } + public EmployeeWraper Responsible { get; set; } + public BidType BidType { get; set; } + public decimal BidValue { get; set; } + public CurrencyInfoDto BidCurrency { get; set; } + public int PerPeriodValue { get; set; } + public DealMilestoneBaseDto Stage { get; set; } + public int SuccessProbability { get; set; } + public ApiDateTime ActualCloseDate { get; set; } + public ApiDateTime ExpectedCloseDate { get; set; } + + public bool IsPrivate { get; set; } + public IEnumerable AccessList { get; set; } + public bool CanEdit { get; set; } + public IEnumerable CustomFields { get; set; } + + public static OpportunityDto GetSample() + { + return new OpportunityDto + { + CreateBy = EmployeeWraper.GetSample(), + Created = ApiDateTime.GetSample(), + Responsible = EmployeeWraper.GetSample(), + Title = "Hotel catalogue", + Description = "", + ExpectedCloseDate = ApiDateTime.GetSample(), + Contact = ContactBaseDto.GetSample(), + IsPrivate = false, + SuccessProbability = 65, + BidType = BidType.FixedBid, + Stage = DealMilestoneBaseDto.GetSample() + }; + } + + + public void Mapping(Profile profile) + { + profile.CreateMap().ConvertUsing(); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/ApiModels/ResponsesDto/RelationshipEventDto.cs b/products/ASC.CRM/Server/ApiModels/ResponsesDto/RelationshipEventDto.cs new file mode 100644 index 00000000000..1e141834600 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/ResponsesDto/RelationshipEventDto.cs @@ -0,0 +1,147 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; + + +using ASC.Api.Core; +using ASC.Api.Documents; +using ASC.Common; +using ASC.Common.Mapping; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Mapping; +using ASC.Web.Api.Models; + +using AutoMapper; + +namespace ASC.CRM.ApiModels +{ + public class EntityDto + { + public String EntityType { get; set; } + public int EntityId { get; set; } + public String EntityTitle { get; set; } + public static EntityDto GetSample() + { + return new EntityDto + { + EntityId = 123445, + EntityType = "opportunity", + EntityTitle = "Household appliances internet shop" + }; + } + } + + [Scope] + public class EntityDtoHelper + { + public EntityDtoHelper(DaoFactory daoFactory) + { + DaoFactory = daoFactory; + } + + public DaoFactory DaoFactory { get; } + + public EntityDto Get(EntityType entityType, int entityID) + { + if (entityID == 0) return null; + + var result = new EntityDto + { + EntityId = entityID + }; + + switch (entityType) + { + case EntityType.Case: + var caseObj = DaoFactory.GetCasesDao().GetByID(entityID); + if (caseObj == null) + return null; + + result.EntityType = "case"; + result.EntityTitle = caseObj.Title; + + break; + case EntityType.Opportunity: + var dealObj = DaoFactory.GetDealDao().GetByID(entityID); + if (dealObj == null) + return null; + + result.EntityType = "opportunity"; + result.EntityTitle = dealObj.Title; + + break; + default: + return null; + } + + return result; + } + } + + public class RelationshipEventDto : IMapFrom + { + public RelationshipEventDto() + { + + } + + public int Id { get; set; } + public EmployeeWraper CreateBy { get; set; } + + public ApiDateTime Created { get; set; } + public String Content { get; set; } + public HistoryCategoryBaseDto Category { get; set; } + public ContactBaseDto Contact { get; set; } + public EntityDto Entity { get; set; } + public bool CanEdit { get; set; } + public IEnumerable> Files { get; set; } + public static RelationshipEventDto GetSample() + { + return new RelationshipEventDto + { + CanEdit = true, + Category = HistoryCategoryBaseDto.GetSample(), + Entity = EntityDto.GetSample(), + Contact = ContactBaseDto.GetSample(), + Created = ApiDateTime.GetSample(), + CreateBy = EmployeeWraper.GetSample(), + Files = new[] { FileWrapper.GetSample() }, + Content = @"Agreed to meet at lunch and discuss the client commercial offer" + }; + } + + public void Mapping(Profile profile) + { + profile.CreateMap() + .ConvertUsing(); + } + } + +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/ApiModels/ResponsesDto/ReportDto.cs b/products/ASC.CRM/Server/ApiModels/ResponsesDto/ReportDto.cs new file mode 100644 index 00000000000..1c51a46274d --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/ResponsesDto/ReportDto.cs @@ -0,0 +1,40 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; + + +namespace ASC.CRM.ApiModels +{ + public class ReportDto + { + public String ReportTitle { get; set; } + public String ReportDescription { get; set; } + public IEnumerable Lables { get; set; } + public Object Data { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/ApiModels/ResponsesDto/TaskDto.cs b/products/ASC.CRM/Server/ApiModels/ResponsesDto/TaskDto.cs new file mode 100644 index 00000000000..0d9dde56c2d --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/ResponsesDto/TaskDto.cs @@ -0,0 +1,105 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + + +using ASC.Api.Core; +using ASC.Common.Mapping; +using ASC.CRM.Core.Entities; +using ASC.CRM.Mapping; +using ASC.Web.Api.Models; + +using AutoMapper; + +namespace ASC.CRM.ApiModels +{ + /// + /// Task + /// + public class TaskDto : IMapFrom + { + public int Id { get; set; } + public EmployeeWraper CreateBy { get; set; } + public ApiDateTime Created { get; set; } + public ContactBaseWithEmailDto Contact { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public ApiDateTime DeadLine { get; set; } + public int AlertValue { get; set; } + public EmployeeWraper Responsible { get; set; } + public bool IsClosed { get; set; } + public TaskCategoryBaseDto Category { get; set; } + public EntityDto Entity { get; set; } + public bool CanEdit { get; set; } + public static TaskDto GetSample() + { + return new TaskDto + { + Created = ApiDateTime.GetSample(), + CreateBy = EmployeeWraper.GetSample(), + DeadLine = ApiDateTime.GetSample(), + IsClosed = false, + Responsible = EmployeeWraper.GetSample(), + // Category = TaskCategoryBaseDto.GetSample(), + CanEdit = true, + Title = "Send a commercial offer", + AlertValue = 0 + }; + } + public void Mapping(Profile profile) + { + profile.CreateMap().ConvertUsing(); + } + } + + public class TaskBaseDto + { + public int Id { get; set; } + public String Title { get; set; } + public String Description { get; set; } + public ApiDateTime DeadLine { get; set; } + public int AlertValue { get; set; } + public EmployeeWraper Responsible { get; set; } + public bool IsClosed { get; set; } + public TaskCategoryBaseDto Category { get; set; } + public EntityDto Entity { get; set; } + public bool CanEdit { get; set; } + public static TaskBaseDto GetSample() + { + return new TaskBaseDto + { + DeadLine = ApiDateTime.GetSample(), + IsClosed = false, + Responsible = EmployeeWraper.GetSample(), + Category = TaskCategoryBaseDto.GetSample(), + CanEdit = true, + Title = "Send a commercial offer", + AlertValue = 0 + }; + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/ApiModels/ResponsesDto/TaskTemplateContainerDto.cs b/products/ASC.CRM/Server/ApiModels/ResponsesDto/TaskTemplateContainerDto.cs new file mode 100644 index 00000000000..c1b8dd6080c --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/ResponsesDto/TaskTemplateContainerDto.cs @@ -0,0 +1,92 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; + + +using ASC.Web.Api.Models; + +namespace ASC.CRM.ApiModels +{ + public class TaskTemplateContainerDto + { + public TaskTemplateContainerDto() + { + + } + + public int Id { get; set; } + public String Title { get; set; } + public String EntityType { get; set; } + public IEnumerable Items { get; set; } + public static TaskTemplateContainerDto GetSample() + { + return new TaskTemplateContainerDto + { + EntityType = "contact", + Title = "Birthday greetings", + Items = new List + { + TaskTemplateDto.GetSample() + } + }; + } + } + + public class TaskTemplateDto + { + public TaskTemplateDto() + { + + } + + public int Id { get; set; } + public int ContainerID { get; set; } + public String Title { get; set; } + public String Description { get; set; } + public EmployeeWraper Responsible { get; set; } + public TaskCategoryDto Category { get; set; } + public bool isNotify { get; set; } + public long OffsetTicks { get; set; } + public bool DeadLineIsFixed { get; set; } + + public static TaskTemplateDto GetSample() + { + return new TaskTemplateDto + { + Title = "Send an Email", + Category = TaskCategoryDto.GetSample(), + isNotify = true, + Responsible = EmployeeWraper.GetSample(), + ContainerID = 12, + DeadLineIsFixed = false, + OffsetTicks = TimeSpan.FromDays(10).Ticks, + Description = "" + }; + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/ApiModels/ResponsesDto/VoipCallDto.cs b/products/ASC.CRM/Server/ApiModels/ResponsesDto/VoipCallDto.cs new file mode 100644 index 00000000000..c59c04e12cf --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/ResponsesDto/VoipCallDto.cs @@ -0,0 +1,54 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Collections.Generic; + + +using ASC.Api.Core; +using ASC.Common.Mapping; +using ASC.VoipService; +using ASC.Web.Api.Models; + +namespace ASC.CRM.ApiModels +{ + public class VoipCallDto : IMapFrom + { + public string Id { get; set; } + public string From { get; set; } + public string To { get; set; } + public VoipCallStatus? Status { get; set; } + public EmployeeWraper AnsweredBy { get; set; } + public ApiDateTime DialDate { get; set; } + public int DialDuration { get; set; } + public decimal Cost { get; set; } + public ContactDto Contact { get; set; } + public IEnumerable Calls { get; set; } + public string RecordUrl { get; set; } + public int RecordDuration { get; set; } + + } + +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/ApiModels/Subject.cs b/products/ASC.CRM/Server/ApiModels/Subject.cs new file mode 100644 index 00000000000..ac1740654d8 --- /dev/null +++ b/products/ASC.CRM/Server/ApiModels/Subject.cs @@ -0,0 +1,37 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +namespace ASC.CRM.ApiModels +{ + public enum SubjectEnum + { + Contact, + Person, + Company, + Case, + Opportunity + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Classes/CRMCalendar.cs b/products/ASC.CRM/Server/Classes/CRMCalendar.cs new file mode 100644 index 00000000000..cf1616bc293 --- /dev/null +++ b/products/ASC.CRM/Server/Classes/CRMCalendar.cs @@ -0,0 +1,134 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Globalization; + +using ASC.Common; +using ASC.Common.Utils; +using ASC.Core; +using ASC.Core.Tenants; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; +using ASC.Web.Core; +using ASC.Web.Core.Calendars; + +namespace ASC.CRM.Api +{ + [Scope] + public sealed class CrmCalendar : BaseCalendar + { + [AllDayLongUTC] + private class Event : BaseEvent + { + } + + private WebItemSecurity _webItemSecurity; + private DaoFactory _daoFactory; + private TenantManager _tenantManager; + private TenantUtil _tenantUtil; + + public CrmCalendar(WebItemSecurity webItemSecurity, + DaoFactory daoFactory, + AuthContext authContext, + TimeZoneConverter timeZoneConverter, + TenantManager tenantManager, + TenantUtil tenantUtil + ) : base(authContext, timeZoneConverter) + { + _tenantUtil = tenantUtil; + _tenantManager = tenantManager; + _webItemSecurity = webItemSecurity; + _daoFactory = daoFactory; + + Context.HtmlBackgroundColor = ""; + Context.HtmlTextColor = ""; + Context.CanChangeAlertType = false; + Context.CanChangeTimeZone = false; + Context.GetGroupMethod = () => CRMCommonResource.ProductName; + Id = "crm_calendar"; + EventAlertType = EventAlertType.Never; + Name = CRMCommonResource.ProductName; + Description = ""; + SharingOptions = new SharingOptions(); + // SharingOptions.PublicItems.Add(new SharingOptions.PublicItem { Id = userId, IsGroup = false }); + } + + public override List LoadEvents(Guid userId, DateTime startDate, DateTime endDate) + { + var events = new List(); + + if ( + !_webItemSecurity.IsAvailableForMe(WebItemManager.CRMProductID)) + { + return events; + } + + var tasks = _daoFactory.GetTaskDao().GetTasks(String.Empty, userId, 0, false, DateTime.MinValue, + DateTime.MinValue, EntityType.Any, 0, 0, 0, null); + + foreach (var t in tasks) + { + if (t.DeadLine == DateTime.MinValue) continue; + + var allDayEvent = t.DeadLine.Hour == 0 && t.DeadLine.Minute == 0; + var utcDate = allDayEvent ? t.DeadLine.Date : _tenantUtil.DateTimeToUtc(t.DeadLine); + + var e = new Event + { + AlertType = EventAlertType.Never, + AllDayLong = allDayEvent, + CalendarId = Id, + UtcStartDate = utcDate, + UtcEndDate = utcDate, + Id = "crm_task_" + t.ID.ToString(CultureInfo.InvariantCulture), + Name = CRMCommonResource.ProductName + ": " + t.Title, + Description = t.Description + }; + + if (IsVisibleEvent(startDate, endDate, e.UtcStartDate, e.UtcEndDate)) + events.Add(e); + } + + return events; + + } + + public override TimeZoneInfo TimeZone + { + get { return TimeZoneInfo.FindSystemTimeZoneById(_tenantManager.GetCurrentTenant().TimeZone); } + } + + private bool IsVisibleEvent(DateTime startDate, DateTime endDate, DateTime eventStartDate, DateTime eventEndDate) + { + return (startDate <= eventStartDate && eventStartDate <= endDate) || + (startDate <= eventEndDate && eventEndDate <= endDate) || + (eventStartDate < startDate && eventEndDate > endDate); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Classes/CRMSettings.cs b/products/ASC.CRM/Server/Classes/CRMSettings.cs new file mode 100644 index 00000000000..e88f7d54a67 --- /dev/null +++ b/products/ASC.CRM/Server/Classes/CRMSettings.cs @@ -0,0 +1,195 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Text.Json.Serialization; + +using ASC.Common; +using ASC.Core.Common.Settings; + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace ASC.Web.CRM.Classes +{ + public class SMTPServerSetting + { + public SMTPServerSetting() + { + } + + public SMTPServerSetting(ASC.Core.Configuration.SmtpSettings smtpSettings) + { + Host = smtpSettings.Host; + Port = smtpSettings.Port; + EnableSSL = smtpSettings.EnableSSL; + RequiredHostAuthentication = smtpSettings.EnableAuth; + HostLogin = smtpSettings.CredentialsUserName; + HostPassword = smtpSettings.CredentialsUserPassword; + SenderDisplayName = smtpSettings.SenderDisplayName; + SenderEmailAddress = smtpSettings.SenderAddress; + } + + + public String Host { get; set; } + public int Port { get; set; } + public bool EnableSSL { get; set; } + public bool RequiredHostAuthentication { get; set; } + public String HostLogin { get; set; } + public String HostPassword { get; set; } + public String SenderDisplayName { get; set; } + public String SenderEmailAddress { get; set; } + + } + + [Scope] + public class InvoiceSetting + { + public InvoiceSetting() + { + + } + + public InvoiceSetting(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public InvoiceSetting DefaultSettings + { + get + { + return new InvoiceSetting(Configuration) + { + Autogenerated = true, + Prefix = Configuration["crm:invoice:prefix"] ?? "INV-", + Number = "0000001", + Terms = String.Empty, + CompanyName = String.Empty, + CompanyLogoID = 0, + CompanyAddress = String.Empty + }; + } + } + + + public bool Autogenerated { get; set; } + public String Prefix { get; set; } + public String Number { get; set; } + public String Terms { get; set; } + public String CompanyName { get; set; } + public Int32 CompanyLogoID { get; set; } + public String CompanyAddress { get; set; } + } + + public class CrmSettings : ISettings + { + public CrmSettings() + { + + } + + public Guid ID + { + get { return new Guid("fdf39b9a-ec96-4eb7-aeab-63f2c608eada"); } + } + + [JsonPropertyName("SMTPServerSetting")] + public SMTPServerSetting SMTPServerSettingOld { get; set; } + public InvoiceSetting InvoiceSetting { get; set; } + public Guid WebFormKey { get; set; } + + [JsonPropertyName("DefaultCurrency")] + public String DefaultCurrency { get; set; } + + [JsonPropertyName("ChangeContactStatusGroupAuto")] + public string ChangeContactStatusGroupAutoDto { get; set; } + + [JsonIgnore] + public Boolean? ChangeContactStatusGroupAuto + { + get { return string.IsNullOrEmpty(ChangeContactStatusGroupAutoDto) ? null : (bool?)bool.Parse(ChangeContactStatusGroupAutoDto); } + set { ChangeContactStatusGroupAutoDto = value.HasValue ? value.Value.ToString().ToLowerInvariant() : null; } + } + + [JsonPropertyName("AddTagToContactGroupAuto")] + public string AddTagToContactGroupAutoDto { get; set; } + + [JsonIgnore] + public Boolean? AddTagToContactGroupAuto + { + get { return string.IsNullOrEmpty(AddTagToContactGroupAutoDto) ? null : (bool?)bool.Parse(AddTagToContactGroupAutoDto); } + set { AddTagToContactGroupAutoDto = value.HasValue ? value.Value.ToString().ToLowerInvariant() : null; } + } + + [JsonPropertyName("WriteMailToHistoryAuto")] + public Boolean WriteMailToHistoryAuto { get; set; } + + [JsonPropertyName("IsConfiguredPortal")] + public bool IsConfiguredPortal { get; set; } + + [JsonPropertyName("IsConfiguredSmtp")] + public bool IsConfiguredSmtp { get; set; } + public ISettings GetDefault(IServiceProvider serviceProvider) + { + var currencyProvider = serviceProvider.GetService(); + var configuration = serviceProvider.GetService(); + + var languageName = System.Threading.Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName; + + var findedCurrency = currencyProvider.GetAll().Find(item => String.Compare(item.CultureName, languageName, true) == 0); + + return new CrmSettings() + { + DefaultCurrency = findedCurrency != null ? findedCurrency.Abbreviation : "USD", + IsConfiguredPortal = false, + ChangeContactStatusGroupAuto = null, + AddTagToContactGroupAuto = null, + WriteMailToHistoryAuto = false, + WebFormKey = Guid.Empty, + InvoiceSetting = new InvoiceSetting(configuration).DefaultSettings + }; + } + } + + public class CrmReportSampleSettings : ISettings + { + [JsonPropertyName("NeedToGenerate")] + public bool NeedToGenerate { get; set; } + + public Guid ID + { + get { return new Guid("{54CD64AD-E73B-45A3-89E4-4D42A234D7A3}"); } + } + + public ISettings GetDefault(IServiceProvider serviceProvider) + { + return new CrmReportSampleSettings { NeedToGenerate = true }; + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Classes/CSVReader.cs b/products/ASC.CRM/Server/Classes/CSVReader.cs new file mode 100644 index 00000000000..739d2a14f5e --- /dev/null +++ b/products/ASC.CRM/Server/Classes/CSVReader.cs @@ -0,0 +1,361 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +/* CSVReader - a simple open source C# class library to read CSV data + * by Andrew Stellman - http://www.stellman-greene.com/CSVReader + * + * CSVReader.cs - Class to read CSV data from a string, file or stream + * + * download the latest version: http://svn.stellman-greene.com/CSVReader + * + * (c) 2008, Stellman & Greene Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Stellman & Greene Consulting nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY STELLMAN & GREENE CONSULTING ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL STELLMAN & GREENE CONSULTING BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Text; + +namespace ASC.Web.CRM.Classes +{ + /// + /// Read CSV-formatted data from a file or TextReader + /// + public class CSVReader : IDisposable + { + public const string NEWLINE = "\r\n"; + public const string COMMASEP = ","; + public const string SEMICOLONSEP = ";"; + + // Field Separator (default is comma) + private static string FieldSep = COMMASEP; + + /// + /// This reader will read all of the CSV data + /// + private readonly BinaryReader reader; + + /// + /// The number of rows to scan for types when building a DataTable (0 to scan the whole file) + /// + public int ScanRows = 0; + + #region Constructors + + /// + /// Read CSV-formatted data from a file + /// + /// Name of the CSV file + public CSVReader(FileInfo csvFileInfo) + { + if (csvFileInfo == null) + throw new ArgumentNullException("Null FileInfo passed to CSVReader"); + + this.reader = new BinaryReader(File.OpenRead(csvFileInfo.FullName)); + } + + /// + /// Read CSV-formatted data from a string + /// + /// String containing CSV data + public CSVReader(string csvData) + { + if (csvData == null) + throw new ArgumentNullException("Null string passed to CSVReader"); + + + this.reader = new BinaryReader(new MemoryStream(System.Text.Encoding.UTF8.GetBytes(csvData))); + } + + /// + /// Read CSV-formatted data from a TextReader + /// + /// TextReader that's reading CSV-formatted data + public CSVReader(TextReader reader) + { + if (reader == null) + throw new ArgumentNullException("Null TextReader passed to CSVReader"); + + this.reader = new BinaryReader(new MemoryStream(System.Text.Encoding.UTF8.GetBytes(reader.ReadToEnd()))); + } + + #endregion + + + + string currentLine = ""; + /// + /// Read the next row from the CSV data + /// + /// A list of objects read from the row, or null if there is no next row + public List ReadRow() + { + // ReadLine() will return null if there's no next line + if (reader.BaseStream.Position >= reader.BaseStream.Length) + return null; + + StringBuilder builder = new StringBuilder(); + + // Read the next line + while ((reader.BaseStream.Position < reader.BaseStream.Length) && (!builder.ToString().EndsWith(NEWLINE))) + { + char c = reader.ReadChar(); + builder.Append(c); + } + + currentLine = builder.ToString(); + if (currentLine.EndsWith(NEWLINE)) + currentLine = currentLine.Remove(currentLine.IndexOf(NEWLINE), NEWLINE.Length); + + // Build the list of objects in the line + List objects = new List(); + while (currentLine != "") + objects.Add(ReadNextObject()); + return objects; + } + + /// + /// Read the next object from the currentLine string + /// + /// The next object in the currentLine string + private object ReadNextObject() + { + if (currentLine == null) + return null; + + // Check to see if the next value is quoted + bool quoted = false; + if (currentLine.StartsWith("\"")) + quoted = true; + + // Find the end of the next value + string nextObjectString = ""; + int i = 0; + int len = currentLine.Length; + bool foundEnd = false; + while (!foundEnd && i <= len) + { + // Check if we've hit the end of the string + if ((!quoted && i == len) // non-quoted strings end with a comma or end of line + || (!quoted && currentLine.Substring(i, 1) == FieldSep) + // quoted strings end with a quote followed by a comma or end of line + || (quoted && i == len - 1 && currentLine.EndsWith("\"")) + || (quoted && currentLine.Substring(i, 2) == "\"" + FieldSep)) + foundEnd = true; + else + i++; + } + if (quoted) + { + if (i > len || !currentLine.Substring(i, 1).StartsWith("\"")) + throw new FormatException("Invalid CSV format: " + currentLine.Substring(0, i)); + i++; + } + nextObjectString = currentLine.Substring(0, i).Replace("\"\"", "\""); + + if (i < len) + currentLine = currentLine.Substring(i + 1); + else + currentLine = ""; + + if (quoted) + { + if (nextObjectString.StartsWith("\"")) + nextObjectString = nextObjectString.Substring(1); + if (nextObjectString.EndsWith("\"")) + nextObjectString = nextObjectString.Substring(0, nextObjectString.Length - 1); + return nextObjectString; + } + else + { + object convertedValue; + StringConverter.ConvertString(nextObjectString, out convertedValue); + return convertedValue; + } + } + + /// + /// Read the row data read using repeated ReadRow() calls and build a DataColumnCollection with types and column names + /// + /// True if the first row contains headers + /// System.Data.DataTable object populated with the row data + public DataTable CreateDataTable(bool headerRow) + { + // Read the CSV data into rows + List> rows = new List>(); + List readRow = null; + while ((readRow = ReadRow()) != null) + rows.Add(readRow); + + // The types and names (if headerRow is true) will be stored in these lists + List columnTypes = new List(); + List columnNames = new List(); + + // Read the column names from the header row (if there is one) + if (headerRow) + foreach (object name in rows[0]) + columnNames.Add(name.ToString()); + + // Read the column types from each row in the list of rows + bool headerRead = false; + foreach (List row in rows) + if (headerRead || !headerRow) + for (int i = 0; i < row.Count; i++) + // If we're adding a new column to the columnTypes list, use its type. + // Otherwise, find the common type between the one that's there and the new row. + if (columnTypes.Count < i + 1) + columnTypes.Add(row[i].GetType()); + else + columnTypes[i] = StringConverter.FindCommonType(columnTypes[i], row[i].GetType()); + else + headerRead = true; + + // Create the table and add the columns + DataTable table = new DataTable(); + for (int i = 0; i < columnTypes.Count; i++) + { + table.Columns.Add(); + table.Columns[i].DataType = columnTypes[i]; + if (i < columnNames.Count) + table.Columns[i].ColumnName = columnNames[i]; + } + + // Add the data from the rows + headerRead = false; + foreach (List row in rows) + if (headerRead || !headerRow) + { + DataRow dataRow = table.NewRow(); + for (int i = 0; i < row.Count; i++) + dataRow[i] = row[i]; + table.Rows.Add(dataRow); + } + else + headerRead = true; + + return table; + } + + /// + /// Read a CSV file into a table + /// + /// Filename of CSV file + /// True if the first row contains column names + /// The number of rows to scan for types when building a DataTable (0 to scan the whole file) + /// The field separator character + /// System.Data.DataTable object that contains the CSV data + public static DataTable ReadCSVFile(string filename, bool headerRow, int scanRows, String fieldSeparator) + { + FieldSep = fieldSeparator; + return ReadCSVFile(filename, headerRow, scanRows); + } + + /// + /// Read a CSV file into a table + /// + /// Filename of CSV file + /// True if the first row contains column names + /// The field separator character + /// System.Data.DataTable object that contains the CSV data + public static DataTable ReadCSVFile(string filename, bool headerRow, String fieldSeparator) + { + FieldSep = fieldSeparator; + return ReadCSVFile(filename, headerRow); + } + + /// + /// Read a CSV file into a table + /// + /// Filename of CSV file + /// True if the first row contains column names + /// The number of rows to scan for types when building a DataTable (0 to scan the whole file) + /// System.Data.DataTable object that contains the CSV data + public static DataTable ReadCSVFile(string filename, bool headerRow, int scanRows) + { + using (CSVReader reader = new CSVReader(new FileInfo(filename))) + { + reader.ScanRows = scanRows; + return reader.CreateDataTable(headerRow); + } + } + + /// + /// Read a CSV file into a table + /// + /// Filename of CSV file + /// True if the first row contains column names + /// System.Data.DataTable object that contains the CSV data + public static DataTable ReadCSVFile(string filename, bool headerRow) + { + using (CSVReader reader = new CSVReader(new FileInfo(filename))) + return reader.CreateDataTable(headerRow); + } + + + + #region IDisposable Members + + public void Dispose() + { + if (reader != null) + { + try + { + // Can't call BinaryReader.Dispose due to its protection level + reader.Close(); + } + catch { } + } + } + + #endregion + } +} diff --git a/products/ASC.CRM/Server/Classes/CSVReaderExtension.cs b/products/ASC.CRM/Server/Classes/CSVReaderExtension.cs new file mode 100644 index 00000000000..515cee5f632 --- /dev/null +++ b/products/ASC.CRM/Server/Classes/CSVReaderExtension.cs @@ -0,0 +1,89 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +/* CSVReader - a simple open source C# class library to read CSV data + * by Andrew Stellman - http://www.stellman-greene.com/CSVReader + * + * CSVReaderExtension.cs - string extension methods to read CSV rows and tables + * + * download the latest version: http://svn.stellman-greene.com/CSVReader + * + * (c) 2008, Stellman & Greene Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Stellman & Greene Consulting nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY STELLMAN & GREENE CONSULTING ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL STELLMAN & GREENE CONSULTING BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +using System.Collections.Generic; +using System.Data; + +namespace ASC.Web.CRM.Classes +{ + public static class CSVReaderExtension + { + /// + /// Convert a CSV-formatted string into a list of objects + /// + /// List of objects that contains the CSV data + public static List ReadCSVLine(this string input) + { + using (CSVReader reader = new CSVReader(input)) + return reader.ReadRow(); + } + + /// + /// Convert a CSV-formatted string into a DataTable + /// + /// True if the first row contains headers + /// System.Data.DataTable that contains the CSV data + public static DataTable ReadCSVTable(this string input, bool headerRow) + { + using (CSVReader reader = new CSVReader(input)) + return reader.CreateDataTable(headerRow); + } + } +} diff --git a/products/ASC.CRM/Server/Classes/ContactPhotoManager.cs b/products/ASC.CRM/Server/Classes/ContactPhotoManager.cs new file mode 100644 index 00000000000..78324513f02 --- /dev/null +++ b/products/ASC.CRM/Server/Classes/ContactPhotoManager.cs @@ -0,0 +1,633 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Net; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Common.Threading; +using ASC.CRM.Resources; +using ASC.Data.Storage; +using ASC.Web.Core; +using ASC.Web.Core.Utility.Skins; +using ASC.Web.CRM.Configuration; + +using Microsoft.Extensions.Options; + +namespace ASC.Web.CRM.Classes +{ + public class ResizeWorkerItem : DistributedTask + { + public int ContactID { get; set; } + + public bool UploadOnly { get; set; } + + public String TmpDirName { get; set; } + + public Size[] RequireFotoSize { get; set; } + + public byte[] ImageData { get; set; } + + public IDataStore DataStore { get; set; } + + public override bool Equals(object obj) + { + if (!(obj is ResizeWorkerItem)) return false; + + var item = (ResizeWorkerItem)obj; + + return item.ContactID.Equals(ContactID) && RequireFotoSize.Equals(item.RequireFotoSize) && ImageData.Length == item.ImageData.Length; + } + + public override int GetHashCode() + { + return ContactID ^ RequireFotoSize.GetHashCode() ^ ImageData.Length; + } + } + + [Scope] + public class ContactPhotoManager + { + public readonly ILog _logger; + public readonly Global _global; + public readonly WebImageSupplier _webImageSupplier; + private readonly DistributedTaskQueue _resizeQueue; + private readonly ICacheNotify _cacheNotify; + private readonly ICache _cache; + + private const string PhotosBaseDirName = "photos"; + private const string PhotosDefaultTmpDirName = "temp"; + + private static readonly Size _oldBigSize = new Size(145, 145); + + private readonly Size _bigSize = new Size(200, 200); + private readonly Size _mediumSize = new Size(82, 82); + private readonly Size _smallSize = new Size(40, 40); + + private readonly object locker = new object(); + + + public ContactPhotoManager(Global global, + WebImageSupplier webImageSupplier, + IOptionsMonitor logger, + ICache cache, + ICacheNotify cacheNotify, + DistributedTaskQueueOptionsManager optionsQueue) + { + _global = global; + _webImageSupplier = webImageSupplier; + _cacheNotify = cacheNotify; + _cache = cache; + _resizeQueue = optionsQueue.Get(); + _logger = logger.Get("ASC.CRM"); + + _cacheNotify.Subscribe((x) => + { + _cache.Remove($"contact_photo_cache_key_id_{x.Id}"); + + }, CacheNotifyAction.Remove); + } + + #region Cache and DataStore Methods + + private String FromCache(int contactID, Size photoSize) + { + var cacheItem = _cache.Get>($"contact_photo_cache_key_id_{contactID}"); + + if (cacheItem is null) return String.Empty; + + if (cacheItem.ContainsKey(photoSize)) return cacheItem[photoSize]; + + return String.Empty; + } + + private void ToCache(int contactID, String photoUri, Size photoSize) + { + var photoUriBySize = _cache.Get>($"contact_photo_cache_key_id_{contactID}"); + + if (photoUriBySize.ContainsKey(photoSize)) + photoUriBySize[photoSize] = photoUri; + else + photoUriBySize.Add(photoSize, photoUri); + + _cache.Insert($"contact_photo_cache_key_id_{contactID}", photoUriBySize, TimeSpan.FromMinutes(15)); + } + + private String FromDataStore(int contactID, Size photoSize) + { + return FromDataStore(contactID, photoSize, false, null); + } + + private String FromDataStore(int contactID, Size photoSize, Boolean isTmpDir, String tmpDirName) + { + var directoryPath = !isTmpDir + ? BuildFileDirectory(contactID) + : (String.IsNullOrEmpty(tmpDirName) ? BuildFileTmpDirectory(contactID) : BuildFileTmpDirectory(tmpDirName)); + + var filesURI = _global.GetStore().ListFiles(directoryPath, BuildFileName(contactID, photoSize) + "*", false); + + if (filesURI.Length == 0 && photoSize == _bigSize) + { + filesURI = _global.GetStore().ListFiles(directoryPath, BuildFileName(contactID, _oldBigSize) + "*", false); + } + + if (filesURI.Length == 0) + { + return String.Empty; + } + + return filesURI[0].ToString(); + } + + private String FromDataStoreRelative(int contactID, Size photoSize, Boolean isTmpDir, String tmpDirName) + { + var directoryPath = !isTmpDir + ? BuildFileDirectory(contactID) + : (String.IsNullOrEmpty(tmpDirName) ? BuildFileTmpDirectory(contactID) : BuildFileTmpDirectory(tmpDirName)); + + var filesPaths = _global.GetStore().ListFilesRelative("", directoryPath, BuildFileName(contactID, photoSize) + "*", false); + + if (filesPaths.Length == 0 && photoSize == _bigSize) + { + filesPaths = _global.GetStore().ListFilesRelative("", directoryPath, BuildFileName(contactID, _oldBigSize) + "*", false); + } + + if (filesPaths.Length == 0) + { + return String.Empty; + } + + return Path.Combine(directoryPath, filesPaths[0]); + } + + private PhotoData FromDataStore(Size photoSize, String tmpDirName) + { + var directoryPath = BuildFileTmpDirectory(tmpDirName); + + if (!_global.GetStore().IsDirectory(directoryPath)) + return null; + + var filesURI = _global.GetStore().ListFiles(directoryPath, BuildFileName(0, photoSize) + "*", false); + + if (filesURI.Length == 0) return null; + + return new PhotoData { Url = filesURI[0].ToString(), Path = tmpDirName }; + } + + #endregion + + #region Private Methods + + private String GetPhotoUri(int contactID, bool isCompany, Size photoSize) + { + var photoUri = FromCache(contactID, photoSize); + + if (!String.IsNullOrEmpty(photoUri)) return photoUri; + + photoUri = FromDataStore(contactID, photoSize); + + if (String.IsNullOrEmpty(photoUri)) + photoUri = GetDefaultPhoto(isCompany, photoSize); + + ToCache(contactID, photoUri, photoSize); + + return photoUri; + } + + private String BuildFileDirectory(int contactID) + { + var s = contactID.ToString("000000"); + + return String.Concat(PhotosBaseDirName, "/", s.Substring(0, 2), "/", + s.Substring(2, 2), "/", + s.Substring(4), "/"); + } + + private String BuildFileTmpDirectory(int contactID) + { + return String.Concat(BuildFileDirectory(contactID), PhotosDefaultTmpDirName, "/"); + } + + private String BuildFileTmpDirectory(string tmpDirName) + { + return String.Concat(PhotosBaseDirName, "/", tmpDirName.TrimEnd('/'), "/"); + } + + private String BuildFileName(int contactID, Size photoSize) + { + return String.Format("contact_{0}_{1}_{2}", contactID, photoSize.Width, photoSize.Height); + } + + private String BuildFilePath(int contactID, Size photoSize, String imageExtension) + { + if (photoSize.IsEmpty || contactID == 0) + throw new ArgumentException(); + + return String.Concat(BuildFileDirectory(contactID), BuildFileName(contactID, photoSize), imageExtension); + } + + private String BuildFileTmpPath(int contactID, Size photoSize, String imageExtension, String tmpDirName) + { + if (photoSize.IsEmpty || (contactID == 0 && String.IsNullOrEmpty(tmpDirName))) + throw new ArgumentException(); + + return String.Concat( + String.IsNullOrEmpty(tmpDirName) + ? BuildFileTmpDirectory(contactID) + : BuildFileTmpDirectory(tmpDirName), + BuildFileName(contactID, photoSize), imageExtension); + } + + private void ExecResizeImage(ResizeWorkerItem resizeWorkerItem) + { + foreach (var fotoSize in resizeWorkerItem.RequireFotoSize) + { + var data = resizeWorkerItem.ImageData; + using (var stream = new MemoryStream(data)) + using (var img = new Bitmap(stream)) + { + var imgFormat = img.RawFormat; + if (fotoSize != img.Size) + { + using (var img2 = CommonPhotoManager.DoThumbnail(img, fotoSize, false, false, false)) + { + data = CommonPhotoManager.SaveToBytes(img2, Global.GetImgFormatName(imgFormat)); + } + } + else + { + data = Global.SaveToBytes(img); + } + + var fileExtension = String.Concat("." + Global.GetImgFormatName(imgFormat)); + + var photoPath = !resizeWorkerItem.UploadOnly + ? BuildFilePath(resizeWorkerItem.ContactID, fotoSize, fileExtension) + : BuildFileTmpPath(resizeWorkerItem.ContactID, fotoSize, fileExtension, resizeWorkerItem.TmpDirName); + + using (var fileStream = new MemoryStream(data)) + { + var photoUri = resizeWorkerItem.DataStore.Save(photoPath, fileStream).ToString(); + photoUri = String.Format("{0}?cd={1}", photoUri, DateTime.UtcNow.Ticks); + + if (!resizeWorkerItem.UploadOnly) + { + ToCache(resizeWorkerItem.ContactID, photoUri, fotoSize); + } + } + } + } + } + + private String GetDefaultPhoto(bool isCompany, Size photoSize) + { + int contactID; + + if (isCompany) + contactID = -1; + else + contactID = -2; + + var defaultPhotoUri = FromCache(contactID, photoSize); + + if (!String.IsNullOrEmpty(defaultPhotoUri)) return defaultPhotoUri; + + if (isCompany) + defaultPhotoUri = _webImageSupplier.GetAbsoluteWebPath(String.Format("empty_company_logo_{0}_{1}.png", photoSize.Height, photoSize.Width), ProductEntryPoint.ID); + else + defaultPhotoUri = _webImageSupplier.GetAbsoluteWebPath(String.Format("empty_people_logo_{0}_{1}.png", photoSize.Height, photoSize.Width), ProductEntryPoint.ID); + + ToCache(contactID, defaultPhotoUri, photoSize); + + return defaultPhotoUri; + } + + #endregion + + #region Delete Methods + + public void DeletePhoto(int contactID) + { + DeletePhoto(contactID, false, null, true); + } + + public void DeletePhoto(int contactID, bool isTmpDir, string tmpDirName, bool recursive) + { + if (contactID == 0) + throw new ArgumentException(); + + lock (locker) + { + _resizeQueue.GetTasks().Where(item => item.ContactID == contactID) + .All(item => + { + _resizeQueue.RemoveTask(item.Id); + + return true; + }); + + var photoDirectory = !isTmpDir + ? BuildFileDirectory(contactID) + : (String.IsNullOrEmpty(tmpDirName) ? BuildFileTmpDirectory(contactID) : BuildFileTmpDirectory(tmpDirName)); + var store = _global.GetStore(); + + if (store.IsDirectory(photoDirectory)) + { + store.DeleteFiles(photoDirectory, "*", recursive); + if (recursive) + { + store.DeleteDirectory(photoDirectory); + } + } + + if (!isTmpDir) + { + _cache.Remove($"contact_photo_cache_key_id_{contactID}"); + _cacheNotify.Publish(new ContactPhotoManagerCacheItem { Id = contactID }, CacheNotifyAction.Remove); + } + } + } + + public void DeletePhoto(string tmpDirName) + { + lock (locker) + { + + var photoDirectory = BuildFileTmpDirectory(tmpDirName); + var store = _global.GetStore(); + + if (store.IsDirectory(photoDirectory)) + { + store.DeleteFiles(photoDirectory, "*", false); + } + } + } + + #endregion + + public void TryUploadPhotoFromTmp(int contactID, bool isNewContact, string tmpDirName) + { + var directoryPath = BuildFileDirectory(contactID); + var dataStore = _global.GetStore(); + + try + { + if (dataStore.IsDirectory(directoryPath)) + { + DeletePhoto(contactID, false, null, false); + } + foreach (var photoSize in new[] { _bigSize, _mediumSize, _smallSize }) + { + var photoTmpPath = FromDataStoreRelative(isNewContact ? 0 : contactID, photoSize, true, tmpDirName); + if (string.IsNullOrEmpty(photoTmpPath)) throw new Exception("Temp phono not found"); + + var imageExtension = Path.GetExtension(photoTmpPath); + + var photoPath = String.Concat(directoryPath, BuildFileName(contactID, photoSize), imageExtension).TrimStart('/'); + + byte[] data; + using (var photoTmpStream = dataStore.GetReadStream(photoTmpPath)) + { + data = Global.ToByteArray(photoTmpStream); + } + using (var fileStream = new MemoryStream(data)) + { + var photoUri = dataStore.Save(photoPath, fileStream).ToString(); + photoUri = String.Format("{0}?cd={1}", photoUri, DateTime.UtcNow.Ticks); + ToCache(contactID, photoUri, photoSize); + } + } + DeletePhoto(contactID, true, tmpDirName, true); + } + catch (Exception ex) + { + _logger.ErrorFormat("TryUploadPhotoFromTmp for contactID={0} failed witth error: {1}", contactID, ex); + + return; + } + } + + #region Get Photo Methods + + public String GetSmallSizePhoto(int contactID, bool isCompany) + { + if (contactID <= 0) + return GetDefaultPhoto(isCompany, _smallSize); + + return GetPhotoUri(contactID, isCompany, _smallSize); + } + + public String GetMediumSizePhoto(int contactID, bool isCompany) + { + if (contactID <= 0) + return GetDefaultPhoto(isCompany, _mediumSize); + + return GetPhotoUri(contactID, isCompany, _mediumSize); + } + + public String GetBigSizePhoto(int contactID, bool isCompany) + { + if (contactID <= 0) + return GetDefaultPhoto(isCompany, _bigSize); + + return GetPhotoUri(contactID, isCompany, _bigSize); + } + + #endregion + + private PhotoData ResizeToBigSize(byte[] imageData, string tmpDirName) + { + return ResizeToBigSize(imageData, 0, true, tmpDirName); + } + + private PhotoData ResizeToBigSize(byte[] imageData, int contactID, bool uploadOnly, string tmpDirName) + { + var resizeWorkerItem = new ResizeWorkerItem + { + ContactID = contactID, + UploadOnly = uploadOnly, + RequireFotoSize = new[] { _bigSize }, + ImageData = imageData, + DataStore = _global.GetStore(), + TmpDirName = tmpDirName + }; + + ExecResizeImage(resizeWorkerItem); + + if (!uploadOnly) + { + return new PhotoData { Url = FromCache(contactID, _bigSize) }; + } + else if (String.IsNullOrEmpty(tmpDirName)) + { + return new PhotoData { Url = FromDataStore(contactID, _bigSize, true, null), Path = PhotosDefaultTmpDirName }; + } + else + { + return FromDataStore(_bigSize, tmpDirName); + } + } + + private void ExecGenerateThumbnail(byte[] imageData, int contactID, bool uploadOnly) + { + ExecGenerateThumbnail(imageData, contactID, uploadOnly, null); + } + + private void ExecGenerateThumbnail(byte[] imageData, string tmpDirName) + { + ExecGenerateThumbnail(imageData, 0, true, tmpDirName); + } + + + private void ExecGenerateThumbnail(byte[] imageData, int contactID, bool uploadOnly, string tmpDirName) + { + var resizeWorkerItem = new ResizeWorkerItem + { + ContactID = contactID, + UploadOnly = uploadOnly, + RequireFotoSize = new[] { _mediumSize, _smallSize }, + ImageData = imageData, + DataStore = _global.GetStore(), + TmpDirName = tmpDirName + }; + + if (!_resizeQueue.GetTasks().Contains(resizeWorkerItem)) + { + //Add + _resizeQueue.QueueTask((a, b) => ExecResizeImage(resizeWorkerItem), resizeWorkerItem); + } + } + + private byte[] ToByteArray(Stream inputStream, int streamLength) + { + using (var br = new BinaryReader(inputStream)) + { + return br.ReadBytes(streamLength); + } + } + + #region UploadPhoto Methods + + public PhotoData UploadPhoto(String imageUrl, int contactID, bool uploadOnly, bool checkFormat = true) + { + var request = (HttpWebRequest)WebRequest.Create(imageUrl); + using (var response = request.GetResponse()) + { + using (var inputStream = response.GetResponseStream()) + { + var imageData = ToByteArray(inputStream, (int)response.ContentLength); + return UploadPhoto(imageData, contactID, uploadOnly, checkFormat); + } + } + } + + public PhotoData UploadPhoto(Stream inputStream, int contactID, bool uploadOnly, bool checkFormat = true) + { + var imageData = Global.ToByteArray(inputStream); + return UploadPhoto(imageData, contactID, uploadOnly, checkFormat); + } + + public PhotoData UploadPhoto(byte[] imageData, int contactID, bool uploadOnly, bool checkFormat = true) + { + if (contactID == 0) + throw new ArgumentException(); + + if (checkFormat) + CheckImgFormat(imageData); + + DeletePhoto(contactID, uploadOnly, null, false); + + ExecGenerateThumbnail(imageData, contactID, uploadOnly); + + return ResizeToBigSize(imageData, contactID, uploadOnly, null); + } + + + public PhotoData UploadPhotoToTemp(String imageUrl, String tmpDirName, bool checkFormat = true) + { + var request = (HttpWebRequest)WebRequest.Create(imageUrl); + using (var response = request.GetResponse()) + { + using (var inputStream = response.GetResponseStream()) + { + var imageData = ToByteArray(inputStream, (int)response.ContentLength); + if (string.IsNullOrEmpty(tmpDirName)) + { + tmpDirName = Guid.NewGuid().ToString(); + } + return UploadPhotoToTemp(imageData, tmpDirName, checkFormat); + } + } + } + + public PhotoData UploadPhotoToTemp(Stream inputStream, String tmpDirName, bool checkFormat = true) + { + var imageData = Global.ToByteArray(inputStream); + return UploadPhotoToTemp(imageData, tmpDirName, checkFormat); + } + + public PhotoData UploadPhotoToTemp(byte[] imageData, String tmpDirName, bool checkFormat = true) + { + if (checkFormat) + CheckImgFormat(imageData); + + DeletePhoto(tmpDirName); + + ExecGenerateThumbnail(imageData, tmpDirName); + + return ResizeToBigSize(imageData, tmpDirName); + } + + public ImageFormat CheckImgFormat(byte[] imageData) + { + using (var stream = new MemoryStream(imageData)) + using (var img = new Bitmap(stream)) + { + var format = img.RawFormat; + + if (!format.Equals(ImageFormat.Png) && !format.Equals(ImageFormat.Jpeg)) + throw new Exception(CRMJSResource.ErrorMessage_NotImageSupportFormat); + + return format; + } + } + + public class PhotoData + { + public string Url; + public string Path; + } + + #endregion + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Classes/Global.cs b/products/ASC.CRM/Server/Classes/Global.cs new file mode 100644 index 00000000000..498feadc422 --- /dev/null +++ b/products/ASC.CRM/Server/Classes/Global.cs @@ -0,0 +1,277 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Globalization; +using System.IO; +using System.Text; +using System.Text.Json; + +using ASC.Common; +using ASC.Core; +using ASC.Core.Common.Settings; +using ASC.CRM.Core; +using ASC.CRM.Resources; +using ASC.Data.Storage; +using ASC.Web.Core; +using ASC.Web.Core.Files; +using ASC.Web.Studio.Core; + +using Microsoft.Extensions.Configuration; + +namespace ASC.Web.CRM.Classes +{ + [Scope] + public class Global + { + + private IConfiguration _configuration; + private SettingsManager _settingsManager; + protected int _tenantID; + private FilesLinkUtility _filesLinkUtility; + private SetupInfo _setupInfo; + private SecurityContext _securityContext; + private StorageFactory _storageFactory; + private CrmSecurity _crmSecurity; + + public Global(StorageFactory storageFactory, + SecurityContext securityContext, + SetupInfo setupInfo, + FilesLinkUtility filesLinkUtility, + CrmSecurity crmSecurity, + TenantManager tenantManager, + SettingsManager settingsManager, + IConfiguration configuration + ) + { + _storageFactory = storageFactory; + _filesLinkUtility = filesLinkUtility; + _setupInfo = setupInfo; + _securityContext = securityContext; + _crmSecurity = crmSecurity; + _tenantID = tenantManager.GetCurrentTenant().TenantId; + _settingsManager = settingsManager; + _configuration = configuration; + } + + + public static readonly int EntryCountOnPage = 25; + public static readonly int VisiblePageCount = 10; + + public static readonly int MaxCustomFieldSize = 150; + public static readonly int MaxCustomFieldRows = 25; + public static readonly int MaxCustomFieldCols = 150; + + public static readonly int DefaultCustomFieldSize = 40; + public static readonly int DefaultCustomFieldRows = 2; + public static readonly int DefaultCustomFieldCols = 40; + + public static readonly int MaxHistoryEventCharacters = 65000; + public static readonly decimal MaxInvoiceItemPrice = (decimal)99999999.99; + + + public IDataStore GetStore() + { + return _storageFactory.GetStorage(_tenantID.ToString(), "crm"); + } + + public IDataStore GetStoreTemplate() + { + return _storageFactory.GetStorage(String.Empty, "crm_template"); + } + + public bool CanCreateProjects() + { + throw new NotImplementedException(); + + //try + //{ + // var apiUrl = String.Format("{0}project/securityinfo.json", SetupInfo.WebApiBaseUrl); + + // var cacheKey = String.Format("{0}-{1}", SecurityContext.CurrentAccount.ID, apiUrl); + + // bool canCreateProject = false; + + // //if (HttpRuntime.Cache[cacheKey] != null) + // // return Convert.ToBoolean(HttpRuntime.Cache[cacheKey]); + + // //var apiServer = new Api.ApiServer(); + + // //var responseApi = JObject.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(apiServer.GetApiResponse(apiUrl, "GET"))))["response"]; + + // //if (responseApi.HasValues) + // // canCreateProject = Convert.ToBoolean(responseApi["canCreateProject"].Value()); + // //else + // // canCreateProject = false; + + // //HttpRuntime.Cache.Remove(cacheKey); + // //HttpRuntime.Cache.Insert(cacheKey, canCreateProject, null, System.Web.Caching.Cache.NoAbsoluteExpiration, + // // TimeSpan.FromMinutes(5)); + + // return canCreateProject; + + //} + //catch + //{ + // return false; + //} + + } + + public bool CanDownloadInvoices + { + get + { + var value = _configuration["crm:invoice:download:enable"]; + + if (string.IsNullOrEmpty(value)) return false; + + bool canDownloadFiles = Convert.ToBoolean(value); + if (canDownloadFiles && string.IsNullOrEmpty(_filesLinkUtility.DocServiceConverterUrl)) + { + canDownloadFiles = false; + } + + return canDownloadFiles; + } + } + + public bool CanCreateReports + { + get + { + return !string.IsNullOrEmpty(_filesLinkUtility.DocServiceDocbuilderUrl) && _crmSecurity.IsAdmin; + } + } + + public void SaveDefaultCurrencySettings(CurrencyInfo currency) + { + var tenantSettings = _settingsManager.Load(); + + tenantSettings.DefaultCurrency = currency.Abbreviation; + _settingsManager.Save(tenantSettings); + } + + + /// + /// The method to Decode your Base64 strings. + /// + /// The String containing the characters to decode. + /// A String containing the results of decoding the specified sequence of bytes. + public static string DecodeFrom64(string encodedData) + { + var encodedDataAsBytes = Convert.FromBase64String(encodedData); + return System.Text.Encoding.UTF8.GetString(encodedDataAsBytes); + } + + /// + /// The method create a Base64 encoded string from a normal string. + /// + /// The String containing the characters to encode. + /// The Base64 encoded string. + public static string EncodeTo64(string toEncode) + { + var toEncodeAsBytes = System.Text.Encoding.UTF8.GetBytes(toEncode); + + return Convert.ToBase64String(toEncodeAsBytes); + } + + public static byte[] ToByteArray(Stream inputStream) + { + var br = new MemoryStream(); + var data = new byte[1024]; + var readed = 0; + + while ((readed = inputStream.Read(data, 0, data.Length)) > 0) + { + br.Write(data, 0, readed); + } + br.Close(); + return br.ToArray(); + } + + public static string GetImgFormatName(ImageFormat format) + { + if (format.Equals(ImageFormat.Bmp)) return "bmp"; + if (format.Equals(ImageFormat.Emf)) return "emf"; + if (format.Equals(ImageFormat.Exif)) return "exif"; + if (format.Equals(ImageFormat.Gif)) return "gif"; + if (format.Equals(ImageFormat.Icon)) return "icon"; + if (format.Equals(ImageFormat.Jpeg)) return "jpeg"; + if (format.Equals(ImageFormat.MemoryBmp)) return "MemoryBMP"; + if (format.Equals(ImageFormat.Png)) return "png"; + if (format.Equals(ImageFormat.Tiff)) return "tiff"; + if (format.Equals(ImageFormat.Wmf)) return "wmf"; + + return "jpg"; + } + + public static byte[] SaveToBytes(Image img) + { + return CommonPhotoManager.SaveToBytes(img, GetImgFormatName(img.RawFormat)); + } + + private static readonly string[] Formats = new[] + { + "o", + "yyyy'-'MM'-'dd'T'HH'-'mm'-'ss'.'fffK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK", + "yyyy-MM-ddTHH:mm:ss" + }; + + public static DateTime ApiDateTimeParse(string data) + { + if (string.IsNullOrEmpty(data)) throw new ArgumentNullException("data"); + + if (data.Length < 7) throw new ArgumentException(CRMErrorsResource.DateTimeFormatInvalid); + + DateTime dateTime; + if (DateTime.TryParseExact(data, Formats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dateTime)) + { + return new DateTime(dateTime.Ticks, DateTimeKind.Unspecified); + } + throw new ArgumentException(CRMErrorsResource.DateTimeFormatInvalid); + } + + public static JsonDocument JObjectParseWithDateAsString(string data) + { + var readOnlySpan = new ReadOnlySpan(Encoding.UTF8.GetBytes(data)); + + Utf8JsonReader reader = new Utf8JsonReader(readOnlySpan); + + JsonDocument result; + + if (JsonDocument.TryParseValue(ref reader, out result)) + { + return result; + } + + return null; + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Classes/ImportFromCSVManager.cs b/products/ASC.CRM/Server/Classes/ImportFromCSVManager.cs new file mode 100644 index 00000000000..5da675c55ec --- /dev/null +++ b/products/ASC.CRM/Server/Classes/ImportFromCSVManager.cs @@ -0,0 +1,121 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; + +using ASC.Common; +using ASC.CRM.Core.Enums; +using ASC.MessagingSystem; +using ASC.Web.Core.Utility; + +#endregion + +namespace ASC.Web.CRM.Classes +{ + [Scope] + public class ImportFromCSVManager + { + private MessageService _messageService; + private ImportFromCSV _importFromCSV; + private Global _global; + + public ImportFromCSVManager(Global global, + ImportFromCSV importFromCSV, + MessageService messageService) + { + _global = global; + _importFromCSV = importFromCSV; + _messageService = messageService; + } + + public void StartImport(EntityType entityType, String CSVFileURI, String importSettingsJSON) + { + _importFromCSV.Start(entityType, CSVFileURI, importSettingsJSON); + + _messageService.Send(GetMessageAction(entityType)); + } + + public FileUploadResult ProcessUploadFake(string fileTemp, string importSettingsJSON) + { + var fileUploadResult = new FileUploadResult(); + + if (String.IsNullOrEmpty(fileTemp) || String.IsNullOrEmpty(importSettingsJSON)) return fileUploadResult; + + if (!_global.GetStore().IsFile("temp", fileTemp)) return fileUploadResult; + + JsonDocument jObject; + + //Read contents + using (Stream storeStream = _global.GetStore().GetReadStream("temp", fileTemp)) + { + using (var CSVFileStream = new MemoryStream()) + { + //Copy + var buffer = new byte[4096]; + int readed; + while ((readed = storeStream.Read(buffer, 0, 4096)) != 0) + { + CSVFileStream.Write(buffer, 0, readed); + } + CSVFileStream.Position = 0; + + jObject = _importFromCSV.GetInfo(CSVFileStream, importSettingsJSON); + } + } + + var jsonDocumentAsDictionary = JsonSerializer.Deserialize>(jObject.ToString()); + + jsonDocumentAsDictionary.Add("assignedPath", fileTemp); + + fileUploadResult.Success = true; + fileUploadResult.Data = Global.EncodeTo64(JsonSerializer.Serialize(jsonDocumentAsDictionary)); + + return fileUploadResult; + } + + private static MessageAction GetMessageAction(EntityType entityType) + { + switch (entityType) + { + case EntityType.Contact: + return MessageAction.ContactsImportedFromCSV; + case EntityType.Task: + return MessageAction.CrmTasksImportedFromCSV; + case EntityType.Opportunity: + return MessageAction.OpportunitiesImportedFromCSV; + case EntityType.Case: + return MessageAction.CasesImportedFromCSV; + default: + throw new ArgumentException("entityType"); + } + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Classes/InvoiceFormattedData.cs b/products/ASC.CRM/Server/Classes/InvoiceFormattedData.cs new file mode 100644 index 00000000000..8fee0d4dc94 --- /dev/null +++ b/products/ASC.CRM/Server/Classes/InvoiceFormattedData.cs @@ -0,0 +1,685 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Text.Json; + +using ASC.Common; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; + +namespace ASC.Web.CRM.Classes +{ + [Scope] + public class InvoiceFormattedData + { + private OrganisationLogoManager _organisationLogoManager; + private DaoFactory _daoFactory; + + public InvoiceFormattedData(DaoFactory daoFactory, + OrganisationLogoManager organisationLogoManager) + { + _daoFactory = daoFactory; + _organisationLogoManager = organisationLogoManager; + } + + + public int TemplateType { get; set; } + public Tuple Seller { get; set; } + public int LogoBase64Id { get; set; } + public string LogoBase64 { get; set; } + public string LogoSrcFormat { get; set; } + public Tuple Number { get; set; } + public List> Invoice { get; set; } + public Tuple Customer { get; set; } + public List TableHeaderRow { get; set; } + public List> TableBodyRows { get; set; } + public List> TableFooterRows { get; set; } + public Tuple TableTotalRow { get; set; } + public Tuple Terms { get; set; } + public Tuple Notes { get; set; } + public Tuple Consignee { get; set; } + + public int DeliveryAddressID { get; set; } + public int BillingAddressID { get; set; } + + public InvoiceFormattedData GetData(Invoice invoice, int billingAddressID, int deliveryAddressID) + { + return invoice.JsonData != null ? ReadData(invoice.JsonData) : CreateData(invoice, billingAddressID, deliveryAddressID); + } + + public InvoiceFormattedData GetDataAfterLinesUpdated(Invoice invoice) + { + if (invoice.JsonData != null) + { + var oldData = ReadData(invoice.JsonData); + return CreateDataAfterLinesUpdated(invoice, oldData); + } + else + { + return CreateData(invoice, 0, 0); + } + } + + private InvoiceFormattedData CreateData(Invoice invoice, int billingAddressID, int deliveryAddressID) + { + var data = new InvoiceFormattedData(_daoFactory, _organisationLogoManager); + var sb = new StringBuilder(); + var list = new List(); + var cultureInfo = string.IsNullOrEmpty(invoice.Language) + ? CultureInfo.CurrentCulture + : CultureInfo.GetCultureInfo(invoice.Language); + + #region TemplateType + + data.TemplateType = (int)invoice.TemplateType; + + #endregion + + #region Seller, LogoBase64, LogoSrcFormat + + var invoiceSettings = _daoFactory.GetInvoiceDao().GetSettings(); + + if (!string.IsNullOrEmpty(invoiceSettings.CompanyName)) + { + sb.Append(invoiceSettings.CompanyName); + } + + if (!string.IsNullOrEmpty(invoiceSettings.CompanyAddress)) + { + var obj = JsonDocument.Parse(invoiceSettings.CompanyAddress).RootElement; + + var str = obj.GetProperty("street").GetString(); + if (!string.IsNullOrEmpty(str)) + list.Add(str); + + str = obj.GetProperty("city").GetString(); + if (!string.IsNullOrEmpty(str)) + list.Add(str); + + str = obj.GetProperty("state").GetString(); + if (!string.IsNullOrEmpty(str)) + list.Add(str); + + str = obj.GetProperty("zip").GetString(); + if (!string.IsNullOrEmpty(str)) + list.Add(str); + + str = obj.GetProperty("country").GetString(); + if (!string.IsNullOrEmpty(str)) + list.Add(str); + + if (list.Count > 0) + { + sb.AppendLine(); + sb.Append(string.Join(", ", list)); + } + } + + data.Seller = + new Tuple(CRMInvoiceResource.ResourceManager.GetString("Seller", cultureInfo), + sb.ToString()); + + if (invoiceSettings.CompanyLogoID != 0) + { + data.LogoBase64Id = invoiceSettings.CompanyLogoID; + //data.LogoBase64 = OrganisationLogoManager.GetOrganisationLogoBase64(invoiceSettings.CompanyLogoID); + data.LogoSrcFormat = _organisationLogoManager.OrganisationLogoSrcFormat; + } + + #endregion + + #region Number + + data.Number = + new Tuple(CRMInvoiceResource.ResourceManager.GetString("Invoice", cultureInfo), + invoice.Number); + + #endregion + + + #region Invoice + + data.Invoice = new List>(); + data.Invoice.Add( + new Tuple(CRMInvoiceResource.ResourceManager.GetString("IssueDate", cultureInfo), + invoice.IssueDate.ToShortDateString())); + + if (!string.IsNullOrEmpty(invoice.PurchaseOrderNumber)) + { + data.Invoice.Add( + new Tuple( + CRMInvoiceResource.ResourceManager.GetString("PONumber", cultureInfo), + invoice.PurchaseOrderNumber)); + } + data.Invoice.Add( + new Tuple(CRMInvoiceResource.ResourceManager.GetString("DueDate", cultureInfo), + invoice.DueDate.ToShortDateString())); + + #endregion + + + #region Customer + + var customer = _daoFactory.GetContactDao().GetByID(invoice.ContactID); + + if (customer != null) + { + sb = new StringBuilder(); + + sb.Append(customer.GetTitle()); + + var billingAddress = billingAddressID != 0 + ? _daoFactory.GetContactInfoDao().GetByID(billingAddressID) + : null; + if (billingAddress != null && billingAddress.InfoType == ContactInfoType.Address && + billingAddress.Category == (int)AddressCategory.Billing) + { + list = new List(); + + var obj = JsonDocument.Parse(billingAddress.Data).RootElement; + + var str = obj.GetProperty("street").GetString(); + if (!string.IsNullOrEmpty(str)) + list.Add(str); + + str = obj.GetProperty("city").GetString(); + if (!string.IsNullOrEmpty(str)) + list.Add(str); + + str = obj.GetProperty("state").GetString(); + if (!string.IsNullOrEmpty(str)) + list.Add(str); + + str = obj.GetProperty("zip").GetString(); + if (!string.IsNullOrEmpty(str)) + list.Add(str); + + str = obj.GetProperty("country").GetString(); + if (!string.IsNullOrEmpty(str)) + list.Add(str); + + if (list.Count > 0) + { + sb.AppendLine(); + sb.Append(string.Join(", ", list)); + } + } + + data.Customer = + new Tuple(CRMInvoiceResource.ResourceManager.GetString("BillTo", cultureInfo), + sb.ToString()); + } + + #endregion + + + #region TableHeaderRow, TableBodyRows, TableFooterRows, TableTotalRow + + data.TableHeaderRow = new List + { + CRMInvoiceResource.ResourceManager.GetString("ItemCol", cultureInfo), + CRMInvoiceResource.ResourceManager.GetString("QuantityCol", cultureInfo), + CRMInvoiceResource.ResourceManager.GetString("PriceCol", cultureInfo), + CRMInvoiceResource.ResourceManager.GetString("DiscountCol", cultureInfo), + CRMInvoiceResource.ResourceManager.GetString("TaxCol", cultureInfo), + CRMInvoiceResource.ResourceManager.GetString("TaxCol", cultureInfo), + CRMInvoiceResource.ResourceManager.GetString("AmountCol", cultureInfo) + }; + + data.TableBodyRows = new List>(); + + var invoiceLines = invoice.GetInvoiceLines(_daoFactory); + var invoiceTaxes = new Dictionary(); + + decimal subtotal = 0; + decimal discount = 0; + decimal amount = 0; + + foreach (var line in invoiceLines) + { + var item = _daoFactory.GetInvoiceItemDao().GetByID(line.InvoiceItemID); + var tax1 = line.InvoiceTax1ID > 0 + ? _daoFactory.GetInvoiceTaxDao().GetByID(line.InvoiceTax1ID) + : null; + var tax2 = line.InvoiceTax2ID > 0 + ? _daoFactory.GetInvoiceTaxDao().GetByID(line.InvoiceTax2ID) + : null; + + var subtotalValue = Math.Round(line.Quantity * line.Price, 2); + var discountValue = Math.Round(subtotalValue * line.Discount / 100, 2); + + decimal rate = 0; + if (tax1 != null) + { + rate += tax1.Rate; + if (invoiceTaxes.ContainsKey(tax1.ID)) + { + invoiceTaxes[tax1.ID] = invoiceTaxes[tax1.ID] + + Math.Round((subtotalValue - discountValue) * tax1.Rate / 100, 2); + } + else + { + invoiceTaxes.Add(tax1.ID, Math.Round((subtotalValue - discountValue) * tax1.Rate / 100, 2)); + } + } + if (tax2 != null) + { + rate += tax2.Rate; + if (invoiceTaxes.ContainsKey(tax2.ID)) + { + invoiceTaxes[tax2.ID] = invoiceTaxes[tax2.ID] + + Math.Round((subtotalValue - discountValue) * tax2.Rate / 100, 2); + } + else + { + invoiceTaxes.Add(tax2.ID, Math.Round((subtotalValue - discountValue) * tax2.Rate / 100, 2)); + } + } + + decimal taxValue = Math.Round((subtotalValue - discountValue) * rate / 100, 2); + decimal amountValue = Math.Round(subtotalValue - discountValue + taxValue, 2); + + subtotal += subtotalValue; + discount += discountValue; + amount += amountValue; + + data.TableBodyRows.Add(new List + { + item.Title + (string.IsNullOrEmpty(line.Description) ? string.Empty : ": " + line.Description), + line.Quantity.ToString(CultureInfo.InvariantCulture), + line.Price.ToString(CultureInfo.InvariantCulture), + line.Discount.ToString(CultureInfo.InvariantCulture), + tax1 != null ? tax1.Name : string.Empty, + tax2 != null ? tax2.Name : string.Empty, + (subtotalValue - discountValue).ToString(CultureInfo.InvariantCulture) + }); + } + + data.TableFooterRows = new List>(); + data.TableFooterRows.Add( + new Tuple(CRMInvoiceResource.ResourceManager.GetString("Subtotal", cultureInfo), + (subtotal - discount).ToString(CultureInfo.InvariantCulture))); + + foreach (var invoiceTax in invoiceTaxes) + { + var iTax = _daoFactory.GetInvoiceTaxDao().GetByID(invoiceTax.Key); + data.TableFooterRows.Add(new Tuple( + string.Format("{0} ({1}%)", iTax.Name, iTax.Rate), + invoiceTax.Value.ToString(CultureInfo.InvariantCulture))); + } + + //data.TableFooterRows.Add(new Tuple(CRMInvoiceResource.ResourceManager.GetString("Discount", cultureInfo), "-" + discount.ToString(CultureInfo.InvariantCulture))); + + data.TableTotalRow = + new Tuple( + string.Format("{0} ({1})", CRMInvoiceResource.ResourceManager.GetString("Total", cultureInfo), + invoice.Currency), amount.ToString(CultureInfo.InvariantCulture)); + + + #endregion + + + #region Terms + + data.Terms = + new Tuple(CRMInvoiceResource.ResourceManager.GetString("Terms", cultureInfo), + invoice.Terms); + + #endregion + + + #region Notes + + if (!string.IsNullOrEmpty(invoice.Description)) + { + data.Notes = + new Tuple( + CRMInvoiceResource.ResourceManager.GetString("ClientNotes", cultureInfo), + invoice.Description); + } + + #endregion + + + #region Consignee + + var consignee = _daoFactory.GetContactDao().GetByID(invoice.ConsigneeID); + + if (consignee != null) + { + sb = new StringBuilder(); + + sb.Append(consignee.GetTitle()); + + var deliveryAddress = deliveryAddressID != 0 + ? _daoFactory.GetContactInfoDao().GetByID(deliveryAddressID) + : null; + if (deliveryAddress != null && deliveryAddress.InfoType == ContactInfoType.Address && + deliveryAddress.Category == (int)AddressCategory.Postal) + { + list = new List(); + + var obj = JsonDocument.Parse(deliveryAddress.Data).RootElement; + + var str = obj.GetProperty("street").GetString(); + if (!string.IsNullOrEmpty(str)) + list.Add(str); + + str = obj.GetProperty("city").GetString(); + if (!string.IsNullOrEmpty(str)) + list.Add(str); + + str = obj.GetProperty("state").GetString(); + if (!string.IsNullOrEmpty(str)) + list.Add(str); + + str = obj.GetProperty("zip").GetString(); + if (!string.IsNullOrEmpty(str)) + list.Add(str); + + str = obj.GetProperty("country").GetString(); + if (!string.IsNullOrEmpty(str)) + list.Add(str); + + if (list.Count > 0) + { + sb.AppendLine(); + sb.Append(string.Join(", ", list)); + } + } + + data.Consignee = + new Tuple(CRMInvoiceResource.ResourceManager.GetString("ShipTo", cultureInfo), + sb.ToString()); + } + + #endregion + + #region Addresses + + data.BillingAddressID = billingAddressID; + data.DeliveryAddressID = deliveryAddressID; + + #endregion + + return data; + + } + + private InvoiceFormattedData ReadData(string jsonData) + { + var data = new InvoiceFormattedData(_daoFactory, _organisationLogoManager); + var jsonObj = JsonDocument.Parse(jsonData).RootElement; + + #region TemplateType + + data.TemplateType = jsonObj.GetProperty("TemplateType").GetInt32(); + + #endregion + + + #region Seller, LogoBase64, LogoSrcFormat + + JsonElement seller; + if (jsonObj.TryGetProperty("Seller", out seller)) + { + data.Seller = JsonSerializer.Deserialize>(seller.ToString()); + } + + data.LogoBase64 = jsonObj.GetProperty("LogoBase64").GetString(); + data.LogoBase64Id = !String.IsNullOrEmpty(jsonObj.GetProperty("LogoBase64Id").GetString()) ? jsonObj.GetProperty("LogoBase64Id").GetInt32() : 0; + + if (string.IsNullOrEmpty(data.LogoBase64) && data.LogoBase64Id != 0) + { + data.LogoBase64 = _organisationLogoManager.GetOrganisationLogoBase64(data.LogoBase64Id); + } + + + data.LogoSrcFormat = jsonObj.GetProperty("LogoSrcFormat").GetString(); + + #endregion + + + #region Number + + JsonElement number; + if (jsonObj.TryGetProperty("Number", out number)) + { + data.Number = JsonSerializer.Deserialize>(number.ToString()); + } + + #endregion + + + #region Invoice + + JsonElement invoice; + if (jsonObj.TryGetProperty("Invoice", out invoice)) + { + data.Invoice = JsonSerializer.Deserialize>>(invoice.ToString()); + } + + #endregion + + + #region Customer + + JsonElement customer; + if (jsonObj.TryGetProperty("Customer", out customer)) + { + data.Customer = JsonSerializer.Deserialize>(customer.ToString()); + } + + #endregion + + + #region TableHeaderRow, TableBodyRows, TableFooterRows, Total + + JsonElement tableHeaderRow; + if (jsonObj.TryGetProperty("TableHeaderRow", out tableHeaderRow)) + { + data.TableHeaderRow = tableHeaderRow.EnumerateArray().Select(x => x.GetString()).ToList(); + } + + JsonElement tableBodyRows; + if (jsonObj.TryGetProperty("TableBodyRows", out tableBodyRows)) + { + data.TableBodyRows = JsonSerializer.Deserialize>>(tableBodyRows.ToString()); + } + + JsonElement tableFooterRows; + if (jsonObj.TryGetProperty("TableFooterRows", out tableFooterRows)) + { + data.TableFooterRows = JsonSerializer.Deserialize>>(tableFooterRows.ToString()); + } + + JsonElement tableTotalRow; + if (jsonObj.TryGetProperty("TableTotalRow", out tableTotalRow)) + { + data.TableTotalRow = JsonSerializer.Deserialize>(tableTotalRow.ToString()); + } + + #endregion + + + #region Terms + + JsonElement terms; + if (jsonObj.TryGetProperty("Terms", out terms)) + { + data.Terms = JsonSerializer.Deserialize>(terms.ToString()); + } + + #endregion + + + #region Notes + + JsonElement notes; + if (jsonObj.TryGetProperty("Notes", out notes)) + { + data.Notes = JsonSerializer.Deserialize>(notes.ToString()); + } + + #endregion + + + #region Consignee + + JsonElement consignee; + if (jsonObj.TryGetProperty("Consignee", out consignee)) + { + data.Consignee = JsonSerializer.Deserialize>(consignee.ToString()); + } + + #endregion + + + #region Addresses + + data.DeliveryAddressID = !String.IsNullOrEmpty(jsonObj.GetProperty("DeliveryAddressID").GetString()) ? jsonObj.GetProperty("DeliveryAddressID").GetInt32() : 0; + data.BillingAddressID = !String.IsNullOrEmpty(jsonObj.GetProperty("BillingAddressID").GetString()) ? jsonObj.GetProperty("BillingAddressID").GetInt32() : 0; + + #endregion + + return data; + } + + private InvoiceFormattedData CreateDataAfterLinesUpdated(Invoice invoice, + InvoiceFormattedData invoiceOldData) + { + var data = invoiceOldData; + + var cultureInfo = string.IsNullOrEmpty(invoice.Language) + ? CultureInfo.CurrentCulture + : CultureInfo.GetCultureInfo(invoice.Language); + + #region TableBodyRows, TableFooterRows, TableTotalRow + + data.TableBodyRows = new List>(); + + var invoiceLines = invoice.GetInvoiceLines(_daoFactory); + var invoiceTaxes = new Dictionary(); + + decimal subtotal = 0; + decimal discount = 0; + decimal amount = 0; + + foreach (var line in invoiceLines) + { + var item = _daoFactory.GetInvoiceItemDao().GetByID(line.InvoiceItemID); + var tax1 = line.InvoiceTax1ID > 0 + ? _daoFactory.GetInvoiceTaxDao().GetByID(line.InvoiceTax1ID) + : null; + var tax2 = line.InvoiceTax2ID > 0 + ? _daoFactory.GetInvoiceTaxDao().GetByID(line.InvoiceTax2ID) + : null; + + var subtotalValue = Math.Round(line.Quantity * line.Price, 2); + var discountValue = Math.Round(subtotalValue * line.Discount / 100, 2); + + decimal rate = 0; + if (tax1 != null) + { + rate += tax1.Rate; + if (invoiceTaxes.ContainsKey(tax1.ID)) + { + invoiceTaxes[tax1.ID] = invoiceTaxes[tax1.ID] + + Math.Round((subtotalValue - discountValue) * tax1.Rate / 100, 2); + } + else + { + invoiceTaxes.Add(tax1.ID, Math.Round((subtotalValue - discountValue) * tax1.Rate / 100, 2)); + } + } + if (tax2 != null) + { + rate += tax2.Rate; + if (invoiceTaxes.ContainsKey(tax2.ID)) + { + invoiceTaxes[tax2.ID] = invoiceTaxes[tax2.ID] + + Math.Round((subtotalValue - discountValue) * tax2.Rate / 100, 2); + } + else + { + invoiceTaxes.Add(tax2.ID, Math.Round((subtotalValue - discountValue) * tax2.Rate / 100, 2)); + } + } + + decimal taxValue = Math.Round((subtotalValue - discountValue) * rate / 100, 2); + decimal amountValue = Math.Round(subtotalValue - discountValue + taxValue, 2); + + subtotal += subtotalValue; + discount += discountValue; + amount += amountValue; + + data.TableBodyRows.Add(new List + { + item.Title + (string.IsNullOrEmpty(line.Description) ? string.Empty : ": " + line.Description), + line.Quantity.ToString(CultureInfo.InvariantCulture), + line.Price.ToString(CultureInfo.InvariantCulture), + line.Discount.ToString(CultureInfo.InvariantCulture), + tax1 != null ? tax1.Name : string.Empty, + tax2 != null ? tax2.Name : string.Empty, + (subtotalValue - discountValue).ToString(CultureInfo.InvariantCulture) + }); + } + + data.TableFooterRows = new List>(); + data.TableFooterRows.Add( + new Tuple(CRMInvoiceResource.ResourceManager.GetString("Subtotal", cultureInfo), + (subtotal - discount).ToString(CultureInfo.InvariantCulture))); + + foreach (var invoiceTax in invoiceTaxes) + { + var iTax = _daoFactory.GetInvoiceTaxDao().GetByID(invoiceTax.Key); + data.TableFooterRows.Add(new Tuple( + string.Format("{0} ({1}%)", iTax.Name, iTax.Rate), + invoiceTax.Value.ToString(CultureInfo.InvariantCulture))); + } + + //data.TableFooterRows.Add(new Tuple(CRMInvoiceResource.ResourceManager.GetString("Discount", cultureInfo), "-" + discount.ToString(CultureInfo.InvariantCulture))); + + data.TableTotalRow = + new Tuple( + string.Format("{0} ({1})", CRMInvoiceResource.ResourceManager.GetString("Total", cultureInfo), + invoice.Currency), amount.ToString(CultureInfo.InvariantCulture)); + + + #endregion + + return data; + } + + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Classes/LocalizedEnumConverter.cs b/products/ASC.CRM/Server/Classes/LocalizedEnumConverter.cs new file mode 100644 index 00000000000..24875c55e58 --- /dev/null +++ b/products/ASC.CRM/Server/Classes/LocalizedEnumConverter.cs @@ -0,0 +1,292 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Linq; + +using ASC.CRM.Resources; + +#endregion + +namespace ASC.CRM.Classes +{ + + public static class EnumExtension + { + public static String ToLocalizedString(this Enum value) + { + return LocalizedEnumConverter.ConvertToString(value); + } + + public static bool TryParse(string value, out T result) + where T : struct // error CS0702: Constraint cannot be special class 'System.Enum' + { + return TryParse(value, false, out result); + } + + public static bool TryParse(string value, bool ignoreCase, out T result) + where T : struct // error CS0702: Constraint cannot be special class 'System.Enum' + { + if (value == null) + value = String.Empty; + + result = default(T); + try + { + result = (T)Enum.Parse(typeof(T), value, ignoreCase); + return true; + } + catch { } + + return false; + } + + + } + + public class LocalizedEnumConverter : EnumConverter + { + private class LookupTable : Dictionary { } + private readonly Dictionary _lookupTables = new Dictionary(); + private readonly System.Resources.ResourceManager _resourceManager; + private readonly bool _isFlagEnum = false; + private readonly Array _flagValues; + + /// + /// GetList the lookup table for the given culture (creating if necessary) + /// + /// + /// + private LookupTable GetLookupTable(CultureInfo culture) + { + LookupTable result = null; + if (culture == null) + culture = CultureInfo.CurrentCulture; + + if (!_lookupTables.TryGetValue(culture, out result)) + { + result = new LookupTable(); + foreach (object value in GetStandardValues()) + { + string text = GetValueText(culture, value); + if (text != null) + { + result.Add(text, value); + } + } + _lookupTables.Add(culture, result); + } + return result; + } + + /// + /// Return the text to display for a simple value in the given culture + /// + /// The culture to get the text for + /// The enum value to get the text for + /// The localized text + private string GetValueText(CultureInfo culture, object value) + { + Type type = value.GetType(); + string resourceName = string.Format("{0}_{1}", type.Name, value.ToString()); + string result = _resourceManager.GetString(resourceName, culture); + if (result == null) + result = resourceName; + return result; + } + + /// + /// Return true if the given value is can be represented using a single bit + /// + /// + /// + private bool IsSingleBitValue(ulong value) + { + switch (value) + { + case 0: + return false; + case 1: + return true; + } + return ((value & (value - 1)) == 0); + } + + /// + /// Return the text to display for a flag value in the given culture + /// + /// The culture to get the text for + /// The flag enum value to get the text for + /// The localized text + private string GetFlagValueText(CultureInfo culture, object value) + { + // if there is a standard value then use it + // + if (Enum.IsDefined(value.GetType(), value)) + { + return GetValueText(culture, value); + } + + // otherwise find the combination of flag bit values + // that makes up the value + // + ulong lValue = Convert.ToUInt32(value); + string result = null; + foreach (object flagValue in _flagValues) + { + ulong lFlagValue = Convert.ToUInt32(flagValue); + if (IsSingleBitValue(lFlagValue)) + { + if ((lFlagValue & lValue) == lFlagValue) + { + string valueText = GetValueText(culture, flagValue); + if (result == null) + { + result = valueText; + } + else + { + result = string.Format("{0}, {1}", result, valueText); + } + } + } + } + return result; + } + + /// + /// Return the Enum value for a simple (non-flagged enum) + /// + /// The culture to convert using + /// The text to convert + /// The enum value + private object GetValue(CultureInfo culture, string text) + { + LookupTable lookupTable = GetLookupTable(culture); + object result = null; + lookupTable.TryGetValue(text, out result); + return result; + } + + private object GetFlagValue(CultureInfo culture, string text) + { + LookupTable lookupTable = GetLookupTable(culture); + string[] textValues = text.Split(','); + ulong result = 0; + foreach (string textValue in textValues) + { + object value = null; + string trimmedTextValue = textValue.Trim(); + if (!lookupTable.TryGetValue(trimmedTextValue, out value)) + { + return null; + } + result |= Convert.ToUInt32(value); + } + return Enum.ToObject(EnumType, result); + } + + public LocalizedEnumConverter(Type type) + : base(type) + { + _resourceManager = CRMEnumResource.ResourceManager; + object[] flagAttributes = type.GetCustomAttributes(typeof(FlagsAttribute), true); + _isFlagEnum = flagAttributes.Length > 0; + if (_isFlagEnum) + { + _flagValues = Enum.GetValues(type); + } + } + + public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) + { + if (value is string) + { + object result = (_isFlagEnum) ? + GetFlagValue(culture, (string)value) : GetValue(culture, (string)value); + if (result == null) + { + result = base.ConvertFrom(context, culture, value); + } + return result; + } + + return base.ConvertFrom(context, culture, value); + + } + + public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) + { + if (value != null && destinationType == typeof(string)) + { + object result = (_isFlagEnum) ? + GetFlagValueText(culture, value) : GetValueText(culture, value); + return result; + } + else + { + return base.ConvertTo(context, culture, value, destinationType); + } + } + + public static string ConvertToString(Enum value) + { + TypeConverter converter = TypeDescriptor.GetConverter(value.GetType()); + return converter.ConvertToString(value); + } + + public static List> GetValues(Type enumType, CultureInfo culture) + { + List> result = new List>(); + TypeConverter converter = TypeDescriptor.GetConverter(enumType); + foreach (Enum value in Enum.GetValues(enumType)) + { + KeyValuePair pair = new KeyValuePair(value, converter.ConvertToString(null, culture, value)); + result.Add(pair); + } + return result; + } + + public static List> GetValues(Type enumType) + { + return GetValues(enumType, CultureInfo.CurrentUICulture); + } + + public static List GetLocalizedValues(Type enumType) + { + var converter = TypeDescriptor.GetConverter(enumType); + + return (from Enum value in Enum.GetValues(enumType) + select converter.ConvertToString(null, CultureInfo.CurrentUICulture, value)).ToList(); + + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Classes/OrganisationLogoManager.cs b/products/ASC.CRM/Server/Classes/OrganisationLogoManager.cs new file mode 100644 index 00000000000..bbb932eb0eb --- /dev/null +++ b/products/ASC.CRM/Server/Classes/OrganisationLogoManager.cs @@ -0,0 +1,198 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.CRM.Core.Dao; +using ASC.Data.Storage; +using ASC.Web.Core; +using ASC.Web.Core.Utility.Skins; +using ASC.Web.CRM.Configuration; + +using Microsoft.Extensions.Options; + +namespace ASC.Web.CRM.Classes +{ + [Scope] + public class OrganisationLogoManager + { + private DaoFactory _daoFactory; + private ILog _logger; + private Global _global; + private WebImageSupplier _webImageSupplier; + + public OrganisationLogoManager(WebImageSupplier webImageSupplier, + Global global, + IOptionsMonitor logger, + DaoFactory daoFactory) + { + _webImageSupplier = webImageSupplier; + _global = global; + _logger = logger.Get("ASC.CRM"); + _daoFactory = daoFactory; + } + + + #region Members + + public readonly String OrganisationLogoBaseDirName = "organisationlogo"; + + public readonly String OrganisationLogoImgName = "logo"; + + public readonly String OrganisationLogoSrcFormat = "data:image/jpeg;base64,{0}"; + + public readonly Size OrganisationLogoSize = new Size(200, 150); + + private readonly Object _synchronizedObj = new Object(); + + #endregion + + #region Private Methods + + private String BuildFileDirectory() + { + return String.Concat(OrganisationLogoBaseDirName, "/"); + } + + private String BuildFilePath(String imageExtension) + { + return String.Concat(BuildFileDirectory(), OrganisationLogoImgName, imageExtension); + } + + private String ExecResizeImage(byte[] imageData, Size fotoSize, IDataStore dataStore, String photoPath) + { + var data = imageData; + using (var stream = new MemoryStream(data)) + using (var img = new Bitmap(stream)) + { + var imgFormat = img.RawFormat; + if (fotoSize != img.Size) + { + using (var img2 = CommonPhotoManager.DoThumbnail(img, fotoSize, false, false, false)) + { + data = CommonPhotoManager.SaveToBytes(img2, Global.GetImgFormatName(imgFormat)); + } + } + else + { + data = Global.SaveToBytes(img); + } + + using (var fileStream = new MemoryStream(data)) + { + var photoUri = dataStore.Save(photoPath, fileStream).ToString(); + photoUri = String.Format("{0}?cd={1}", photoUri, DateTime.UtcNow.Ticks); + return photoUri; + } + } + } + + #endregion + + public String GetDefaultLogoUrl() + { + return _webImageSupplier.GetAbsoluteWebPath("org_logo_default.png", ProductEntryPoint.ID); + } + + public String GetOrganisationLogoBase64(int logoID) + { + if (logoID <= 0) { return ""; } + + return _daoFactory.GetInvoiceDao().GetOrganisationLogoBase64(logoID); + + + } + + public String GetOrganisationLogoSrc(int logoID) + { + var bytestring = GetOrganisationLogoBase64(logoID); + return String.IsNullOrEmpty(bytestring) ? "" : String.Format(OrganisationLogoSrcFormat, bytestring); + } + + public void DeletePhoto(bool recursive) + { + var photoDirectory = BuildFileDirectory(); + var store = _global.GetStore(); + + lock (_synchronizedObj) + { + if (store.IsDirectory(photoDirectory)) + { + store.DeleteFiles(photoDirectory, "*", recursive); + if (recursive) + { + store.DeleteDirectory(photoDirectory); + } + } + } + } + + public int TryUploadOrganisationLogoFromTmp(DaoFactory factory) + { + var directoryPath = BuildFileDirectory(); + var dataStore = _global.GetStore(); + + if (!dataStore.IsDirectory(directoryPath)) + return 0; + + try + { + var photoPaths = _global.GetStore().ListFilesRelative("", directoryPath, OrganisationLogoImgName + "*", false); + if (photoPaths.Length == 0) + return 0; + + byte[] bytes; + using (var photoTmpStream = dataStore.GetReadStream(Path.Combine(directoryPath, photoPaths[0]))) + { + bytes = Global.ToByteArray(photoTmpStream); + } + + var logoID = factory.GetInvoiceDao().SaveOrganisationLogo(bytes); + dataStore.DeleteFiles(directoryPath, "*", false); + return logoID; + } + + catch (Exception ex) + { + _logger.ErrorFormat("TryUploadOrganisationLogoFromTmp failed with error: {0}", ex); + + return 0; + } + } + + public String UploadLogo(byte[] imageData, ImageFormat imageFormat) + { + var photoPath = BuildFilePath("." + Global.GetImgFormatName(imageFormat)); + + return ExecResizeImage(imageData, OrganisationLogoSize, _global.GetStore(), photoPath); + } + } +} diff --git a/products/ASC.CRM/Server/Classes/PathProvider.cs b/products/ASC.CRM/Server/Classes/PathProvider.cs index ce01b278227..0002a9c38bb 100644 --- a/products/ASC.CRM/Server/Classes/PathProvider.cs +++ b/products/ASC.CRM/Server/Classes/PathProvider.cs @@ -1,93 +1,111 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2018 - * - * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU - * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). - * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that - * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. - * - * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html - * - * You can contact Ascensio System SIA by email at sales@onlyoffice.com - * - * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display - * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. - * - * Pursuant to Section 7 � 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains - * relevant author attributions when distributing the software. If the display of the logo in its graphic - * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" - * in every copy of the program you distribute. - * Pursuant to Section 7 � 3(e) we decline to grant you any rights under trademark law for use of our trademarks. - * -*/ - - -using ASC.Common; -using ASC.Core.Common; - -namespace ASC.CRM -{ - [Scope] - public class PathProvider - { - - public static readonly string BaseVirtualPath = "~/Products/CRM/"; - public static string BaseAbsolutePath { get; private set; } - - public PathProvider(BaseCommonLinkUtility commonLinkUtility) - { - BaseAbsolutePath = commonLinkUtility.ToAbsolute(BaseVirtualPath); - } - - public string StartURL() - { - return BaseAbsolutePath; - } - - //public string BaseSiteUrl - //{ - // get - // { - // HttpContext context = HttpContext.Current; - // string baseUrl = context.Request.GetUrlRewriter().Scheme + "://" + context.Request.GetUrlRewriter().Authority + context.Request.ApplicationPath.TrimEnd('/') + '/'; - // return baseUrl; - // } - //} - - //public string GetVirtualPath(string physicalPath) - //{ - // string rootpath = HttpContext.Current.Server.MapPath("~/"); - // physicalPath = physicalPath.Replace(rootpath, ""); - // physicalPath = physicalPath.Replace("\\", "/"); - - // return "~/" + physicalPath; - //} - - //public string GetFileStaticRelativePath(string fileName) - //{ - // if (fileName.EndsWith(".js")) - // { - // //Attention: Only for ResourceBundleControl - // return VirtualPathUtility.ToAbsolute("~/Products/CRM/js/" + fileName); - // } - // if (fileName.EndsWith(".ascx")) - // { - // return VirtualPathUtility.ToAbsolute("~/Products/CRM/Controls/" + fileName); - // } - // if (fileName.EndsWith(".css") || fileName.EndsWith(".less")) - // { - // //Attention: Only for ResourceBundleControl - // return VirtualPathUtility.ToAbsolute("~/Products/CRM/App_Themes/default/css/" + fileName); - // } - // if (fileName.EndsWith(".png") || fileName.EndsWith(".gif") || fileName.EndsWith(".jpg")) - // { - // return WebPath.GetPath("/Products/CRM/App_Themes/default/images/" + fileName); - // } - - // return fileName; - //} - - } +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System; + +using ASC.Common; +using ASC.Data.Storage; +using ASC.Web.Studio.Utility; + +using Microsoft.AspNetCore.Http; + +#endregion + +namespace ASC.Web.CRM +{ + [Scope] + public class PathProvider + { + private HttpContext _httpContext; + private CommonLinkUtility _commonLinkUtility; + private WebPath _webPath; + + public PathProvider(IHttpContextAccessor httpContextAccessor, + CommonLinkUtility commonLinkUtility) + { + _commonLinkUtility = commonLinkUtility; + _httpContext = httpContextAccessor.HttpContext; + + BaseAbsolutePath = _commonLinkUtility.ToAbsolute(BaseVirtualPath); + } + + + public readonly String BaseVirtualPath = "~/Products/CRM/"; + public readonly String BaseAbsolutePath; + + public String StartURL() + { + return BaseVirtualPath; + } + + //public string BaseSiteUrl + //{ + // get + // { + // string baseUrl = HttpContext.Request.GetUrlRewriter().Scheme + "://" + HttpContext.Request.GetUrlRewriter().Authority + HttpContext.Request.ApplicationPath.TrimEnd('/') + '/'; + // return baseUrl; + // } + //} + + //public string GetVirtualPath(string physicalPath) + //{ + // string rootpath = HttpContext.Server.MapPath("~/"); + // physicalPath = physicalPath.Replace(rootpath, ""); + // physicalPath = physicalPath.Replace("\\", "/"); + + // return "~/" + physicalPath; + //} + + // TODO: Remove GetFileStaticRelativePath method + //public String GetFileStaticRelativePath(String fileName) + //{ + // if (fileName.EndsWith(".js")) + // { + // //Attention: Only for ResourceBundleControl + // return VirtualPathUtility.ToAbsolute("~/Products/CRM/js/" + fileName); + // } + // if (fileName.EndsWith(".ascx")) + // { + // return VirtualPathUtility.ToAbsolute("~/Products/CRM/Controls/" + fileName); + // } + // if (fileName.EndsWith(".css") || fileName.EndsWith(".less")) + // { + // //Attention: Only for ResourceBundleControl + // return VirtualPathUtility.ToAbsolute("~/Products/CRM/App_Themes/default/css/" + fileName); + // } + // if (fileName.EndsWith(".png") || fileName.EndsWith(".gif") || fileName.EndsWith(".jpg")) + // { + // return WebPath.GetPath("/Products/CRM/App_Themes/default/images/" + fileName); + // } + + // return fileName; + //} + + } + } \ No newline at end of file diff --git a/products/ASC.CRM/Server/Classes/SignalRHelper.cs b/products/ASC.CRM/Server/Classes/SignalRHelper.cs new file mode 100644 index 00000000000..d780aa1efe7 --- /dev/null +++ b/products/ASC.CRM/Server/Classes/SignalRHelper.cs @@ -0,0 +1,73 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; + +using ASC.Core.Notify.Signalr; +using ASC.VoipService; + +namespace ASC.Web.CRM.Classes +{ + public class SignalRHelper + { + private readonly string numberId; + + private SignalrServiceClient _signalrServiceClient; + + public SignalRHelper(string numberId, + SignalrServiceClient signalrServiceClient) + { + _signalrServiceClient = signalrServiceClient; + this.numberId = numberId.TrimStart('+'); + } + + public void Enqueue(string call, string agent) + { + _signalrServiceClient.EnqueueCall(numberId, call, agent); + } + + public void Incoming(string call, string agent) + { + _signalrServiceClient.IncomingCall(call, agent); + } + + public void MissCall(string call, string agent) + { + _signalrServiceClient.MissCall(numberId, call, agent); + } + + public void Reload(string agentId = null) + { + _signalrServiceClient.Reload(numberId, agentId); + } + + public Tuple GetAgent(List contactsResponsibles) + { + return _signalrServiceClient.GetAgent>(numberId, contactsResponsibles); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Classes/SocialMedia/SocialMediaAccountNotFound.cs b/products/ASC.CRM/Server/Classes/SocialMedia/SocialMediaAccountNotFound.cs new file mode 100644 index 00000000000..692ac745cb9 --- /dev/null +++ b/products/ASC.CRM/Server/Classes/SocialMedia/SocialMediaAccountNotFound.cs @@ -0,0 +1,35 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +namespace ASC.Web.CRM.SocialMedia +{ + public class SocialMediaAccountNotFound : Exception + { + public SocialMediaAccountNotFound(string message) : base(message) { } + } +} diff --git a/products/ASC.CRM/Server/Classes/SocialMedia/SocialMediaImageDescription.cs b/products/ASC.CRM/Server/Classes/SocialMedia/SocialMediaImageDescription.cs new file mode 100644 index 00000000000..45dec5e47c4 --- /dev/null +++ b/products/ASC.CRM/Server/Classes/SocialMedia/SocialMediaImageDescription.cs @@ -0,0 +1,34 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +namespace ASC.Web.CRM.Classes.SocialMedia +{ + public class SocialMediaImageDescription + { +// public SocialNetworks SocialNetwork { get; set; } + public string ImageUrl { get; set; } + public string Identity { get; set; } + } +} diff --git a/products/ASC.CRM/Server/Classes/SocialMedia/SocialMediaUI.cs b/products/ASC.CRM/Server/Classes/SocialMedia/SocialMediaUI.cs new file mode 100644 index 00000000000..d48f538f7c2 --- /dev/null +++ b/products/ASC.CRM/Server/Classes/SocialMedia/SocialMediaUI.cs @@ -0,0 +1,163 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Tenants; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; +using ASC.Web.CRM.Classes.SocialMedia; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + + +namespace ASC.Web.CRM.SocialMedia +{ + public class SocialMediaUI + { + private ILog _logger = LogManager.GetLogger("ASC"); + public DaoFactory DaoFactory { get; set; } + public TenantManager TenantManager { get; set; } + + public SocialMediaUI(DaoFactory factory, + TenantManager tenantManager) + { + DaoFactory = factory; + TenantManager = tenantManager; + } + + public List GetContactSMImages(int contactID) + { + var contact = DaoFactory.ContactDao.GetByID(contactID); + + var images = new List(); + + + var socialNetworks = DaoFactory.ContactInfoDao.GetList(contact.ID, null, null, null); + + var twitterAccounts = socialNetworks.Where(sn => sn.InfoType == ContactInfoType.Twitter).Select(sn => sn.Data.Trim()).ToList(); + + Func, Tenant, List> dlgGetTwitterImageDescriptionList = GetTwitterImageDescriptionList; + + // Parallelizing + + var waitHandles = new List(); + + var currentTenant = TenantManager.GetCurrentTenant(); + + var arGetAvatarsFromTwitter = dlgGetTwitterImageDescriptionList.BeginInvoke(twitterAccounts, currentTenant, null, null); + waitHandles.Add(arGetAvatarsFromTwitter.AsyncWaitHandle); + + WaitHandle.WaitAll(waitHandles.ToArray()); + + images.AddRange(dlgGetTwitterImageDescriptionList.EndInvoke(arGetAvatarsFromTwitter)); + + return images; + } + + public List GetContactSMImages(List twitter) + { + var images = new List(); + + Func, Tenant, List> dlgGetTwitterImageDescriptionList = GetTwitterImageDescriptionList; + + // Parallelizing + + var waitHandles = new List(); + + var currentTenant = TenantManager.GetCurrentTenant(); + + var arGetAvatarsFromTwitter = dlgGetTwitterImageDescriptionList.BeginInvoke(twitter, currentTenant, null, null); + waitHandles.Add(arGetAvatarsFromTwitter.AsyncWaitHandle); + + WaitHandle.WaitAll(waitHandles.ToArray()); + + images.AddRange(dlgGetTwitterImageDescriptionList.EndInvoke(arGetAvatarsFromTwitter)); + + return images; + } + + private List GetTwitterImageDescriptionList(List twitterAccounts, Tenant tenant) + { + var images = new List(); + + if (twitterAccounts.Count == 0) + return images; + + try + { + TenantManager.SetCurrentTenant(tenant); + + var provider = new TwitterDataProvider(TwitterApiHelper.GetTwitterApiInfoForCurrentUser()); + + twitterAccounts = twitterAccounts.Distinct().ToList(); + images.AddRange(from twitterAccount in twitterAccounts + let imageUrl = provider.GetUrlOfUserImage(twitterAccount, TwitterDataProvider.ImageSize.Small) + where imageUrl != null + select new SocialMediaImageDescription + { + Identity = twitterAccount, + ImageUrl = imageUrl, + SocialNetwork = SocialNetworks.Twitter + }); + } + catch (Exception ex) + { + _logger.Error(ex); + } + + return images; + } + + public Exception ProcessError(Exception exception, string methodName) + { + if (exception is ConnectionFailureException) + return new Exception(CRMSocialMediaResource.ErrorTwitterConnectionFailure); + + if (exception is RateLimitException) + return new Exception(CRMSocialMediaResource.ErrorTwitterRateLimit); + + if (exception is ResourceNotFoundException) + return new Exception(CRMSocialMediaResource.ErrorTwitterAccountNotFound); + + if (exception is UnauthorizedException) + return new Exception(CRMSocialMediaResource.ErrorTwitterUnauthorized); + + if (exception is SocialMediaException) + return new Exception(CRMSocialMediaResource.ErrorInternalServer); + + if (exception is SocialMediaAccountNotFound) + return exception; + + var unknownErrorText = String.Format("{0} error: Unknown exception:", methodName); + _logger.Error(unknownErrorText, exception); + return new Exception(CRMSocialMediaResource.ErrorInternalServer); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Classes/SocialMedia/TwitterApiHelper.cs b/products/ASC.CRM/Server/Classes/SocialMedia/TwitterApiHelper.cs new file mode 100644 index 00000000000..e6b07fd02b0 --- /dev/null +++ b/products/ASC.CRM/Server/Classes/SocialMedia/TwitterApiHelper.cs @@ -0,0 +1,49 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +namespace ASC.Web.CRM.Classes.SocialMedia +{ + public static class TwitterApiHelper + { + public static TwitterApiInfo GetTwitterApiInfoForCurrentUser() + { + TwitterApiInfo apiInfo = new TwitterApiInfo + { + ConsumerKey = TwitterLoginProvider.TwitterKey, + ConsumerSecret = TwitterLoginProvider.TwitterSecret + }; + + SetDefaultTokens(apiInfo); + + return apiInfo; + } + + private static void SetDefaultTokens(TwitterApiInfo apiInfo) + { + apiInfo.AccessToken = TwitterLoginProvider.TwitterDefaultAccessToken; + apiInfo.AccessTokenSecret = TwitterLoginProvider.TwitterAccessTokenSecret; + } + } +} diff --git a/products/ASC.CRM/Server/Classes/StringConverter.cs b/products/ASC.CRM/Server/Classes/StringConverter.cs new file mode 100644 index 00000000000..d9542add5d6 --- /dev/null +++ b/products/ASC.CRM/Server/Classes/StringConverter.cs @@ -0,0 +1,368 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +/* CSVReader - a simple open source C# class library to read CSV data + * by Andrew Stellman - http://www.stellman-greene.com/CSVReader + * + * StringConverter.cs - Class to convert strings to typed objects + * + * download the latest version: http://svn.stellman-greene.com/CSVReader + * + * (c) 2008, Stellman & Greene Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Stellman & Greene Consulting nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY STELLMAN & GREENE CONSULTING ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL STELLMAN & GREENE CONSULTING BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +using System; +using System.Collections.Generic; + +namespace ASC.Web.CRM.Classes +{ + /// + /// Static class to convert strings to typed values + /// + public static class StringConverter + { + public static Type ConvertString(string value, out object convertedValue) + { + // First check the whole number types, because floating point types will always parse whole numbers + // Start with the smallest types + byte byteResult; + if (byte.TryParse(value, out byteResult)) + { + convertedValue = byteResult; + return typeof(byte); + } + + short shortResult; + if (short.TryParse(value, out shortResult)) + { + convertedValue = shortResult; + return typeof(short); + } + + int intResult; + if (int.TryParse(value, out intResult)) + { + convertedValue = intResult; + return typeof(int); + } + + long longResult; + if (long.TryParse(value, out longResult)) + { + convertedValue = longResult; + return typeof(long); + } + + ulong ulongResult; + if (ulong.TryParse(value, out ulongResult)) + { + convertedValue = ulongResult; + return typeof(ulong); + } + + // No need to check the rest of the unsigned types, which will fit into the signed whole number types + + // Next check the floating point types + float floatResult; + if (float.TryParse(value, out floatResult)) + { + convertedValue = floatResult; + return typeof(float); + } + + + // It's not clear that there's anything that double.TryParse() and decimal.TryParse() will parse + // but which float.TryParse() won't + double doubleResult; + if (double.TryParse(value, out doubleResult)) + { + convertedValue = doubleResult; + return typeof(double); + } + + decimal decimalResult; + if (decimal.TryParse(value, out decimalResult)) + { + convertedValue = decimalResult; + return typeof(decimal); + } + + // It's not a number, so it's either a bool, char or string + bool boolResult; + if (bool.TryParse(value, out boolResult)) + { + convertedValue = boolResult; + return typeof(bool); + } + + char charResult; + if (char.TryParse(value, out charResult)) + { + convertedValue = charResult; + return typeof(char); + } + + convertedValue = value; + return typeof(string); + } + + /// + /// Compare two types and find a type that can fit both of them + /// + /// First type to compare + /// Second type to compare + /// The type that can fit both types, or string if they're incompatible + public static Type FindCommonType(Type typeA, Type typeB) + { + // Build the singleton type map (which will rebuild it in a typesafe manner + // if it's not already built). + BuildTypeMap(); + + if (!typeMap.ContainsKey(typeA)) + return typeof(string); + + if (!typeMap[typeA].ContainsKey(typeB)) + return typeof(string); + + return typeMap[typeA][typeB]; + } + + + // Dictionary to map two types to a common type that can hold both of them + private static Dictionary> typeMap = null; + + // Locker object to build the singleton typeMap in a typesafe manner + private static readonly object locker = new object(); + + /// + /// Build the singleton type map in a typesafe manner. + /// This map is a dictionary that maps a pair of types to a common type. + /// So typeMap[typeof(float)][typeof(uint)] will return float, while + /// typemap[typeof(char)][typeof(bool)] will return string. + /// + private static void BuildTypeMap() + { + lock (locker) + { + if (typeMap == null) + { + typeMap = new Dictionary>() + { + // Comparing byte + {typeof(byte), new Dictionary() { + { typeof(byte), typeof(byte) }, + { typeof(short), typeof(short) }, + { typeof(int), typeof(int) }, + { typeof(long), typeof(long) }, + { typeof(ulong), typeof(ulong) }, + { typeof(float), typeof(float) }, + { typeof(double), typeof(double) }, + { typeof(decimal), typeof(decimal) }, + { typeof(bool), typeof(string) }, + { typeof(char), typeof(string) }, + { typeof(string), typeof(string) }, + }}, + + // Comparing short + {typeof(short), new Dictionary() { + { typeof(byte), typeof(short) }, + { typeof(short), typeof(short) }, + { typeof(int), typeof(int) }, + { typeof(long), typeof(long) }, + { typeof(ulong), typeof(ulong) }, + { typeof(float), typeof(float) }, + { typeof(double), typeof(double) }, + { typeof(decimal), typeof(decimal) }, + { typeof(bool), typeof(string) }, + { typeof(char), typeof(string) }, + { typeof(string), typeof(string) }, + }}, + + // Comparing int + {typeof(int), new Dictionary() { + { typeof(byte), typeof(int) }, + { typeof(short), typeof(int) }, + { typeof(int), typeof(int) }, + { typeof(long), typeof(long) }, + { typeof(ulong), typeof(ulong) }, + { typeof(float), typeof(float) }, + { typeof(double), typeof(double) }, + { typeof(decimal), typeof(decimal) }, + { typeof(bool), typeof(string) }, + { typeof(char), typeof(string) }, + { typeof(string), typeof(string) }, + }}, + + // Comparing long + {typeof(long), new Dictionary() { + { typeof(byte), typeof(long) }, + { typeof(short), typeof(long) }, + { typeof(int), typeof(long) }, + { typeof(long), typeof(long) }, + { typeof(ulong), typeof(ulong) }, + { typeof(float), typeof(float) }, + { typeof(double), typeof(double) }, + { typeof(decimal), typeof(decimal) }, + { typeof(bool), typeof(string) }, + { typeof(char), typeof(string) }, + { typeof(string), typeof(string) }, + }}, + + // Comparing ulong + {typeof(ulong), new Dictionary() { + { typeof(byte), typeof(ulong) }, + { typeof(short), typeof(ulong) }, + { typeof(int), typeof(ulong) }, + { typeof(long), typeof(ulong) }, + { typeof(ulong), typeof(ulong) }, + { typeof(float), typeof(float) }, + { typeof(double), typeof(double) }, + { typeof(decimal), typeof(decimal) }, + { typeof(bool), typeof(string) }, + { typeof(char), typeof(string) }, + { typeof(string), typeof(string) }, + }}, + + // Comparing float + {typeof(float), new Dictionary() { + { typeof(byte), typeof(float) }, + { typeof(short), typeof(float) }, + { typeof(int), typeof(float) }, + { typeof(long), typeof(float) }, + { typeof(ulong), typeof(float) }, + { typeof(float), typeof(float) }, + { typeof(double), typeof(double) }, + { typeof(decimal), typeof(decimal) }, + { typeof(bool), typeof(string) }, + { typeof(char), typeof(string) }, + { typeof(string), typeof(string) }, + }}, + + // Comparing double + {typeof(double), new Dictionary() { + { typeof(byte), typeof(double) }, + { typeof(short), typeof(double) }, + { typeof(int), typeof(double) }, + { typeof(long), typeof(double) }, + { typeof(ulong), typeof(double) }, + { typeof(float), typeof(double) }, + { typeof(double), typeof(double) }, + { typeof(decimal), typeof(decimal) }, + { typeof(bool), typeof(string) }, + { typeof(char), typeof(string) }, + { typeof(string), typeof(string) }, + }}, + + // Comparing decimal + {typeof(decimal), new Dictionary() { + { typeof(byte), typeof(decimal) }, + { typeof(short), typeof(decimal) }, + { typeof(int), typeof(decimal) }, + { typeof(long), typeof(decimal) }, + { typeof(ulong), typeof(decimal) }, + { typeof(float), typeof(decimal) }, + { typeof(double), typeof(decimal) }, + { typeof(decimal), typeof(decimal) }, + { typeof(bool), typeof(string) }, + { typeof(char), typeof(string) }, + { typeof(string), typeof(string) }, + }}, + + // Comparing bool + {typeof(bool), new Dictionary() { + { typeof(byte), typeof(string) }, + { typeof(short), typeof(string) }, + { typeof(int), typeof(string) }, + { typeof(long), typeof(string) }, + { typeof(ulong), typeof(string) }, + { typeof(float), typeof(string) }, + { typeof(double), typeof(string) }, + { typeof(decimal), typeof(string) }, + { typeof(bool), typeof(bool) }, + { typeof(char), typeof(string) }, + { typeof(string), typeof(string) }, + }}, + + // Comparing char + {typeof(char), new Dictionary() { + { typeof(byte), typeof(string) }, + { typeof(short), typeof(string) }, + { typeof(int), typeof(string) }, + { typeof(long), typeof(string) }, + { typeof(ulong), typeof(string) }, + { typeof(float), typeof(string) }, + { typeof(double), typeof(string) }, + { typeof(decimal), typeof(string) }, + { typeof(bool), typeof(string) }, + { typeof(char), typeof(char) }, + { typeof(string), typeof(string) }, + }}, + + // Comparing string + {typeof(string), new Dictionary() { + { typeof(byte), typeof(string) }, + { typeof(short), typeof(string) }, + { typeof(int), typeof(string) }, + { typeof(long), typeof(string) }, + { typeof(ulong), typeof(string) }, + { typeof(float), typeof(string) }, + { typeof(double), typeof(string) }, + { typeof(decimal), typeof(string) }, + { typeof(bool), typeof(string) }, + { typeof(char), typeof(string) }, + { typeof(string), typeof(string) }, + }}, + + }; + } + } + } + } +} diff --git a/products/ASC.CRM/Server/Classes/SubscriptionManager.cs b/products/ASC.CRM/Server/Classes/SubscriptionManager.cs new file mode 100644 index 00000000000..608a788d3ab --- /dev/null +++ b/products/ASC.CRM/Server/Classes/SubscriptionManager.cs @@ -0,0 +1,145 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System; +using System.Collections.Generic; + +using ASC.Core; +using ASC.CRM.Resources; +using ASC.Notify.Model; +using ASC.Web.Core.Subscriptions; +using ASC.Web.CRM.Services.NotifyService; + +#endregion + +namespace ASC.Web.CRM +{ + public class ProductSubscriptionManager : IProductSubscriptionManager + { + private readonly Guid _setAccess = new Guid("{D4D58C55-D32E-41dc-9D22-D123AFAFC7E7}"); + private readonly Guid _responsibleForTask = new Guid("{2479B115-EAEB-4d9a-86DA-51BD708DEFDC}"); + private readonly Guid _responsibleForOpprotunity = new Guid("{73720A31-1981-480f-AB34-074E8BAEDBA9}"); + private readonly Guid _addRelationshipEvent = new Guid("{4E16DBC5-A427-469e-9EF7-A8DEA0F61310}"); + private readonly Guid _exportCompleted = new Guid("{88D3DC5E-3E46-46a1-9FEF-6B8FFF020BA4}"); + private readonly Guid _importCompleted = new Guid("{6A717AAD-16AE-4713-A782-B887766BEB9F}"); + private readonly Guid _createNewContact = new Guid("{ADAC1E70-4163-41c1-8968-67A44E4D24E7}"); + + private CoreBaseSettings _coreBaseSettings; + + public ProductSubscriptionManager(CoreBaseSettings coreBaseSettings, NotifySource notifySource) + { + _coreBaseSettings = coreBaseSettings; + } + + public NotifySource NotifySource { get; } + + public List GetSubscriptionObjects(Guid subItem) + { + return new List(); + } + + public List GetSubscriptionTypes() + { + return new List + { + new SubscriptionType + { + ID = _setAccess, + Name = CRMCommonResource.SubscriptionType_SetAccess, + NotifyAction = NotifyConstants.Event_SetAccess, + Single = true, + CanSubscribe = true + }, + new SubscriptionType + { + ID = _responsibleForTask, + Name = CRMCommonResource.SubscriptionType_ResponsibleForTask, + NotifyAction = NotifyConstants.Event_ResponsibleForTask, + Single = true, + CanSubscribe = true + }, + new SubscriptionType + { + ID = _responsibleForOpprotunity, + Name = CRMCommonResource.SubscriptionType_ResponsibleForOpportunity, + NotifyAction = NotifyConstants.Event_ResponsibleForOpportunity, + Single = true, + CanSubscribe = true + }, + new SubscriptionType + { + ID = _addRelationshipEvent, + Name = CRMCommonResource.SubscriptionType_AddRelationshipEvent, + NotifyAction = NotifyConstants.Event_AddRelationshipEvent, + Single = true, + CanSubscribe = true + }, + new SubscriptionType + { + ID = _exportCompleted, + Name = CRMCommonResource.SubscriptionType_ExportCompleted, + NotifyAction = _coreBaseSettings.CustomMode ? NotifyConstants.Event_ExportCompletedCustomMode : NotifyConstants.Event_ExportCompleted, + Single = true, + CanSubscribe = true + }, + new SubscriptionType + { + ID = _importCompleted, + Name = CRMCommonResource.SubscriptionType_ImportCompleted, + NotifyAction = _coreBaseSettings.CustomMode ? NotifyConstants.Event_ImportCompletedCustomMode : NotifyConstants.Event_ImportCompleted, + Single = true, + CanSubscribe = true + }, + new SubscriptionType + { + ID = _createNewContact, + Name = CRMCommonResource.SubscriptionType_CreateNewContact, + NotifyAction = NotifyConstants.Event_CreateNewContact, + Single = true, + CanSubscribe = true + } + }; + } + + public ISubscriptionProvider SubscriptionProvider + { + get { return NotifySource.GetSubscriptionProvider(); } + } + + public GroupByType GroupByType + { + get { return GroupByType.Simple; } + } + + public List GetSubscriptionGroups() + { + return new List(); + } + } + +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Classes/TwilioController.cs b/products/ASC.CRM/Server/Classes/TwilioController.cs new file mode 100644 index 00000000000..c26cfcd94cc --- /dev/null +++ b/products/ASC.CRM/Server/Classes/TwilioController.cs @@ -0,0 +1,538 @@ +///* +// * +// * (c) Copyright Ascensio System Limited 2010-2018 +// * +// * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU +// * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). +// * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that +// * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. +// * +// * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR +// * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html +// * +// * You can contact Ascensio System SIA by email at sales@onlyoffice.com +// * +// * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display +// * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. +// * +// * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains +// * relevant author attributions when distributing the software. If the display of the logo in its graphic +// * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" +// * in every copy of the program you distribute. +// * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. +// * +//*/ + + +//using ASC.Common; +//using ASC.Common.Logging; +//using ASC.Core; +//using ASC.Core.Common.Configuration; +//using ASC.Core.Notify.Signalr; +//using ASC.CRM.Core; +//using ASC.CRM.Core.Dao; +//using ASC.CRM.Core.Entities; +//using ASC.VoipService; +//using ASC.VoipService.Twilio; +//using ASC.Web.Studio.Utility; +//using Autofac; +//using Microsoft.AspNetCore.Http; +//using Microsoft.AspNetCore.Mvc; +//using Microsoft.AspNetCore.Mvc.Filters; +//using Microsoft.Extensions.Options; +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Net; +//using System.Net.Http; +//using System.Text; +//using System.Web; +//using Twilio.AspNet.Common; +//using Twilio.AspNet.Core; +//using Twilio.TwiML; + +//namespace ASC.Web.CRM.Classes +//{ +// [ServiceFilter(typeof(ValidateRequestFilter))] +// public class TwilioController : ApiController +// { +// public TwilioController(IOptionsMonitor logger, +// DaoFactory daoFactory, +// VoipEngine voipEngine, +// TenantManager tenantManager, +// SecurityContext securityContext) +// { +// TenantManager = tenantManager; +// Log = logger.Get("ASC"); +// DaoFactory = daoFactory; +// VoipEngine = voipEngine; +// SecurityContext = securityContext; +// } + +// public SecurityContext SecurityContext { get; } +// public TenantManager TenantManager { get; } +// public VoipEngine VoipEngine { get; } +// public DaoFactory DaoFactory { get; } + +// private readonly ILog Log; +// private static readonly object LockObj = new object(); + +// [System.Web.Http.HttpPost] +// public HttpResponseMessage Index(TwilioVoiceRequest request, [FromUri]Guid? callerId = null, [FromUri]int contactId = 0) +// { +// try +// { +// lock (LockObj) +// { +// request.AddAdditionalFields(callerId, contactId); + +// var response = request.IsInbound ? Inbound(request, DaoFactory) : Outbound(request, DaoFactory); + +// return GetHttpResponse(response); + +// } +// } +// catch (Exception e) +// { +// Log.Error(e); +// throw; +// } +// } + +// [System.Web.Http.HttpPost] +// public HttpResponseMessage Client(TwilioVoiceRequest request, [FromUri]Guid callerId) +// { +// try +// { +// request.AddAdditionalFields(callerId); + +// VoipEngine.SaveOrUpdateCall(CallFromTwilioRequest(request)); + +// return GetHttpResponse(new VoiceResponse()); +// } +// catch (Exception e) +// { +// Log.Error(e); +// throw; +// } +// } + +// [System.Web.Http.HttpPost] +// public HttpResponseMessage Dial(TwilioVoiceRequest request, [FromUri]Guid callerId, [FromUri]int contactId = 0, [FromUri]string reject = null) +// { +// try +// { +// request.AddAdditionalFields(callerId, contactId, reject); + +// var call = CallFromTwilioRequest(request); +// call = VoipEngine.SaveOrUpdateCall(call); + +// var parentCall = DaoFactory.GetVoipDao().GetCall(call.ParentID); + +// if (!string.IsNullOrEmpty(request.RecordingSid)) +// { +// if (parentCall.VoipRecord == null || string.IsNullOrEmpty(parentCall.VoipRecord.Id)) +// { +// parentCall.VoipRecord = new VoipRecord { Id = request.RecordingSid }; +// } + +// DaoFactory.GetVoipDao().SaveOrUpdateCall(parentCall); +// } + +// VoipEngine.SaveAdditionalInfo(parentCall.Id); + +// return GetHttpResponse(request.Dial()); + +// } +// catch (Exception e) +// { +// Log.Error(e); +// throw; +// } +// } + +// [System.Web.Http.HttpPost] +// public HttpResponseMessage Enqueue(TwilioVoiceRequest request, [FromUri]Guid? callerId = null, [FromUri]int contactId = 0) +// { +// try +// { +// request.AddAdditionalFields(callerId, contactId); + +// if (request.QueueResult != "bridged" && request.QueueResult != "redirected") +// { +// MissCall(request, VoipEngine); +// } + +// return GetHttpResponse(request.Enqueue(request.QueueResult)); + +// } +// catch (Exception e) +// { +// Log.Error(e); +// throw; +// } +// } + +// [System.Web.Http.HttpPost] +// public HttpResponseMessage Queue(TwilioVoiceRequest request, [FromUri]Guid? callerId = null, [FromUri]int contactId = 0) +// { +// try +// { +// request.AddAdditionalFields(callerId, contactId); +// return GetHttpResponse(request.Queue()); +// } +// catch (Exception e) +// { +// Log.Error(e); +// throw; +// } +// } + +// [System.Web.Http.HttpPost] +// public HttpResponseMessage Dequeue(TwilioVoiceRequest request, [FromUri]Guid? callerId = null, [FromUri]int contactId = 0, [FromUri]string reject = "") +// { +// try +// { +// request.AddAdditionalFields(callerId, contactId, reject); + +// if (Convert.ToBoolean(request.Reject)) +// { +// MissCall(request, VoipEngine); +// return GetHttpResponse(request.Leave()); +// } + +// VoipEngine.AnswerCall(CallFromTwilioRequest(request)); + +// return GetHttpResponse(request.Dequeue()); + +// } +// catch (Exception e) +// { +// Log.Error(e); +// throw; +// } +// } + +// [System.Web.Http.HttpPost] +// public HttpResponseMessage Wait(TwilioVoiceRequest request, [FromUri]Guid? callerId = null, [FromUri]int contactId = 0, [FromUri]string redirectTo = null) +// { +// try +// { + +// request.AddAdditionalFields(callerId, contactId, redirectTo: redirectTo); +// if (Convert.ToInt32(request.QueueTime) == 0) +// { +// var history = CallFromTwilioRequest(request); + +// history.ParentID = history.Id; + +// VoipEngine.SaveOrUpdateCall(history); + +// var to = request.RedirectTo; +// if (string.IsNullOrEmpty(to)) +// { +// request.GetSignalRHelper() +// .Enqueue(request.CallSid, callerId.HasValue ? callerId.Value.ToString() : ""); +// } +// else +// { +// request.GetSignalRHelper().Incoming(request.CallSid, to); +// } +// } + +// return GetHttpResponse(request.Wait()); + +// } +// catch (Exception e) +// { +// Log.Error(e); +// throw; +// } +// } + +// [System.Web.Http.HttpPost] +// public HttpResponseMessage GatherQueue(TwilioVoiceRequest request, [FromUri]Guid? callerId = null, [FromUri]int contactId = 0) +// { +// try +// { +// request.AddAdditionalFields(callerId, contactId); +// return GetHttpResponse(request.GatherQueue()); +// } +// catch (Exception e) +// { +// Log.Error(e); +// throw; +// } +// } + +// [System.Web.Http.HttpPost] +// public HttpResponseMessage Redirect(TwilioVoiceRequest request, [FromUri]string redirectTo, [FromUri]Guid? callerId = null) +// { +// try +// { +// request.AddAdditionalFields(callerId, redirectTo: redirectTo); +// return GetHttpResponse(request.Redirect()); +// } +// catch (Exception e) +// { +// Log.Error(e); +// throw; +// } +// } + +// [System.Web.Http.HttpPost] +// public HttpResponseMessage VoiceMail(TwilioVoiceRequest request, [FromUri]Guid? callerId = null, [FromUri]int contactId = 0) +// { +// try +// { +// request.AddAdditionalFields(callerId, contactId); + +// MissCall(request, VoipEngine); + +// return GetHttpResponse(request.VoiceMail()); + +// } +// catch (Exception e) +// { +// Log.Error(e); +// throw; +// } +// } + +// private VoiceResponse Inbound(TwilioVoiceRequest request, DaoFactory daoFactory) +// { +// SecurityContext.AuthenticateMe(TenantManager.GetCurrentTenant().OwnerId); +// var call = SaveCall(request, VoipCallStatus.Incoming, daoFactory); + +// return request.Inbound(call, daoFactory); +// } + +// private VoiceResponse Outbound(TwilioVoiceRequest request, DaoFactory daoFactory) +// { +// SaveCall(request, VoipCallStatus.Outcoming, daoFactory); + +// var history = CallFromTwilioRequest(request); +// history.ParentID = history.Id; + +// VoipEngine.SaveOrUpdateCall(history); + +// return request.Outbound(); +// } + +// private VoipCall SaveCall(TwilioVoiceRequest request, VoipCallStatus status, DaoFactory daoFactory) +// { +// var call = CallFromTwilioRequest(request); +// call.Status = status; + +// return DaoFactory.GetVoipDao().SaveOrUpdateCall(call); +// } + +// private void MissCall(TwilioVoiceRequest request, VoipEngine voipEngine) +// { +// var voipCall = CallFromTwilioRequest(request); +// voipCall.Status = VoipCallStatus.Missed; + +// if (!string.IsNullOrEmpty(request.RecordingSid)) +// { +// if (voipCall.VoipRecord == null || string.IsNullOrEmpty(voipCall.VoipRecord.Id)) +// { +// voipCall.VoipRecord = new VoipRecord { Id = request.RecordingSid }; +// } +// } + +// voipCall = voipEngine.SaveOrUpdateCall(voipCall); +// request.GetSignalRHelper().MissCall(request.CallSid, voipCall.AnsweredBy.ToString()); +// voipEngine.SaveAdditionalInfo(voipCall.Id); +// } + +// private VoipCall CallFromTwilioRequest(TwilioVoiceRequest request) +// { +// if (!string.IsNullOrEmpty(request.DialCallSid)) +// { +// return new VoipCall +// { +// Id = request.DialCallSid, +// ParentID = request.CallSid, +// From = request.From, +// To = request.To, +// AnsweredBy = request.CallerId, +// EndDialDate = DateTime.UtcNow +// }; +// } + +// return new VoipCall +// { +// Id = request.CallSid, +// ParentID = request.ParentCallSid, +// From = request.From, +// To = request.To, +// AnsweredBy = request.CallerId, +// Date = DateTime.UtcNow, +// ContactId = request.ContactId +// }; +// } + + +// private HttpResponseMessage GetHttpResponse(VoiceResponse response) +// { +// Log.Info(response); +// return new HttpResponseMessage { Content = new StringContent(response.ToString(), Encoding.UTF8, "application/xml") }; +// } +// } + +// [Scope] +// public class TwilioVoiceRequest : VoiceRequest +// { +// public TwilioVoiceRequest(TwilioResponseHelper twilioResponseHelper, +// CommonLinkUtility commonLinkUtility, +// DaoFactory daoFactory, +// SecurityContext securityContext, +// CRMSecurity crmSecurity, +// VoipEngine voipEngine, +// SignalrServiceClient signalrServiceClient) +// { + +// DaoFactory = daoFactory; +// CommonLinkUtility = commonLinkUtility; +// TwilioResponseHelper = twilioResponseHelper; +// SecurityContext = securityContext; +// CRMSecurity = crmSecurity; +// VoipEngine = voipEngine; +// SignalrServiceClient = signalrServiceClient; + +// } + +// public SignalrServiceClient SignalrServiceClient { get; } + +// public VoipEngine VoipEngine { get; } +// public CRMSecurity CRMSecurity { get; } +// public TwilioResponseHelper TwilioResponseHelper { get; } +// public CommonLinkUtility CommonLinkUtility { get; } +// public DaoFactory DaoFactory { get; } +// public SecurityContext SecurityContext { get; } +// public Guid CallerId { get; set; } +// public int ContactId { get; set; } +// public string ParentCallSid { get; set; } +// public string QueueResult { get; set; } +// public string QueueTime { get; set; } +// public string QueueSid { get; set; } +// public bool Reject { get; set; } +// public string RedirectTo { get; set; } +// public string CurrentQueueSize { get; set; } + +// public bool Pause { get { return GetSettings().Pause; } } + +// private TwilioResponseHelper GetTwilioResponseHelper() +// { +// return TwilioResponseHelper; +// } + +// private VoipSettings settings; +// private VoipSettings GetSettings() +// { +// return settings ?? (settings = DaoFactory.GetVoipDao().GetNumber(IsInbound ? To : From).Settings); +// } + +// private SignalRHelper signalRHelper; +// public SignalRHelper GetSignalRHelper() +// { +// return signalRHelper ?? (signalRHelper = new SignalRHelper(IsInbound ? To : From, SignalrServiceClient)); +// } + +// public bool IsInbound +// { +// get { return Direction == "inbound"; } +// } + +// public void AddAdditionalFields(Guid? callerId, int contactId = 0, string reject = null, string redirectTo = null) +// { +// if (callerId.HasValue && !callerId.Value.Equals(ASC.Core.Configuration.Constants.Guest.ID)) +// { +// CallerId = callerId.Value; +// SecurityContext.AuthenticateMe(CallerId); +// } +// if (contactId != 0) +// { +// ContactId = contactId; +// } + +// if (!string.IsNullOrEmpty(reject)) +// { +// Reject = Convert.ToBoolean(reject); +// } + +// if (!string.IsNullOrEmpty(redirectTo)) +// { +// RedirectTo = redirectTo; +// } +// } + +// internal VoiceResponse Inbound(VoipCall call, DaoFactory daoFactory) +// { +// var contactPhone = call.Status == VoipCallStatus.Incoming || call.Status == VoipCallStatus.Answered +// ? call.From +// : call.To; + +// Contact contact; +// var contacts = VoipEngine.GetContacts(contactPhone, daoFactory); +// var managers = contacts.SelectMany(CRMSecurity.GetAccessSubjectGuidsTo).ToList(); +// var agent = GetSignalRHelper().GetAgent(managers); + +// if (agent != null && agent.Item1 != null) +// { +// var agentId = agent.Item1.Id; +// SecurityContext.AuthenticateMe(agentId); +// call.AnsweredBy = agentId; + +// contact = contacts.FirstOrDefault(CRMSecurity.CanAccessTo); + +// DaoFactory.GetVoipDao().SaveOrUpdateCall(call); +// } +// else +// { +// contact = contacts.FirstOrDefault(); +// } + +// if (contact == null) +// { +// contact = VoipEngine.CreateContact(call.From.TrimStart('+')); +// call.ContactId = contact.ID; +// DaoFactory.GetVoipDao().SaveOrUpdateCall(call); +// } + +// return GetTwilioResponseHelper().Inbound(agent); +// } +// internal VoiceResponse Outbound() { return GetTwilioResponseHelper().Outbound(); } +// internal VoiceResponse Dial() { return GetTwilioResponseHelper().Dial(); } +// internal VoiceResponse Enqueue(string queueResult) { return GetTwilioResponseHelper().Enqueue(queueResult); } +// internal VoiceResponse Queue() { return GetTwilioResponseHelper().Queue(); } +// internal VoiceResponse Leave() { return GetTwilioResponseHelper().Leave(); } +// internal VoiceResponse Dequeue() { return GetTwilioResponseHelper().Dequeue(); } +// internal VoiceResponse Wait() { return GetTwilioResponseHelper().Wait(QueueSid, QueueTime, QueueTime); } +// internal VoiceResponse GatherQueue() { return GetTwilioResponseHelper().GatherQueue(Digits, To.Substring(1), new List()); } +// internal VoiceResponse Redirect() { return GetTwilioResponseHelper().Redirect(RedirectTo); } +// internal VoiceResponse VoiceMail() { return GetTwilioResponseHelper().VoiceMail(); } +// } + +// [Scope] +// public class ValidateRequestFilter : ActionFilterAttribute +// { +// public ValidateRequestFilter(ConsumerFactory consumerFactory, IHttpContextAccessor httpContextAccessor) +// { +// ConsumerFactory = consumerFactory; +// HttpContext = httpContextAccessor?.HttpContext; +// } + +// public HttpContext HttpContext { get; } +// public ConsumerFactory ConsumerFactory { get; } + +// public override void OnActionExecuting(ActionExecutingContext filterContext) +// { +// if (!new RequestValidationHelper().IsValidRequest(filterContext.HttpContext, ConsumerFactory.Get()["twilioAuthToken"], HttpContext.Request.GetUrlRewriter().AbsoluteUri)) +// filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.Forbidden); + +// base.OnActionExecuting(filterContext); + +// } +// } +//} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Classes/UrlConstant.cs b/products/ASC.CRM/Server/Classes/UrlConstant.cs new file mode 100644 index 00000000000..60091339d3b --- /dev/null +++ b/products/ASC.CRM/Server/Classes/UrlConstant.cs @@ -0,0 +1,51 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +namespace ASC.CRM.Classes +{ + public static class UrlConstant + { + public const String Filter = "filter"; + public const String Tag = "tag"; + public const String Status = "status"; + public const String ID = "id"; + public const String Action = "action"; + public const String PageNumber = "page"; + public const String Version = "version"; + public const String Search = "searchbytext"; + public const String ReportType = "reportType"; + public const String UserID = "userID"; + public const String View = "view"; + public const String ContactID = "contactID"; + public const String Type = "type"; + public const String FullName = "fullname"; + public const String Email = "email"; + public const String LinkMessageId = "linkMessageId"; + + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Classes/VoipEngine.cs b/products/ASC.CRM/Server/Classes/VoipEngine.cs new file mode 100644 index 00000000000..ac776c178a3 --- /dev/null +++ b/products/ASC.CRM/Server/Classes/VoipEngine.cs @@ -0,0 +1,331 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.Common.Threading; +using ASC.Core; +using ASC.Core.Tenants; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; +using ASC.VoipService; +using ASC.VoipService.Dao; + +using Microsoft.Extensions.Options; + +namespace ASC.Web.CRM.Classes +{ + [Scope] + public class VoipEngine + { + private readonly DistributedTaskQueue _queue; + + public readonly DaoFactory _daoFactory; + public readonly VoipDao _voipDao; + public readonly TenantManager _tenantManager; + public readonly ILog _logger; + public readonly int _tenantId; + public readonly SecurityContext _securityContext; + public readonly TenantUtil _tenantUtil; + public readonly CrmSecurity _crmSecurity; + + private readonly object Locker = new object(); + + public VoipEngine(DaoFactory daoFactory, + CrmSecurity crmSecurity, + TenantUtil tenantUtil, + DistributedTaskQueueOptionsManager optionsManager, + SecurityContext securityContext, + IOptionsMonitor logger, + TenantManager tenantManager, + VoipDao voipDao) + { + _crmSecurity = crmSecurity; + _tenantUtil = tenantUtil; + _securityContext = securityContext; + _queue = optionsManager.Get(); + _logger = logger.Get("ASC.CRM"); + _tenantManager = tenantManager; + _voipDao = voipDao; + _daoFactory = daoFactory; + } + + + public VoipCall SaveOrUpdateCall(VoipCall callHistory) + { + var dao = _daoFactory.GetVoipDao(); + var call = dao.GetCall(callHistory.Id) ?? callHistory; + + if (string.IsNullOrEmpty(call.ParentID)) + { + GetContact(call); + } + + if (!callHistory.AnsweredBy.Equals(Guid.Empty)) + { + call.AnsweredBy = callHistory.AnsweredBy; + } + + if (!callHistory.Date.Equals(default(DateTime))) + { + call.Date = callHistory.Date; + } + + if (!callHistory.EndDialDate.Equals(default(DateTime))) + { + call.EndDialDate = callHistory.EndDialDate; + } + + if (call.Price == 0 && callHistory.Price != default(decimal)) + { + call.Price = callHistory.Price; + } + + if (call.DialDuration == 0) + { + call.DialDuration = callHistory.DialDuration; + } + + if (call.VoipRecord == null) + { + call.VoipRecord = new VoipRecord(); + } + + if (string.IsNullOrEmpty(call.VoipRecord.Id)) + { + call.VoipRecord.Id = callHistory.VoipRecord.Id; + } + + if (call.VoipRecord.Price == default(decimal)) + { + call.VoipRecord.Price = callHistory.VoipRecord.Price; + } + + if (string.IsNullOrEmpty(call.VoipRecord.Uri)) + { + call.VoipRecord.Uri = callHistory.VoipRecord.Uri; + } + + if (call.VoipRecord.Duration == 0) + { + call.VoipRecord.Duration = callHistory.VoipRecord.Duration; + } + + if (callHistory.Status.HasValue) + { + call.Status = callHistory.Status; + } + + return dao.SaveOrUpdateCall(call); + } + + public void AddHistoryToCallContact(VoipCall call, DaoFactory daoFactory) + { + var listItemDao = daoFactory.GetListItemDao(); + + if (call == null || call.ContactId == 0) return; + + var category = listItemDao.GetByTitle(ListType.HistoryCategory, CRMCommonResource.HistoryCategory_Call); + if (category == null) + { + category = new ListItem(CRMCommonResource.HistoryCategory_Call, "event_category_call.png"); + category.ID = listItemDao.CreateItem(ListType.HistoryCategory, category); + } + + var contact = daoFactory.GetContactDao().GetByID(call.ContactId); + + if (contact != null && _crmSecurity.CanAccessTo(contact)) + { + var note = call.Status == VoipCallStatus.Incoming || call.Status == VoipCallStatus.Answered + ? CRMContactResource.HistoryVoipIncomingNote + : CRMContactResource.HistoryVoipOutcomingNote; + var content = string.Format(note, call.DialDuration); + + var relationshipEvent = new RelationshipEvent + { + CategoryID = category.ID, + EntityType = EntityType.Any, + EntityID = 0, + Content = content, + ContactID = contact.ID, + CreateOn = _tenantUtil.DateTimeFromUtc(DateTime.UtcNow), + CreateBy = _securityContext.CurrentAccount.ID + }; + + daoFactory.GetRelationshipEventDao().CreateItem(relationshipEvent); + } + } + + public Contact GetContact(VoipCall call) + { + if (call.ContactId != 0) + { + return null; + } + + var contactPhone = call.Status == VoipCallStatus.Incoming || call.Status == VoipCallStatus.Answered ? call.From : call.To; + + var newContactIds = _daoFactory.GetContactDao().GetContactIDsByContactInfo(ContactInfoType.Phone, contactPhone.TrimStart('+'), null, true); + + foreach (var newContactId in newContactIds) + { + if (newContactId != 0) + { + var existContact = _daoFactory.GetContactDao().GetByID(newContactId); + if (_crmSecurity.CanAccessTo(existContact)) + { + call.ContactId = newContactId; + return existContact; + } + } + } + + return null; + } + + public List GetContacts(string contactPhone, DaoFactory daoFactory) + { + var dao = daoFactory.GetContactDao(); + var ids = dao.GetContactIDsByContactInfo(ContactInfoType.Phone, contactPhone.TrimStart('+'), null, true); + return ids.Select(r => dao.GetByID(r)).ToList(); + } + + public void SaveAdditionalInfo(string callId) + { + lock (Locker) + { + var _queueItem = new QueueItem { CallID = callId, TenantID = _tenantId }; + + _queue.QueueTask((a, b) => SaveAdditionalInfoAction(_queueItem), _queueItem); ; + } + } + + private void SaveAdditionalInfoAction(QueueItem queueItem) + { + try + { + _tenantManager.SetCurrentTenant(queueItem.TenantID); + + var voipEngine = new VoipEngine(_daoFactory, + _crmSecurity, + _tenantUtil, + null, + _securityContext, + null, + _tenantManager, + _voipDao); + + var dao = _daoFactory.GetVoipDao(); + + var call = dao.GetCall(queueItem.CallID); + + GetPriceAndDuration(call); + + if (call.ChildCalls.Any()) + { + call.ChildCalls.ForEach(r => + { + GetPriceAndDuration(r); + voipEngine.SaveOrUpdateCall(r); + }); + } + + call = voipEngine.SaveOrUpdateCall(call); + + if (!string.IsNullOrEmpty(call.VoipRecord.Id)) + { + call.VoipRecord = _voipDao.GetProvider().GetRecord((string)call.Id, (string)call.VoipRecord.Id); + voipEngine.SaveOrUpdateCall(call); + } + + _securityContext.AuthenticateMe(call.AnsweredBy); + AddHistoryToCallContact(call, _daoFactory); + + } + catch (Exception ex) + { + _logger.ErrorFormat("SaveAdditionalInfo {0}, {1}", ex, ex.StackTrace); + } + } + + private void GetPriceAndDuration(VoipCall call) + { + var provider = _voipDao.GetProvider(); + var twilioCall = provider.GetCall(call.Id); + + call.Price = twilioCall.Price; + call.DialDuration = twilioCall.DialDuration; + } + + public void AnswerCall(VoipCall call) + { + call.AnsweredBy = _securityContext.CurrentAccount.ID; + call.Status = VoipCallStatus.Answered; + + _daoFactory.GetVoipDao().SaveOrUpdateCall(call); + } + + public Contact CreateContact(string contactPhone) + { + var contact = new Person + { + FirstName = contactPhone, + LastName = _tenantUtil.DateTimeFromUtc(DateTime.UtcNow).ToString("yyyy-MM-dd hh:mm"), + ShareType = ShareType.None, + CreateBy = _securityContext.CurrentAccount.ID, + CreateOn = DateTime.UtcNow + }; + + contact.ID = _daoFactory.GetContactDao().SaveContact(contact); + + _daoFactory.GetContactInfoDao() + .Save(new ContactInfo + { + ContactID = contact.ID, + IsPrimary = true, + InfoType = ContactInfoType.Phone, + Data = contactPhone + }); + + _crmSecurity.SetAccessTo(contact, new List { _securityContext.CurrentAccount.ID }); + + return contact; + } + + private class QueueItem : DistributedTask + { + public int TenantID { get; set; } + public string CallID { get; set; } + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Configuration/CRMsSpaceUsageStatManager.cs b/products/ASC.CRM/Server/Configuration/CRMsSpaceUsageStatManager.cs new file mode 100644 index 00000000000..08ed2f1c381 --- /dev/null +++ b/products/ASC.CRM/Server/Configuration/CRMsSpaceUsageStatManager.cs @@ -0,0 +1,88 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Common.Web; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.CRM.Resources; +using ASC.Files.Core.EF; +using ASC.Web.Core; + +using Microsoft.EntityFrameworkCore; + +namespace ASC.Web.CRM.Configuration +{ + + [Scope] + public class CrmSpaceUsageStatManager : SpaceUsageStatManager + { + private int _tenantId; + private FilesDbContext _filesDbContext; + private PathProvider _pathProvider; + + public CrmSpaceUsageStatManager(DbContextManager filesDbContext, + PathProvider pathProvider, + TenantManager tenantManager) + { + _pathProvider = pathProvider; + _filesDbContext = filesDbContext.Value; + _tenantId = tenantManager.GetCurrentTenant().TenantId; + } + + + public override List GetStatData() + { + var spaceUsage = _filesDbContext.Files.Join(_filesDbContext.Tree, + x => x.FolderId, + y => y.FolderId, + (x, y) => new { x, y } + ) + .Join(_filesDbContext.BunchObjects, + x => x.y.ParentId, + y => Convert.ToInt32(y.LeftNode), + (x, y) => new { x, y }) + .Where(x => x.y.TenantId == _tenantId && + Microsoft.EntityFrameworkCore.EF.Functions.Like(x.y.RightNode, "crm/crm_common/%")) + .Sum(x => x.x.x.ContentLength); + + return new List + {new UsageSpaceStatItem + { + + Name = CRMCommonResource.WholeCRMModule, + SpaceUsage = spaceUsage, + Url = VirtualPathUtility.ToAbsolute(_pathProvider.StartURL()) + } + + }; + } + } +} diff --git a/products/ASC.CRM/Server/Configuration/ProductEntryPoint.cs b/products/ASC.CRM/Server/Configuration/ProductEntryPoint.cs index a77744ba630..3704e50e7e9 100644 --- a/products/ASC.CRM/Server/Configuration/ProductEntryPoint.cs +++ b/products/ASC.CRM/Server/Configuration/ProductEntryPoint.cs @@ -1,307 +1,141 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2018 - * - * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU - * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). - * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that - * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. - * - * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR - * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html - * - * You can contact Ascensio System SIA by email at sales@onlyoffice.com - * - * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display - * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. - * - * Pursuant to Section 7 � 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains - * relevant author attributions when distributing the software. If the display of the logo in its graphic - * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" - * in every copy of the program you distribute. - * Pursuant to Section 7 � 3(e) we decline to grant you any rights under trademark law for use of our trademarks. - * -*/ - - -using System; -using System.Linq; - -using ASC.Common; -using ASC.Core; -using ASC.CRM.Resources; -using ASC.Web.Core; - - -namespace ASC.CRM.Configuration -{ - [Scope] - public class ProductEntryPoint : Product - { - public static readonly Guid ID = WebItemManager.CRMProductID; - private ProductContext context; - - //private static readonly object Locker = new object(); - //private static bool registered; - - private AuthContext AuthContext { get; } - private UserManager UserManager { get; } - private PathProvider PathProvider { get; } - - public ProductEntryPoint( - AuthContext authContext, - UserManager userManager, - PathProvider pathProvider) - { - AuthContext = authContext; - UserManager = userManager; - PathProvider = pathProvider; - } - - public override Guid ProductID { get { return ID; } } - - public override string Name { get { return CRMCommonResource.ProductName; } } - - public override string Description - { - get - { - var id = AuthContext.CurrentAccount.ID; - - if (UserManager.IsUserInGroup(id, ASC.Core.Users.Constants.GroupAdmin.ID) || UserManager.IsUserInGroup(id, ID)) - return CRMCommonResource.ProductDescriptionEx; - - return CRMCommonResource.ProductDescription; - } - } - - public override string StartURL { get { return PathProvider.StartURL(); } } - - public override string HelpURL { get { return "https://helpcenter.onlyoffice.com/userguides/crm.aspx"; } } - - public override string ProductClassName { get { return "crm"; } } - - public override bool Visible { get { return true; } } - - public override ProductContext Context { get { return context; } } - - public string ModuleSysName { get; set; } - - public override string ApiURL => "api/2.0/crm/info.json"; - - public override void Init() - { - context = new ProductContext - { - //MasterPageFile = String.Concat(PathProvider.BaseVirtualPath, "Masters/BasicTemplate.Master"), - DisabledIconFileName = "product_disabled_logo.png", - IconFileName = "images/crm.menu.svg", - LargeIconFileName = "images/crm.svg", - DefaultSortOrder = 30, - //SubscriptionManager = new ProductSubscriptionManager(), - //SpaceUsageStatManager = new CRMSpaceUsageStatManager(), - AdminOpportunities = () => CRMCommonResource.ProductAdminOpportunities.Split('|').ToList(), - UserOpportunities = () => CRMCommonResource.ProductUserOpportunities.Split('|').ToList(), - }; - - //if (!FilesIntegration.IsRegisteredFileSecurityProvider("crm", "crm_common")) - //{ - // FilesIntegration.RegisterFileSecurityProvider("crm", "crm_common", new FileSecurityProvider()); - //} - //if (!FilesIntegration.IsRegisteredFileSecurityProvider("crm", "opportunity")) - //{ - // FilesIntegration.RegisterFileSecurityProvider("crm", "opportunity", new FileSecurityProvider()); - //} - - //SearchHandlerManager.Registry(new SearchHandler()); - } - - - //public static void ConfigurePortal() - //{ - // if (!Global.TenantSettings.IsConfiguredPortal) - // { - // using (var scope = DIHelper.Resolve()) - // { - // var daoFactory = scope.Resolve(); - // // Task Category - // var listItemDao = daoFactory.ListItemDao; - // listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_Call, "task_category_call.png")); - // listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_Deal, "task_category_deal.png")); - // listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_Demo, "task_category_demo.png")); - // listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_Email, "task_category_email.png")); - // listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_Fax, "task_category_fax.png")); - // listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_FollowUP, "task_category_follow_up.png")); - // listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_Lunch, "task_category_lunch.png")); - // listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_Meeting, "task_category_meeting.png")); - // listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_Note, "task_category_note.png")); - // listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_Ship, "task_category_ship.png")); - // listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_SocialNetworks, "task_category_social_networks.png")); - // listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_ThankYou, "task_category_thank_you.png")); - - // // Deal Milestone New - // var milestoneDao = daoFactory.DealMilestoneDao; - // milestoneDao.Create(new DealMilestone - // { - // Title = CRMDealResource.DealMilestone_InitialContact_Title, - // Description = CRMDealResource.DealMilestone_InitialContact_Description, - // Probability = 1, - // Color = "#e795c1", - // Status = DealMilestoneStatus.Open - // }); - // milestoneDao.Create(new DealMilestone - // { - // Title = CRMDealResource.DealMilestone_Preapproach_Title, - // Description = CRMDealResource.DealMilestone_Preapproach_Description, - // Probability = 2, - // Color = "#df7895", - // Status = DealMilestoneStatus.Open - // }); - // milestoneDao.Create(new DealMilestone - // { - // Title = CRMDealResource.DealMilestone_Suspect_Title, - // Description = CRMDealResource.DealMilestone_Suspect_Description, - // Probability = 3, - // Color = "#f48454", - // SortOrder = 1, - // Status = DealMilestoneStatus.Open - // }); - // milestoneDao.Create(new DealMilestone - // { - // Title = CRMDealResource.DealMilestone_Champion_Title, - // Description = CRMDealResource.DealMilestone_Champion_Description, - // Probability = 20, - // Color = "#b58fd6", - // SortOrder = 2, - // Status = DealMilestoneStatus.Open - // }); - // milestoneDao.Create(new DealMilestone - // { - // Title = CRMDealResource.DealMilestone_Opportunity_Title, - // Description = CRMDealResource.DealMilestone_Opportunity_Description, - // Probability = 50, - // Color = "#d28cc8", - // SortOrder = 3, - // Status = DealMilestoneStatus.Open - // }); - // milestoneDao.Create(new DealMilestone - // { - // Title = CRMDealResource.DealMilestone_Prospect_Title, - // Description = CRMDealResource.DealMilestone_Prospect_Description, - // Probability = 75, - // Color = "#ffb45e", - // SortOrder = 4, - // Status = DealMilestoneStatus.Open - // }); - // milestoneDao.Create(new DealMilestone - // { - // Title = CRMDealResource.DealMilestone_Verbal_Title, - // Description = CRMDealResource.DealMilestone_Verbal_Description, - // Probability = 90, - // Color = "#ffd267", - // SortOrder = 5, - // Status = DealMilestoneStatus.Open - // }); - // milestoneDao.Create(new DealMilestone - // { - // Title = CRMDealResource.DealMilestone_Won_Title, - // Description = CRMDealResource.DealMilestone_Won_Description, - // Probability = 100, - // Color = "#6bbd72", - // SortOrder = 6, - // Status = DealMilestoneStatus.ClosedAndWon - // }); - // milestoneDao.Create(new DealMilestone - // { - // Title = CRMDealResource.DealMilestone_Lost_Title, - // Description = CRMDealResource.DealMilestone_Lost_Description, - // Probability = 0, - // Color = "#f2a9be", - // SortOrder = 7, - // Status = DealMilestoneStatus.ClosedAndLost - // }); - - // // Contact Status - // listItemDao.CreateItem(ListType.ContactStatus, new ListItem {Title = CRMContactResource.ContactStatus_Cold, Color = "#8a98d8", SortOrder = 1}); - // listItemDao.CreateItem(ListType.ContactStatus, new ListItem {Title = CRMContactResource.ContactStatus_Warm, Color = "#ffd267", SortOrder = 2}); - // listItemDao.CreateItem(ListType.ContactStatus, new ListItem {Title = CRMContactResource.ContactStatus_Hot, Color = "#df7895", SortOrder = 3}); - // // Contact Type - // listItemDao.CreateItem(ListType.ContactType, new ListItem {Title = CRMContactResource.ContactType_Client, SortOrder = 1}); - // listItemDao.CreateItem(ListType.ContactType, new ListItem {Title = CRMContactResource.ContactType_Supplier, SortOrder = 2}); - // listItemDao.CreateItem(ListType.ContactType, new ListItem {Title = CRMContactResource.ContactType_Partner, SortOrder = 3}); - // listItemDao.CreateItem(ListType.ContactType, new ListItem {Title = CRMContactResource.ContactType_Competitor, SortOrder = 4}); - - // // History Category - // listItemDao.CreateItem(ListType.HistoryCategory, new ListItem(CRMCommonResource.HistoryCategory_Note, "event_category_note.png")); - // listItemDao.CreateItem(ListType.HistoryCategory, new ListItem(CRMCommonResource.HistoryCategory_Email, "event_category_email.png")); - // listItemDao.CreateItem(ListType.HistoryCategory, new ListItem(CRMCommonResource.HistoryCategory_Call, "event_category_call.png")); - // listItemDao.CreateItem(ListType.HistoryCategory, new ListItem(CRMCommonResource.HistoryCategory_Meeting, "event_category_meeting.png")); - // // Tags - // daoFactory.TagDao.AddTag(EntityType.Contact, CRMContactResource.Lead, true); - // daoFactory.TagDao.AddTag(EntityType.Contact, CRMContactResource.Customer, true); - // daoFactory.TagDao.AddTag(EntityType.Contact, CRMContactResource.Supplier, true); - // daoFactory.TagDao.AddTag(EntityType.Contact, CRMContactResource.Staff, true); - - // var tenantSettings = Global.TenantSettings; - // tenantSettings.WebFormKey = Guid.NewGuid(); - // tenantSettings.IsConfiguredPortal = true; - // tenantSettings.Save(); - // } - // } - - // if (!Global.TenantSettings.IsConfiguredSmtp) - // { - // var smtp = CRMSettings.Load().SMTPServerSettingOld; - // if (smtp != null && CoreContext.Configuration.SmtpSettings.IsDefaultSettings) - // { - // try - // { - // var newSettings = new SmtpSettings(smtp.Host, smtp.Port, smtp.SenderEmailAddress, - // smtp.SenderDisplayName) - // { - // EnableSSL = smtp.EnableSSL, - // EnableAuth = smtp.RequiredHostAuthentication, - // }; - - // if (!string.IsNullOrEmpty(smtp.HostLogin) && !string.IsNullOrEmpty(smtp.HostPassword)) - // { - // newSettings.SetCredentials(smtp.HostLogin, smtp.HostPassword); - //} - - // CoreContext.Configuration.SmtpSettings = newSettings; - // } - // catch (Exception e) - // { - // LogManager.GetLogger("ASC").Error("ConfigurePortal", e); - // } - // } - // var tenantSettings = Global.TenantSettings; - // tenantSettings.IsConfiguredSmtp = true; - // tenantSettings.Save(); - // } - //} - - //public override void Shutdown() - //{ - // if (registered) - // { - // NotifyClient.Instance.Client.UnregisterSendMethod(NotifyClient.SendAutoReminderAboutTask); - - // } - //} - - //public static void RegisterSendMethods() - //{ - // lock (Locker) - // { - // if (!registered) - // { - // registered = true; - - // NotifyClient.Instance.Client.RegisterSendMethod(NotifyClient.SendAutoReminderAboutTask, "0 * * ? * *"); - - // } - // } - //} - } +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using System; +using System.Linq; + +using ASC.Common; +using ASC.Core; +using ASC.CRM.Resources; +using ASC.Web.Core; +using ASC.Web.Files.Api; + + +namespace ASC.Web.CRM.Configuration +{ + [Scope] + public class ProductEntryPoint : Product + { + private ProductContext _context; + private FilesIntegration _filesIntegration; + private PathProvider _pathProvider; + private SecurityContext _securityContext; + private UserManager _userManager; + + public ProductEntryPoint(SecurityContext securityContext, + UserManager userManager, + PathProvider pathProvider, + FilesIntegration filesIntegration) + { + _securityContext = securityContext; + _userManager = userManager; + _pathProvider = pathProvider; + _filesIntegration = filesIntegration; + } + + public static readonly Guid ID = WebItemManager.CRMProductID; + public override string ApiURL + { + get => "api/2.0/crm/info.json"; + } + public override Guid ProductID { get { return ID; } } + public override string Name { get { return CRMCommonResource.ProductName; } } + public override string Description + { + get + { + var id = _securityContext.CurrentAccount.ID; + + if (_userManager.IsUserInGroup(id, ASC.Core.Users.Constants.GroupAdmin.ID) || _userManager.IsUserInGroup(id, ID)) + return CRMCommonResource.ProductDescriptionEx; + + return CRMCommonResource.ProductDescription; + } + } + + public override string StartURL { get { return _pathProvider.StartURL(); } } + public override string HelpURL { get { return string.Concat(_pathProvider.BaseVirtualPath, "help.aspx"); } } + public override string ProductClassName { get { return "crm"; } } + public override bool Visible { get { return true; } } + public override ProductContext Context { get { return _context; } } + public string ModuleSysName { get; set; } + + public override void Init() + { + _context = new ProductContext + { + DisabledIconFileName = "product_disabled_logo.png", + IconFileName = "product_logo.png", + LargeIconFileName = "product_logolarge.svg", + DefaultSortOrder = 30, + // SubscriptionManager = new ProductSubscriptionManager(), + // SpaceUsageStatManager = new CRMSpaceUsageStatManager(), + AdminOpportunities = () => CRMCommonResource.ProductAdminOpportunities.Split('|').ToList(), + UserOpportunities = () => CRMCommonResource.ProductUserOpportunities.Split('|').ToList(), + }; + + //if (!FilesIntegration.IsRegisteredFileSecurityProvider("crm", "crm_common")) + //{ + // FilesIntegration.RegisterFileSecurityProvider("crm", "crm_common", FileSecurityProvider); + //} + //if (!FilesIntegration.IsRegisteredFileSecurityProvider("crm", "opportunity")) + //{ + // FilesIntegration.RegisterFileSecurityProvider("crm", "opportunity", FileSecurityProvider); + //} + + // SearchHandlerManager.Registry(new SearchHandler()); + + //GlobalConfiguration.Configuration.Routes.MapHttpRoute( + // name: "Twilio", + // routeTemplate: "twilio/{action}", + // defaults: new {controller = "Twilio", action = "index" }); + + // ClientScriptLocalization = new ClientLocalizationResources(); + } + + //public override void Shutdown() + //{ + // if (registered) + // { + // NotifyClient.Client.UnregisterSendMethod(NotifyClient.SendAutoReminderAboutTask); + + // } + //} + + //public static void RegisterSendMethods() + //{ + // lock (Locker) + // { + // if (!registered) + // { + // registered = true; + + // NotifyClient.Client.RegisterSendMethod(NotifyClient.SendAutoReminderAboutTask, "0 * * ? * *"); + + // } + // } + //} + } } \ No newline at end of file diff --git a/products/ASC.CRM/Server/Configuration/VoipModule.cs b/products/ASC.CRM/Server/Configuration/VoipModule.cs new file mode 100644 index 00000000000..7e57c0e9f67 --- /dev/null +++ b/products/ASC.CRM/Server/Configuration/VoipModule.cs @@ -0,0 +1,103 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +using ASC.CRM.Resources; +using ASC.Web.Core; +using ASC.Web.Core.WebZones; +using ASC.Web.Studio.Core; + +namespace ASC.Web.CRM.Configuration +{ + [WebZone(WebZoneType.CustomProductList)] + public class VoipModule : IAddon + { + private PathProvider _pathProvider; + private SetupInfo _setupInfo; + + public VoipModule(PathProvider pathProvider, + SetupInfo setupInfo) + { + _pathProvider = pathProvider; + _setupInfo = setupInfo; + } + + + public Guid ID + { + get { return WebItemManager.VoipModuleID; } + } + + public string Name + { + get { return CRMVoipResource.VoipModuleTitle; } + } + + public string Description + { + get { return CRMVoipResource.VoipModuleDescription; } + } + + public string StartURL + { + get { return _pathProvider.StartURL() + "settings.aspx?type=voip.common&sysname=/modules/voip"; } + } + + public string HelpURL + { + get { return null; } + } + + public string ProductClassName { get { return "voip"; } } + + public bool Visible { get { return _setupInfo.VoipEnabled; } } + + public AddonContext Context { get; private set; } + + public void Init() + { + Context = new AddonContext + { + DefaultSortOrder = 90, + IconFileName = "voip_logo.png", + CanNotBeDisabled = true + }; + } + + public void Shutdown() + { + + } + + WebItemContext IWebItem.Context + { + get { return Context; } + } + + public string ApiURL => throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/CRMConstants.cs b/products/ASC.CRM/Server/Core/CRMConstants.cs new file mode 100644 index 00000000000..b160015338f --- /dev/null +++ b/products/ASC.CRM/Server/Core/CRMConstants.cs @@ -0,0 +1,37 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +namespace ASC.CRM.Core +{ + public static class CrmConstants + { + public static readonly String StorageModule = "crm"; + public static readonly String DatabaseId = "crm"; + public static readonly String FileKeyFormat = "{0}/{1}/{2}/{3}"; // ProjectID/FileID/FileVersion/FileTitle + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Dao/AbstractDao.cs b/products/ASC.CRM/Server/Core/Dao/AbstractDao.cs new file mode 100644 index 00000000000..4c7e4add004 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/AbstractDao.cs @@ -0,0 +1,308 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Enums; + +using AutoMapper; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.CRM.Core.Dao +{ + public class AbstractDao + { + protected readonly List _supportedEntityType = new List(); + public CrmDbContext CrmDbContext { get; } + protected readonly SecurityContext _securityContext; + protected readonly ICache _cache; + protected ILog _logger; + protected IMapper _mapper; + + public AbstractDao( + DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + IOptionsMonitor logger, + ICache ascCache, + IMapper mapper + ) + { + _mapper = mapper; + _logger = logger.Get("ASC.CRM"); + + _cache = ascCache; + + CrmDbContext = dbContextManager.Get(CrmConstants.DatabaseId); + + TenantID = tenantManager.GetCurrentTenant().TenantId; + _securityContext = securityContext; + + _supportedEntityType.Add(EntityType.Company); + _supportedEntityType.Add(EntityType.Person); + _supportedEntityType.Add(EntityType.Contact); + _supportedEntityType.Add(EntityType.Opportunity); + _supportedEntityType.Add(EntityType.Case); + + /* + _invoiceItemCacheKey = String.Concat(TenantID, "/invoiceitem"); + _invoiceTaxCacheKey = String.Concat(TenantID, "/invoicetax"); + _invoiceLineCacheKey = String.Concat(TenantID, "/invoiceline"); + + if (_cache.Get(_invoiceItemCacheKey) == null) + { + _cache.Insert(_invoiceItemCacheKey, String.Empty); + } + if (_cache.Get(_invoiceTaxCacheKey) == null) + { + _cache.Insert(_invoiceTaxCacheKey, String.Empty); + } + if (_cache.Get(_invoiceLineCacheKey) == null) + { + _cache.Insert(_invoiceLineCacheKey, String.Empty); + } + */ + + + } + + /* + protected readonly String _invoiceItemCacheKey; + protected readonly String _invoiceTaxCacheKey; + protected readonly String _invoiceLineCacheKey; + */ + + protected int TenantID { get; private set; } + + protected List SearchByTags(EntityType entityType, int[] exceptIDs, IEnumerable tags) + { + if (tags == null || !tags.Any()) + throw new ArgumentException(); + + var tagIDs = new List(); + + foreach (var tag in tags) + { + tagIDs.Add(CrmDbContext + .Tags + .Where(x => x.EntityType == entityType && String.Compare(x.Title, tag.Trim(), true) == 0) + .Select(x => x.Id).Single()); + } + + var sqlQuery = CrmDbContext.EntityTags.Where(x => x.EntityType == entityType && tagIDs.Contains(x.TagId)); + + if (exceptIDs != null && exceptIDs.Length > 0) + sqlQuery = sqlQuery.Where(x => exceptIDs.Contains(x.EntityId)); + + return sqlQuery.GroupBy(x => x.EntityId) + .Where(x => x.Count() == tags.Count()) + .Select(x => x.Key) + .ToList(); + } + + protected Dictionary GetRelativeToEntity(int[] contactID, EntityType entityType, int[] entityID) + { + Expression> exp = null; + + if (contactID != null && contactID.Length > 0 && (entityID == null || entityID.Length == 0)) + exp = x => x.EntityType == entityType && contactID.Contains(x.ContactId); + else if (entityID != null && entityID.Length > 0 && (contactID == null || contactID.Length == 0)) + exp = x => x.EntityType == entityType && entityID.Contains(x.EntityId); + + return CrmDbContext.EntityContact + .Where(exp) + .Select(x => new { EntityId = x.EntityId, ContactId = x.ContactId }) + .ToList() + .GroupBy(x => x.EntityId) + .ToDictionary(x => x.Key, y => y.Select(c => c.ContactId).ToArray()); + } + + protected int[] GetRelativeToEntity(int? contactID, EntityType entityType, int? entityID) + { + return GetRelativeToEntityInDb(contactID, entityType, entityID); + } + + protected int[] GetRelativeToEntityInDb(int? contactID, EntityType entityType, int? entityID) + { + if (contactID.HasValue && !entityID.HasValue) + return CrmDbContext.EntityContact + .Where(x => x.EntityType == entityType && x.ContactId == contactID.Value) + .Select(x => x.EntityId) + .ToArray(); + + if (!contactID.HasValue && entityID.HasValue) + return CrmDbContext.EntityContact + .Where(x => x.EntityType == entityType && x.EntityId == entityID.Value) + .Select(x => x.ContactId) + .ToArray(); + + throw new ArgumentException(); + } + + protected void SetRelative(int[] contactID, EntityType entityType, int entityID) + { + if (entityID == 0) + throw new ArgumentException(); + + using var tx = CrmDbContext.Database.BeginTransaction(); + + var exists = CrmDbContext.EntityContact + .Where(x => x.EntityType == entityType && x.EntityId == entityID) + .Select(x => x.ContactId) + .ToArray(); + + foreach (var existContact in exists) + { + var items = CrmDbContext.EntityContact + .Where(x => x.EntityType == entityType && x.EntityId == entityID && x.ContactId == existContact); + + CrmDbContext.EntityContact.RemoveRange(items); + } + + if (!(contactID == null || contactID.Length == 0)) + foreach (var id in contactID) + SetRelative(id, entityType, entityID); + + tx.Commit(); + } + + protected void SetRelative(int contactID, EntityType entityType, int entityID) + { + var dbEntity = new DbEntityContact + { + ContactId = contactID, + EntityType = entityType, + EntityId = entityID + }; + + CrmDbContext.EntityContact.Add(dbEntity); + + CrmDbContext.SaveChanges(); + } + + protected void RemoveRelativeInDb(int[] contactID, EntityType entityType, int[] entityID) + { + if ((contactID == null || contactID.Length == 0) && (entityID == null || entityID.Length == 0)) + throw new ArgumentException(); + + Expression> expr = null; + + if (contactID != null && contactID.Length > 0) + expr = x => contactID.Contains(x.ContactId); + + if (entityID != null && entityID.Length > 0) + expr = x => entityID.Contains(x.EntityId) && x.EntityType == entityType; + + var dbCrmEntity = CrmDbContext.EntityContact; + + dbCrmEntity.RemoveRange(dbCrmEntity.Where(expr)); + + CrmDbContext.SaveChanges(); + } + + protected void RemoveRelative(int contactID, EntityType entityType, int entityID) + { + int[] contactIDs = null; + int[] entityIDs = null; + + + if (contactID > 0) + contactIDs = new[] { contactID }; + + if (entityID > 0) + entityIDs = new[] { entityID }; + + + RemoveRelativeInDb(contactIDs, entityType, entityIDs); + } + + + public int SaveOrganisationLogo(byte[] bytes) + { + var dbEntity = new DbOrganisationLogo + { + Content = Convert.ToBase64String(bytes), + CreateOn = DateTime.UtcNow, + CreateBy = _securityContext.CurrentAccount.ID.ToString() + }; + + CrmDbContext.OrganisationLogo.Add(dbEntity); + + CrmDbContext.SaveChanges(); + + return dbEntity.Id; + } + + public string GetOrganisationLogoBase64(int logo_id) + { + if (logo_id <= 0) throw new ArgumentException(); + + return Query(CrmDbContext.OrganisationLogo) + .Where(x => x.Id == logo_id) + .Select(x => x.Content) + .FirstOrDefault(); + } + + public bool HasActivity() + { + return Query(CrmDbContext.Cases).Any() && + Query(CrmDbContext.Deals).Any() && + Query(CrmDbContext.Tasks).Any() && + Query(CrmDbContext.Contacts).Any(); + } + + protected IQueryable Query(DbSet set) where T : class, IDbCrm + { + return set.Where(r => r.TenantId == TenantID); + } + + protected string GetTenantColumnName(string table) + { + var tenant = "tenant_id"; + + if (!table.Contains(" ")) return tenant; + + return table.Substring(table.IndexOf(" ")).Trim() + "." + tenant; + } + + protected static Guid ToGuid(object guid) + { + var str = guid as string; + + return !string.IsNullOrEmpty(str) ? new Guid(str) : Guid.Empty; + } + } +} diff --git a/products/ASC.CRM/Server/Core/Dao/CasesDao.cs b/products/ASC.CRM/Server/Core/Dao/CasesDao.cs new file mode 100644 index 00000000000..8c3296d124b --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/CasesDao.cs @@ -0,0 +1,605 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text.RegularExpressions; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.Core.Tenants; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.Files.Core; +using ASC.Web.CRM.Core.Search; +using ASC.Web.Files.Api; + +using AutoMapper; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +using OrderBy = ASC.CRM.Core.Entities.OrderBy; +using SortedByType = ASC.CRM.Core.Enums.SortedByType; + +namespace ASC.CRM.Core.Dao +{ + [Scope] + public class CasesDao : AbstractDao + { + private readonly BundleSearch _bundleSearch; + private readonly AuthorizationManager _authorizationManager; + private readonly FilesIntegration _filesIntegration; + private readonly TenantUtil _tenantUtil; + private readonly CrmSecurity _crmSecurity; + + public CasesDao( + DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + CrmSecurity crmSecurity, + TenantUtil tenantUtil, + FilesIntegration filesIntegration, + AuthorizationManager authorizationManager, + IOptionsMonitor logger, + ICache ascCache, + BundleSearch bundleSearch, + IMapper mapper + ) : + base(dbContextManager, + tenantManager, + securityContext, + logger, + ascCache, + mapper) + { + _crmSecurity = crmSecurity; + _tenantUtil = tenantUtil; + _filesIntegration = filesIntegration; + _authorizationManager = authorizationManager; + _bundleSearch = bundleSearch; + } + + public void AddMember(int caseID, int memberID) + { + SetRelative(memberID, EntityType.Case, caseID); + } + public Dictionary GetMembers(int[] caseID) + { + return GetRelativeToEntity(null, EntityType.Case, caseID); + } + + public int[] GetMembers(int caseID) + { + return GetRelativeToEntity(null, EntityType.Case, caseID); + } + + public void SetMembers(int caseID, int[] memberID) + { + SetRelative(memberID, EntityType.Case, caseID); + } + + public void RemoveMember(int caseID, int memberID) + { + RemoveRelative(memberID, EntityType.Case, caseID); + } + + public int[] SaveCasesList(List items) + { + using var tx = CrmDbContext.Database.BeginTransaction(); + + var result = items.Select(item => CreateCasesInDb(item.Title)).ToArray(); + + tx.Commit(); + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "cases.*")); + + return result; + } + + public Cases CloseCases(int id) + { + var dbEntity = CrmDbContext.Cases.Find(id); + + if (dbEntity == null) + throw new ArgumentException(); + + var entity = _mapper.Map(dbEntity); + + if (dbEntity.IsClosed) return entity; + if (dbEntity.TenantId != TenantID) return null; + + _crmSecurity.DemandEdit(entity); + + dbEntity.IsClosed = true; + + CrmDbContext.SaveChanges(); + + return entity; + } + + public Cases ReOpenCases(int id) + { + var dbEntity = CrmDbContext.Cases.Find(id); + + if (dbEntity == null) + throw new ArgumentException(); + + var entity = _mapper.Map(dbEntity); + + if (!dbEntity.IsClosed) return entity; + if (dbEntity.TenantId != TenantID) return null; + + _crmSecurity.DemandEdit(entity); + + dbEntity.IsClosed = false; + + CrmDbContext.SaveChanges(); + + return entity; + } + + public int CreateCases(String title) + { + var result = CreateCasesInDb(title); + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "invoice.*")); + + return result; + } + + private int CreateCasesInDb(String title) + { + var dbCase = new DbCase + { + Title = title, + IsClosed = false, + CreateOn = _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow()), + CreateBy = _securityContext.CurrentAccount.ID, + LastModifedOn = _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow()), + LastModifedBy = _securityContext.CurrentAccount.ID, + TenantId = TenantID + }; + + CrmDbContext.Cases.Add(dbCase); + + CrmDbContext.SaveChanges(); + + return dbCase.Id; + + } + + public void UpdateCases(Cases cases) + { + _crmSecurity.DemandEdit(cases); + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "invoice.*")); + + CrmDbContext.Cases.Update(new DbCase + { + Id = cases.ID, + Title = cases.Title, + IsClosed = cases.IsClosed, + LastModifedOn = _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow()), + LastModifedBy = _securityContext.CurrentAccount.ID, + TenantId = TenantID, + CreateBy = cases.CreateBy, + CreateOn = cases.CreateOn + }); + + CrmDbContext.SaveChanges(); + } + + public Cases DeleteCases(int casesID) + { + if (casesID <= 0) return null; + + var cases = GetByID(casesID); + + if (cases == null) return null; + + _crmSecurity.DemandDelete(cases); + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "invoice.*")); + + DeleteBatchCases(new[] { casesID }); + return cases; + } + + public List DeleteBatchCases(List caseses) + { + caseses = caseses.FindAll(_crmSecurity.CanDelete).ToList(); + + if (!caseses.Any()) return caseses; + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "invoice.*")); + + DeleteBatchCasesExecute(caseses); + + return caseses; + } + + public List DeleteBatchCases(int[] casesID) + { + if (casesID == null || !casesID.Any()) return null; + + var cases = GetCases(casesID).FindAll(_crmSecurity.CanDelete).ToList(); + + if (!cases.Any()) return cases; + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "invoice.*")); + + DeleteBatchCasesExecute(cases); + + return cases; + } + + private void DeleteBatchCasesExecute(List caseses) + { + var casesID = caseses.Select(x => x.ID).ToArray(); + + var tagdao = _filesIntegration.DaoFactory.GetTagDao(); + + var tagNames = Query(CrmDbContext.RelationshipEvent) + .Where(x => x.HaveFiles && casesID.Contains(x.EntityId) && x.EntityType == EntityType.Case) + .Select(x => String.Format("RelationshipEvent_{0}", x.Id)).ToArray(); + + var filesIDs = tagdao.GetTags(tagNames, TagType.System).Where(t => t.EntryType == FileEntryType.File).Select(t => Convert.ToInt32(t.EntryId)).ToArray(); + + using var tx = CrmDbContext.Database.BeginTransaction(); + + CrmDbContext.RemoveRange(Query(CrmDbContext.FieldValue) + .Where(x => casesID.Contains(x.EntityId) && x.EntityType == EntityType.Case)); + + CrmDbContext.RemoveRange(Query(CrmDbContext.RelationshipEvent) + .Where(x => casesID.Contains(x.EntityId) && x.EntityType == EntityType.Case)); + + CrmDbContext.RemoveRange(Query(CrmDbContext.Tasks) + .Where(x => casesID.Contains(x.EntityId) && x.EntityType == EntityType.Case)); + + CrmDbContext.RemoveRange(CrmDbContext.EntityTags.Where(x => casesID.Contains(x.EntityId) && x.EntityType == EntityType.Case)); + + CrmDbContext.Cases.RemoveRange(caseses.ConvertAll(x => new DbCase + { + Id = x.ID + })); + + CrmDbContext.SaveChanges(); + + tx.Commit(); + + caseses.ForEach(item => _authorizationManager.RemoveAllAces(item)); + + if (0 < tagNames.Length) + { + var filedao = _filesIntegration.DaoFactory.GetFileDao(); + + foreach (var filesID in filesIDs) + { + filedao.DeleteFile(filesID); + } + } + + //todo: remove indexes + } + + public List GetAllCases() + { + return GetCases(String.Empty, 0, null, null, 0, 0, new OrderBy(Enums.SortedByType.Title, true)); + } + + public int GetCasesCount() + { + return GetCasesCount(String.Empty, 0, null, null); + } + + public int GetCasesCount( + String searchText, + int contactID, + bool? isClosed, + IEnumerable tags) + { + + var cacheKey = TenantID.ToString(CultureInfo.InvariantCulture) + + "cases" + + _securityContext.CurrentAccount.ID.ToString() + + searchText + + contactID; + + if (tags != null) + cacheKey += String.Join("", tags.ToArray()); + + if (isClosed.HasValue) + cacheKey += isClosed.Value; + + var fromCache = _cache.Get(cacheKey); + + if (fromCache != null) return Convert.ToInt32(fromCache); + + + var withParams = !(String.IsNullOrEmpty(searchText) && + contactID <= 0 && + isClosed == null && + (tags == null || !tags.Any())); + + + var exceptIDs = _crmSecurity.GetPrivateItems(typeof(Cases)).ToList(); + + int result; + + if (withParams) + { + var dbCasesQuery = GetDbCasesByFilters(exceptIDs, searchText, contactID, isClosed, tags); + + result = dbCasesQuery != null ? dbCasesQuery.Count() : 0; + + } + else + { + + var countWithoutPrivate = Query(CrmDbContext.Cases).Count(); + + var privateCount = exceptIDs.Count; + + if (privateCount > countWithoutPrivate) + { + _logger.ErrorFormat(@"Private cases count more than all cases. Tenant: {0}. CurrentAccount: {1}", + TenantID, + _securityContext.CurrentAccount.ID); + privateCount = 0; + } + + result = countWithoutPrivate - privateCount; + + } + + if (result > 0) + { + _cache.Insert(cacheKey, result, TimeSpan.FromSeconds(30)); + } + + return result; + + } + + + private IQueryable GetDbCasesByFilters( + ICollection exceptIDs, + String searchText, + int contactID, + bool? isClosed, + IEnumerable tags) + { + + var result = Query(CrmDbContext.Cases).AsNoTracking(); + + var ids = new List(); + + if (!String.IsNullOrEmpty(searchText)) + { + searchText = searchText.Trim(); + + var keywords = searchText.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) + .ToArray(); + + if (keywords.Length > 0) + { + if (!_bundleSearch.TrySelectCase(searchText, out ids)) + { + foreach (var k in keywords) + { + result = result.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Title, k + "%")); + } + } + else if (!ids.Any()) + { + return null; + } + } + } + + if (contactID > 0) + { + var sqlQuery = CrmDbContext.EntityContact + .Where(x => x.ContactId == contactID && x.EntityType == EntityType.Case); + + if (ids.Count > 0) + sqlQuery = sqlQuery.Where(x => ids.Contains(x.EntityId)); + + ids = sqlQuery.Select(x => x.EntityId).ToList(); + + if (ids.Count == 0) return null; + } + + if (isClosed.HasValue) + { + result = result.Where(x => x.IsClosed == isClosed); + } + + if (tags != null && tags.Any()) + { + ids = SearchByTags(EntityType.Case, ids.ToArray(), tags); + + if (ids.Count == 0) return null; + } + + if (ids.Count > 0) + { + if (exceptIDs.Count > 0) + { + ids = ids.Except(exceptIDs).ToList(); + + if (ids.Count == 0) return null; + } + + result = result.Where(x => ids.Contains(x.Id)); + + } + else if (exceptIDs.Count > 0) + { + result = result.Where(x => !exceptIDs.Contains(x.Id)); + } + + return result; + } + + public List GetCases(IEnumerable casesID) + { + if (casesID == null || !casesID.Any()) return new List(); + + var result = Query(CrmDbContext.Cases) + .Where(x => casesID.Contains(x.Id)) + .AsNoTracking() + .ToList(); + + return _mapper.Map, List>(result) + .FindAll(_crmSecurity.CanAccessTo); + } + + public List GetCases( + String searchText, + int contactID, + bool? isClosed, + IEnumerable tags, + int from, + int count, + OrderBy orderBy) + { + var dbCasesQuery = GetDbCasesByFilters(_crmSecurity.GetPrivateItems(typeof(Cases)).ToList(), searchText, + contactID, isClosed, + tags); + + if (dbCasesQuery == null) return new List(); + + if (0 < from && from < int.MaxValue) dbCasesQuery = dbCasesQuery.Skip(from); + if (0 < count && count < int.MaxValue) dbCasesQuery = dbCasesQuery.Take(count); + + dbCasesQuery = dbCasesQuery.OrderBy("IsClosed", orderBy.IsAsc); + + if (orderBy != null && Enum.IsDefined(typeof(Enums.SortedByType), orderBy.SortedBy)) + { + switch ((SortedByType)orderBy.SortedBy) + { + case SortedByType.Title: + dbCasesQuery = dbCasesQuery.OrderBy("Title", orderBy.IsAsc); + break; + case SortedByType.CreateBy: + dbCasesQuery = dbCasesQuery.OrderBy("CreateBy", orderBy.IsAsc); + break; + case SortedByType.DateAndTime: + dbCasesQuery = dbCasesQuery.OrderBy("CreateOn", orderBy.IsAsc); + break; + } + } + + return _mapper.Map, List>(dbCasesQuery.ToList()); + } + + public List GetCasesByPrefix(String prefix, int from, int count) + { + if (count == 0) + throw new ArgumentException(); + + prefix = prefix.Trim(); + + var keywords = prefix.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToArray(); + + var q = Query(CrmDbContext.Cases).AsNoTracking(); + + if (keywords.Length == 1) + { + q = q.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Title, keywords[0])); + } + else + { + foreach (var k in keywords) + { + q = q.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Title, k)); + } + } + + if (0 < from && from < int.MaxValue) q = q.Skip(from); + if (0 < count && count < int.MaxValue) q = q.Take(count); + + q = q.OrderBy(x => x.Title); + + return _mapper.Map, List>(q.ToList()) + .FindAll(_crmSecurity.CanAccessTo); + } + + + public List GetByID(int[] ids) + { + var result = Query(CrmDbContext.Cases) + .Where(x => ids.Contains(x.Id)) + .AsNoTracking() + .ToList(); + + return _mapper.Map, List>(result); + } + + public Cases GetByID(int id) + { + var dbEntity = CrmDbContext.Cases.Find(id); + + if (dbEntity.TenantId != TenantID) return null; + + var entity = _mapper.Map(dbEntity); + + _crmSecurity.DemandAccessTo(entity); + + return _mapper.Map(dbEntity); + } + + public void ReassignCasesResponsible(Guid fromUserId, Guid toUserId) + { + var cases = GetAllCases(); + + foreach (var item in cases) + { + var responsibles = _crmSecurity.GetAccessSubjectGuidsTo(item); + + if (!responsibles.Any()) continue; + + responsibles.Remove(fromUserId); + responsibles.Add(toUserId); + + _crmSecurity.SetAccessTo(item, responsibles.Distinct().ToList()); + } + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Dao/ContactDao.cs b/products/ASC.CRM/Server/Core/Dao/ContactDao.cs new file mode 100644 index 00000000000..0bcfcb3a0a5 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/ContactDao.cs @@ -0,0 +1,2006 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text.RegularExpressions; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.Core.Tenants; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.ElasticSearch; +using ASC.Files.Core; +using ASC.Web.CRM.Core.Search; +using ASC.Web.Files.Api; + +using AutoMapper; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +using OrderBy = ASC.CRM.Core.Entities.OrderBy; +namespace ASC.CRM.Core.Dao +{ + [Scope] + public class ContactDao : AbstractDao + { + private readonly BundleSearch _bundleSearch; + private readonly CoreDbContext _coreDbContext; + private readonly FactoryIndexerContact _factoryIndexerContact; + private readonly FactoryIndexerContactInfo _factoryIndexerContactInfo; + private readonly FilesIntegration _filesIntegration; + private readonly AuthorizationManager _authorizationManager; + private readonly TenantUtil _tenantUtil; + private readonly CrmSecurity _crmSecurity; + + public ContactDao( + DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + CrmSecurity crmSecurity, + TenantUtil tenantUtil, + AuthorizationManager authorizationManager, + FilesIntegration filesIntegration, + FactoryIndexerContact factoryIndexerContact, + FactoryIndexerContactInfo factoryIndexerContactInfo, + IOptionsMonitor logger, + ICache ascCache, + DbContextManager coreDbContext, + BundleSearch bundleSearch, + IMapper mapper + ) : + base(dbContextManager, + tenantManager, + securityContext, + logger, + ascCache, + mapper) + { + _crmSecurity = crmSecurity; + _tenantUtil = tenantUtil; + _authorizationManager = authorizationManager; + _filesIntegration = filesIntegration; + _factoryIndexerContact = factoryIndexerContact; + _factoryIndexerContactInfo = factoryIndexerContactInfo; + _coreDbContext = coreDbContext.Value; + _bundleSearch = bundleSearch; + } + + + private readonly String _displayNameSeparator = "!=!"; + + public List GetContactsByPrefix(String prefix, int searchType, int from, int count) + { + if (count == 0) + throw new ArgumentException(); + + var keywords = prefix.Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + + var sqlQuery = Query(CrmDbContext.Contacts).AsNoTracking(); + + switch (searchType) + { + case 0: // Company + sqlQuery = sqlQuery.Where(x => x.IsCompany); + break; + case 1: // Persons + sqlQuery = sqlQuery.Where(x => !x.IsCompany); + break; + case 2: // PersonsWithoutCompany + sqlQuery = sqlQuery.Where(x => x.CompanyId == 0 && !x.IsCompany); + break; + case 3: // CompaniesAndPersonsWithoutCompany + sqlQuery = sqlQuery.Where(x => x.CompanyId == 0); + break; + } + + foreach (var k in keywords) + { + sqlQuery = sqlQuery.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.DisplayName, k)); + } + + sqlQuery = sqlQuery.OrderBy(x => x.DisplayName); + + if (!_crmSecurity.IsAdmin) + { + var idsFromAcl = _coreDbContext.Acl.Where(x => x.Tenant == TenantID && + x.Action == _crmSecurity._actionRead.ID && + x.Subject == _securityContext.CurrentAccount.ID && + (Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Object, typeof(Company).FullName + "%") || + Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Object, typeof(Person).FullName + "%"))) + .Select(x => Convert.ToInt32(x.Object.Split('|', StringSplitOptions.None)[1])) + .ToList(); + + // oldContacts || publicContact || contact is private, but user is ManagerContact + sqlQuery = sqlQuery.Where(x => x.IsShared == null || x.IsShared > 0 || idsFromAcl.Contains(x.Id)); + } + + + if (0 < from && from < int.MaxValue) sqlQuery = sqlQuery.Skip(from); + if (0 < count && count < int.MaxValue) sqlQuery = sqlQuery.Take(count); + + return sqlQuery.ToList().ConvertAll(ToContact); + } + + public int GetAllContactsCount() + { + return GetContactsCount(String.Empty, null, -1, -1, ContactListViewType.All, DateTime.MinValue, DateTime.MinValue); + } + + public List GetAllContacts() + { + return GetContacts(String.Empty, new List(), -1, -1, ContactListViewType.All, DateTime.MinValue, DateTime.MinValue, 0, 0, + new OrderBy(ContactSortedByType.DisplayName, true)); + } + + public int GetContactsCount(String searchText, + IEnumerable tags, + int contactStage, + int contactType, + ContactListViewType contactListView, + DateTime fromDate, + DateTime toDate, + Guid? responsibleid = null, + bool? isShared = null) + { + + var cacheKey = TenantID.ToString(CultureInfo.InvariantCulture) + + "contacts" + + _securityContext.CurrentAccount.ID + + searchText + + contactStage + + contactType + + (int)contactListView + + responsibleid + + isShared; + + if (tags != null) + cacheKey += String.Join("", tags.ToArray()); + + if (fromDate != DateTime.MinValue) + cacheKey += fromDate.ToString(); + + if (toDate != DateTime.MinValue) + cacheKey += toDate.ToString(); + + var fromCache = _cache.Get(cacheKey); + + if (fromCache != null) return Convert.ToInt32(fromCache); + + var withParams = HasSearchParams(searchText, + tags, + contactStage, + contactType, + contactListView, + fromDate, + toDate, + responsibleid, + isShared); + int result; + + if (withParams) + { + ICollection excludedContactIDs; + + var sharedTypes = new[] { ShareType.Read, ShareType.ReadWrite }.ToList(); + + switch (contactListView) + { + case ContactListViewType.Person: + { + + excludedContactIDs = _crmSecurity.GetPrivateItems(typeof(Person)) + .Except(Query(CrmDbContext.Contacts).Where(x => x.IsShared.HasValue ? + sharedTypes.Contains(x.IsShared.Value) : + true && !x.IsCompany).Select(x => x.Id)) + .ToList(); + + break; + + } + case ContactListViewType.Company: + { + excludedContactIDs = _crmSecurity.GetPrivateItems(typeof(Person)) + .Except(Query(CrmDbContext.Contacts).Where(x => x.IsShared.HasValue ? + sharedTypes.Contains(x.IsShared.Value) : + true && x.IsCompany).Select(x => x.Id)) + .ToList(); + + break; + } + default: + { + excludedContactIDs = _crmSecurity.GetPrivateItems(typeof(Company)) + .Union(_crmSecurity.GetPrivateItems(typeof(Person))) + .Except(Query(CrmDbContext.Contacts).Where(x => x.IsShared.HasValue ? + sharedTypes.Contains(x.IsShared.Value) : + true && x.IsCompany).Select(x => x.Id)) + .ToList(); + + break; + } + } + + var dbContactsByFilters = GetDbContactsByFilters(excludedContactIDs, + searchText, + tags, + contactStage, + contactType, + contactListView, + fromDate, + toDate, + responsibleid, + isShared); + + if (dbContactsByFilters != null) + { + if (!isShared.HasValue) + { + result = dbContactsByFilters.Count(); + } + else + { + var sqlResultRows = dbContactsByFilters.Select(x => new { x.Id, x.IsCompany, x.IsShared }).ToList(); + + var resultContactsNewScheme_Count = sqlResultRows.Where(x => x.IsShared != null).ToList().Count; //new scheme + + var fakeContactsOldScheme = sqlResultRows + .Where(x => x.IsShared == null).ToList() // old scheme + .ConvertAll(x => x.IsCompany ? new Company() { ID = x.Id } as Contact : new Person() { ID = x.Id } as Contact); + + var resultFakeContactsOldScheme_Count = fakeContactsOldScheme.Where(fc => + { + var accessSubjectToContact = _crmSecurity.GetAccessSubjectTo(fc); + if (isShared.Value == true) + { + return !accessSubjectToContact.Any(); + } + else + { + return accessSubjectToContact.Any(); + } + + }).ToList().Count; + + return resultContactsNewScheme_Count + resultFakeContactsOldScheme_Count; + } + } + else + { + result = 0; + } + } + else + { + var countWithoutPrivate = Query(CrmDbContext.Contacts).Count(); + + var sharedTypes = new[] { ShareType.Read, ShareType.ReadWrite }.ToList(); + + var privateCount = _crmSecurity.GetPrivateItemsCount(typeof(Person)) + + _crmSecurity.GetPrivateItemsCount(typeof(Company)) - + Query(CrmDbContext.Contacts).Where(x => x.IsShared.HasValue ? + sharedTypes.Contains(x.IsShared.Value) : + true).Count(); + if (privateCount < 0) + privateCount = 0; + + if (privateCount > countWithoutPrivate) + { + _logger.Error("Private contacts count more than all contacts"); + + privateCount = 0; + } + + result = countWithoutPrivate - privateCount; + } + if (result > 0) + { + _cache.Insert(cacheKey, result, TimeSpan.FromMinutes(1)); + } + + return result; + } + + public List SearchContactsByEmail(string searchText, int maxCount) + { + var contacts = new List(); + + if (string.IsNullOrEmpty(searchText) || maxCount <= 0) + return contacts; + + var ids = new List(); + + List contactsIds; + + IReadOnlyCollection dbContactInfos; + + if (_factoryIndexerContactInfo.TrySelect(s => s.MatchAll(searchText), out dbContactInfos)) + { + if (!dbContactInfos.Any()) + return contacts; + + ids = dbContactInfos.Select(r => r.Id).ToList(); + } + + var isAdmin = _crmSecurity.IsAdmin; + + const int count_per_query = 100; + + var f = 0; + + do + { + var query = Query(CrmDbContext.Contacts).Join(CrmDbContext.ContactsInfo, + x => new { Column1 = x.TenantId, Column2 = x.Id }, + y => new { Column1 = y.TenantId, Column2 = y.ContactId }, + (x, y) => new { x, y }) + .Where(x => x.y.Type == ContactInfoType.Email); + + if (ids.Any()) + { + var partIds = ids.Skip(f).Take(count_per_query).ToList(); + + if (!partIds.Any()) + + break; + + query = query.Where(x => partIds.Contains(x.x.Id)); + + } + else + { + + query = query.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(String.Concat(x.x.DisplayName, " ", x.y.Data), "%" + searchText + "%")); + + query = query.Skip(f).Take(count_per_query); + } + + var partContacts = query.Select(x => x.x).ToList().ConvertAll(ToContact); + + foreach (var partContact in partContacts) + { + if (maxCount - contacts.Count == 0) + return contacts; + + if (isAdmin || _crmSecurity.CanAccessTo(partContact)) + contacts.Add(partContact); + } + + if (maxCount - contacts.Count == 0 || !partContacts.Any() || partContacts.Count < count_per_query) + break; + + f += count_per_query; + + } while (true); + + return contacts; + } + + public List GetContacts(String searchText, + IEnumerable tags, + int contactStage, + int contactType, + ContactListViewType contactListView, + DateTime fromDate, + DateTime toDate, + int from, + int count, + OrderBy orderBy, + Guid? responsibleId = null, + bool? isShared = null) + { + if (_crmSecurity.IsAdmin) + { + if (!isShared.HasValue) + { + return GetCrudeContacts( + searchText, + tags, + contactStage, + contactType, + contactListView, + fromDate, + toDate, + from, + count, + orderBy, + responsibleId, + isShared, + false); + } + else + { + var crudeContacts = GetCrudeContacts( + searchText, + tags, + contactStage, + contactType, + contactListView, + fromDate, + toDate, + 0, + from + count, + orderBy, + responsibleId, + isShared, + false); + + if (crudeContacts.Count == 0) return crudeContacts; + + var result = crudeContacts.Where(c => (isShared.Value == true ? c.ShareType != ShareType.None : c.ShareType == ShareType.None)).ToList(); + + if (result.Count == crudeContacts.Count) return result.Skip(from).ToList(); + + var localCount = count; + var localFrom = from + count; + + while (true) + { + crudeContacts = GetCrudeContacts( + searchText, + tags, + contactStage, + contactType, + contactListView, + fromDate, + toDate, + localFrom, + localCount, + orderBy, + responsibleId, + isShared, + false); + + if (crudeContacts.Count == 0) break; + + result.AddRange(crudeContacts.Where(c => (isShared.Value == true ? c.ShareType != ShareType.None : c.ShareType == ShareType.None))); + + if ((result.Count >= count + from) || (crudeContacts.Count < localCount)) break; + + localFrom += localCount; + localCount = localCount * 2; + } + + return result.Skip(from).Take(count).ToList(); + } + } + else + { + var crudeContacts = GetCrudeContacts( + searchText, + tags, + contactStage, + contactType, + contactListView, + fromDate, + toDate, + 0, + from + count, + orderBy, + responsibleId, + isShared, + false); + + if (crudeContacts.Count == 0) return crudeContacts; + + var tmp = isShared.HasValue ? crudeContacts.Where(c => (isShared.Value == true ? c.ShareType != ShareType.None : c.ShareType == ShareType.None)).ToList() : crudeContacts; + + if (crudeContacts.Count < from + count) + { + return tmp.FindAll(_crmSecurity.CanAccessTo).Skip(from).ToList(); + } + + var result = tmp.FindAll(_crmSecurity.CanAccessTo); + + if (result.Count == crudeContacts.Count) return result.Skip(from).ToList(); + + var localCount = count; + var localFrom = from + count; + + while (true) + { + crudeContacts = GetCrudeContacts( + searchText, + tags, + contactStage, + contactType, + contactListView, + fromDate, + toDate, + localFrom, + localCount, + orderBy, + responsibleId, + isShared, + false); + + if (crudeContacts.Count == 0) break; + + tmp = isShared.HasValue ? crudeContacts.Where(c => (isShared.Value == true ? c.ShareType != ShareType.None : c.ShareType == ShareType.None)).ToList() : crudeContacts; + + result.AddRange(tmp.Where(_crmSecurity.CanAccessTo)); + + if ((result.Count >= count + from) || (crudeContacts.Count < localCount)) break; + + localFrom += localCount; + localCount = localCount * 2; + } + + return result.Skip(from).Take(count).ToList(); + } + } + + private bool HasSearchParams(String searchText, + IEnumerable tags, + int contactStage, + int contactType, + ContactListViewType contactListView, + DateTime fromDate, + DateTime toDate, + Guid? responsibleid = null, + bool? isShared = null) + { + var hasNoParams = String.IsNullOrEmpty(searchText) && + (tags == null || !tags.Any()) && + contactStage < 0 && + contactType < 0 && + contactListView == ContactListViewType.All && + !isShared.HasValue && + fromDate == DateTime.MinValue && + toDate == DateTime.MinValue && + !responsibleid.HasValue; + + return !hasNoParams; + } + + private IQueryable GetDbContactsByFilters( + ICollection exceptIDs, + String searchText, + IEnumerable tags, + int contactStage, + int contactType, + ContactListViewType contactListView, + DateTime fromDate, + DateTime toDate, + Guid? responsibleid = null, + bool? isShared = null) + { + + var sqlQuery = Query(CrmDbContext.Contacts).AsNoTracking(); + + var ids = new List(); + + if (responsibleid.HasValue) + { + + if (responsibleid != default(Guid)) + { + ids = _crmSecurity.GetContactsIdByManager(responsibleid.Value).ToList(); + if (ids.Count == 0) return null; + } + else + { + if (exceptIDs == null) + exceptIDs = new List(); + + exceptIDs = exceptIDs.Union(_crmSecurity.GetContactsIdByManager(Guid.Empty)).ToList(); + + if (!exceptIDs.Any()) // HACK + exceptIDs.Add(0); + } + + } + + if (!String.IsNullOrEmpty(searchText)) + { + searchText = searchText.Trim(); + + var keywords = searchText.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) + .ToArray(); + + List contactsIds; + if (!_bundleSearch.TrySelectContact(searchText, out contactsIds)) + { + _logger.Debug("FullTextSearch.SupportModule('CRM.Contacts') = false"); + + foreach (var k in keywords) + { + sqlQuery = sqlQuery.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.DisplayName, k + "%")); + } + } + else + { + _logger.Debug("FullTextSearch.SupportModule('CRM.Contacts') = true"); + _logger.DebugFormat("FullTextSearch.Search: searchText = {0}", searchText); + + var full_text_ids = contactsIds; + + if (full_text_ids.Count == 0) return null; + + if (ids.Count != 0) + { + ids = ids.Where(full_text_ids.Contains).ToList(); + if (ids.Count == 0) return null; + } + else + { + ids = full_text_ids; + } + + if (contactListView == ContactListViewType.All || contactListView == ContactListViewType.Person) + { + ids.AddRange(GetContactIDsByCompanyIds(ids)); + } + } + } + + if (tags != null && tags.Any()) + { + ids = SearchByTags(EntityType.Contact, ids.ToArray(), tags); + + if (ids.Count == 0) return null; + } + + + switch (contactListView) + { + case ContactListViewType.Company: + { + sqlQuery = sqlQuery.Where(x => x.IsCompany); + + break; + } + case ContactListViewType.Person: + { + sqlQuery = sqlQuery.Where(x => !x.IsCompany); + + break; + } + case ContactListViewType.WithOpportunity: + if (ids.Count > 0) + { + ids = CrmDbContext.EntityContact.Where(x => ids.Contains(x.ContactId) && x.EntityType == EntityType.Opportunity) + .Select(x => x.ContactId) + .Distinct() + .ToList(); + } + else + { + ids = CrmDbContext.EntityContact.Where(x => x.EntityType == EntityType.Opportunity) + .Select(x => x.ContactId) + .Distinct() + .ToList(); + } + + if (ids.Count == 0) return null; + + break; + } + + if (contactStage >= 0) + sqlQuery = sqlQuery.Where(x => x.StatusId == contactStage); + + if (contactType >= 0) + sqlQuery = sqlQuery.Where(x => x.ContactTypeId == contactType); + + if (fromDate != DateTime.MinValue && toDate != DateTime.MinValue) + { + sqlQuery = sqlQuery.Where(x => x.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.CreateOn <= _tenantUtil.DateTimeToUtc(toDate.AddDays(1).AddMinutes(-1))); + } + else if (fromDate != DateTime.MinValue) + { + sqlQuery = sqlQuery.Where(x => x.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate)); + } + else if (toDate != DateTime.MinValue) + { + sqlQuery = sqlQuery.Where(x => x.CreateOn <= _tenantUtil.DateTimeToUtc(toDate.AddDays(1).AddMinutes(-1))); + } + + if (isShared.HasValue) + { + var sharedTypes = new[] { ShareType.Read, ShareType.ReadWrite }.ToList(); + + if (isShared.Value) + { + sqlQuery = sqlQuery.Where(x => x.IsShared.HasValue ? sharedTypes.Contains(x.IsShared.Value) : x.IsShared == null); + } + else + { + sqlQuery = sqlQuery.Where(x => x.IsShared.HasValue ? x.IsShared == ShareType.None : x.IsShared == null); + } + } + + if (ids.Count > 0) + { + if (exceptIDs.Count > 0) + { + ids = ids.Except(exceptIDs).ToList(); + if (ids.Count == 0) return null; + } + + sqlQuery = sqlQuery.Where(x => ids.Contains(x.Id)); + } + else if (exceptIDs.Count > 0) + { + var _MaxCountForSQLNotIn = 500; + + if (exceptIDs.Count > _MaxCountForSQLNotIn) + { + ids = Query(CrmDbContext.Contacts).Select(x => x.Id).ToList(); + + ids = ids.Except(exceptIDs).ToList(); + if (ids.Count == 0) return null; + + sqlQuery = sqlQuery.Where(x => ids.Contains(x.Id)); + + } + else + { + sqlQuery = sqlQuery.Where(x => !exceptIDs.Contains(x.Id)); + } + } + + return sqlQuery; + } + + private List GetCrudeContacts( + String searchText, + IEnumerable tags, + int contactStage, + int contactType, + ContactListViewType contactListView, + DateTime fromDate, + DateTime toDate, + int from, + int count, + OrderBy orderBy, + Guid? responsibleid = null, + bool? isShared = null, + bool selectIsSharedInNewScheme = true) + { + var withParams = HasSearchParams(searchText, + tags, + contactStage, + contactType, + contactListView, + fromDate, + toDate, + responsibleid, + isShared); + + var dbContactsQuery = GetDbContactsByFilters(new List(), + searchText, + tags, + contactStage, + contactType, + contactListView, + fromDate, + toDate, + responsibleid, + isShared); + + if (withParams && dbContactsQuery == null) return new List(); + + if (0 < from && from < int.MaxValue) dbContactsQuery = dbContactsQuery.Skip(from); + if (0 < count && count < int.MaxValue) dbContactsQuery = dbContactsQuery.Take(count); + + if (orderBy != null) + { + + if (!Enum.IsDefined(typeof(ContactSortedByType), orderBy.SortedBy.ToString())) + { + orderBy.SortedBy = ContactSortedByType.Created; + } + + switch ((ContactSortedByType)orderBy.SortedBy) + { + case ContactSortedByType.DisplayName: + dbContactsQuery = dbContactsQuery.OrderBy("DisplayName", orderBy.IsAsc); + break; + case ContactSortedByType.Created: + dbContactsQuery = dbContactsQuery.OrderBy("CreateOn", orderBy.IsAsc); + break; + case ContactSortedByType.ContactType: + + dbContactsQuery = dbContactsQuery.OrderBy("StatusId", orderBy.IsAsc); + + break; + case ContactSortedByType.FirstName: + { + dbContactsQuery = dbContactsQuery.OrderBy("FirstName", orderBy.IsAsc); + dbContactsQuery = dbContactsQuery.OrderBy("LastName", orderBy.IsAsc); + } + + break; + case ContactSortedByType.LastName: + { + dbContactsQuery = dbContactsQuery.OrderBy("LastName", orderBy.IsAsc); + dbContactsQuery = dbContactsQuery.OrderBy("FirstName", orderBy.IsAsc); + } + break; + case ContactSortedByType.History: + { + dbContactsQuery = dbContactsQuery.GroupJoin(Query(CrmDbContext.RelationshipEvent), + x => x.Id, + y => y.ContactId, + (x, y) => new { x, y }) + .OrderBy(x => x.y.Max(x => x.LastModifedOn)) + .ThenBy(x => x.x.CreateOn) + .Select(x => x.x); + + + } + + break; + default: + { + dbContactsQuery = dbContactsQuery.OrderBy("DisplayName", orderBy.IsAsc); + } + + break; + } + } + + + var contacts = dbContactsQuery.ToList().ConvertAll(ToContact); + + return selectIsSharedInNewScheme && isShared.HasValue ? + contacts.Where(c => (isShared.Value ? c.ShareType != ShareType.None : c.ShareType == ShareType.None)).ToList() : + contacts; + } + + public List GetContactsByName(String title, bool isCompany) + { + if (String.IsNullOrEmpty(title)) return new List(); + + title = title.Trim(); + + if (isCompany) + { + return Query(CrmDbContext.Contacts) + .AsNoTracking() + .Where(x => String.Compare(x.DisplayName, title) == 0 && x.IsCompany) + .ToList() + .ConvertAll(ToContact) + .FindAll(_crmSecurity.CanAccessTo); + } + else + { + var titleParts = title.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries); + + if (titleParts.Length == 1 || titleParts.Length == 2) + { + if (titleParts.Length == 1) + { + return Query(CrmDbContext.Contacts) + .AsNoTracking() + .Where(x => String.Compare(x.DisplayName, title) == 0 && !x.IsCompany) + .ToList() + .ConvertAll(ToContact) + .FindAll(_crmSecurity.CanAccessTo); + } + else + { + return Query(CrmDbContext.Contacts) + .AsNoTracking() + .Where(x => String.Compare(x.DisplayName, String.Concat(titleParts[0], _displayNameSeparator, titleParts[1])) == 0 && !x.IsCompany) + .ToList() + .ConvertAll(ToContact) + .FindAll(_crmSecurity.CanAccessTo); + } + } + } + + return GetContacts(title, null, -1, -1, isCompany ? ContactListViewType.Company : ContactListViewType.Person, DateTime.MinValue, DateTime.MinValue, 0, 0, null); + } + + public void RemoveMember(int[] peopleID) + { + if ((peopleID == null) || (peopleID.Length == 0)) return; + + foreach (var id in peopleID) + { + var dbEntity = CrmDbContext.Contacts.Find(id); + + if (dbEntity.TenantId != TenantID) continue; + + dbEntity.CompanyId = 0; + } + + CrmDbContext.SaveChanges(); + + RemoveRelativeInDb(null, EntityType.Person, peopleID); + } + + public void RemoveMember(int peopleID) + { + var dbEntity = CrmDbContext.Contacts.Find(peopleID); + + if (dbEntity.TenantId != TenantID) return; + + var entity = _mapper.Map(dbEntity); + + _crmSecurity.DemandEdit(entity); + + dbEntity.CompanyId = 0; + + CrmDbContext.SaveChanges(); + + RemoveRelative(0, EntityType.Person, peopleID); + } + + public void AddMemberInDb(int peopleID, int companyID) + { + var dbEntity = CrmDbContext.Contacts.Find(peopleID); + + if (dbEntity.TenantId != TenantID) return; + + var entity = _mapper.Map(dbEntity); + + _crmSecurity.DemandEdit(entity); + + dbEntity.CompanyId = companyID; + + CrmDbContext.SaveChanges(); + + SetRelative(companyID, EntityType.Person, peopleID); + } + + public void AddMember(int peopleID, int companyID) + { + AddMemberInDb(peopleID, companyID); + } + + public void SetMembers(int companyID, params int[] peopleIDs) + { + if (companyID == 0) + throw new ArgumentException(); + + var tx = CrmDbContext.Database.BeginTransaction(); + + CrmDbContext.EntityContact + .RemoveRange(CrmDbContext.EntityContact + .Where(x => x.EntityType == EntityType.Person && x.ContactId == companyID)); + + var itemsToUpdate = Query(CrmDbContext.Contacts) + .Where(x => x.CompanyId == companyID).ToList(); + + itemsToUpdate.ForEach(x => x.CompanyId = 0); + + CrmDbContext.SaveChanges(); + + if (!(peopleIDs == null || peopleIDs.Length == 0)) + { + + foreach (var id in peopleIDs) + { + var dbContactEntity = CrmDbContext.Contacts.Find(id); + + if (dbContactEntity.TenantId != TenantID) continue; + + dbContactEntity.CompanyId = companyID; + + } + + CrmDbContext.SaveChanges(); + + foreach (var peopleID in peopleIDs) + { + SetRelative(companyID, EntityType.Person, peopleID); + } + } + + tx.Commit(); + + } + + public void SetRelativeContactProject(IEnumerable contactid, int projectid) + { + var tx = CrmDbContext.Database.BeginTransaction(); + + foreach (var id in contactid) + { + var itemToInsert = new DbProjects + { + ContactId = id, + ProjectId = projectid, + TenantId = TenantID + }; + + CrmDbContext.Projects.Add(itemToInsert); + } + + tx.Commit(); + + } + + public void RemoveRelativeContactProject(int contactid, int projectid) + { + CrmDbContext.RemoveRange(Query(CrmDbContext.Projects) + .Where(x => x.ContactId == contactid && x.ProjectId == projectid)); + } + + + public IEnumerable GetContactsByProjectID(int projectid) + { + var contactIds = Query(CrmDbContext.Projects) + .Where(x => x.ProjectId == projectid) + .Select(x => x.ContactId); + + if (!contactIds.Any()) return new List(); + + return GetContacts(contactIds.ToArray()); + } + + public List GetMembers(int companyID) + { + return GetContacts(GetRelativeToEntity(companyID, EntityType.Person, null)); + } + + public List GetRestrictedMembers(int companyID) + { + return GetRestrictedContacts(GetRelativeToEntity(companyID, EntityType.Person, null)); + } + + public Dictionary GetMembersCount(int[] companyID) + { + return CrmDbContext.EntityContact + .Where(x => companyID.Contains(x.ContactId) && x.EntityType == EntityType.Person) + .GroupBy(x => x.ContactId) + .Select(x => new { GroupId = x.Key, Count = x.Count() }) + .ToDictionary(x => x.GroupId, x => x.Count); + } + + + public int GetMembersCount(int companyID) + { + return CrmDbContext.EntityContact + .Where(x => x.ContactId == companyID && x.EntityType == EntityType.Person) + .Count(); + } + + public List GetMembersIDs(int companyID) + { + return CrmDbContext.EntityContact + .Where(x => x.ContactId == companyID && x.EntityType == EntityType.Person) + .Select(x => x.EntityId) + .ToList(); + } + + public Dictionary GetMembersIDsAndShareType(int companyID) + { + return CrmDbContext.EntityContact + .Where(x => x.ContactId == companyID && x.EntityType == EntityType.Person) + .GroupJoin(CrmDbContext.Contacts, + x => x.EntityId, + y => y.Id, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new + { + EntityId = x.x.EntityId, + IsShared = y.IsShared + }).ToDictionary(x => x.EntityId, y => y.IsShared); + } + + + public void UpdateContact(Contact contact) + { + UpdateContactFromDb(contact); + } + + private void UpdateContactFromDb(Contact contact) + { + var originalContact = GetByID(contact.ID); + if (originalContact == null) throw new ArgumentException(); + _crmSecurity.DemandEdit(originalContact); + + String firstName; + String lastName; + String companyName; + String title; + int companyID; + + var displayName = String.Empty; + + if (contact is Company) + { + firstName = String.Empty; + lastName = String.Empty; + title = String.Empty; + companyName = ((Company)contact).CompanyName.Trim(); + companyID = 0; + displayName = companyName; + + if (String.IsNullOrEmpty(companyName)) + throw new ArgumentException(); + + } + else + { + var people = (Person)contact; + + firstName = people.FirstName.Trim(); + lastName = people.LastName.Trim(); + title = people.JobTitle; + companyName = String.Empty; + companyID = people.CompanyID; + + displayName = String.Concat(firstName, _displayNameSeparator, lastName); + + RemoveMember(people.ID); + + if (companyID > 0) + { + AddMemberInDb(people.ID, companyID); + } + + if (String.IsNullOrEmpty(firstName))// || String.IsNullOrEmpty(lastName)) lastname is not required field now + throw new ArgumentException(); + + } + + if (!String.IsNullOrEmpty(title)) + title = title.Trim(); + + if (!String.IsNullOrEmpty(contact.About)) + contact.About = contact.About.Trim(); + + if (!String.IsNullOrEmpty(contact.Industry)) + contact.Industry = contact.Industry.Trim(); + + + var itemToUpdate = Query(CrmDbContext.Contacts).Single(x => x.Id == contact.ID); + + itemToUpdate.FirstName = firstName; + itemToUpdate.LastName = lastName; + itemToUpdate.CompanyName = companyName; + itemToUpdate.Title = title; + itemToUpdate.Notes = contact.About; + itemToUpdate.Industry = contact.Industry; + itemToUpdate.StatusId = contact.StatusID; + itemToUpdate.CompanyId = companyID; + itemToUpdate.LastModifedOn = _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow()); + itemToUpdate.LastModifedBy = _securityContext.CurrentAccount.ID; + itemToUpdate.DisplayName = displayName; + itemToUpdate.IsShared = contact.ShareType; + itemToUpdate.ContactTypeId = contact.ContactTypeID; + itemToUpdate.Currency = contact.Currency; + itemToUpdate.TenantId = TenantID; + + CrmDbContext.Update(itemToUpdate); + CrmDbContext.SaveChanges(); + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "contacts.*")); + + _factoryIndexerContact.Update(itemToUpdate); + } + + public void UpdateContactStatus(IEnumerable contactid, int statusid) + { + var tx = CrmDbContext.Database.BeginTransaction(); + + foreach (var id in contactid) + { + var dbEntity = CrmDbContext.Contacts.Find(id); + + if (dbEntity.TenantId != TenantID) + throw new ArgumentException(); + + dbEntity.Id = id; + dbEntity.StatusId = statusid; + + } + + CrmDbContext.SaveChanges(); + + tx.Commit(); + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "contacts.*")); + } + + public List FindDuplicateByEmail(List items, bool resultReal) + { + //resultReal - true => real, false => fake + if (items.Count == 0) return new List(); + + var result = new List(); + var emails = items.ConvertAll(i => i.Data).ToList(); + var sqlQuery = Query(CrmDbContext.ContactsInfo) + .Where(x => emails.Contains(x.Data) && x.Type == ContactInfoType.Email); + + if (resultReal) + { + result = sqlQuery.Select(x => x.ContactId).ToList(); + } + else + { + List emailsAlreadyExists; + + emailsAlreadyExists = sqlQuery.Select(x => x.Data).ToList(); + + if (emailsAlreadyExists.Count != 0) + { + result = items.Where(i => emailsAlreadyExists.Contains(i.Data)).Select(i => i.ContactID).ToList(); + } + } + + return result; + } + + public Dictionary SaveContactList(List items) + { + var tx = CrmDbContext.Database.BeginTransaction(); + + var result = new Dictionary(); + + for (int index = 0; index < items.Count; index++) + { + var item = items[index]; + + if (item.ID == 0) + item.ID = index; + + result.Add(item.ID, SaveContactToDb(item)); + } + + tx.Commit(); + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "contacts.*")); + + return result; + } + + public void UpdateContactList(List items) + { + var tx = CrmDbContext.Database.BeginTransaction(); + + for (int index = 0; index < items.Count; index++) + { + var item = items[index]; + + if (item.ID == 0) + item.ID = index; + + UpdateContactFromDb(item); + } + + tx.Commit(); + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "contacts.*")); + } + + public void MakePublic(int contactId, bool isShared) + { + if (contactId <= 0) throw new ArgumentException(); + + var dbEntity = CrmDbContext.Contacts.Find(contactId); + + dbEntity.IsShared = isShared ? ShareType.ReadWrite : ShareType.None; + + CrmDbContext.SaveChanges(); + } + + public int SaveContact(Contact contact) + { + var result = SaveContactToDb(contact); + + var dbEntity = CrmDbContext.Contacts.Find(contact.ID); + + _factoryIndexerContact.Index(dbEntity); + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "contacts.*")); + + return result; + + } + + private int SaveContactToDb(Contact contact) + { + String firstName; + String lastName; + bool isCompany; + String companyName; + String title; + int companyID; + + var displayName = String.Empty; + + if (contact is Company) + { + firstName = String.Empty; + lastName = String.Empty; + title = String.Empty; + companyName = (((Company)contact).CompanyName ?? "").Trim(); + isCompany = true; + companyID = 0; + displayName = companyName; + + if (String.IsNullOrEmpty(companyName)) + throw new ArgumentException(); + + } + else + { + var people = (Person)contact; + + firstName = people.FirstName.Trim(); + lastName = people.LastName.Trim(); + title = people.JobTitle; + companyName = String.Empty; + isCompany = false; + + if (IsExist(people.CompanyID)) + companyID = people.CompanyID; + else + companyID = 0; + + displayName = String.Concat(firstName, _displayNameSeparator, lastName); + + if (String.IsNullOrEmpty(firstName))// || String.IsNullOrEmpty(lastName)) lastname is not required field now + throw new ArgumentException(); + + } + + if (!String.IsNullOrEmpty(title)) + title = title.Trim(); + + if (!String.IsNullOrEmpty(contact.About)) + contact.About = contact.About.Trim(); + + if (!String.IsNullOrEmpty(contact.Industry)) + contact.Industry = contact.Industry.Trim(); + + var itemToInsert = new DbContact + { + Id = 0, + FirstName = firstName, + LastName = lastName, + CompanyName = companyName, + Title = title, + Notes = contact.About, + IsCompany = isCompany, + Industry = contact.Industry, + StatusId = contact.StatusID, + CompanyId = companyID, + CreateBy = _securityContext.CurrentAccount.ID, + CreateOn = _tenantUtil.DateTimeToUtc(contact.CreateOn == DateTime.MinValue ? _tenantUtil.DateTimeNow() : contact.CreateOn), + LastModifedOn = _tenantUtil.DateTimeToUtc(contact.CreateOn == DateTime.MinValue ? _tenantUtil.DateTimeNow() : contact.CreateOn), + LastModifedBy = _securityContext.CurrentAccount.ID, + DisplayName = displayName, + IsShared = contact.ShareType, + ContactTypeId = contact.ContactTypeID, + Currency = contact.Currency + }; + + CrmDbContext.Contacts.Add(itemToInsert); + CrmDbContext.SaveChanges(); + + contact.ID = itemToInsert.Id; + + if (companyID > 0) + AddMemberInDb(contact.ID, companyID); + + return contact.ID; + } + + public Boolean IsExist(int contactID) + { + return Query(CrmDbContext.Contacts).Where(x => x.Id == contactID).Any(); + } + + public Boolean CanDelete(int contactID) + { + return !Query(CrmDbContext.Invoices).Where(x => x.ContactId == contactID || x.ConsigneeId == contactID).Any(); + } + + public Dictionary CanDelete(int[] contactID) + { + var result = new Dictionary(); + + if (contactID.Length == 0) return result; + + var contactIDs = contactID.Distinct().ToList(); + + List hasInvoiceIDs = Query(CrmDbContext.Invoices).Where(x => contactID.Contains(x.ContactId)) + .Distinct() + .Select(x => x.ContactId).Union( + Query(CrmDbContext.Invoices).Where(x => contactID.Contains(x.ConsigneeId)) + .Distinct() + .Select(x => x.ConsigneeId)).ToList(); + + + foreach (var cid in contactIDs) + { + result.Add(cid, !hasInvoiceIDs.Contains(cid)); + } + + return result; + } + + public Contact GetByID(int contactID) + { + return GetByIDFromDb(contactID); + } + + public Contact GetByIDFromDb(int contactID) + { + return ToContact(Query(CrmDbContext.Contacts) + .FirstOrDefault(x => x.Id == contactID)); + } + + public List GetContacts(int[] contactID) + { + if (contactID == null || contactID.Length == 0) return new List(); + + return Query(CrmDbContext.Contacts) + .Where(x => contactID.Contains(x.Id)) + .ToList() + .ConvertAll(ToContact) + .FindAll(_crmSecurity.CanAccessTo); + } + + public List GetRestrictedContacts(int[] contactID) + { + if (contactID == null || contactID.Length == 0) return new List(); + + return Query(CrmDbContext.Contacts) + .Where(x => contactID.Contains(x.Id)) + .ToList() + .ConvertAll(ToContact) + .FindAll(cont => !_crmSecurity.CanAccessTo(cont)); + } + + public List GetRestrictedAndAccessedContacts(int[] contactID) + { + if (contactID == null || contactID.Length == 0) return new List(); + + return Query(CrmDbContext.Contacts) + .Where(x => contactID.Contains(x.Id)) + .ToList() + .ConvertAll(ToContact); + } + + public List DeleteBatchContact(int[] contactID) + { + if (contactID == null || contactID.Length == 0) return null; + + var contacts = GetContacts(contactID).Where(_crmSecurity.CanDelete).ToList(); + if (!contacts.Any()) return contacts; + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "contacts.*")); + + DeleteBatchContactsExecute(contacts); + + return contacts; + } + + public List DeleteBatchContact(List contacts) + { + contacts = contacts.FindAll(_crmSecurity.CanDelete).ToList(); + if (!contacts.Any()) return contacts; + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "contacts.*")); + + DeleteBatchContactsExecute(contacts); + + return contacts; + } + + public Contact DeleteContact(int contactID) + { + if (contactID <= 0) return null; + + var contact = GetByID(contactID); + if (contact == null) return null; + + _crmSecurity.DemandDelete(contact); + + var dbEntity = CrmDbContext.Contacts.Find(contactID); + + _factoryIndexerContact.Delete(dbEntity); + + DeleteBatchContactsExecute(new List() { contact }); + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "contacts.*")); + + return contact; + } + + private void DeleteBatchContactsExecute(List contacts) + { + var personsID = new List(); + var companyID = new List(); + var newContactID = new List(); + + foreach (var contact in contacts) + { + newContactID.Add(contact.ID); + + if (contact is Company) + companyID.Add(contact.ID); + else + personsID.Add(contact.ID); + } + + var contactID = newContactID.ToArray(); + int[] filesIDs = new int[0]; + + var tx = CrmDbContext.Database.BeginTransaction(); + + var tagdao = _filesIntegration.DaoFactory.GetTagDao(); + + var tagNames = Query(CrmDbContext.RelationshipEvent).Where(x => contactID.Contains(x.ContactId) && x.HaveFiles) + .Select(x => String.Format("RelationshipEvent_{0}", x.Id)).ToArray(); + + if (0 < tagNames.Length) + { + filesIDs = tagdao.GetTags(tagNames, TagType.System) + .Where(t => t.EntryType == FileEntryType.File) + .Select(t => Convert.ToInt32(t.EntryId)) + .ToArray(); + } + + CrmDbContext.RemoveRange(Query(CrmDbContext.FieldValue) + .Where(x => contactID.Contains(x.EntityId)) + .Where(x => x.EntityType == EntityType.Contact || x.EntityType == EntityType.Person || x.EntityType == EntityType.Company) + ); + + CrmDbContext.RemoveRange(Query(CrmDbContext.Tasks) + .Where(x => contactID.Contains(x.ContactId))); + + CrmDbContext.RemoveRange(CrmDbContext.EntityTags + .Where(x => contactID.Contains(x.EntityId) && x.EntityType == EntityType.Contact)); + + CrmDbContext.RemoveRange(CrmDbContext.RelationshipEvent.Where(x => contactID.Contains(x.ContactId))); + + var dealToUpdate = CrmDbContext.Deals.Where(x => contactID.Contains(x.ContactId)).ToList(); + + dealToUpdate.ForEach(x => x.ContactId = 0); + + CrmDbContext.SaveChanges(); + + if (companyID.Count > 0) + { + var itemToUpdate = Query(CrmDbContext.Contacts).Where(x => companyID.Contains(x.CompanyId)).ToList(); + + itemToUpdate.ForEach(x => x.CompanyId = 0); + + CrmDbContext.SaveChanges(); + } + + if (personsID.Count > 0) + { + RemoveRelativeInDb(null, EntityType.Person, personsID.ToArray()); + } + + RemoveRelativeInDb(contactID, EntityType.Any, null); + + CrmDbContext.RemoveRange(Query(CrmDbContext.ContactsInfo).Where(x => contactID.Contains(x.ContactId))); + + CrmDbContext.RemoveRange(contactID.ToList().ConvertAll(x => new DbContact + { + Id = x, + TenantId = TenantID + })); + + CrmDbContext.SaveChanges(); + + tx.Commit(); + + contacts.ForEach(contact => _authorizationManager.RemoveAllAces(contact)); + + var filedao = _filesIntegration.DaoFactory.GetFileDao(); + + foreach (var filesID in filesIDs) + { + filedao.DeleteFile(filesID); + } + + //todo: remove indexes + } + + private void MergeContactInfo(Contact fromContact, Contact toContact) + { + if ((toContact is Person) && (fromContact is Person)) + { + var fromPeople = (Person)fromContact; + var toPeople = (Person)toContact; + + if (toPeople.CompanyID == 0) + toPeople.CompanyID = fromPeople.CompanyID; + + if (String.IsNullOrEmpty(toPeople.JobTitle)) + toPeople.JobTitle = fromPeople.JobTitle; + } + + if (String.IsNullOrEmpty(toContact.Industry)) + toContact.Industry = fromContact.Industry; + + if (toContact.StatusID == 0) + toContact.StatusID = fromContact.StatusID; + if (toContact.ContactTypeID == 0) + toContact.ContactTypeID = fromContact.ContactTypeID; + + if (String.IsNullOrEmpty(toContact.About)) + toContact.About = fromContact.About; + + UpdateContactFromDb(toContact); + } + + public void MergeDublicate(int fromContactID, int toContactID) + { + if (fromContactID == toContactID) + { + if (GetByID(fromContactID) == null) + throw new ArgumentException(); + return; + } + + var fromContact = GetByID(fromContactID); + var toContact = GetByID(toContactID); + + if (fromContact == null || toContact == null) + throw new ArgumentException(); + + using (var tx = CrmDbContext.Database.BeginTransaction()) + { + var taskToUpdate = Query(CrmDbContext.Tasks) + .Where(x => x.ContactId == fromContactID) + .ToList(); + + taskToUpdate.ForEach(x => x.ContactId = toContactID); + CrmDbContext.SaveChanges(); + + // crm_entity_contact + CrmDbContext.EntityContact.RemoveRange( + CrmDbContext.EntityContact + .Join(CrmDbContext.EntityContact, + x => new { x.EntityId, x.EntityType }, + y => new { y.EntityId, y.EntityType }, + (x, y) => new { x, y }) + .Where(x => x.x.ContactId == fromContactID && x.y.ContactId == toContactID) + .Select(x => x.x)); + + var entityContactToUpdate = CrmDbContext.EntityContact.Where(x => x.ContactId == fromContactID).ToList(); + + entityContactToUpdate.ForEach(x => x.ContactId = toContactID); + CrmDbContext.SaveChanges(); + + // crm_deal + var dealsToUpdate = Query(CrmDbContext.Deals) + .AsNoTracking() + .Where(x => x.ContactId == fromContactID) + .ToList(); + + dealsToUpdate.ForEach(x => x.ContactId = toContactID); + + CrmDbContext.SaveChanges(); + + // crm_invoice + var invoicesToUpdate = Query(CrmDbContext.Invoices) + .AsNoTracking() + .Where(x => x.ContactId == fromContactID) + .ToList(); + + invoicesToUpdate.ForEach(x => x.ContactId = toContactID); + + CrmDbContext.SaveChanges(); + + // crm_projects + var dublicateProjectID = Query(CrmDbContext.Projects) + .Join(CrmDbContext.Projects, + x => x.ProjectId, + y => y.ProjectId, + (x, y) => new { x, y }) + .Where(x => x.x.ContactId == fromContactID && x.y.ContactId == toContactID) + .Select(x => x.x.ProjectId); + + CrmDbContext.RemoveRange(Query(CrmDbContext.Projects) + .Where(x => x.ContactId == fromContactID && dublicateProjectID.Contains(x.ProjectId))); + + var projectsToUpdate = Query(CrmDbContext.Projects) + .AsNoTracking() + .Where(x => x.ContactId == fromContactID).ToList(); + + projectsToUpdate.ForEach(x => x.ContactId = toContactID); + + CrmDbContext.SaveChanges(); + + // crm_relationship_event + var relationshipEventToUpdate = Query(CrmDbContext.RelationshipEvent) + .AsNoTracking() + .Where(x => x.ContactId == fromContactID) + .ToList(); + + relationshipEventToUpdate.ForEach(x => x.ContactId = toContactID); + + CrmDbContext.SaveChanges(); + + // crm_entity_tag + var dublicateTagsID = CrmDbContext.EntityTags.Join(CrmDbContext.EntityTags, + x => new { x.TagId, x.EntityType }, + y => new { y.TagId, y.EntityType }, + (x, y) => new { x, y } + ) + .Where(x => x.x.EntityId == fromContactID && x.y.EntityId == toContactID) + .Select(x => x.x.TagId).ToList(); + + CrmDbContext.EntityTags.Where(x => x.EntityId == fromContactID && + x.EntityType == EntityType.Contact && + dublicateTagsID.Contains(x.TagId)); + + + var entityTagToUpdate = CrmDbContext.EntityTags.Where(x => x.EntityId == fromContactID && x.EntityType == EntityType.Contact).ToList(); + + entityTagToUpdate.ForEach(x => x.EntityId = toContactID); + + CrmDbContext.SaveChanges(); + + // crm_field_value + var dublicateCustomFieldValueID = Query(CrmDbContext.FieldValue) + .Join(CrmDbContext.FieldValue, + x => new { x.TenantId, x.FieldId, x.EntityType }, + y => new { y.TenantId, y.FieldId, y.EntityType }, + (x, y) => new { x, y }) + .Where(x => x.x.EntityId == fromContactID && x.y.EntityId == toContactID) + .Select(x => x.x.FieldId); + + CrmDbContext.RemoveRange(CrmDbContext.FieldValue.Where(x => x.EntityId == fromContactID && + (new[] { EntityType.Contact, EntityType.Person, EntityType.Company }).Contains(x.EntityType) && + dublicateCustomFieldValueID.Contains(x.FieldId))); + + var fieldValueToUpdate = Query(CrmDbContext.FieldValue) + .AsNoTracking() + .Where(x => x.EntityId == fromContactID && x.EntityType == EntityType.Contact) + .ToList(); + + fieldValueToUpdate.ForEach(x => x.EntityId = toContactID); + CrmDbContext.SaveChanges(); + + // crm_contact_info + var dublicatePrimaryContactInfoID = Query(CrmDbContext.ContactsInfo) + .Join(CrmDbContext.ContactsInfo, + x => new { x.TenantId, x.Type, x.Category, x.IsPrimary }, + y => new { y.TenantId, y.Type, y.Category, y.IsPrimary }, + (x, y) => new { x, y }) + .Where(x => x.x.IsPrimary && + x.x.Data != x.y.Data && + x.x.ContactId == fromContactID && + x.y.ContactId == toContactID) + .Select(x => x.x.Id); + + var contactInfosToUpdate = Query(CrmDbContext.ContactsInfo) + .Where(x => x.ContactId == fromContactID && dublicatePrimaryContactInfoID.Contains(x.Id)) + .ToList(); + + contactInfosToUpdate.ForEach(x => x.IsPrimary = false); + + CrmDbContext.SaveChanges(); + + var dublicateContactInfoID = Query(CrmDbContext.ContactsInfo) + .Join(CrmDbContext.ContactsInfo, + x => new { x.TenantId, x.Type, x.IsPrimary, x.Category, x.Data }, + y => new { y.TenantId, y.Type, y.IsPrimary, y.Category, y.Data }, + (x, y) => new { x, y }) + .Where(x => x.x.ContactId == fromContactID && x.y.ContactId == toContactID) + .Select(x => x.x.Id) + .ToList(); + + CrmDbContext.ContactsInfo.RemoveRange(dublicateContactInfoID.ConvertAll(x => new DbContactInfo + { + Id = x, + ContactId = fromContactID, + TenantId = TenantID + })); + + CrmDbContext.SaveChanges(); + + var contactInfoToUpdate = Query(CrmDbContext.ContactsInfo) + .Where(x => x.ContactId == fromContactID) + .ToList(); + + contactInfoToUpdate.ForEach(x => x.ContactId = toContactID); + + CrmDbContext.SaveChanges(); + + MergeContactInfo(fromContact, toContact); + + // crm_contact + if ((fromContact is Company) && (toContact is Company)) + { + var contactsToUpdate = Query(CrmDbContext.Contacts) + .Where(x => x.CompanyId == fromContactID) + .ToList(); + + contactsToUpdate.ForEach(x => x.CompanyId = toContactID); + + CrmDbContext.SaveChanges(); + + } + + CrmDbContext.Contacts.Remove(new DbContact + { + Id = fromContactID + }); + + CrmDbContext.SaveChanges(); + + tx.Commit(); + } + + _authorizationManager.RemoveAllAces(fromContact); + } + + public List GetContactIDsByCompanyIds(List companyIDs) + { + return Query(CrmDbContext.Contacts) + .Where(x => companyIDs.Contains(x.CompanyId) && !x.IsCompany) + .Select(x => x.Id) + .Distinct() + .ToList(); + } + + public List GetContactIDsByContactInfo(ContactInfoType infoType, String data, int? category, bool? isPrimary) + { + var q = Query(CrmDbContext.ContactsInfo) + .Where(x => x.Type == infoType); + + if (!string.IsNullOrWhiteSpace(data)) + { + q = q.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Data, "%" + data + "%")); + } + + if (category.HasValue) + { + q = q.Where(x => x.Category == category.Value); + } + + if (isPrimary.HasValue) + { + q = q.Where(x => x.IsPrimary == isPrimary.Value); + } + + return q.Select(x => x.ContactId).ToList(); + } + + protected Contact ToContact(DbContact dbContact) + { + if (dbContact == null) return null; + + Contact contact; + + var isCompany = dbContact.IsCompany; + + if (isCompany) + contact = new Company + { + CompanyName = dbContact.CompanyName + }; + else + contact = new Person + { + FirstName = dbContact.FirstName, + LastName = dbContact.LastName, + JobTitle = dbContact.Title, + CompanyID = dbContact.CompanyId + }; + + contact.ID = dbContact.Id; + contact.About = dbContact.Notes; + contact.Industry = dbContact.Industry; + contact.StatusID = dbContact.StatusId; + contact.CreateOn = _tenantUtil.DateTimeFromUtc(dbContact.CreateOn); + contact.CreateBy = dbContact.CreateBy; + contact.ContactTypeID = dbContact.ContactTypeId; + contact.Currency = dbContact.Currency; + + if (dbContact.IsShared == null) + { + var accessSubjectToContact = _crmSecurity.GetAccessSubjectTo(contact); + + contact.ShareType = !accessSubjectToContact.Any() ? ShareType.ReadWrite : ShareType.None; + } + else + { + contact.ShareType = (ShareType)(Convert.ToInt32(dbContact.IsShared)); + } + + return contact; + } + + public void ReassignContactsResponsible(Guid fromUserId, Guid toUserId) + { + var ids = _crmSecurity.GetContactsIdByManager(fromUserId).ToList(); + + foreach (var id in ids) + { + var contact = GetByID(id); + + if (contact == null) continue; + + var responsibles = _crmSecurity.GetAccessSubjectGuidsTo(contact); + + if (!responsibles.Any()) continue; + + responsibles.Remove(fromUserId); + responsibles.Add(toUserId); + + _crmSecurity.SetAccessTo(contact, responsibles.Distinct().ToList()); + } + } + + + /// + /// Test method + /// + /// + /// + public void SetContactCreationDate(int id, DateTime creationDate) + { + var entity = CrmDbContext.Contacts.Find(id); + + entity.CreateOn = _tenantUtil.DateTimeToUtc(creationDate); + + CrmDbContext.SaveChanges(); + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "contacts.*")); + } + + /// + /// Test method + /// + /// + /// + public void SetContactLastModifedDate(int id, DateTime lastModifedDate) + { + var entity = CrmDbContext.Contacts.Find(id); + + entity.LastModifedOn = _tenantUtil.DateTimeToUtc(lastModifedDate); + + CrmDbContext.SaveChanges(); + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "contacts.*")); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Dao/ContactInfoDao.cs b/products/ASC.CRM/Server/Core/Dao/ContactInfoDao.cs new file mode 100644 index 00000000000..9633570343c --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/ContactInfoDao.cs @@ -0,0 +1,293 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.Core.Tenants; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.Web.CRM.Core.Search; + +using AutoMapper; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.CRM.Core.Dao +{ + [Scope] + public class ContactInfoDao : AbstractDao + { + private TenantUtil _tenantUtil; + private FactoryIndexerContactInfo _factoryIndexerContactInfo; + + public ContactInfoDao( + DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + TenantUtil tenantUtil, + IOptionsMonitor logger, + ICache ascCache, + FactoryIndexerContactInfo factoryIndexerContactInfo, + IMapper mapper) + : base(dbContextManager, + tenantManager, + securityContext, + logger, + ascCache, + mapper) + { + _tenantUtil = tenantUtil; + _factoryIndexerContactInfo = factoryIndexerContactInfo; + } + + public ContactInfo GetByID(int id) + { + var dbEntity = CrmDbContext.ContactsInfo.Find(id); + + if (dbEntity.TenantId != TenantID) return null; + + return _mapper.Map(dbEntity); + } + + public void Delete(int id) + { + var dbEntity = CrmDbContext.ContactsInfo.Find(id); + + CrmDbContext.Remove(dbEntity); + CrmDbContext.SaveChanges(); + + _factoryIndexerContactInfo.Delete(r => r.Where(a => a.Id, id)); + } + + public void DeleteByContact(int contactID) + { + if (contactID <= 0) return; + + var dbEntities = Query(CrmDbContext.ContactsInfo) + .AsNoTracking() + .Where(x => x.ContactId == contactID); + + CrmDbContext.RemoveRange(dbEntities); + + CrmDbContext.SaveChanges(); + + _factoryIndexerContactInfo.Delete(r => r.Where(a => a.ContactId, contactID)); + } + + public int Update(ContactInfo contactInfo) + { + var result = UpdateInDb(contactInfo); + + var dbEntity = Query(CrmDbContext.ContactsInfo) + .AsNoTracking() + .Where(x => x.ContactId == contactInfo.ID) + .Single(); + + _factoryIndexerContactInfo.Update(dbEntity); + + return result; + } + + private int UpdateInDb(ContactInfo contactInfo) + { + if (contactInfo == null || contactInfo.ID == 0 || contactInfo.ContactID == 0) + throw new ArgumentException(); + + var itemToUpdate = new DbContactInfo + { + Id = contactInfo.ID, + Data = contactInfo.Data, + Category = contactInfo.Category, + IsPrimary = contactInfo.IsPrimary, + ContactId = contactInfo.ContactID, + Type = contactInfo.InfoType, + LastModifedOn = _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow()), + LastModifedBy = _securityContext.CurrentAccount.ID, + TenantId = TenantID + }; + + CrmDbContext.Update(itemToUpdate); + + CrmDbContext.SaveChanges(); + + return contactInfo.ID; + } + + + public int Save(ContactInfo contactInfo) + { + var id = SaveInDb(contactInfo); + + contactInfo.ID = id; + + var dbEntity = Query(CrmDbContext.ContactsInfo) + .AsNoTracking() + .Where(x => x.ContactId == contactInfo.ID) + .Single(); + + _factoryIndexerContactInfo.Index(dbEntity); + + return id; + } + + private int SaveInDb(ContactInfo contactInfo) + { + var itemToInsert = new DbContactInfo + { + Data = contactInfo.Data, + Category = contactInfo.Category, + IsPrimary = contactInfo.IsPrimary, + ContactId = contactInfo.ContactID, + Type = contactInfo.InfoType, + LastModifedOn = _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow()), + LastModifedBy = _securityContext.CurrentAccount.ID, + TenantId = TenantID + + }; + + CrmDbContext.Add(itemToInsert); + + CrmDbContext.SaveChanges(); + + return itemToInsert.Id; + + } + + public List GetListData(int contactID, ContactInfoType infoType) + { + return GetList(contactID, infoType, null, null).ConvertAll(item => item.Data); + } + + public List GetAll() + { + return GetList(0, null, null, null); + } + + public List GetAll(int[] contactID) + { + if (contactID == null || contactID.Length == 0) return null; + + var result = Query(CrmDbContext.ContactsInfo) + .Where(x => contactID.Contains(x.ContactId)) + .ToList(); + + return _mapper.Map, List>(result); + } + + public List GetList(int contactID, ContactInfoType? infoType, int? categoryID, bool? isPrimary) + { + var items = Query(CrmDbContext.ContactsInfo) + .AsNoTracking(); + + if (contactID > 0) + items = items.Where(x => x.ContactId == contactID); + + if (infoType.HasValue) + items = items.Where(x => x.Type == infoType.Value); + + if (categoryID.HasValue) + items = items.Where(x => x.Category == categoryID.Value); + + if (isPrimary.HasValue) + items = items.Where(x => x.IsPrimary == isPrimary.Value); + + items = items.OrderBy(x => x.Type); + + return _mapper.Map, List>(items.ToList()); + } + + + public int[] UpdateList(List items, Contact contact = null) + { + if (items == null || items.Count == 0) return null; + + var result = new List(); + + var tx = CrmDbContext.Database.BeginTransaction(); + + foreach (var contactInfo in items) + result.Add(UpdateInDb(contactInfo)); + + tx.Commit(); + + if (contact != null) + { + var itemIDs = items.Select(y => y.ID); + + var dbContactInfos = Query(CrmDbContext.ContactsInfo) + .Where(x => itemIDs.Contains(x.Id)); + + foreach (var dbContactInfo in dbContactInfos) + { + _factoryIndexerContactInfo.Index(dbContactInfo); + } + } + + return result.ToArray(); + } + + public int[] SaveList(List items, Contact contact = null) + { + if (items == null || items.Count == 0) return null; + + var result = new List(); + + var tx = CrmDbContext.Database.BeginTransaction(); + + foreach (var contactInfo in items) + { + var contactInfoId = SaveInDb(contactInfo); + contactInfo.ID = contactInfoId; + result.Add(contactInfoId); + } + + tx.Commit(); + + if (contact != null) + { + var itemIDs = items.Select(y => y.ID); + + var dbContactInfos = Query(CrmDbContext.ContactsInfo) + .Where(x => itemIDs.Contains(x.Id)); + + foreach (var dbContactInfo in dbContactInfos) + { + _factoryIndexerContactInfo.Index(dbContactInfo); + } + } + + return result.ToArray(); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Dao/CurrencyInfoDao.cs b/products/ASC.CRM/Server/Core/Dao/CurrencyInfoDao.cs new file mode 100644 index 00000000000..ab6b2f26bf8 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/CurrencyInfoDao.cs @@ -0,0 +1,99 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.CRM.Core.EF; + +using AutoMapper; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.CRM.Core.Dao +{ + [Scope] + public class CurrencyInfoDao : AbstractDao + { + public CurrencyInfoDao(DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + IOptionsMonitor logger, + ICache ascCache, + IMapper mapper) : + base(dbContextManager, + tenantManager, + securityContext, + logger, + ascCache, + mapper) + { + + + } + + public List GetAll() + { + var dbEntities = CrmDbContext.CurrencyInfo + .AsNoTracking() + .ToList(); + + return _mapper.Map, List>(dbEntities); + } + + public CurrencyInfo GetByAbbreviation(string abbreviation) + { + var dbEntity = CrmDbContext.CurrencyInfo.Find(abbreviation); + + return _mapper.Map(dbEntity); + + } + + public List GetBasic() + { + var dbItems = CrmDbContext.CurrencyInfo + .Where(x => x.IsBasic) + .ToList(); + + return _mapper.Map, List>(dbItems); + } + + public List GetOther() + { + var dbItems = CrmDbContext.CurrencyInfo + .Where(x => !x.IsBasic) + .ToList(); + + return _mapper.Map, List>(dbItems); + } + } +} diff --git a/products/ASC.CRM/Server/Core/Dao/CurrencyRateDao.cs b/products/ASC.CRM/Server/Core/Dao/CurrencyRateDao.cs new file mode 100644 index 00000000000..19d7b2ff107 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/CurrencyRateDao.cs @@ -0,0 +1,162 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.CRM.Core.EF; + +using AutoMapper; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.CRM.Core.Dao +{ + [Scope] + public class CurrencyRateDao : AbstractDao + { + public CurrencyRateDao( + DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + IOptionsMonitor logger, + ICache ascCache, + IMapper mapper) : + base(dbContextManager, + tenantManager, + securityContext, + logger, + ascCache, + mapper) + { + + } + + public List GetAll() + { + var rates = Query(CrmDbContext.CurrencyRate).ToList(); + + return _mapper.Map, List>(rates); + } + + public CurrencyRate GetByID(int id) + { + var entity = CrmDbContext.CurrencyRate.Find(id); + + if (entity.TenantId != TenantID) return null; + + return _mapper.Map(entity); + } + + public CurrencyRate GetByCurrencies(string fromCurrency, string toCurrency) + { + var dbEntity = Query(CrmDbContext.CurrencyRate).FirstOrDefault(x => + string.Compare(x.FromCurrency, fromCurrency, true) == 0 && + string.Compare(x.ToCurrency, toCurrency, true) == 0); + + return _mapper.Map(dbEntity); + + } + + public int SaveOrUpdate(CurrencyRate entity) + { + if (String.IsNullOrEmpty(entity.FromCurrency) || String.IsNullOrEmpty(entity.ToCurrency) || entity.Rate < 0) + throw new ArgumentException(); + + if (entity.ID > 0 && entity.Rate == 0) + return Delete(entity.ID); + + var dbEntity = new DbCurrencyRate + { + Id = entity.ID, + FromCurrency = entity.FromCurrency.ToUpper(), + ToCurrency = entity.ToCurrency.ToUpper(), + Rate = entity.Rate, + CreateOn = entity.CreateOn == DateTime.MinValue ? DateTime.UtcNow : entity.CreateOn, + CreateBy = entity.CreateBy == Guid.Empty ? _securityContext.CurrentAccount.ID : entity.CreateBy, + LastModifedOn = DateTime.UtcNow, + LastModifedBy = _securityContext.CurrentAccount.ID, + TenantId = TenantID + }; + + CrmDbContext.Update(dbEntity); + CrmDbContext.SaveChanges(); + + return dbEntity.Id; + } + + public int Delete(int id) + { + var dbEntity = CrmDbContext.CurrencyRate.Find(id); + + CrmDbContext.Remove(dbEntity); + + CrmDbContext.SaveChanges(); + + return id; + } + + public List SetCurrencyRates(List rates) + { + using var tx = CrmDbContext.Database.BeginTransaction(); + + var items = Query(CrmDbContext.CurrencyRate).AsNoTracking(); + + CrmDbContext.RemoveRange(items); + + foreach (var rate in rates) + { + var itemToInsert = new DbCurrencyRate + { + FromCurrency = rate.FromCurrency.ToUpper(), + ToCurrency = rate.ToCurrency.ToUpper(), + Rate = rate.Rate, + CreateBy = _securityContext.CurrentAccount.ID, + CreateOn = DateTime.UtcNow, + LastModifedBy = _securityContext.CurrentAccount.ID, + LastModifedOn = DateTime.UtcNow, + TenantId = TenantID + }; + + CrmDbContext.CurrencyRate.Add(itemToInsert); + + CrmDbContext.SaveChanges(); + + rate.ID = itemToInsert.Id; + } + + tx.Commit(); + + return rates; + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Dao/CustomFieldDao.cs b/products/ASC.CRM/Server/Core/Dao/CustomFieldDao.cs new file mode 100644 index 00000000000..cfd0eabed9f --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/CustomFieldDao.cs @@ -0,0 +1,583 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.Core.Tenants; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; +using ASC.ElasticSearch; +using ASC.Web.CRM.Classes; +using ASC.Web.CRM.Core.Search; + +using AutoMapper; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.CRM.Core.Dao +{ + [Scope] + public class CustomFieldDao : AbstractDao + { + private readonly TenantUtil _tenantUtil; + private readonly FactoryIndexerFieldValue _factoryIndexer; + + public CustomFieldDao( + DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + TenantUtil tenantUtil, + FactoryIndexerFieldValue factoryIndexer, + IOptionsMonitor logger, + ICache ascCache, + IMapper mapper + ) : + base(dbContextManager, + tenantManager, + securityContext, + logger, + ascCache, + mapper) + { + _tenantUtil = tenantUtil; + _factoryIndexer = factoryIndexer; + } + + + public void SaveList(List items) + { + if (items == null || items.Count == 0) return; + + var tx = CrmDbContext.Database.BeginTransaction(); + + foreach (var customField in items) + { + SetFieldValueInDb(customField.EntityType, customField.EntityID, customField.ID, customField.Value); + } + + tx.Commit(); + } + + public void SetFieldValue(EntityType entityType, int entityID, int fieldID, String fieldValue) + { + if (!_supportedEntityType.Contains(entityType)) + throw new ArgumentException(); + + fieldValue = fieldValue.Trim(); + + SetFieldValueInDb(entityType, entityID, fieldID, fieldValue); + } + + private void SetFieldValueInDb(EntityType entityType, int entityID, int fieldID, string fieldValue) + { + if (!_supportedEntityType.Contains(entityType)) + throw new ArgumentException(); + + fieldValue = fieldValue.Trim(); + + var dbEntity = Query(CrmDbContext.FieldValue) + .FirstOrDefault(x => x.EntityId == entityID && + x.EntityType == entityType && + x.FieldId == fieldID); + + if (string.IsNullOrEmpty(fieldValue)) + { + _factoryIndexer.Delete(dbEntity); + + CrmDbContext.Remove(dbEntity); + } + else + { + dbEntity.Value = fieldValue; + dbEntity.LastModifedOn = _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow()); + dbEntity.LastModifedBy = _securityContext.CurrentAccount.ID; + + _factoryIndexer.Index(dbEntity); + } + + CrmDbContext.SaveChanges(); + } + + private string GetValidMask(CustomFieldType customFieldType, String mask) + { + var resultMask = new Dictionary(); + + if (customFieldType == CustomFieldType.CheckBox || customFieldType == CustomFieldType.Heading || customFieldType == CustomFieldType.Date) + return String.Empty; + + if (String.IsNullOrEmpty(mask)) + throw new ArgumentException(CRMErrorsResource.CustomFieldMaskNotValid); + + try + { + var maskObj = JsonDocument.Parse(mask).RootElement; + if (customFieldType == CustomFieldType.TextField) + { + var size = maskObj.GetProperty("size").GetInt32(); + if (size == 0) + { + resultMask.Add("size", 1); + } + else if (size > Global.MaxCustomFieldSize) + { + resultMask.Add("size", Global.MaxCustomFieldSize); + } + else + { + resultMask.Add("size", size); + } + } + if (customFieldType == CustomFieldType.TextArea) + { + var rows = maskObj.GetProperty("rows").GetInt32(); + var cols = maskObj.GetProperty("cols").GetInt32(); + + if (rows == 0) + { + resultMask.Add("rows", 1); + } + else if (rows > Global.MaxCustomFieldRows) + { + resultMask.Add("rows", Global.MaxCustomFieldRows); + } + else + { + resultMask.Add("rows", rows); + } + + if (cols == 0) + { + resultMask.Add("cols", 1); + } + else if (cols > Global.MaxCustomFieldCols) + { + resultMask.Add("cols", Global.MaxCustomFieldCols); + } + else + { + resultMask.Add("cols", cols); + } + } + if (customFieldType == CustomFieldType.SelectBox) + { + try + { + var arrayLength = maskObj.GetArrayLength(); + + return mask; + + } + catch (Exception ex) + { + throw new ArgumentException(CRMErrorsResource.CustomFieldMaskNotValid); + } + } + } + catch (Exception ex) + { + if (customFieldType == CustomFieldType.TextField) + { + resultMask.Add("size", Global.DefaultCustomFieldSize); + } + if (customFieldType == CustomFieldType.TextArea) + { + resultMask.Add("rows", Global.DefaultCustomFieldRows); + resultMask.Add("cols", Global.DefaultCustomFieldCols); + } + if (customFieldType == CustomFieldType.SelectBox) + { + _logger.Error(ex); + + throw; + } + } + return JsonSerializer.Serialize(resultMask); + } + + public int CreateField(EntityType entityType, String label, CustomFieldType customFieldType, String mask) + { + if (!_supportedEntityType.Contains(entityType) || String.IsNullOrEmpty(label)) + throw new ArgumentException(); + + var resultMask = GetValidMask(customFieldType, mask); + + var sortOrder = Query(CrmDbContext.FieldDescription).Select(x => x.SortOrder).Max() + 1; + + var dbEntity = new DbFieldDescription + { + Label = label, + Type = customFieldType, + Mask = resultMask, + SortOrder = sortOrder, + EntityType = entityType, + TenantId = TenantID + }; + + CrmDbContext.FieldDescription.Add(dbEntity); + + CrmDbContext.SaveChanges(); + + return dbEntity.Id; + } + + public String GetValue(EntityType entityType, int entityID, int fieldID) + { + var sqlQuery = Query(CrmDbContext.FieldValue).Where(x => x.FieldId == fieldID && x.EntityId == entityID); + + if (entityType == EntityType.Company || entityType == EntityType.Person) + { + sqlQuery = sqlQuery.Where(x => x.EntityType == entityType || x.EntityType == EntityType.Contact); + } + else + { + sqlQuery = sqlQuery.Where(x => x.EntityType == entityType); + } + + return sqlQuery.Select(x => x.Value).FirstOrDefault(); + } + + public List GetEntityIds(EntityType entityType, int fieldID, String fieldValue) + { + var sqlQuery = Query(CrmDbContext.FieldValue) + .Where(x => x.FieldId == fieldID && String.Compare(x.Value, fieldValue, true) == 0); + + if (entityType == EntityType.Company || entityType == EntityType.Person) + { + sqlQuery = sqlQuery.Where(x => x.EntityType == entityType || x.EntityType == EntityType.Contact); + } + else + { + sqlQuery = sqlQuery.Where(x => x.EntityType == entityType); + } + + + return sqlQuery.Select(x => x.EntityId).ToList(); + } + + public bool IsExist(int id) + { + return Query(CrmDbContext.FieldDescription).Where(x => x.Id == id).Any(); + } + + public int GetFieldId(EntityType entityType, String label, CustomFieldType customFieldType) + { + var sqlQuery = Query(CrmDbContext.FieldDescription).Where(x => x.Type == customFieldType && x.Label == label); + + if (entityType == EntityType.Company || entityType == EntityType.Person) + { + sqlQuery = sqlQuery.Where(x => x.EntityType == entityType || x.EntityType == EntityType.Contact); + } + else + { + sqlQuery = sqlQuery.Where(x => x.EntityType == entityType); + } + + + var result = sqlQuery.FirstOrDefault(); + + if (result == null) return 0; + + return result.Id; + } + + public void EditItem(CustomField customField) + { + if (string.IsNullOrEmpty(customField.Label)) + throw new ArgumentException(); + + if (HaveRelativeLink(customField.ID)) + { + try + { + var resultMask = ""; + + var row = Query(CrmDbContext.FieldDescription).Where(x => x.Id == customField.ID).Select(x => new { x.Type, x.Mask }).Single(); + + var fieldType = row.Type; + var oldMask = row.Mask; + + if (fieldType == CustomFieldType.SelectBox) + { + if (oldMask == customField.Mask || customField.Mask == "") + { + resultMask = oldMask; + } + else + { + try + { + + var maskObjOld = JsonSerializer.Deserialize>(oldMask); + var maskObjNew = JsonSerializer.Deserialize>(customField.Mask); + + var inm = maskObjNew.Intersect(maskObjOld).ToList(); + + if (inm.Count == maskObjOld.Count) + { + resultMask = customField.Mask; + } + } + catch (Exception ex) + { + throw new ArgumentException(CRMErrorsResource.CustomFieldMaskNotValid); + } + } + } + else + { + resultMask = GetValidMask(fieldType, customField.Mask); + } + + } + catch (Exception ex) + { + _logger.Error(ex); + + throw; + } + + var itemToUpdate = Query(CrmDbContext.FieldDescription).FirstOrDefault(x => x.Id == customField.ID); + + itemToUpdate.Label = customField.Label; + itemToUpdate.Mask = customField.Mask; + + CrmDbContext.SaveChanges(); + + } + else + { + var resultMask = GetValidMask(customField.Type, customField.Mask); + + var itemToUpdate = Query(CrmDbContext.FieldDescription).FirstOrDefault(x => x.Id == customField.ID); + + itemToUpdate.Label = customField.Label; + itemToUpdate.Type = customField.Type; + itemToUpdate.Mask = customField.Mask; + + CrmDbContext.SaveChanges(); + } + } + + public void ReorderFields(int[] ids) + { + var tx = CrmDbContext.Database.BeginTransaction(); + + for (int index = 0; index < ids.Length; index++) + { + var dbEntity = CrmDbContext.FieldDescription.Find(ids[index]); + + dbEntity.SortOrder = index; + + CrmDbContext.SaveChanges(); + } + + tx.Commit(); + } + + private bool HaveRelativeLink(int fieldID) + { + return Query(CrmDbContext.FieldValue) + .Where(x => x.FieldId == fieldID).Any(); + } + + public String GetContactLinkCountJSON(EntityType entityType) + { + if (!_supportedEntityType.Contains(entityType)) + throw new ArgumentException(); + + var sqlQuery = Query(CrmDbContext.FieldDescription).Join(Query(CrmDbContext.FieldValue), + x => x.Id, + y => y.FieldId, + (x, y) => new { x, y } + ); + + if (entityType == EntityType.Company || entityType == EntityType.Person) + { + sqlQuery = sqlQuery.Where(x => x.x.EntityType == entityType || x.x.EntityType == EntityType.Contact); + } + else + { + sqlQuery = sqlQuery.Where(x => x.x.EntityType == entityType); + } + + return JsonSerializer.Serialize(sqlQuery.GroupBy(x => x.x.Id) + .Select(x => x.Count()).ToList()); + } + + public int GetContactLinkCount(EntityType entityType, int entityID) + { + if (!_supportedEntityType.Contains(entityType)) + throw new ArgumentException(); + + var sqlQuery = Query(CrmDbContext.FieldDescription).Join(Query(CrmDbContext.FieldValue), + x => x.Id, + y => y.FieldId, + (x, y) => new { x, y } + ); + + if (entityType == EntityType.Company || entityType == EntityType.Person) + { + sqlQuery = sqlQuery.Where(x => x.x.EntityType == entityType || x.x.EntityType == EntityType.Contact); + } + else + { + sqlQuery = sqlQuery.Where(x => x.x.EntityType == entityType); + } + + sqlQuery = sqlQuery.Where(x => x.x.Id == entityID); + + return sqlQuery.GroupBy(x => x.x.Id) + .Select(x => x.Count()).SingleOrDefault(); + } + + public List GetEnityFields(EntityType entityType, int entityID, bool includeEmptyFields) + { + return GetEnityFields(entityType, entityID == 0 ? null : new[] { entityID }, includeEmptyFields); + } + + public List GetEnityFields(EntityType entityType, int[] entityID) + { + return GetEnityFields(entityType, entityID, false); + } + + private List GetEnityFields(EntityType entityType, int[] entityID, bool includeEmptyFields) + { + // TODO: Refactoring Query!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + if (!_supportedEntityType.Contains(entityType)) + throw new ArgumentException(); + + var sqlQuery = Query(CrmDbContext.FieldDescription).GroupJoin(Query(CrmDbContext.FieldValue), + x => x.Id, + y => y.FieldId, + (x, y) => new { x, y } + ).SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { x.x, y }); + + if (entityType == EntityType.Company || entityType == EntityType.Person) + { + sqlQuery = sqlQuery.Where(x => x.x.EntityType == entityType || x.x.EntityType == EntityType.Contact); + } + else + { + sqlQuery = sqlQuery.Where(x => x.x.EntityType == entityType); + } + + if (entityID != null && entityID.Length > 0) + { + sqlQuery = sqlQuery.Where(x => entityID.Contains(x.y.EntityId)); + sqlQuery = sqlQuery.OrderBy(x => x.x.SortOrder); + } + else + { + sqlQuery = sqlQuery.OrderBy(x => x.y.EntityId); + sqlQuery = sqlQuery.OrderBy(x => x.x.SortOrder); + } + + if (!includeEmptyFields) + sqlQuery = sqlQuery.Where(x => x.y != null || x.x.Type == CustomFieldType.Heading); + + return sqlQuery.ToList().ConvertAll(x => ToCustomField(x.x, x.y)); + + } + + public CustomField GetFieldDescription(int id) + { + var dbEntity = CrmDbContext.FieldDescription.Find(id); + + if (dbEntity.TenantId != TenantID) return null; + + return _mapper.Map(dbEntity); + } + + public List GetFieldsDescription(EntityType entityType) + { + if (!_supportedEntityType.Contains(entityType)) + throw new ArgumentException(); + + var sqlQuery = Query(CrmDbContext.FieldDescription) + .AsNoTracking(); + + if (entityType == EntityType.Company || entityType == EntityType.Person) + { + sqlQuery = sqlQuery.Where(x => x.EntityType == entityType || x.EntityType == EntityType.Contact); + } + else + { + sqlQuery = sqlQuery.Where(x => x.EntityType == entityType); + } + + var dbEntities = sqlQuery.ToList(); + + return _mapper.Map, List>(dbEntities); + } + + public void DeleteField(int id) + { + if (HaveRelativeLink(id)) + throw new ArgumentException(); + + var tx = CrmDbContext.Database.BeginTransaction(); + + var dbFieldDescription = CrmDbContext.FieldDescription.Find(id); + + CrmDbContext.Remove(dbFieldDescription); + + var dbFieldValue = Query(CrmDbContext.FieldValue).FirstOrDefault(x => x.FieldId == id); + + _factoryIndexer.Delete(dbFieldValue); + + CrmDbContext.Remove(dbFieldValue); + + CrmDbContext.SaveChanges(); + + tx.Commit(); + } + + public CustomField ToCustomField(DbFieldDescription dbFieldDescription, + DbFieldValue dbFieldValue = null) + { + var customField = _mapper.Map(dbFieldDescription); + + if (customField != null && dbFieldValue != null) + { + customField.Value = dbFieldValue.Value; + customField.EntityID = dbFieldValue.EntityId; + } + + return customField; + } + } + +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Dao/DaoFactory.cs b/products/ASC.CRM/Server/Core/Dao/DaoFactory.cs new file mode 100644 index 00000000000..33c6fb95d81 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/DaoFactory.cs @@ -0,0 +1,222 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using System; + +using ASC.Common; +using ASC.VoipService.Dao; + +using Microsoft.Extensions.DependencyInjection; + +namespace ASC.CRM.Core.Dao +{ + [Scope(Additional = typeof(DaoFactoryExtension))] + public class DaoFactory + { + public DaoFactory(IServiceProvider serviceProvider) + { + ServiceProvider = serviceProvider; + } + + public IServiceProvider ServiceProvider { get; } + + public TaskDao GetTaskDao() + { + return ServiceProvider.GetService(); + } + + public ListItemDao GetCachedListItem() + { + return ServiceProvider.GetService(); + } + + public ContactDao GetContactDao() + { + return ServiceProvider.GetService(); + } + + public CustomFieldDao GetCustomFieldDao() + { + return ServiceProvider.GetService(); + } + + public DealDao GetDealDao() + { + return ServiceProvider.GetService(); + } + + public DealMilestoneDao GetDealMilestoneDao() + { + return ServiceProvider.GetService(); + + } + + public ListItemDao GetListItemDao() + { + return ServiceProvider.GetService(); + } + + public TagDao GetTagDao() + { + return ServiceProvider.GetService(); + } + + public SearchDao GetSearchDao() + { + return ServiceProvider.GetService(); + } + + public RelationshipEventDao GetRelationshipEventDao() + { + return ServiceProvider.GetService(); + } + + public FileDao GetFileDao() + { + return ServiceProvider.GetService(); + } + + public CasesDao GetCasesDao() + { + return ServiceProvider.GetService(); + } + + public TaskTemplateContainerDao GetTaskTemplateContainerDao() + { + return ServiceProvider.GetService(); + } + + public TaskTemplateDao GetTaskTemplateDao() + { + return ServiceProvider.GetService(); + } + + public ReportDao GetReportDao() + { + return ServiceProvider.GetService(); + } + + public CurrencyRateDao GetCurrencyRateDao() + { + return ServiceProvider.GetService(); + } + + public CurrencyInfoDao GetCurrencyInfoDao() + { + return ServiceProvider.GetService(); + } + + public ContactInfoDao GetContactInfoDao() + { + return ServiceProvider.GetService(); + } + + public InvoiceDao GetInvoiceDao() + { + return ServiceProvider.GetService(); + } + + public InvoiceItemDao GetInvoiceItemDao() + { + return ServiceProvider.GetService(); + } + + public InvoiceTaxDao GetInvoiceTaxDao() + { + return ServiceProvider.GetService(); + } + + public InvoiceLineDao GetInvoiceLineDao() + { + return ServiceProvider.GetService(); + } + + public VoipDao GetVoipDao() + { + return ServiceProvider.GetService(); + } + } + + public class DaoFactoryExtension + { + public static void Register(DIHelper services) + { + services.TryAdd(); + services.TryAdd(); + services.TryAdd(); + services.TryAdd(); + services.TryAdd(); + services.TryAdd(); + services.TryAdd(); + services.TryAdd(); + services.TryAdd(); + services.TryAdd(); + services.TryAdd(); + services.TryAdd(); + services.TryAdd(); + + services.TryAdd(); + services.TryAdd(); + + services.TryAdd(); + services.TryAdd(); + + + services.TryAdd(); + services.TryAdd(); + services.TryAdd(); + services.TryAdd(); + } + } + +} + +//TaskDao +//ListItemDao +//ContactDao +//CustomFieldDao +//DealDao +//DealMilestoneDao + +//TagDao +//SearchDao +//RelationshipEventDao +//FileDao +//CasesDao +//TaskTemplateContainerDao +//TaskTemplateDao + + +//ReportDao +//CurrencyRateDao +//CurrencyInfoDao +//ContactInfoDao + + +//InvoiceDao +//InvoiceItemDao +//InvoiceTaxDao +//InvoiceLineDao +//VoipDao \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Dao/DealDao.cs b/products/ASC.CRM/Server/Core/Dao/DealDao.cs new file mode 100644 index 00000000000..3035c6ecade --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/DealDao.cs @@ -0,0 +1,918 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text.RegularExpressions; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.Core.Tenants; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.ElasticSearch; +using ASC.Files.Core; +using ASC.Web.CRM.Core.Search; +using ASC.Web.Files.Api; + +using AutoMapper; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +using OrderBy = ASC.CRM.Core.Entities.OrderBy; + +namespace ASC.CRM.Core.Dao +{ + [Scope] + public class DealDao : AbstractDao + { + private readonly BundleSearch _bundleSearch; + private readonly FilesIntegration _filesIntegration; + private readonly FactoryIndexerDeal _factoryIndexer; + private readonly TenantUtil _tenantUtil; + private readonly CrmSecurity _crmSecurity; + private readonly AuthorizationManager _authorizationManager; + + public DealDao(DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + CrmSecurity crmSecurity, + FactoryIndexerDeal factoryIndexer, + FilesIntegration filesIntegration, + TenantUtil tenantUtil, + AuthorizationManager authorizationManager, + IOptionsMonitor logger, + ICache ascCache, + IMapper mapper, + BundleSearch bundleSearch) : + base(dbContextManager, + tenantManager, + securityContext, + logger, + ascCache, + mapper) + { + _crmSecurity = crmSecurity; + _factoryIndexer = factoryIndexer; + _filesIntegration = filesIntegration; + _bundleSearch = bundleSearch; + _mapper = mapper; + _tenantUtil = tenantUtil; + _authorizationManager = authorizationManager; + } + + + public void AddMember(int dealID, int memberID) + { + SetRelative(memberID, EntityType.Opportunity, dealID); + } + + public Dictionary GetMembers(int[] dealID) + { + return GetRelativeToEntity(null, EntityType.Opportunity, dealID); + } + + public int[] GetMembers(int dealID) + { + return GetRelativeToEntity(null, EntityType.Opportunity, dealID); + } + + public void SetMembers(int dealID, int[] memberID) + { + SetRelative(memberID, EntityType.Opportunity, dealID); + } + + public void RemoveMember(int dealID, int memberID) + { + RemoveRelative(memberID, EntityType.Opportunity, dealID); + } + + public List GetDeals(int[] id) + { + if (id == null || !id.Any()) return new List(); + + var dbDeals = Query(CrmDbContext.Deals).Where(x => id.Contains(x.Id)).ToList(); + + return _mapper.Map, List>(dbDeals); + } + + public Deal GetByID(int dealID) + { + var deals = GetDeals(new[] { dealID }); + + return deals.Count == 0 ? null : deals[0]; + } + + public int CreateNewDeal(Deal deal) + { + var result = CreateNewDealInDb(deal); + + deal.ID = result; + + + _factoryIndexer.Index(Query(CrmDbContext.Deals).Where(x => x.Id == deal.ID).FirstOrDefault()); + + return result; + } + + private int CreateNewDealInDb(Deal deal) + { + if (String.IsNullOrEmpty(deal.Title) || deal.ResponsibleID == Guid.Empty || deal.DealMilestoneID <= 0) + throw new ArgumentException(); + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "deals.*")); + + var itemToInsert = new DbDeal + { + Title = deal.Title, + Description = deal.Description, + ResponsibleId = deal.ResponsibleID, + ContactId = deal.ContactID, + BidCurrency = deal.BidCurrency, + BidValue = deal.BidValue, + BidType = deal.BidType, + DealMilestoneId = deal.DealMilestoneID, + DealMilestoneProbability = deal.DealMilestoneProbability, + ExpectedCloseDate = deal.ExpectedCloseDate, + ActualCloseDate = deal.ActualCloseDate, + PerPeriodValue = deal.PerPeriodValue, + CreateOn = _tenantUtil.DateTimeToUtc(deal.CreateOn == DateTime.MinValue ? _tenantUtil.DateTimeNow() : deal.CreateOn), + CreateBy = _securityContext.CurrentAccount.ID, + LastModifedOn = _tenantUtil.DateTimeToUtc(deal.CreateOn == DateTime.MinValue ? _tenantUtil.DateTimeNow() : deal.CreateOn), + LastModifedBy = _securityContext.CurrentAccount.ID, + TenantId = TenantID + }; + + CrmDbContext.Deals.Add(itemToInsert); + CrmDbContext.SaveChanges(); + + var dealID = itemToInsert.Id; + + // if (deal.ContactID > 0) + // AddMember(dealID, deal.ContactID); + + return dealID; + } + + public int[] SaveDealList(List items) + { + var tx = CrmDbContext.Database.BeginTransaction(); + + var result = items.Select(item => CreateNewDealInDb(item)).ToArray(); + + tx.Commit(); + + var dbDeals = Query(CrmDbContext.Deals).Where(x => result.Contains(x.Id)); + + foreach (var deal in dbDeals) + { + _factoryIndexer.Index(deal); + } + + return result; + } + + public void EditDeal(Deal deal) + { + _crmSecurity.DemandEdit(deal); + + // var oldDeal = GetByID(deal.ID); + + // if (oldDeal.ContactID > 0) + // RemoveMember(oldDeal.ID, oldDeal.ContactID); + + // AddMember(deal.ID, deal.ContactID); + + var itemToUpdate = CrmDbContext.Deals.Find(deal.ID); + + if (itemToUpdate.TenantId != TenantID) + throw new ArgumentException(); + + itemToUpdate.Title = deal.Title; + itemToUpdate.Description = deal.Description; + itemToUpdate.ResponsibleId = deal.ResponsibleID; + itemToUpdate.ContactId = deal.ContactID; + itemToUpdate.BidCurrency = deal.BidCurrency; + itemToUpdate.BidValue = deal.BidValue; + itemToUpdate.BidType = deal.BidType; + itemToUpdate.DealMilestoneId = deal.DealMilestoneID; + itemToUpdate.DealMilestoneProbability = deal.DealMilestoneProbability; + itemToUpdate.ExpectedCloseDate = deal.ExpectedCloseDate; + itemToUpdate.PerPeriodValue = deal.PerPeriodValue; + itemToUpdate.ActualCloseDate = _tenantUtil.DateTimeToUtc(deal.ActualCloseDate); + itemToUpdate.LastModifedOn = _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow()); + itemToUpdate.LastModifedBy = _securityContext.CurrentAccount.ID; + + CrmDbContext.SaveChanges(); + + _factoryIndexer.Index(itemToUpdate); + } + + public int GetDealsCount() + { + return GetDealsCount(String.Empty, Guid.Empty, 0, null, 0, null, null, DateTime.MinValue, DateTime.MinValue); + } + + public List GetAllDeals() + { + return GetDeals(String.Empty, + Guid.Empty, + 0, + null, + 0, + null, + null, + DateTime.MinValue, + DateTime.MinValue, + 0, + 0, + new OrderBy(DealSortedByType.Stage, true)); + } + + private IQueryable GetDbDealByFilters( + ICollection exceptIDs, + String searchText, + Guid responsibleID, + int milestoneID, + IEnumerable tags, + int contactID, + DealMilestoneStatus? stageType, + bool? contactAlsoIsParticipant, + DateTime fromDate, + DateTime toDate, + OrderBy orderBy) + { + + var sqlQuery = Query(CrmDbContext.Deals).Join(CrmDbContext.DealMilestones, + x => x.DealMilestoneId, + y => y.Id, + (x, y) => new { x, y } + ); + + var ids = new List(); + + if (!String.IsNullOrEmpty(searchText)) + { + searchText = searchText.Trim(); + + var keywords = searchText.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToArray(); + + if (keywords.Length > 0) + { + if (!_bundleSearch.TrySelectOpportunity(searchText, out ids)) + { + foreach (var k in keywords) + { + sqlQuery = sqlQuery.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.x.Title, k + "%") || Microsoft.EntityFrameworkCore.EF.Functions.Like(x.x.Description, k + "%")); + } + } + else if (ids.Count == 0) return null; + } + } + + if (tags != null && tags.Any()) + { + ids = SearchByTags(EntityType.Opportunity, ids.ToArray(), tags); + + if (ids.Count == 0) return null; + } + + if (contactID > 0) + { + if (contactAlsoIsParticipant.HasValue && contactAlsoIsParticipant.Value) + { + var relativeContactsID = GetRelativeToEntity(contactID, EntityType.Opportunity, null).ToList(); + + if (relativeContactsID.Count == 0) + { + sqlQuery = sqlQuery.Where(x => x.x.ContactId == contactID); + } + else + { + if (ids.Count > 0) + { + ids = relativeContactsID.Intersect(ids).ToList(); + + if (ids.Count == 0) return null; + } + else + { + ids = relativeContactsID; + } + } + } + else + { + sqlQuery = sqlQuery.Where(x => x.x.ContactId == contactID); + } + } + + if (0 < milestoneID && milestoneID < int.MaxValue) + { + sqlQuery = sqlQuery.Where(x => x.x.DealMilestoneId == milestoneID); + } + + if (responsibleID != Guid.Empty) + { + sqlQuery = sqlQuery.Where(x => x.x.ResponsibleId == responsibleID); + } + + + if (ids.Count > 0) + { + if (exceptIDs.Count > 0) + { + ids = ids.Except(exceptIDs).ToList(); + if (ids.Count == 0) return null; + } + + sqlQuery = sqlQuery.Where(x => ids.Contains(x.x.Id)); + } + else if (exceptIDs.Count > 0) + { + sqlQuery = sqlQuery.Where(x => !exceptIDs.Contains(x.x.Id)); + } + + if ((stageType != null) || (fromDate != DateTime.MinValue && toDate != DateTime.MinValue)) + { + if (stageType != null) + sqlQuery = sqlQuery.Where(x => x.y.Status == stageType.Value); + + if (fromDate != DateTime.MinValue && toDate != DateTime.MinValue) + sqlQuery = sqlQuery.Where(x => x.y.Status == 0 ? x.x.ExpectedCloseDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.x.ExpectedCloseDate <= _tenantUtil.DateTimeToUtc(toDate) + : x.x.ActualCloseDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.x.ActualCloseDate <= _tenantUtil.DateTimeToUtc(toDate)); + + } + + + if (orderBy != null && Enum.IsDefined(typeof(DealSortedByType), orderBy.SortedBy)) + switch ((DealSortedByType)orderBy.SortedBy) + { + case DealSortedByType.Title: + { + sqlQuery = sqlQuery.OrderBy("x.x.Title", orderBy.IsAsc); + + break; + } + case DealSortedByType.BidValue: + { + sqlQuery = sqlQuery.OrderBy("x.x.BidValue", orderBy.IsAsc); + + break; + } + case DealSortedByType.Responsible: + { + if (orderBy.IsAsc) + { + sqlQuery = sqlQuery.OrderBy(x => x.x.ResponsibleId) + .ThenBy(x => x.y.SortOrder) + .ThenBy(x => x.x.ContactId) + .ThenByDescending(x => x.x.ActualCloseDate) + .ThenBy(x => x.x.ExpectedCloseDate) + .ThenBy(x => x.x.Title); + } + else + { + sqlQuery = sqlQuery.OrderByDescending(x => x.x.ResponsibleId) + .OrderByDescending(x => x.y.SortOrder) + .ThenBy(x => x.x.ContactId) + .ThenByDescending(x => x.x.ActualCloseDate) + .ThenBy(x => x.x.ExpectedCloseDate) + .ThenBy(x => x.x.Title); + + } + + break; + } + case DealSortedByType.Stage: + { + if (orderBy.IsAsc) + { + sqlQuery = sqlQuery.OrderBy(x => x.y.SortOrder) + .ThenBy(x => x.x.ContactId) + .ThenByDescending(x => x.x.ActualCloseDate) + .ThenBy(x => x.x.ExpectedCloseDate) + .ThenBy(x => x.x.Title); + } + else + { + sqlQuery = sqlQuery.OrderByDescending(x => x.y.SortOrder) + .ThenBy(x => x.x.ContactId) + .ThenByDescending(x => x.x.ActualCloseDate) + .ThenBy(x => x.x.ExpectedCloseDate) + .ThenBy(x => x.x.Title); + + } + + break; + + } + case DealSortedByType.DateAndTime: + { + sqlQuery.OrderBy("x.x.close_date", orderBy.IsAsc); + + break; + + } + default: + throw new ArgumentException(); + } + else + { + sqlQuery = sqlQuery.OrderBy(x => x.y.SortOrder) + .ThenBy(x => x.x.ContactId) + .ThenBy(x => x.x.Title); + } + + return sqlQuery.Select(x => x.x); + } + + public int GetDealsCount(String searchText, + Guid responsibleID, + int milestoneID, + IEnumerable tags, + int contactID, + DealMilestoneStatus? stageType, + bool? contactAlsoIsParticipant, + DateTime fromDate, + DateTime toDate) + { + var cacheKey = TenantID.ToString(CultureInfo.InvariantCulture) + + "deals" + + _securityContext.CurrentAccount.ID.ToString() + + searchText + + responsibleID + + milestoneID + + contactID; + + if (tags != null) + cacheKey += String.Join("", tags.ToArray()); + + if (stageType.HasValue) + cacheKey += stageType.Value; + + if (contactAlsoIsParticipant.HasValue) + cacheKey += contactAlsoIsParticipant.Value; + + if (fromDate != DateTime.MinValue) + cacheKey += fromDate.ToString(); + + if (toDate != DateTime.MinValue) + cacheKey += toDate.ToString(); + + var fromCache = _cache.Get(cacheKey); + + if (fromCache != null) return Convert.ToInt32(fromCache); + + var withParams = !(String.IsNullOrEmpty(searchText) && + responsibleID == Guid.Empty && + milestoneID <= 0 && + (tags == null || !tags.Any()) && + contactID <= 0 && + stageType == null && + contactAlsoIsParticipant == null && + fromDate == DateTime.MinValue && + toDate == DateTime.MinValue); + + ICollection exceptIDs = _crmSecurity.GetPrivateItems(typeof(Deal)).ToList(); + + int result; + + if (withParams) + { + var sqlQuery = GetDbDealByFilters(exceptIDs, searchText, responsibleID, milestoneID, tags, + contactID, stageType, contactAlsoIsParticipant, + fromDate, + toDate, null); + + if (sqlQuery == null) + { + result = 0; + } + else + { + result = sqlQuery.Count(); + } + } + else + { + + var countWithoutPrivate = Query(CrmDbContext.Deals).Count(); + var privateCount = exceptIDs.Count; + + if (privateCount > countWithoutPrivate) + { + _logger.Error("Private deals count more than all deals"); + + privateCount = 0; + } + + result = countWithoutPrivate - privateCount; + } + if (result > 0) + { + _cache.Insert(cacheKey, result, TimeSpan.FromSeconds(30)); + } + + return result; + } + + public List GetDeals( + String searchText, + Guid responsibleID, + int milestoneID, + IEnumerable tags, + int contactID, + DealMilestoneStatus? stageType, + bool? contactAlsoIsParticipant, + DateTime fromDate, + DateTime toDate, + int from, + int count, + OrderBy orderBy) + { + + if (_crmSecurity.IsAdmin) + return GetCrudeDeals(searchText, + responsibleID, + milestoneID, + tags, + contactID, + stageType, + contactAlsoIsParticipant, + fromDate, + toDate, + from, + count, + orderBy); + + var crudeDeals = GetCrudeDeals(searchText, + responsibleID, + milestoneID, + tags, + contactID, + stageType, + contactAlsoIsParticipant, + fromDate, + toDate, + 0, + from + count, + orderBy); + + if (crudeDeals.Count == 0) return crudeDeals; + + if (crudeDeals.Count < from + count) return crudeDeals.FindAll(_crmSecurity.CanAccessTo).Skip(from).ToList(); + + var result = crudeDeals.FindAll(_crmSecurity.CanAccessTo); + + if (result.Count == crudeDeals.Count) return result.Skip(from).ToList(); + + var localCount = count; + var localFrom = from + count; + + while (true) + { + crudeDeals = GetCrudeDeals(searchText, + responsibleID, + milestoneID, + tags, + contactID, + stageType, + contactAlsoIsParticipant, + fromDate, + toDate, + localFrom, + localCount, + orderBy); + + if (crudeDeals.Count == 0) break; + + result.AddRange(crudeDeals.Where(_crmSecurity.CanAccessTo)); + + if ((result.Count >= count + from) || (crudeDeals.Count < localCount)) break; + + localFrom += localCount; + localCount = localCount * 2; + } + + return result.Skip(from).Take(count).ToList(); + } + + + private List GetCrudeDeals( + String searchText, + Guid responsibleID, + int milestoneID, + IEnumerable tags, + int contactID, + DealMilestoneStatus? stageType, + bool? contactAlsoIsParticipant, + DateTime fromDate, + DateTime toDate, + int from, + int count, + OrderBy orderBy) + { + var withParams = !(String.IsNullOrEmpty(searchText) && + responsibleID == Guid.Empty && + milestoneID <= 0 && + (tags == null || !tags.Any()) && + contactID <= 0 && + stageType == null && + contactAlsoIsParticipant == null && + fromDate == DateTime.MinValue && + toDate == DateTime.MinValue); + + var sqlQuery = GetDbDealByFilters(new List(), + searchText, + responsibleID, + milestoneID, + tags, + contactID, + stageType, + contactAlsoIsParticipant, + fromDate, + toDate, + orderBy); + + if (withParams && sqlQuery == null) + { + return new List(); + } + + if (0 < from && from < int.MaxValue) + { + sqlQuery = sqlQuery.Skip(from); + } + + if (0 < count && count < int.MaxValue) + { + sqlQuery = sqlQuery.Take(count); + } + + return _mapper.Map, List>(sqlQuery.ToList()); + } + + public List GetDealsByContactID(int contactID) + { + + return GetDeals(String.Empty, Guid.Empty, 0, null, contactID, null, true, DateTime.MinValue, + DateTime.MinValue, 0, 0, new OrderBy(DealSortedByType.Title, true)); + + } + + public List GetDealsByPrefix(String prefix, int from, int count, int contactId = 0, bool internalSearch = true) + { + if (count == 0) + throw new ArgumentException(); + + prefix = prefix.Trim(); + + var keywords = prefix.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToArray(); + + var sqlQuery = Query(CrmDbContext.Deals); + + if (keywords.Length == 1) + { + sqlQuery = sqlQuery.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Title, keywords[0])); + } + else + { + foreach (var k in keywords) + { + sqlQuery = sqlQuery.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Title, k)); + } + } + + if (0 < from && from < int.MaxValue) sqlQuery = sqlQuery.Skip(from); + if (0 < count && count < int.MaxValue) sqlQuery = sqlQuery.Take(count); + + if (contactId > 0) + { + var ids = GetRelativeToEntity(contactId, EntityType.Opportunity, null); + + if (internalSearch) + sqlQuery = sqlQuery.Where(x => x.ContactId == contactId || ids.Contains(x.Id)); + else + sqlQuery = sqlQuery.Where(x => x.ContactId != contactId && !ids.Contains(x.Id)); + } + + sqlQuery = sqlQuery.OrderBy(x => x.Title); + + var dbDeals = sqlQuery.ToList(); + + return _mapper.Map, List>(dbDeals).FindAll(_crmSecurity.CanAccessTo); + } + + public Deal DeleteDeal(int id) + { + if (id <= 0) return null; + + var deal = GetByID(id); + + if (deal == null) return null; + + _crmSecurity.DemandDelete(deal); + + var dbEntity = CrmDbContext.Deals.Find(id); + + _factoryIndexer.Delete(dbEntity); + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "deals.*")); + + DeleteBatchDealsExecute(new List() { deal }); + + return deal; + } + + public List DeleteBatchDeals(int[] dealID) + { + var deals = GetDeals(dealID).FindAll(_crmSecurity.CanDelete).ToList(); + + if (!deals.Any()) return deals; + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "deals.*")); + + DeleteBatchDealsExecute(deals); + return deals; + } + + public List DeleteBatchDeals(List deals) + { + deals = deals.FindAll(_crmSecurity.CanDelete).ToList(); + + if (!deals.Any()) return deals; + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "deals.*")); + + DeleteBatchDealsExecute(deals); + + return deals; + } + + private void DeleteBatchDealsExecute(List deals) + { + if (deals == null || !deals.Any()) return; + + var dealID = deals.Select(x => x.ID).ToArray(); + + object[] filesIDs; + + var tagdao = _filesIntegration.DaoFactory.GetTagDao(); + + var tagNames = Query(CrmDbContext.RelationshipEvent) + .Where(x => x.HaveFiles && dealID.Contains(x.EntityId) && x.EntityType == EntityType.Opportunity) + .Select(x => string.Format("RelationshipEvent_{0}", x.Id)) + .ToArray(); + + filesIDs = tagdao.GetTags(tagNames, TagType.System).Where(t => t.EntryType == FileEntryType.File).Select(t => t.EntryId).ToArray(); + + var tx = CrmDbContext.Database.BeginTransaction(); + + + CrmDbContext.RemoveRange(Query(CrmDbContext.FieldValue) + .AsNoTracking() + .Where(x => dealID.Contains(x.EntityId) && x.EntityType == EntityType.Opportunity) + ); + + CrmDbContext.RemoveRange(CrmDbContext.EntityContact + .AsNoTracking() + .Where(x => dealID.Contains(x.EntityId) && x.EntityType == EntityType.Opportunity)); + + CrmDbContext.RemoveRange(Query(CrmDbContext.RelationshipEvent) + .AsNoTracking() + .Where(x => dealID.Contains(x.EntityId) && x.EntityType == EntityType.Opportunity)); + + CrmDbContext.RemoveRange(Query(CrmDbContext.Tasks) + .AsNoTracking() + .Where(x => dealID.Contains(x.EntityId) && x.EntityType == EntityType.Opportunity)); + + CrmDbContext.RemoveRange(CrmDbContext.EntityTags + .AsNoTracking() + .Where(x => dealID.Contains(x.EntityId) && x.EntityType == EntityType.Opportunity)); + + CrmDbContext.RemoveRange(Query(CrmDbContext.Deals) + .AsNoTracking() + .Where(x => dealID.Contains(x.Id))); + + tx.Commit(); + + deals.ForEach(deal => _authorizationManager.RemoveAllAces(deal)); + + var filedao = _filesIntegration.DaoFactory.GetFileDao(); + + foreach (var filesID in filesIDs) + { + filedao.DeleteFile(Convert.ToInt32(filesID)); + } + + } + + public void ReassignDealsResponsible(Guid fromUserId, Guid toUserId) + { + var deals = GetDeals(String.Empty, + fromUserId, + 0, + null, + 0, + DealMilestoneStatus.Open, + null, + DateTime.MinValue, + DateTime.MinValue, + 0, + 0, + null); + + foreach (var deal in deals) + { + deal.ResponsibleID = toUserId; + + EditDeal(deal); + + var responsibles = _crmSecurity.GetAccessSubjectGuidsTo(deal); + + if (!responsibles.Any()) continue; + + responsibles.Remove(fromUserId); + responsibles.Add(toUserId); + + _crmSecurity.SetAccessTo(deal, responsibles.Distinct().ToList()); + } + } + + /// + /// Test method + /// + /// + /// + public void SetDealCreationDate(int id, DateTime creationDate) + { + var dbEntity = CrmDbContext.Deals.Find(id); + + if (dbEntity.TenantId != TenantID) + throw new ArgumentException(); + + dbEntity.CreateOn = _tenantUtil.DateTimeToUtc(creationDate); + + CrmDbContext.SaveChanges(); + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "deals.*")); + } + + /// + /// Test method + /// + /// + /// + public void SetDealLastModifedDate(int id, DateTime lastModifedDate) + { + var dbEntity = CrmDbContext.Deals.Find(id); + + if (dbEntity.TenantId != TenantID) + throw new ArgumentException(); + + dbEntity.LastModifedOn = _tenantUtil.DateTimeToUtc(lastModifedDate); + + CrmDbContext.SaveChanges(); + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "deals.*")); + + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Dao/DealMilestoneDao.cs b/products/ASC.CRM/Server/Core/Dao/DealMilestoneDao.cs new file mode 100644 index 00000000000..214d1bb1cab --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/DealMilestoneDao.cs @@ -0,0 +1,230 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Entities; +using ASC.CRM.Resources; + +using AutoMapper; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +#endregion + +namespace ASC.CRM.Core.Dao +{ + [Scope] + public class DealMilestoneDao : AbstractDao + { + + public DealMilestoneDao(DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + IOptionsMonitor logger, + ICache ascCache, + IMapper mapper) : + base(dbContextManager, + tenantManager, + securityContext, + logger, + ascCache, + mapper) + { + + + + + } + + public void Reorder(int[] ids) + { + using var tx = CrmDbContext.Database.BeginTransaction(); + + for (int index = 0; index < ids.Length; index++) + { + var itemToUpdate = CrmDbContext.DealMilestones.Find(ids[index]); + + if (itemToUpdate.TenantId != TenantID) continue; + + itemToUpdate.SortOrder = index; + + CrmDbContext.Update(itemToUpdate); + } + + CrmDbContext.SaveChanges(); + + tx.Commit(); + } + + public int GetCount() + { + return Query(CrmDbContext.DealMilestones).Count(); + } + + + public Dictionary GetRelativeItemsCount() + { + return Query(CrmDbContext.DealMilestones) + .Join(CrmDbContext.Deals, + x => x.Id, + x => x.DealMilestoneId, + (x, y) => new { x = x, y = y }) + .GroupBy(x => x.x.Id) + .Select(x => new { Id = x.Key, Count = x.Count() }) + .ToDictionary(x => x.Id, y => y.Count); + } + + public int GetRelativeItemsCount(int id) + { + return Query(CrmDbContext.Deals) + .Count(x => x.DealMilestoneId == id); + } + + public int Create(DealMilestone item) + { + if (String.IsNullOrEmpty(item.Title) || String.IsNullOrEmpty(item.Color)) + throw new ArgumentException(); + + if (item.SortOrder == 0) + item.SortOrder = Query(CrmDbContext.DealMilestones).Select(x => x.SortOrder).Max() + 1; + + var dbEntity = new DbDealMilestone + { + Title = item.Title, + Description = item.Description, + Color = item.Color, + Probability = item.Probability, + Status = item.Status, + SortOrder = item.SortOrder, + TenantId = TenantID + }; + + CrmDbContext.DealMilestones.Add(dbEntity); + CrmDbContext.SaveChanges(); + + return dbEntity.Id; + } + + public void ChangeColor(int id, String newColor) + { + var dbEntity = CrmDbContext.DealMilestones.Find(id); + + if (dbEntity.TenantId != TenantID) + throw new ArgumentException(); + + dbEntity.Color = newColor; + + CrmDbContext.SaveChanges(); + } + + public void Edit(DealMilestone item) + { + if (HaveContactLink(item.ID)) + throw new ArgumentException(String.Format("{0}. {1}.", CRMErrorsResource.BasicCannotBeEdited, CRMErrorsResource.DealMilestoneHasRelatedDeals)); + + var dbEntity = CrmDbContext.DealMilestones.Find(item.ID); + + if (dbEntity.TenantId != TenantID) + throw new ArgumentException(); + + dbEntity.Title = item.Title; + dbEntity.Description = item.Description; + dbEntity.Color = item.Color; + dbEntity.Probability = item.Probability; + dbEntity.Status = item.Status; + + CrmDbContext.SaveChanges(); + } + + public bool HaveContactLink(int id) + { + return Query(CrmDbContext.Deals) + .Any(x => x.DealMilestoneId == id); + } + + public void Delete(int id) + { + if (HaveContactLink(id)) + throw new ArgumentException(String.Format("{0}. {1}.", CRMErrorsResource.BasicCannotBeDeleted, CRMErrorsResource.DealMilestoneHasRelatedDeals)); + + var dbEntity = CrmDbContext.DealMilestones.Find(id); + + if (dbEntity.TenantId != TenantID) + throw new ArgumentException(); + + CrmDbContext.DealMilestones.Remove(dbEntity); + + CrmDbContext.SaveChanges(); + } + + public DealMilestone GetByID(int id) + { + var dbEntity = CrmDbContext.DealMilestones.Find(id); + + if (dbEntity.TenantId != TenantID) + throw new ArgumentException(); + + return _mapper.Map(dbEntity); + } + + public Boolean IsExist(int id) + { + return Query(CrmDbContext.DealMilestones).Any(x => x.Id == id); + } + + public List GetAll(int[] id) + { + var result = Query(CrmDbContext.DealMilestones) + .AsNoTracking() + .OrderBy(x => x.SortOrder) + .Where(x => id.Contains(x.Id)).ToList(); + + return _mapper.Map, List>(result); + } + + public List GetAll() + { + var result = Query(CrmDbContext.DealMilestones) + .AsNoTracking() + .OrderBy(x => x.SortOrder) + .ToList(); + + return _mapper.Map, List>(result); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Dao/FileDao.cs b/products/ASC.CRM/Server/Core/Dao/FileDao.cs new file mode 100644 index 00000000000..aa5c6821847 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/FileDao.cs @@ -0,0 +1,112 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.CRM.Core.EF; +using ASC.Files.Core; +using ASC.Web.Files.Api; + +using AutoMapper; + +using Microsoft.Extensions.Options; + +namespace ASC.CRM.Core.Dao +{ + [Scope] + public class FileDao : AbstractDao + { + private FilesIntegration _filesIntegration; + public FileDao(FilesIntegration filesIntegration, + DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + IOptionsMonitor logger, + ICache ascCache, + IMapper mapper) : + base(dbContextManager, + tenantManager, + securityContext, + logger, + ascCache, + mapper) + { + _filesIntegration = filesIntegration; + } + + + public File GetFile(int id, int version) + { + var dao = _filesIntegration.DaoFactory.GetFileDao(); + + var file = 0 < version ? dao.GetFile(id, version) : dao.GetFile(id); + + return file; + } + + public void DeleteFile(int id) + { + var dao = _filesIntegration.DaoFactory.GetFileDao(); + + dao.DeleteFile(id); + } + + public int GetRoot() + { + return _filesIntegration.RegisterBunch("crm", "crm_common", ""); + } + + public int GetMy() + { + return _filesIntegration.RegisterBunch("files", "my", _securityContext.CurrentAccount.ID.ToString()); + } + + public File SaveFile(File file, System.IO.Stream stream) + { + var dao = _filesIntegration.DaoFactory.GetFileDao(); + + return dao.SaveFile(file, stream); + } + + public List GetEventsByFile(int id) + { + var tagdao = _filesIntegration.DaoFactory.GetTagDao(); + + var tags = tagdao.GetTags(id, FileEntryType.File, TagType.System).ToList().FindAll(tag => tag.TagName.StartsWith("RelationshipEvent_")); + + return tags.Select(item => Convert.ToInt32(item.TagName.Split(new[] { '_' })[1])).ToList(); + } + + } + +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Dao/InvoiceDao.cs b/products/ASC.CRM/Server/Core/Dao/InvoiceDao.cs new file mode 100644 index 00000000000..e054367fdf6 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/InvoiceDao.cs @@ -0,0 +1,893 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text.Json; +using System.Text.RegularExpressions; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.Core.Common.Settings; +using ASC.Core.Tenants; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.ElasticSearch; +using ASC.Web.CRM.Classes; +using ASC.Web.CRM.Core.Search; + +using AutoMapper; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +using SecurityContext = ASC.Core.SecurityContext; + +namespace ASC.CRM.Core.Dao +{ + [Scope] + public class InvoiceDao : AbstractDao + { + public List> invoiceStatusMap = new List>() + { + new KeyValuePair(InvoiceStatus.Draft, InvoiceStatus.Sent), + new KeyValuePair(InvoiceStatus.Sent, InvoiceStatus.Paid), + new KeyValuePair(InvoiceStatus.Sent, InvoiceStatus.Rejected), + new KeyValuePair(InvoiceStatus.Rejected, InvoiceStatus.Draft), + new KeyValuePair(InvoiceStatus.Paid, InvoiceStatus.Sent)//Bug 23450 + }; + + private readonly InvoiceSetting _invoiceSetting; + private readonly InvoiceFormattedData _invoiceFormattedData; + private readonly SettingsManager _settingsManager; + private readonly FactoryIndexerInvoice _factoryIndexer; + private readonly TenantUtil _tenantUtil; + private readonly CrmSecurity _crmSecurity; + + public InvoiceDao( + DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + FactoryIndexerInvoice factoryIndexer, + IOptionsMonitor logger, + ICache ascCache, + SettingsManager settingsManager, + InvoiceSetting invoiceSetting, + InvoiceFormattedData invoiceFormattedData, + CrmSecurity crmSecurity, + TenantUtil tenantUtil, + IMapper mapper) + : base(dbContextManager, + tenantManager, + securityContext, + logger, + ascCache, + mapper) + { + _factoryIndexer = factoryIndexer; + _settingsManager = settingsManager; + _invoiceSetting = invoiceSetting; + _invoiceFormattedData = invoiceFormattedData; + _crmSecurity = crmSecurity; + _tenantUtil = tenantUtil; + } + + + public Boolean IsExist(int invoiceID) + { + return IsExistFromDb(invoiceID); + } + + public Boolean IsExistFromDb(int id) + { + return Query(CrmDbContext.Invoices).Where(x => x.Id == id).Any(); + } + + public Boolean IsExist(string number) + { + return IsExistFromDb(number); + } + + public Boolean IsExistFromDb(string number) + { + return Query(CrmDbContext.Invoices) + .Where(x => x.Number == number) + .Any(); + } + + public List GetAll() + { + var dbInvoices = Query(CrmDbContext.Invoices) + .ToList(); + + return _mapper.Map, List>(dbInvoices); + } + + public List GetByID(int[] ids) + { + var dbInvoices = Query(CrmDbContext.Invoices) + .AsNoTracking() + .Where(x => ids.Contains(x.Id)) + .ToList(); + + return _mapper.Map, List>(dbInvoices); + } + + public Invoice GetByID(int id) + { + return GetByIDFromDb(id); + } + + public Invoice GetByIDFromDb(int id) + { + var dbEntity = CrmDbContext.Invoices.Find(id); + + if (dbEntity.TenantId != TenantID) + throw new ArgumentException(); + + return _mapper.Map(dbEntity); + } + + public Invoice GetByNumber(string number) + { + var dbEntity = Query(CrmDbContext.Invoices).FirstOrDefault(x => x.Number == number); + + return _mapper.Map(dbEntity); + } + + public Invoice GetByFileId(Int32 fileID) + { + return _mapper.Map(Query(CrmDbContext.Invoices) + .FirstOrDefault(x => x.FileId == fileID)); + } + + public List GetInvoices( + String searchText, + InvoiceStatus? status, + DateTime issueDateFrom, + DateTime issueDateTo, + DateTime dueDateFrom, + DateTime dueDateTo, + EntityType entityType, + int entityID, + String currency, + int from, + int count, + OrderBy orderBy) + { + + if (_crmSecurity.IsAdmin) + return GetCrudeInvoices( + searchText, + status, + issueDateFrom, + issueDateTo, + dueDateFrom, + dueDateTo, + entityType, + entityID, + currency, + from, + count, + orderBy); + + + var crudeInvoices = GetCrudeInvoices( + searchText, + status, + issueDateFrom, + issueDateTo, + dueDateFrom, + dueDateTo, + entityType, + entityID, + currency, + 0, + from + count, + orderBy); + + if (crudeInvoices.Count == 0) return crudeInvoices; + + if (crudeInvoices.Count < from + count) return _crmSecurity.FilterRead(crudeInvoices).Skip(from).ToList(); + + var result = _crmSecurity.FilterRead(crudeInvoices).ToList(); + + if (result.Count == crudeInvoices.Count) return result.Skip(from).ToList(); + + var localCount = count; + var localFrom = from + count; + + while (true) + { + crudeInvoices = GetCrudeInvoices( + searchText, + status, + issueDateFrom, + issueDateTo, + dueDateFrom, + dueDateTo, + entityType, + entityID, + currency, + localFrom, + localCount, + orderBy); + + if (crudeInvoices.Count == 0) break; + + result.AddRange(_crmSecurity.FilterRead(crudeInvoices)); + + if ((result.Count >= count + from) || (crudeInvoices.Count < localCount)) break; + + localFrom += localCount; + localCount = localCount * 2; + } + + return result.Skip(from).Take(count).ToList(); + } + + + public List GetCrudeInvoices( + String searchText, + InvoiceStatus? status, + DateTime issueDateFrom, + DateTime issueDateTo, + DateTime dueDateFrom, + DateTime dueDateTo, + EntityType entityType, + int entityID, + String currency, + int from, + int count, + OrderBy orderBy) + { + var withParams = hasParams(searchText, status, issueDateFrom, issueDateTo, dueDateFrom, dueDateTo, entityType, entityID, currency); + + var sqlQuery = GetDbInvoceByFilters(new List(), searchText, status, issueDateFrom, issueDateTo, dueDateFrom, dueDateTo, entityType, entityID, currency); + + if (withParams && sqlQuery == null) + return new List(); + + if (0 < from && from < int.MaxValue) sqlQuery = sqlQuery.Skip(from); + if (0 < count && count < int.MaxValue) sqlQuery = sqlQuery.Take(count); + + if (orderBy != null && Enum.IsDefined(typeof(InvoiceSortedByType), orderBy.SortedBy)) + { + switch ((InvoiceSortedByType)orderBy.SortedBy) + { + case InvoiceSortedByType.Number: + sqlQuery = sqlQuery.OrderBy("Number", orderBy.IsAsc); + break; + case InvoiceSortedByType.Status: + sqlQuery = sqlQuery.OrderBy("Status", orderBy.IsAsc); + break; + case InvoiceSortedByType.DueDate: + sqlQuery = sqlQuery.OrderBy("DueDate", orderBy.IsAsc); + break; + case InvoiceSortedByType.IssueDate: + sqlQuery = sqlQuery.OrderBy("IssueDate", orderBy.IsAsc); + break; + case InvoiceSortedByType.Contact: + + var subSqlQuery = sqlQuery.GroupJoin(CrmDbContext.Contacts, + x => x.ContactId, + y => y.Id, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { x.x, y }); + + if (orderBy.IsAsc) + { + subSqlQuery = subSqlQuery.OrderBy(x => x.y != null ? x.y.DisplayName : "") + .ThenBy(x => x.x.Number); + } + else + { + subSqlQuery = subSqlQuery.OrderByDescending(x => x.y != null ? x.y.DisplayName : "") + .ThenBy(x => x.x.Number); + + } + + sqlQuery = subSqlQuery.Select(x => x.x); + + break; + default: + sqlQuery = sqlQuery.OrderBy("Number", orderBy.IsAsc); + + break; + } + } + else + { + sqlQuery = sqlQuery.OrderBy("Number", orderBy.IsAsc); + } + + return _mapper.Map, List>(sqlQuery.ToList()); + } + + public int GetAllInvoicesCount() + { + return Query(CrmDbContext.Invoices).Count(); + } + + + public int GetInvoicesCount() + { + return GetInvoicesCount(String.Empty, null, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, EntityType.Any, 0, null); + } + + public int GetInvoicesCount( + String searchText, + InvoiceStatus? status, + DateTime issueDateFrom, + DateTime issueDateTo, + DateTime dueDateFrom, + DateTime dueDateTo, + EntityType entityType, + int entityID, + String currency) + { + var cacheKey = TenantID.ToString(CultureInfo.InvariantCulture) + + "invoice" + + _securityContext.CurrentAccount.ID.ToString() + + searchText; + + var fromCache = _cache.Get(cacheKey); + + if (fromCache != null) return Convert.ToInt32(fromCache); + + var withParams = hasParams(searchText, status, issueDateFrom, issueDateTo, dueDateFrom, dueDateTo, entityType, entityID, currency); + + var exceptIDs = _crmSecurity.GetPrivateItems(typeof(Invoice)).ToList(); + + int result; + + if (withParams) + { + var sqlQuery = GetDbInvoceByFilters(exceptIDs, searchText, status, issueDateFrom, issueDateTo, dueDateFrom, dueDateTo, entityType, entityID, currency); + + result = sqlQuery != null ? sqlQuery.Count() : 0; + + } + else + { + var countWithoutPrivate = Query(CrmDbContext.Invoices).Count(); + + var privateCount = exceptIDs.Count; + + if (privateCount > countWithoutPrivate) + { + _logger.ErrorFormat(@"Private invoice count more than all cases. Tenant: {0}. CurrentAccount: {1}", + TenantID, + _securityContext.CurrentAccount.ID); + + privateCount = 0; + } + + result = countWithoutPrivate - privateCount; + } + + if (result > 0) + { + _cache.Insert(cacheKey, result, TimeSpan.FromSeconds(30)); + } + return result; + } + + public List GetInvoices(int[] ids) + { + if (ids == null || !ids.Any()) return new List(); + + var dbInvoices = Query(CrmDbContext.Invoices) + .AsNoTracking() + .Where(x => ids.Contains(x.Id)) + .ToList(); + + return _mapper.Map, List>(dbInvoices) + .FindAll(_crmSecurity.CanAccessTo); + } + + public List GetEntityInvoices(EntityType entityType, int entityID) + { + var result = new List(); + + if (entityID <= 0) + return result; + + if (entityType == EntityType.Opportunity) + { + + var sqlQuery = Query(CrmDbContext.Invoices) + .AsNoTracking() + .Where(x => x.EntityId == entityID && x.EntityType == entityType) + .ToList(); + + return _mapper.Map, List>(sqlQuery) + .FindAll(_crmSecurity.CanAccessTo); + + } + + if (entityType == EntityType.Contact || entityType == EntityType.Person || entityType == EntityType.Company) + return GetContactInvoices(entityID); + + return result; + } + + public List GetContactInvoices(int contactID) + { + var result = new List(); + + if (contactID <= 0) + return result; + + var dbInvoices = Query(CrmDbContext.Invoices) + .AsNoTracking() + .Where(x => x.ContactId == contactID) + .ToList(); + + return _mapper.Map, List>(dbInvoices) + .FindAll(_crmSecurity.CanAccessTo); + } + + public string GetNewInvoicesNumber() + { + var settings = GetSettings(); + + if (!settings.Autogenerated) + return string.Empty; + + var stringNumber = Query(CrmDbContext.Invoices).OrderByDescending(x => x.Id).Select(x => x.Number).ToString(); + + if (string.IsNullOrEmpty(stringNumber) || !stringNumber.StartsWith(settings.Prefix)) + return string.Concat(settings.Prefix, settings.Number); + + if (!string.IsNullOrEmpty(settings.Prefix)) + stringNumber = stringNumber.Replace(settings.Prefix, string.Empty); + + int intNumber; + if (!Int32.TryParse(stringNumber, out intNumber)) + intNumber = 0; + + var format = string.Empty; + for (var i = 0; i < settings.Number.Length; i++) + { + format += "0"; + } + + var nextNumber = intNumber + 1; + + return settings.Prefix + (string.IsNullOrEmpty(format) ? nextNumber.ToString(CultureInfo.InvariantCulture) : nextNumber.ToString(format)); + } + + public InvoiceSetting GetSettings() + { + var tenantSettings = _settingsManager.Load(); + + return tenantSettings.InvoiceSetting ?? _invoiceSetting.DefaultSettings; + } + + public int SaveOrUpdateInvoice(Invoice invoice) + { + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "invoice.*")); + + var result = SaveOrUpdateInvoiceInDb(invoice); + + + + _factoryIndexer.Index(Query(CrmDbContext.Invoices).Where(x => x.Id == invoice.ID).Single()); + + return result; + } + + private int SaveOrUpdateInvoiceInDb(Invoice invoice) + { + if (String.IsNullOrEmpty(invoice.Number) || + invoice.IssueDate == DateTime.MinValue || + invoice.ContactID <= 0 || + invoice.DueDate == DateTime.MinValue || + String.IsNullOrEmpty(invoice.Currency) || + invoice.ExchangeRate <= 0 || + String.IsNullOrEmpty(invoice.Terms)) + throw new ArgumentException(); + + var dbEntity = new DbInvoice + { + Id = invoice.ID, + Status = invoice.Status, + Number = invoice.Number, + IssueDate = _tenantUtil.DateTimeToUtc(invoice.IssueDate), + TemplateType = invoice.TemplateType, + ContactId = invoice.ContactID, + ConsigneeId = invoice.ConsigneeID, + EntityType = invoice.EntityType, + EntityId = invoice.EntityID, + DueDate = _tenantUtil.DateTimeToUtc(invoice.DueDate), + Language = invoice.Language, + Currency = invoice.Currency, + ExchangeRate = invoice.ExchangeRate, + PurchaseOrderNumber = invoice.PurchaseOrderNumber, + Terms = invoice.Terms, + Description = invoice.Description, + JsonData = invoice.JsonData, + FileId = invoice.FileID, + CreateOn = invoice.CreateOn == DateTime.MinValue ? DateTime.UtcNow : invoice.CreateOn, + CreateBy = _securityContext.CurrentAccount.ID, + LastModifedOn = DateTime.UtcNow, + LastModifedBy = _securityContext.CurrentAccount.ID, + TenantId = TenantID + }; + + dbEntity.PurchaseOrderNumber = !String.IsNullOrEmpty(invoice.PurchaseOrderNumber) ? invoice.PurchaseOrderNumber : String.Empty; + + CrmDbContext.Update(dbEntity); + CrmDbContext.SaveChanges(); + + return dbEntity.Id; + } + + public Invoice UpdateInvoiceStatus(int invoiceid, InvoiceStatus status) + { + return UpdateInvoiceStatusInDb(invoiceid, status); + } + + public List UpdateInvoiceBatchStatus(int[] invoiceids, InvoiceStatus status) + { + if (invoiceids == null || !invoiceids.Any()) + throw new ArgumentException(); + + var invoices = new List(); + + foreach (var id in invoiceids) + { + var inv = UpdateInvoiceStatusInDb(id, status); + if (inv != null) + { + invoices.Add(inv); + } + } + return invoices; + } + + private Invoice UpdateInvoiceStatusInDb(int invoiceid, InvoiceStatus status) + { + var invoice = GetByIDFromDb(invoiceid); + if (invoice == null) + { + _logger.Error("Invoice not found"); + + return null; + } + _crmSecurity.DemandAccessTo(invoice); + + if (!invoiceStatusMap.Contains(new KeyValuePair(invoice.Status, status))) + { + _logger.ErrorFormat("Status for invoice with ID={0} can't be changed. Return without changes", invoiceid); + + return invoice; + } + + var itemToUpdate = Query(CrmDbContext.Invoices).FirstOrDefault(x => x.Id == invoiceid); + + itemToUpdate.Status = status; + itemToUpdate.LastModifedOn = DateTime.UtcNow; + itemToUpdate.LastModifedBy = _securityContext.CurrentAccount.ID; + + CrmDbContext.Update(itemToUpdate); + + CrmDbContext.SaveChanges(); + + invoice.Status = status; + + return invoice; + + } + + public int UpdateInvoiceJsonData(int invoiceId, string jsonData) + { + return UpdateInvoiceJsonDataInDb(invoiceId, jsonData); + } + + private int UpdateInvoiceJsonDataInDb(int invoiceId, string jsonData) + { + var itemToUpdate = Query(CrmDbContext.Invoices).FirstOrDefault(x => x.Id == invoiceId); + + itemToUpdate.JsonData = jsonData; + itemToUpdate.LastModifedOn = DateTime.UtcNow; + itemToUpdate.LastModifedBy = _securityContext.CurrentAccount.ID; + + CrmDbContext.Update(itemToUpdate); + CrmDbContext.SaveChanges(); + + return invoiceId; + } + + public void UpdateInvoiceJsonData(Invoice invoice, int billingAddressID, int deliveryAddressID) + { + var jsonData = _invoiceFormattedData.GetData(invoice, billingAddressID, deliveryAddressID); + if (jsonData.LogoBase64Id != 0) + { + jsonData.LogoBase64 = null; + } + + invoice.JsonData = JsonSerializer.Serialize(jsonData); + + UpdateInvoiceJsonData(invoice.ID, invoice.JsonData); + } + + public void UpdateInvoiceJsonDataAfterLinesUpdated(Invoice invoice) + { + var jsonData = _invoiceFormattedData.GetDataAfterLinesUpdated(invoice); + if (jsonData.LogoBase64Id != 0) + { + jsonData.LogoBase64 = null; + } + UpdateInvoiceJsonData(invoice.ID, invoice.JsonData); + } + + public int UpdateInvoiceFileID(int invoiceId, int fileId) + { + return UpdateInvoiceFileIDInDb(invoiceId, fileId); + } + + private int UpdateInvoiceFileIDInDb(int invoiceId, int fileId) + { + + var sqlToUpdate = Query(CrmDbContext.Invoices).FirstOrDefault(x => x.Id == invoiceId); + + sqlToUpdate.FileId = fileId; + sqlToUpdate.LastModifedOn = DateTime.UtcNow; + sqlToUpdate.LastModifedBy = _securityContext.CurrentAccount.ID; + + CrmDbContext.Update(sqlToUpdate); + CrmDbContext.SaveChanges(); + + return invoiceId; + } + + public InvoiceSetting SaveInvoiceSettings(InvoiceSetting invoiceSetting) + { + var tenantSettings = _settingsManager.Load(); + tenantSettings.InvoiceSetting = invoiceSetting; + + _settingsManager.Save(tenantSettings); + + return tenantSettings.InvoiceSetting; + } + + public Invoice DeleteInvoice(int invoiceID) + { + if (invoiceID <= 0) return null; + + var invoice = GetByID(invoiceID); + if (invoice == null) return null; + + _crmSecurity.DemandDelete(invoice); + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "invoice.*")); + + DeleteBatchInvoicesExecute(new List { invoice }); + + return invoice; + } + + public List DeleteBatchInvoices(int[] invoiceID) + { + var invoices = GetInvoices(invoiceID).Where(_crmSecurity.CanDelete).ToList(); + if (!invoices.Any()) return invoices; + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "invoice.*")); + + DeleteBatchInvoicesExecute(invoices); + + return invoices; + } + + private void DeleteBatchInvoicesExecute(List invoices) + { + var invoiceID = invoices.Select(x => x.ID).ToArray(); + + using var tx = CrmDbContext.Database.BeginTransaction(); + + var dbInvoicesQuery = Query(CrmDbContext.Invoices).Where(x => invoiceID.Contains(x.Id)); + + CrmDbContext.InvoiceLine.RemoveRange(Query(CrmDbContext.InvoiceLine).Where(x => invoiceID.Contains(x.InvoiceId))); + CrmDbContext.Invoices.RemoveRange(dbInvoicesQuery); + + CrmDbContext.SaveChanges(); + + tx.Commit(); + + dbInvoicesQuery.ToList().ForEach(invoice => _factoryIndexer.Delete(invoice)); + } + + private IQueryable GetDbInvoceByFilters( + ICollection exceptIDs, + string searchText, + InvoiceStatus? status, + DateTime issueDateFrom, + DateTime issueDateTo, + DateTime dueDateFrom, + DateTime dueDateTo, + EntityType entityType, + int entityID, + String currency) + { + var sqlQuery = Query(CrmDbContext.Invoices) + .AsNoTracking(); + + if (entityID > 0) + { + + switch (entityType) + { + case EntityType.Contact: + case EntityType.Person: + case EntityType.Company: + sqlQuery = sqlQuery.Where(x => x.ContactId == entityID); + break; + case EntityType.Case: + case EntityType.Opportunity: + sqlQuery = sqlQuery.Where(x => x.EntityId == entityID && x.EntityType == entityType); + break; + } + + } + + if (status != null) + { + sqlQuery = sqlQuery.Where(x => x.Status == status.Value); + } + + if (!String.IsNullOrEmpty(searchText)) + { + searchText = searchText.Trim(); + + var keywords = searchText.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) + .ToArray(); + + if (keywords.Length > 0) + { + List invoicesIds; + if (!_factoryIndexer.TrySelectIds(s => s.MatchAll(searchText), out invoicesIds)) + { + foreach (var k in keywords) + { + sqlQuery = sqlQuery.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Number, k + "%") || + Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Description, k + "%")); + } + } + else + { + sqlQuery = sqlQuery.Where(x => invoicesIds.Contains(x.Id)); + } + } + } + + if (exceptIDs.Count > 0) + { + sqlQuery = sqlQuery.Where(x => !exceptIDs.Contains(x.Id)); + } + + if (issueDateFrom != DateTime.MinValue && issueDateTo != DateTime.MinValue) + { + sqlQuery = sqlQuery.Where(x => x.IssueDate >= _tenantUtil.DateTimeToUtc(issueDateFrom) && x.DueDate <= _tenantUtil.DateTimeToUtc(issueDateTo.AddDays(1).AddMinutes(-1))); + } + else if (issueDateFrom != DateTime.MinValue) + { + sqlQuery = sqlQuery.Where(x => x.IssueDate > _tenantUtil.DateTimeToUtc(issueDateFrom)); + } + else if (issueDateTo != DateTime.MinValue) + { + sqlQuery = sqlQuery.Where(x => x.IssueDate < _tenantUtil.DateTimeToUtc(issueDateTo.AddDays(1).AddMinutes(-1))); + } + + if (dueDateFrom != DateTime.MinValue && dueDateTo != DateTime.MinValue) + { + sqlQuery = sqlQuery.Where(x => x.DueDate >= _tenantUtil.DateTimeToUtc(dueDateFrom) && x.DueDate <= _tenantUtil.DateTimeToUtc(dueDateTo.AddDays(1).AddMinutes(-1))); + } + else if (dueDateFrom != DateTime.MinValue) + { + sqlQuery = sqlQuery.Where(x => x.DueDate > _tenantUtil.DateTimeToUtc(dueDateFrom)); + } + else if (dueDateTo != DateTime.MinValue) + { + sqlQuery = sqlQuery.Where(x => x.DueDate < _tenantUtil.DateTimeToUtc(dueDateTo.AddDays(1).AddMinutes(-1))); + } + + if (!String.IsNullOrEmpty(currency)) + { + sqlQuery = sqlQuery.Where(x => x.Currency == currency); + } + + return sqlQuery; + } + + private bool hasParams( + String searchText, + InvoiceStatus? status, + DateTime issueDateFrom, + DateTime issueDateTo, + DateTime dueDateFrom, + DateTime dueDateTo, + EntityType entityType, + int entityID, + String currency) + { + return !(String.IsNullOrEmpty(searchText) && !status.HasValue && + issueDateFrom == DateTime.MinValue && issueDateTo == DateTime.MinValue && + dueDateFrom == DateTime.MinValue && dueDateTo == DateTime.MinValue && + entityID == 0 && String.IsNullOrEmpty(currency)); + } + + /// + /// Test method + /// + /// + /// + public void SetInvoiceCreationDate(int id, DateTime creationDate) + { + var dbEntity = CrmDbContext.Invoices.Find(id); + + var entity = _mapper.Map(dbEntity); + + dbEntity.CreateOn = _tenantUtil.DateTimeToUtc(creationDate); + + CrmDbContext.SaveChanges(); + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "invoice.*")); + } + + /// + /// Test method + /// + /// + /// + public void SetInvoiceLastModifedDate(int id, DateTime lastModifedDate) + { + var dbEntity = CrmDbContext.Invoices.Find(id); + + var entity = _mapper.Map(dbEntity); + + _crmSecurity.DemandAccessTo(entity); + + dbEntity.LastModifedOn = _tenantUtil.DateTimeToUtc(lastModifedDate); + + CrmDbContext.SaveChanges(); + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "invoice.*")); + } + } + +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Dao/InvoiceItemDao.cs b/products/ASC.CRM/Server/Core/Dao/InvoiceItemDao.cs new file mode 100644 index 00000000000..ebaa033dca1 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/InvoiceItemDao.cs @@ -0,0 +1,380 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.Web.CRM.Classes; + +using AutoMapper; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.CRM.Core.Dao +{ + [Scope] + public class InvoiceItemDao : AbstractDao + { + private CrmSecurity _crmSecurity { get; } + + public InvoiceItemDao( + DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + CrmSecurity crmSecurity, + IOptionsMonitor logger, + ICache ascCache, + IMapper mapper + ) : base(dbContextManager, + tenantManager, + securityContext, + logger, + ascCache, + mapper) + { + _crmSecurity = crmSecurity; + } + + public Boolean IsExist(int id) + { + return IsExistInDb(id); + } + + public Boolean IsExistInDb(int id) + { + return Query(CrmDbContext.InvoiceItem).Any(x => x.Id == id); + } + + public Boolean CanDelete(int id) + { + return Query(CrmDbContext.InvoiceLine).Any(x => x.InvoiceItemId == id); + } + + public List GetAll() + { + return GetAllInDb(); + } + public List GetAllInDb() + { + var dbInvoiceItems = Query(CrmDbContext.InvoiceItem) + .AsNoTracking() + .ToList(); + + return _mapper.Map, List>(dbInvoiceItems); + } + + public List GetByID(int[] ids) + { + var dbInvoiceItems = Query(CrmDbContext.InvoiceItem) + .AsNoTracking() + .Where(x => ids.Contains(x.Id)) + .ToList(); + + return _mapper.Map, List>(dbInvoiceItems); + } + + public InvoiceItem GetByID(int id) + { + var dbEntity = CrmDbContext.InvoiceItem.Find(id); + + return _mapper.Map(dbEntity); + } + + public List GetInvoiceItems(IEnumerable ids) + { + if (ids == null || !ids.Any()) return new List(); + + var dbInvoiceItems = Query(CrmDbContext.InvoiceItem) + .AsNoTracking() + .Where(x => ids.Contains(x.Id)) + .ToList(); + + return _mapper.Map, List>(dbInvoiceItems); + } + + public List GetInvoiceItems( + string searchText, + int status, + bool? inventoryStock, + int from, + int count, + OrderBy orderBy) + { + + var sqlQuery = GetDbInvoiceItemByFilters(new List(), searchText, status, inventoryStock); + + var withParams = !(String.IsNullOrEmpty(searchText) || status != 0 || inventoryStock.HasValue); + + if (withParams && sqlQuery == null) + return new List(); + + if (0 < from && from < int.MaxValue) sqlQuery = sqlQuery.Skip(from); + if (0 < count && count < int.MaxValue) sqlQuery = sqlQuery.Take(count); + + if (orderBy != null && Enum.IsDefined(typeof(InvoiceItemSortedByType), orderBy.SortedBy)) + { + switch ((InvoiceItemSortedByType)orderBy.SortedBy) + { + case InvoiceItemSortedByType.Name: + sqlQuery = sqlQuery.OrderBy("Title", orderBy.IsAsc); + break; + case InvoiceItemSortedByType.SKU: + sqlQuery = sqlQuery.OrderBy("StockKeepingUnit", orderBy.IsAsc); + break; + case InvoiceItemSortedByType.Price: + sqlQuery = sqlQuery.OrderBy("Price", orderBy.IsAsc); + break; + case InvoiceItemSortedByType.Quantity: + { + sqlQuery = sqlQuery.OrderBy("StockQuantity", orderBy.IsAsc) + .OrderBy("Title", true); + break; + } + case InvoiceItemSortedByType.Created: + sqlQuery = sqlQuery.OrderBy("CreateOn", orderBy.IsAsc); + break; + default: + sqlQuery = sqlQuery.OrderBy("Title", true); + break; + } + } + else + { + sqlQuery = sqlQuery.OrderBy("Title", true); + } + + var dbInvoiceItems = sqlQuery.ToList(); + + return _mapper.Map, List>(dbInvoiceItems); + } + + + public int GetInvoiceItemsCount() + { + return GetInvoiceItemsCount(String.Empty, 0, null); + } + + public int GetInvoiceItemsCount( + String searchText, + int status, + bool? inventoryStock) + { + var cacheKey = TenantID.ToString(CultureInfo.InvariantCulture) + + "invoiceItem" + + _securityContext.CurrentAccount.ID.ToString() + + searchText; + + var fromCache = _cache.Get(cacheKey); + + if (fromCache != null) return Convert.ToInt32(fromCache); + + var exceptIDs = _crmSecurity.GetPrivateItems(typeof(InvoiceItem)).ToList(); + + int result; + + var withParams = !(String.IsNullOrEmpty(searchText) || status != 0 || inventoryStock.HasValue); + + if (withParams) + { + result = GetDbInvoiceItemByFilters(exceptIDs, searchText, status, inventoryStock).Count(); + } + else + { + var countWithoutPrivate = Query(CrmDbContext.InvoiceItem).Count(); + + var privateCount = exceptIDs.Count; + + if (privateCount > countWithoutPrivate) + { + _logger.ErrorFormat(@"Private invoice items count more than all cases. Tenant: {0}. CurrentAccount: {1}", + TenantID, + _securityContext.CurrentAccount.ID); + + privateCount = 0; + } + + result = countWithoutPrivate - privateCount; + } + + if (result > 0) + { + _cache.Insert(cacheKey, result, TimeSpan.FromSeconds(30)); + } + return result; + } + + public InvoiceItem SaveOrUpdateInvoiceItem(InvoiceItem invoiceItem) + { + /*_cache.Remove(_invoiceItemCacheKey); + _cache.Insert(_invoiceItemCacheKey, String.Empty);*/ + + return SaveOrUpdateInvoiceItemInDb(invoiceItem); + } + + private InvoiceItem SaveOrUpdateInvoiceItemInDb(InvoiceItem invoiceItem) + { + if (invoiceItem.Price <= 0 || string.IsNullOrEmpty(invoiceItem.Title)) + throw new ArgumentException(); + + if (invoiceItem.Price > Global.MaxInvoiceItemPrice) + throw new ArgumentException("Max Invoice Item Price: " + Global.MaxInvoiceItemPrice); + + if (!_crmSecurity.IsAdmin) _crmSecurity.CreateSecurityException(); + + + var dbEntity = new DbInvoiceItem + { + Id = invoiceItem.ID, + Title = invoiceItem.Title, + Description = invoiceItem.Description, + StockKeepingUnit = invoiceItem.StockKeepingUnit, + Price = invoiceItem.Price, + StockQuantity = invoiceItem.StockQuantity, + TrackInventory = invoiceItem.TrackInventory, + InvoiceTax1Id = invoiceItem.InvoiceTax1ID, + InvoiceTax2Id = invoiceItem.InvoiceTax2ID, + Currency = String.Empty, + CreateOn = DateTime.UtcNow, + CreateBy = _securityContext.CurrentAccount.ID, + LastModifedOn = DateTime.Now, + LastModifedBy = _securityContext.CurrentAccount.ID, + TenantId = TenantID + }; + + CrmDbContext.Update(dbEntity); + CrmDbContext.SaveChanges(); + + return invoiceItem; + } + + public InvoiceItem DeleteInvoiceItem(int invoiceItemID) + { + var invoiceItem = GetByID(invoiceItemID); + + if (invoiceItem == null) return null; + + _crmSecurity.DemandDelete(invoiceItem); + + CrmDbContext.Remove(new DbInvoiceItem + { + Id = invoiceItemID, + TenantId = TenantID + }); + + CrmDbContext.SaveChanges(); + + /*_cache.Remove(_invoiceItemCacheKey); + _cache.Insert(_invoiceItemCacheKey, String.Empty);*/ + + return invoiceItem; + } + + public List DeleteBatchInvoiceItems(int[] ids) + { + var result = new List(); + + foreach (var id in ids) + { + var dbEntity = CrmDbContext.InvoiceItem.Find(id); + + var entity = _mapper.Map(dbEntity); + + result.Add(entity); + + _crmSecurity.DemandDelete(entity); + + CrmDbContext.Remove(dbEntity); + } + + CrmDbContext.SaveChanges(); + + // Delete relative keys + /*_cache.Remove(_invoiceItemCacheKey); + _cache.Insert(_invoiceItemCacheKey, String.Empty);*/ + + return result; + } + + private IQueryable GetDbInvoiceItemByFilters( + ICollection exceptIDs, + string searchText, + int status, + bool? inventoryStock) + { + + var sqlQuery = Query(CrmDbContext.InvoiceItem); + + //if (status > 0) + //{ + // sqlQuery = sqlQuery.Where(x => x.); + // conditions.Add(Exp.Eq("status", (int)status.Value)); + //} + + if (!String.IsNullOrEmpty(searchText)) + { + searchText = searchText.Trim(); + + var keywords = searchText.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) + .ToArray(); + + if (keywords.Length > 0) + { + foreach (var k in keywords) + { + sqlQuery = sqlQuery.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Title, k + "%") || + Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Description, k + "%") || + Microsoft.EntityFrameworkCore.EF.Functions.Like(x.StockKeepingUnit, k + "%") + ); + } + } + } + + if (exceptIDs.Count > 0) + { + sqlQuery = sqlQuery.Where(x => !exceptIDs.Contains(x.Id)); + } + + + if (inventoryStock.HasValue) + { + sqlQuery = sqlQuery.Where(x => x.TrackInventory == inventoryStock.Value); + } + + return sqlQuery; + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Dao/InvoiceLineDao.cs b/products/ASC.CRM/Server/Core/Dao/InvoiceLineDao.cs new file mode 100644 index 00000000000..6156e1b1d67 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/InvoiceLineDao.cs @@ -0,0 +1,208 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text.Json; +using System.Text.RegularExpressions; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Entities; + +using AutoMapper; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.CRM.Core.Dao +{ + [Scope] + public class InvoiceLineDao : AbstractDao + { + public InvoiceLineDao(DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + IOptionsMonitor logger, + ICache ascCache, + IMapper mapper) + : base(dbContextManager, + tenantManager, + securityContext, + logger, + ascCache, + mapper) + { + + } + + + public static string GetJson(InvoiceItem invoiceItem) + { + return invoiceItem == null ? + string.Empty : + JsonSerializer.Serialize(new + { + id = invoiceItem.ID, + title = invoiceItem.Title, + description = invoiceItem.Description + }); + } + public static string GetJson(InvoiceTax invoiceTax) + { + return invoiceTax == null ? + string.Empty : + JsonSerializer.Serialize(new + { + id = invoiceTax.ID, + name = invoiceTax.Name, + rate = invoiceTax.Rate, + description = invoiceTax.Description + }); + } + + public List GetAll() + { + var dbEntities = Query(CrmDbContext.InvoiceLine) + .AsNoTracking() + .ToList(); + + return _mapper.Map, List>(dbEntities); + } + + public List GetByID(int[] ids) + { + var dbEntities = Query(CrmDbContext.InvoiceLine) + .AsNoTracking() + .Where(x => ids.Contains(x.Id)) + .ToList(); + + return _mapper.Map, List>(dbEntities); + } + + public InvoiceLine GetByID(int id) + { + var dbEntity = CrmDbContext.InvoiceLine.Find(id); + + if (dbEntity.TenantId != TenantID) return null; + + return _mapper.Map(dbEntity); + } + + public List GetInvoiceLines(int invoiceID) + { + return GetInvoiceLinesInDb(invoiceID); + } + + public List GetInvoiceLinesInDb(int invoiceID) + { + var dbInvoiceLines = Query(CrmDbContext.InvoiceLine) + .Where(x => x.InvoiceId == invoiceID) + .OrderBy(x => x.SortOrder) + .ToList(); + + return _mapper.Map, List>(dbInvoiceLines); + } + + public int SaveOrUpdateInvoiceLine(InvoiceLine invoiceLine) + { + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "invoice.*")); + + return SaveOrUpdateInvoiceLineInDb(invoiceLine); + } + + private int SaveOrUpdateInvoiceLineInDb(InvoiceLine invoiceLine) + { + if (invoiceLine.InvoiceID <= 0 || invoiceLine.InvoiceItemID <= 0) + throw new ArgumentException(); + + var dbEntity = new DbInvoiceLine + { + Id = invoiceLine.ID, + InvoiceId = invoiceLine.InvoiceItemID, + InvoiceItemId = invoiceLine.InvoiceItemID, + InvoiceTax1Id = invoiceLine.InvoiceTax1ID, + InvoiceTax2Id = invoiceLine.InvoiceTax2ID, + SortOrder = invoiceLine.SortOrder, + Description = invoiceLine.Description, + Quantity = invoiceLine.Quantity, + Price = invoiceLine.Price, + Discount = invoiceLine.Discount, + TenantId = TenantID + }; + + CrmDbContext.Update(dbEntity); + CrmDbContext.SaveChanges(); + + return dbEntity.Id; + } + + public void DeleteInvoiceLine(int id) + { + var dbEntity = CrmDbContext.InvoiceLine.Find(id); + + CrmDbContext.Remove(dbEntity); + CrmDbContext.SaveChanges(); + + /*_cache.Remove(_invoiceItemCacheKey); + _cache.Insert(_invoiceLineCacheKey, String.Empty);*/ + } + + public void DeleteInvoiceLines(int invoiceID) + { + var itemToDelete = Query(CrmDbContext.InvoiceLine) + .Where(x => x.InvoiceId == invoiceID); + + CrmDbContext.RemoveRange(itemToDelete); + CrmDbContext.SaveChanges(); + + /*_cache.Remove(_invoiceItemCacheKey); + _cache.Insert(_invoiceLineCacheKey, String.Empty);*/ + } + + public Boolean CanDelete(int invoiceLineID) + { + return CanDeleteInDb(invoiceLineID); + } + + public Boolean CanDeleteInDb(int invoiceLineID) + { + var invoiceID = Query(CrmDbContext.InvoiceLine) + .Where(x => x.Id == invoiceLineID) + .Select(x => x.InvoiceId); + + if (!invoiceID.Any()) return false; + + return Query(CrmDbContext.InvoiceLine).Where(x => x.InvoiceId == invoiceLineID).Any(); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Dao/InvoiceTaxDao.cs b/products/ASC.CRM/Server/Core/Dao/InvoiceTaxDao.cs new file mode 100644 index 00000000000..5dacbcc6bbb --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/InvoiceTaxDao.cs @@ -0,0 +1,177 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Entities; + +using AutoMapper; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.CRM.Core.Dao +{ + [Scope] + public class InvoiceTaxDao : AbstractDao + { + private CrmSecurity _crmSecurity { get; } + + public InvoiceTaxDao( + DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + IOptionsMonitor logger, + ICache ascCache, + IMapper mapper + ) + : base(dbContextManager, + tenantManager, + securityContext, + logger, + ascCache, + mapper) + { + _mapper = mapper; + } + + public Boolean IsExist(int id) + { + return CrmDbContext.InvoiceTax.Where(x => x.Id == id).Any(); + } + + public Boolean IsExist(String invoiceName) + { + return Query(CrmDbContext.InvoiceTax).Where(x => String.Compare(x.Name, invoiceName, true) == 0).Any(); + } + + public Boolean CanDelete(int invoiceTaxID) + { + return !Query(CrmDbContext.InvoiceItem) + .Where(x => x.InvoiceTax1Id == invoiceTaxID || x.InvoiceTax2Id == invoiceTaxID).Any() && + !Query(CrmDbContext.InvoiceLine) + .Where(x => x.InvoiceTax1Id == invoiceTaxID || x.InvoiceTax2Id == invoiceTaxID).Any(); + } + + public List GetAll() + { + var dbEntities = Query(CrmDbContext.InvoiceTax) + .AsNoTracking() + .ToList(); + + return _mapper.Map, List>(dbEntities); + } + + public DateTime GetMaxLastModified() + { + var result = Query(CrmDbContext.InvoiceTax).Max(x => x.LastModifedOn); + + if (result.HasValue) return result.Value; + + return DateTime.MinValue; + } + + public List GetByID(int[] ids) + { + var dbEntities = Query(CrmDbContext.InvoiceTax) + .AsNoTracking() + .Where(x => ids.Contains(x.Id)) + .ToList(); + + return _mapper.Map, List>(dbEntities); + } + + public InvoiceTax GetByID(int id) + { + var dbEntity = CrmDbContext.InvoiceTax.Find(id); + + if (dbEntity.TenantId != TenantID) return null; + + return _mapper.Map(dbEntity); + } + + public InvoiceTax SaveOrUpdateInvoiceTax(InvoiceTax invoiceTax) + { + /*_cache.Remove(_invoiceItemCacheKey); + _cache.Insert(_invoiceTaxCacheKey, String.Empty);*/ + + return SaveOrUpdateInvoiceTaxInDb(invoiceTax); + } + + private InvoiceTax SaveOrUpdateInvoiceTaxInDb(InvoiceTax invoiceTax) + { + if (String.IsNullOrEmpty(invoiceTax.Name)) + throw new ArgumentException(); + + var dbEntity = new DbInvoiceTax + { + Id = invoiceTax.ID, + Name = invoiceTax.Name, + Description = invoiceTax.Description, + Rate = invoiceTax.Rate, + CreateOn = invoiceTax.CreateOn == DateTime.MinValue ? DateTime.UtcNow : invoiceTax.CreateOn, + CreateBy = invoiceTax.CreateBy == Guid.Empty ? _securityContext.CurrentAccount.ID : invoiceTax.CreateBy, + LastModifedOn = DateTime.UtcNow, + LastModifedBy = _securityContext.CurrentAccount.ID, + TenantId = TenantID + }; + + + CrmDbContext.Update(dbEntity); + CrmDbContext.SaveChanges(); + + return invoiceTax; + } + + public InvoiceTax DeleteInvoiceTax(int id) + { + var dbEntity = CrmDbContext.InvoiceTax.Find(id); + + var entity = _mapper.Map(dbEntity); + + if (entity == null) return null; + + _crmSecurity.DemandDelete(entity); + + CrmDbContext.InvoiceTax.Remove(dbEntity); + + CrmDbContext.SaveChanges(); + + /* _cache.Remove(_invoiceItemCacheKey); + _cache.Insert(_invoiceTaxCacheKey, String.Empty);*/ + return entity; + } + } + +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Dao/ListItemDao.cs b/products/ASC.CRM/Server/Core/Dao/ListItemDao.cs new file mode 100644 index 00000000000..582c036f112 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/ListItemDao.cs @@ -0,0 +1,532 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.CRM.Classes; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; + +using AutoMapper; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.CRM.Core.Dao +{ + [Scope] + public class ListItemDao : AbstractDao + { + public ListItemDao( + CrmSecurity crmSecurity, + DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + IOptionsMonitor logger, + ICache ascCache, + IMapper mapper) + : base(dbContextManager, + tenantManager, + securityContext, + logger, + ascCache, + mapper) + { + + } + + public bool IsExist(ListType listType, String title) + { + return Query(CrmDbContext.ListItem) + .Where(x => x.TenantId == TenantID && x.ListType == listType && String.Compare(x.Title, title, true) == 0) + .Any(); + } + + public bool IsExist(int id) + { + return Query(CrmDbContext.ListItem).Where(x => x.Id == id).Any(); + } + + public List GetItems() + { + var dbListItems = Query(CrmDbContext.ListItem) + .AsNoTracking() + .OrderBy(x => x.SortOrder) + .ToList(); + + return _mapper.Map, List>(dbListItems); + } + + public List GetItems(ListType listType) + { + var dbEntities = Query(CrmDbContext.ListItem) + .AsNoTracking() + .Where(x => x.ListType == listType) + .OrderBy(x => x.SortOrder) + .ToList(); + + return _mapper.Map, List>(dbEntities); + } + + public int GetItemsCount(ListType listType) + { + return Query(CrmDbContext.ListItem) + .Where(x => x.ListType == listType) + .OrderBy(x => x.SortOrder) + .Count(); + } + + public ListItem GetSystemListItem(int id) + { + switch (id) + { + case (int)HistoryCategorySystem.TaskClosed: + return new ListItem + { + ID = -1, + Title = HistoryCategorySystem.TaskClosed.ToLocalizedString(), + AdditionalParams = "event_category_close.png" + }; + case (int)HistoryCategorySystem.FilesUpload: + return new ListItem + { + ID = -2, + Title = HistoryCategorySystem.FilesUpload.ToLocalizedString(), + AdditionalParams = "event_category_attach_file.png" + }; + case (int)HistoryCategorySystem.MailMessage: + return new ListItem + { + ID = -3, + Title = HistoryCategorySystem.MailMessage.ToLocalizedString(), + AdditionalParams = "event_category_email.png" + }; + default: + return null; + } + + } + + public List GetSystemItems() + { + return new List + { + new ListItem + { + ID = (int)HistoryCategorySystem.TaskClosed, + Title = HistoryCategorySystem.TaskClosed.ToLocalizedString(), + AdditionalParams = "event_category_close.png" + }, + new ListItem + { + ID = (int)HistoryCategorySystem.FilesUpload, + Title = HistoryCategorySystem.FilesUpload.ToLocalizedString(), + AdditionalParams = "event_category_attach_file.png" + }, + new ListItem + { + ID =(int)HistoryCategorySystem.MailMessage, + Title = HistoryCategorySystem.MailMessage.ToLocalizedString(), + AdditionalParams = "event_category_email.png" + } + }; + } + + public ListItem GetByID(int id) + { + if (id < 0) return GetSystemListItem(id); + + var dbEntity = CrmDbContext.ListItem.Find(id); + + if (dbEntity.TenantId != TenantID) return null; + + return _mapper.Map(dbEntity); + } + + public List GetItems(int[] id) + { + var dbEntities = Query(CrmDbContext.ListItem) + .AsNoTracking() + .Where(x => id.Contains(x.Id)) + .ToList(); + + var entities = _mapper.Map, List>(dbEntities); + + var systemItem = id.Where(item => item < 0).Select(GetSystemListItem); + + return systemItem.Any() ? entities.Union(systemItem).ToList() : entities; + } + + public List GetAll() + { + var dbListItems = Query(CrmDbContext.ListItem) + .AsNoTracking() + .ToList(); + + return _mapper.Map, List>(dbListItems); + } + + public void ChangeColor(int id, string newColor) + { + var dbEntity = CrmDbContext.ListItem.Find(id); + + dbEntity.Color = newColor; + + CrmDbContext.SaveChanges(); + } + + public NameValueCollection GetColors(ListType listType) + { + var result = new NameValueCollection(); + + Query(CrmDbContext.ListItem) + .Where(x => x.ListType == listType) + .Select(x => new { x.Id, x.Color }) + .ToList() + .ForEach(x => result.Add(x.Id.ToString(), x.Color.ToString())); + + return result; + + } + + public ListItem GetByTitle(ListType listType, string title) + { + var dbEntity = Query(CrmDbContext.ListItem) + .FirstOrDefault(x => String.Compare(x.Title, title, true) == 0 && x.ListType == listType); + + return _mapper.Map(dbEntity); + } + + public int GetRelativeItemsCount(ListType listType, int id) + { + int result; + + switch (listType) + { + case ListType.ContactStatus: + result = Query(CrmDbContext.Contacts).Where(x => x.StatusId == id).Count(); + break; + case ListType.ContactType: + result = Query(CrmDbContext.Contacts).Where(x => x.ContactTypeId == id).Count(); + break; + case ListType.TaskCategory: + result = Query(CrmDbContext.Tasks).Where(x => x.CategoryId == id).Count(); + break; + case ListType.HistoryCategory: + result = Query(CrmDbContext.RelationshipEvent).Where(x => x.CategoryId == id).Count(); + break; + default: + throw new ArgumentException(); + + } + + return result; + } + + public Dictionary GetRelativeItemsCount(ListType listType) + { + + Dictionary result; + + switch (listType) + { + case ListType.ContactStatus: + { + result = Query(CrmDbContext.ListItem) + .Join(Query(CrmDbContext.Contacts), + x => x.Id, + y => y.StatusId, + (x, y) => new { x, y }) + .Where(x => x.x.ListType == listType) + .GroupBy(x => x.x.Id) + .Select(x => new { Id = x.Key, Count = x.Count() }) + .ToDictionary(x => x.Id, x => x.Count); + + break; + } + case ListType.ContactType: + { + result = Query(CrmDbContext.ListItem) + .Join(Query(CrmDbContext.Contacts), + x => x.Id, + y => y.ContactTypeId, + (x, y) => new { x, y }) + .Where(x => x.x.ListType == listType) + .GroupBy(x => x.x.Id) + .Select(x => new { Id = x.Key, Count = x.Count() }) + .ToDictionary(x => x.Id, x => x.Count); + + break; + } + case ListType.TaskCategory: + { + result = Query(CrmDbContext.ListItem) + .Join(Query(CrmDbContext.Tasks), + x => x.Id, + y => y.CategoryId, + (x, y) => new { x, y }) + .Where(x => x.x.ListType == listType) + .GroupBy(x => x.x.Id) + .Select(x => new { Id = x.Key, Count = x.Count() }) + .ToDictionary(x => x.Id, x => x.Count); + + break; + } + case ListType.HistoryCategory: + { + + result = Query(CrmDbContext.ListItem) + .Join(Query(CrmDbContext.RelationshipEvent), + x => x.Id, + y => y.CategoryId, + (x, y) => new { x, y }) + .Where(x => x.x.ListType == listType) + .GroupBy(x => x.x.Id) + .Select(x => new { Id = x.Key, Count = x.Count() }) + .ToDictionary(x => x.Id, x => x.Count); + + break; + } + default: + throw new ArgumentException(); + } + + return result; + } + + public int CreateItem(ListType listType, ListItem enumItem) + { + + if (IsExist(listType, enumItem.Title)) + return GetByTitle(listType, enumItem.Title).ID; + + if (string.IsNullOrEmpty(enumItem.Title)) + throw new ArgumentException(); + + if (listType == ListType.TaskCategory || listType == ListType.HistoryCategory) + if (string.IsNullOrEmpty(enumItem.AdditionalParams)) + throw new ArgumentException(); + else + enumItem.AdditionalParams = System.IO.Path.GetFileName(enumItem.AdditionalParams); + + if (listType == ListType.ContactStatus) + if (string.IsNullOrEmpty(enumItem.Color)) + throw new ArgumentException(); + + var sortOrder = enumItem.SortOrder; + + if (sortOrder == 0) + sortOrder = Query(CrmDbContext.ListItem) + .Where(x => x.ListType == listType) + .Max(x => x.SortOrder) + 1; + + var listItem = new DbListItem + { + ListType = listType, + Description = enumItem.Description, + Title = enumItem.Title, + AdditionalParams = enumItem.AdditionalParams, + Color = enumItem.Color, + SortOrder = sortOrder, + TenantId = TenantID + }; + + CrmDbContext.Add(listItem); + + CrmDbContext.SaveChanges(); + + return listItem.Id; + } + + public void EditItem(ListType listType, ListItem enumItem) + { + if (HaveRelativeItemsLink(listType, enumItem.ID)) + { + switch (listType) + { + case ListType.ContactStatus: + case ListType.ContactType: + throw new ArgumentException(string.Format("{0}. {1}.", CRMErrorsResource.BasicCannotBeEdited, CRMErrorsResource.HasRelatedContacts)); + case ListType.TaskCategory: + throw new ArgumentException(string.Format("{0}. {1}.", CRMErrorsResource.BasicCannotBeEdited, CRMErrorsResource.TaskCategoryHasRelatedTasks)); + case ListType.HistoryCategory: + throw new ArgumentException(string.Format("{0}. {1}.", CRMErrorsResource.BasicCannotBeEdited, CRMErrorsResource.HistoryCategoryHasRelatedEvents)); + default: + throw new ArgumentException(string.Format("{0}.", CRMErrorsResource.BasicCannotBeEdited)); + } + } + + var itemToUpdate = Query(CrmDbContext.ListItem).Single(x => x.Id == enumItem.ID); + + itemToUpdate.Description = enumItem.Description; + itemToUpdate.Title = enumItem.Title; + itemToUpdate.AdditionalParams = enumItem.AdditionalParams; + itemToUpdate.Color = enumItem.Color; + + CrmDbContext.SaveChanges(); + } + + public void ChangePicture(int id, String newPicture) + { + var itemToUpdate = Query(CrmDbContext.ListItem).Single(x => x.Id == id); + + itemToUpdate.AdditionalParams = newPicture; + + CrmDbContext.Update(itemToUpdate); + + CrmDbContext.SaveChanges(); + } + + private bool HaveRelativeItemsLink(ListType listType, int itemID) + { + bool result; + + switch (listType) + { + case ListType.ContactStatus: + result = Query(CrmDbContext.Contacts).Where(x => x.StatusId == itemID).Any(); + break; + case ListType.ContactType: + result = Query(CrmDbContext.Contacts).Where(x => x.ContactTypeId == itemID).Any(); + break; + case ListType.TaskCategory: + result = Query(CrmDbContext.Tasks).Where(x => x.CategoryId == itemID).Any(); + break; + case ListType.HistoryCategory: + result = Query(CrmDbContext.RelationshipEvent).Where(x => x.CategoryId == itemID).Any(); + break; + default: + throw new ArgumentException(); + } + + return result; + } + + public void ChangeRelativeItemsLink(ListType listType, int fromItemID, int toItemID) + { + if (!IsExist(fromItemID)) + throw new ArgumentException("", "toItemID"); + + if (!HaveRelativeItemsLink(listType, fromItemID)) return; + + if (!IsExist(toItemID)) + throw new ArgumentException("", "toItemID"); + + switch (listType) + { + case ListType.ContactStatus: + { + var itemToUpdate = Query(CrmDbContext.Contacts).Single(x => x.StatusId == fromItemID); + + itemToUpdate.StatusId = toItemID; + + CrmDbContext.Update(itemToUpdate); + } + break; + case ListType.ContactType: + { + var itemToUpdate = Query(CrmDbContext.Contacts).Single(x => x.ContactTypeId == fromItemID); + + itemToUpdate.ContactTypeId = toItemID; + + CrmDbContext.Update(itemToUpdate); + } + break; + case ListType.TaskCategory: + { + var itemToUpdate = Query(CrmDbContext.Tasks).Single(x => x.CategoryId == fromItemID); + itemToUpdate.CategoryId = toItemID; + + CrmDbContext.Update(itemToUpdate); + } + break; + case ListType.HistoryCategory: + { + var itemToUpdate = Query(CrmDbContext.RelationshipEvent).Single(x => x.CategoryId == fromItemID); + itemToUpdate.CategoryId = toItemID; + + CrmDbContext.Update(itemToUpdate); + } + break; + default: + throw new ArgumentException(); + } + + CrmDbContext.SaveChanges(); + } + + public void DeleteItem(ListType listType, int itemID, int toItemID) + { + if (HaveRelativeItemsLink(listType, itemID)) + { + switch (listType) + { + case ListType.ContactStatus: + case ListType.ContactType: + throw new ArgumentException(string.Format("{0}. {1}.", CRMErrorsResource.BasicCannotBeDeleted, CRMErrorsResource.HasRelatedContacts)); + case ListType.TaskCategory: + var exMsg = string.Format("{0}. {1}.", CRMErrorsResource.BasicCannotBeDeleted, CRMErrorsResource.TaskCategoryHasRelatedTasks); + if (itemID == toItemID) throw new ArgumentException(exMsg); + ChangeRelativeItemsLink(listType, itemID, toItemID); + break; + case ListType.HistoryCategory: + throw new ArgumentException(string.Format("{0}. {1}.", CRMErrorsResource.BasicCannotBeDeleted, CRMErrorsResource.HistoryCategoryHasRelatedEvents)); + default: + throw new ArgumentException(string.Format("{0}.", CRMErrorsResource.BasicCannotBeDeleted)); + } + } + + var dbEntity = CrmDbContext.ListItem.Find(itemID); + + CrmDbContext.ListItem.Remove(dbEntity); + + CrmDbContext.SaveChanges(); + + } + + public void ReorderItems(ListType listType, string[] titles) + { + for (int index = 0; index < titles.Length; index++) + { + var dbEntity = Query(CrmDbContext.ListItem) + .Single(x => string.Compare(x.Title, titles[index]) == 0 && x.ListType == listType); + + dbEntity.SortOrder = index; + } + + CrmDbContext.SaveChanges(); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Dao/RelationshipEventDao.cs b/products/ASC.CRM/Server/Core/Dao/RelationshipEventDao.cs new file mode 100644 index 00000000000..0367e98b01b --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/RelationshipEventDao.cs @@ -0,0 +1,617 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.Core.Tenants; +using ASC.CRM.Classes; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.Files.Core; +using ASC.Web.CRM.Core.Search; +using ASC.Web.Files.Api; + +using AutoMapper; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +using OrderBy = ASC.CRM.Core.Entities.OrderBy; + +namespace ASC.CRM.Core.Dao +{ + [Scope] + public class RelationshipEventDao : AbstractDao + { + private readonly CrmSecurity _crmSecurity; + private readonly TenantUtil _tenantUtil; + private readonly FilesIntegration _filesIntegration; + private readonly FactoryIndexerEvents _factoryIndexer; + + public RelationshipEventDao(DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + FilesIntegration filesIntegration, + FactoryIndexerEvents factoryIndexerEvents, + CrmSecurity crmSecurity, + TenantUtil tenantUtil, + IOptionsMonitor logger, + ICache ascCache, + IMapper mapper + ) : + base(dbContextManager, + tenantManager, + securityContext, + logger, + ascCache, + mapper) + { + _filesIntegration = filesIntegration; + _tenantUtil = tenantUtil; + _crmSecurity = crmSecurity; + _factoryIndexer = factoryIndexerEvents; + } + + public RelationshipEvent AttachFiles(int contactID, EntityType entityType, int entityID, int[] fileIDs) + { + if (entityID > 0 && !_supportedEntityType.Contains(entityType)) + throw new ArgumentException(); + + var relationshipEvent = new RelationshipEvent + { + CategoryID = (int)HistoryCategorySystem.FilesUpload, + ContactID = contactID, + EntityType = entityType, + EntityID = entityID, + Content = HistoryCategorySystem.FilesUpload.ToLocalizedString() + }; + + relationshipEvent = CreateItem(relationshipEvent); + + AttachFiles(relationshipEvent.ID, fileIDs); + + return relationshipEvent; + } + + public void AttachFiles(int eventID, int[] fileIDs) + { + if (fileIDs.Length == 0) return; + + var dao = _filesIntegration.DaoFactory.GetTagDao(); + + var tags = fileIDs.ToList().ConvertAll(fileID => new Tag("RelationshipEvent_" + eventID, TagType.System, Guid.Empty) { EntryType = FileEntryType.File, EntryId = fileID }); + + dao.SaveTags(tags); + + if (fileIDs.Length > 0) + { + var dbEntity = CrmDbContext.RelationshipEvent.Find(eventID); + + dbEntity.HaveFiles = true; + + CrmDbContext.SaveChanges(); + } + } + + public int GetFilesCount(int[] contactID, EntityType entityType, int entityID) + { + return GetFilesIDs(contactID, entityType, entityID).Length; + } + + private int[] GetFilesIDs(int[] contactID, EntityType entityType, int entityID) + { + if (entityID > 0 && entityType != EntityType.Opportunity && entityType != EntityType.Case) + throw new ArgumentException(); + + var sqlQuery = Query(CrmDbContext.RelationshipEvent); + + if (contactID != null && contactID.Length > 0) + sqlQuery = sqlQuery.Where(x => contactID.Contains(x.ContactId)); + + if (entityID > 0) + sqlQuery = sqlQuery.Where(x => x.EntityId == entityID && x.EntityType == entityType); + + sqlQuery = sqlQuery.Where(x => x.HaveFiles); + + var tagNames = sqlQuery.Select(x => String.Format("RelationshipEvent_{0}", x.Id)); + var tagdao = _filesIntegration.DaoFactory.GetTagDao(); + + return tagdao.GetTags(tagNames.ToArray(), TagType.System) + .Where(t => t.EntryType == FileEntryType.File) + .Select(t => Convert.ToInt32(t.EntryId)).ToArray(); + + } + + public List> GetAllFiles(int[] contactID, EntityType entityType, int entityID) + { + var filedao = _filesIntegration.DaoFactory.GetFileDao(); + + var ids = GetFilesIDs(contactID, entityType, entityID); + var files = 0 < ids.Length ? filedao.GetFiles(ids) : new List>(); + + files.ForEach(_crmSecurity.SetAccessTo); + + return files.ToList(); + } + + public Dictionary>> GetFiles(int[] eventID) + { + if (eventID == null || eventID.Length == 0) + throw new ArgumentException("eventID"); + + var tagdao = _filesIntegration.DaoFactory.GetTagDao(); + var filedao = _filesIntegration.DaoFactory.GetFileDao(); + + var findedTags = tagdao.GetTags(eventID.Select(item => String.Concat("RelationshipEvent_", item)).ToArray(), + TagType.System).Where(t => t.EntryType == FileEntryType.File); + + var filesID = findedTags.Select(t => Convert.ToInt32(t.EntryId)).ToArray(); + + var files = 0 < filesID.Length ? filedao.GetFiles(filesID) : new List>(); + + var filesTemp = new Dictionary>(); + + files.ForEach(item => + { + if (!filesTemp.ContainsKey(item.ID)) + filesTemp.Add(item.ID, item); + }); + + return findedTags.Where(x => filesTemp.ContainsKey(x.EntryId)).GroupBy(x => x.TagName).ToDictionary(x => Convert.ToInt32(x.Key.Split(new[] { '_' })[1]), + x => x.Select(item => filesTemp[item.EntryId]).ToList()); + } + + public List> GetFiles(int eventID) + { + if (eventID == 0) + throw new ArgumentException("eventID"); + + var tagdao = _filesIntegration.DaoFactory.GetTagDao(); + var filedao = _filesIntegration.DaoFactory.GetFileDao(); + + var ids = tagdao.GetTags(String.Concat("RelationshipEvent_", eventID), TagType.System).Where(t => t.EntryType == FileEntryType.File).Select(t => Convert.ToInt32(t.EntryId)).ToArray(); + var files = 0 < ids.Length ? filedao.GetFiles(ids) : new List>(); + + files.ForEach(_crmSecurity.SetAccessTo); + + return files.ToList(); + } + + private void RemoveAllFiles(int[] contactID, EntityType entityType, int entityID) + { + if (entityID > 0 && entityType != EntityType.Opportunity && entityType != EntityType.Case) + { + throw new ArgumentException(); + } + + var files = GetAllFiles(contactID, entityType, entityID); + + var dao = _filesIntegration.DaoFactory.GetFileDao(); + + foreach (var file in files) + { + dao.DeleteFile(file.ID); + } + } + + public List RemoveFile(File file) + { + _crmSecurity.DemandDelete(file); + + List eventIDs; + + var tagdao = _filesIntegration.DaoFactory.GetTagDao(); + + var tags = tagdao.GetTags(file.ID, FileEntryType.File, TagType.System).ToList().FindAll(tag => tag.TagName.StartsWith("RelationshipEvent_")); + + eventIDs = tags.Select(item => Convert.ToInt32(item.TagName.Split(new[] { '_' })[1])).ToList(); + + var dao = _filesIntegration.DaoFactory.GetFileDao(); + + dao.DeleteFile(file.ID); + + foreach (var eventID in eventIDs) + { + if (GetFiles(eventID).Count == 0) + { + var dbEntity = CrmDbContext.RelationshipEvent.Find(eventID); + + if (dbEntity.TenantId != TenantID) continue; + + dbEntity.HaveFiles = false; + + CrmDbContext.SaveChanges(); + } + } + + var itemToUpdate = Query(CrmDbContext.Invoices).FirstOrDefault(x => x.FileId == Convert.ToInt32(file.ID)); + + itemToUpdate.FileId = 0; + + CrmDbContext.SaveChanges(); + + return eventIDs; + } + + + public int GetCount(int[] contactID, EntityType entityType, int entityID) + { + if (entityID > 0 && entityType != EntityType.Opportunity && entityType != EntityType.Case) + throw new ArgumentException(); + + var sqlQuery = Query(CrmDbContext.RelationshipEvent); + + if (contactID.Length > 0) + sqlQuery = sqlQuery.Where(x => contactID.Contains(x.ContactId)); + + if (entityID > 0) + sqlQuery = sqlQuery.Where(x => x.EntityId == entityID && x.EntityType == entityType); + + return sqlQuery.Count(); + } + + public RelationshipEvent CreateItem(RelationshipEvent item) + { + _crmSecurity.DemandCreateOrUpdate(item); + + var htmlBody = String.Empty; + + if (item.CreateOn == DateTime.MinValue) + item.CreateOn = _tenantUtil.DateTimeNow(); + + item.CreateBy = _securityContext.CurrentAccount.ID; + item.LastModifedBy = _securityContext.CurrentAccount.ID; + + if (item.CategoryID == (int)HistoryCategorySystem.MailMessage) + { + var jsonObj = JsonDocument.Parse(item.Content).RootElement; + var messageId = jsonObj.GetProperty("message_id").GetInt32(); + + //var apiServer = new ApiServer(); + //var msg = apiServer.GetApiResponse( + // String.Format("{0}mail/messages/{1}.json?id={1}&loadImages=true&needSanitize=true", SetupInfo.WebApiBaseUrl, messageId), "GET"); + + String msg = null; + // if (msg == null) + throw new ArgumentException("Mail message cannot be found"); + + //var msgResponseDto = JObject.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(msg))); + //var msgRequestObj = msgResponseDto.Value("response"); + //string messageUrl; + + //htmlBody = msgRequestObj.Value("htmlBody"); + + //using (var fileStream = new MemoryStream(Encoding.UTF8.GetBytes(htmlBody))) + //{ + // var filePath = String.Format("folder_{0}/message_{1}.html", (messageId / 1000 + 1) * 1000, messageId); + + // Global.GetStore().Save("mail_messages", filePath, fileStream); + + // messageUrl = String.Format("{0}HttpHandlers/filehandler.ashx?action=mailmessage&message_id={1}", PathProvider.BaseAbsolutePath, messageId).ToLower(); + + //} + + //var msg_date_created = msgRequestObj.Value("date"); + //var message_id = msgRequestObj.Value("id"); + //item.Content = JsonConvert.SerializeObject(new + //{ + // @from = msgRequestObj.Value("from"), + // to = msgRequestObj.Value("to"), + // cc = msgRequestObj.Value("cc"), + // bcc = msgRequestObj.Value("bcc"), + // subject = msgRequestObj.Value("subject"), + // important = msgRequestObj.Value("important"), + // chain_id = msgRequestObj.Value("chainId"), + // is_sended = msgRequestObj.Value("folder") != 1, + // date_created = msg_date_created, + // introduction = msgRequestObj.Value("introduction"), + // message_id = message_id, + // message_url = messageUrl + //}); + + //item.CreateOn = DateTime.Parse(msg_date_created, CultureInfo.InvariantCulture); + + //var sqlQueryFindMailsAlready = Query(CRMDbContext.RelationshipEvent) + // .Where(x => x.ContactId == item.ContactID) + // .Where(x => x.EntityType == item.EntityType) + // .Where(x => x.EntityId == item.EntityID) + // .Where(x => x.CategoryId == item.CategoryID) + // .Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Content, String.Format("\"message_id\":{0},", message_id))); + + //if (sqlQueryFindMailsAlready.Count() > 0) + // throw new Exception("Already exists"); + } + + var itemToInsert = new DbRelationshipEvent + { + ContactId = item.ContactID, + Content = item.Content, + CreateOn = _tenantUtil.DateTimeToUtc(item.CreateOn), + CreateBy = item.CreateBy, + EntityType = item.EntityType, + EntityId = item.EntityID, + CategoryId = item.CategoryID, + LastModifedOn = DateTime.UtcNow, + LastModifedBy = item.LastModifedBy, + TenantId = TenantID, + HaveFiles = false + }; + + CrmDbContext.RelationshipEvent.Add(itemToInsert); + CrmDbContext.SaveChanges(); + + item.ID = itemToInsert.Id; + + if (item.CreateOn.Kind == DateTimeKind.Utc) + item.CreateOn = _tenantUtil.DateTimeFromUtc(item.CreateOn); + + _factoryIndexer.Index(itemToInsert); + + return item; + } + + public RelationshipEvent GetByID(int id) + { + var dbEntity = CrmDbContext.RelationshipEvent.Find(id); + + if (dbEntity.TenantId != TenantID) return null; + + var entity = _mapper.Map(dbEntity); + + _crmSecurity.DemandAccessTo(entity); + + return entity; + + } + + public int GetAllItemsCount() + { + return Query(CrmDbContext.RelationshipEvent).Count(); + } + + public List GetAllItems() + { + return GetItems(String.Empty, + EntityType.Any, + 0, + Guid.Empty, + 0, + DateTime.MinValue, + DateTime.MinValue, + 0, + 0, null); + } + + public List GetItems( + String searchText, + EntityType entityType, + int entityID, + Guid createBy, + int categoryID, + DateTime fromDate, + DateTime toDate, + int from, + int count, + OrderBy orderBy) + { + + var sqlQuery = Query(CrmDbContext.RelationshipEvent).AsNoTracking(); + + if (entityID > 0) + switch (entityType) + { + case EntityType.Contact: + var isCompany = false; + + isCompany = Query(CrmDbContext.Contacts).Where(x => x.Id == entityID).Select(x => x.IsCompany).Single(); + + if (isCompany) + return GetItems(searchText, EntityType.Company, entityID, createBy, categoryID, fromDate, toDate, from, count, orderBy); + else + return GetItems(searchText, EntityType.Person, entityID, createBy, categoryID, fromDate, toDate, from, count, orderBy); + case EntityType.Person: + sqlQuery = sqlQuery.Where(x => x.ContactId == entityID); + break; + case EntityType.Company: + + var personIDs = GetRelativeToEntity(entityID, EntityType.Person, null).ToList(); + + if (personIDs.Count == 0) + { + sqlQuery = sqlQuery.Where(x => x.ContactId == entityID); + } + else + { + personIDs.Add(entityID); + sqlQuery = sqlQuery.Where(x => personIDs.Contains(x.ContactId)); + } + + break; + case EntityType.Case: + case EntityType.Opportunity: + sqlQuery = sqlQuery.Where(x => x.EntityId == entityID && x.EntityType == entityType); + break; + } + + if (fromDate != DateTime.MinValue && toDate != DateTime.MinValue) + { + sqlQuery = sqlQuery.Where(x => x.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.CreateOn <= _tenantUtil.DateTimeToUtc(toDate.AddDays(1).AddMinutes(-1))); + } + else if (fromDate != DateTime.MinValue) + { + sqlQuery = sqlQuery.Where(x => x.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate)); + } + else if (toDate != DateTime.MinValue) + { + sqlQuery = sqlQuery.Where(x => x.CreateOn <= _tenantUtil.DateTimeToUtc(toDate).AddDays(1).AddMinutes(-1)); + } + + if (createBy != Guid.Empty) + { + sqlQuery = sqlQuery.Where(x => x.CreateBy == createBy); + } + + if (categoryID != 0) + { + sqlQuery = sqlQuery.Where(x => x.CategoryId == categoryID); + } + + if (!String.IsNullOrEmpty(searchText)) + { + searchText = searchText.Trim(); + + var keywords = searchText.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) + .ToArray(); + + List eventsIds; + + if (!_factoryIndexer.TrySelectIds(s => s.MatchAll(searchText), out eventsIds)) + { + if (keywords.Length > 0) + { + foreach (var k in keywords) + { + sqlQuery = sqlQuery.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Content, k + "%")); + } + } + } + else + { + if (eventsIds.Count == 0) return new List(); + + sqlQuery = sqlQuery.Where(x => eventsIds.Contains(x.Id)); + } + } + + if (0 < from && from < int.MaxValue) + sqlQuery = sqlQuery.Skip(from); + + if (0 < count && count < int.MaxValue) + sqlQuery = sqlQuery.Take(count); + + if (orderBy != null && Enum.IsDefined(typeof(RelationshipEventByType), orderBy.SortedBy)) + { + + switch ((RelationshipEventByType)orderBy.SortedBy) + { + case RelationshipEventByType.Category: + sqlQuery = sqlQuery.OrderBy(x => x.CategoryId); + break; + case RelationshipEventByType.Content: + sqlQuery = sqlQuery.OrderBy(x => x.Content); + break; + case RelationshipEventByType.CreateBy: + sqlQuery = sqlQuery.OrderBy(x => x.CreateBy); + break; + case RelationshipEventByType.Created: + sqlQuery = sqlQuery.OrderBy(x => x.CreateOn); + break; + } + } + else + { + sqlQuery = sqlQuery.OrderBy(x => x.CreateOn); + } + + return _mapper.Map, List>(sqlQuery.ToList()); + } + + public void DeleteItem(int id) + { + var item = GetByID(id); + if (item == null) throw new ArgumentException(); + + DeleteItem(item); + } + + public void DeleteItem(RelationshipEvent item) + { + _crmSecurity.DemandDelete(item); + + var relativeFiles = GetFiles(item.ID); + + var nowFilesEditing = relativeFiles.Where(file => (file.FileStatus & FileStatus.IsEditing) == FileStatus.IsEditing); + + if (nowFilesEditing.Count() != 0) + throw new ArgumentException(); + + relativeFiles.ForEach(f => RemoveFile(f)); + + var itemToDelete = Query(CrmDbContext.RelationshipEvent).Where(x => x.Id == item.ID).Single(); + + _factoryIndexer.Delete(itemToDelete); + + CrmDbContext.RelationshipEvent.Remove(itemToDelete); + + CrmDbContext.SaveChanges(); + + } + + internal class CrmHistoryContent + { + public string to; + public string from; + public string cc; + public string bcc; + public string subject; + public bool important; + public string chain_id; + public bool is_sended; + public string date_created; + public string introduction; + public long message_id; + } + + private static string GetHistoryContentJson(JsonElement apiResponse) + { + var content_struct = new CrmHistoryContent + { + @from = apiResponse.GetProperty("from").GetString(), + to = apiResponse.GetProperty("to").GetString(), + cc = apiResponse.GetProperty("cc").GetString(), + bcc = apiResponse.GetProperty("bcc").GetString(), + subject = apiResponse.GetProperty("subject").GetString(), + important = apiResponse.GetProperty("important").GetBoolean(), + chain_id = apiResponse.GetProperty("chainId").GetString(), + is_sended = apiResponse.GetProperty("folder").GetInt32() == 1, + date_created = apiResponse.GetProperty("date").GetString(), + introduction = apiResponse.GetProperty("introduction").GetString(), + message_id = apiResponse.GetProperty("id").GetInt32() + }; + + return JsonSerializer.Serialize(content_struct); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Dao/ReportDao.cs b/products/ASC.CRM/Server/Core/Dao/ReportDao.cs new file mode 100644 index 00000000000..e6c6b9e8c49 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/ReportDao.cs @@ -0,0 +1,2345 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.Core.Common.Settings; +using ASC.Core.Tenants; +using ASC.CRM.Classes; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; +using ASC.VoipService; +using ASC.Web.Core.Users; +using ASC.Web.CRM.Classes; +using ASC.Web.Files.Api; + +using AutoMapper; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + + +#endregion + +namespace ASC.CRM.Core.Dao +{ + [Scope] + public class ReportDao : AbstractDao + { + const string TimeFormat = "[h]:mm:ss;@"; + const string ShortDateFormat = "M/d/yyyy"; + + private IServiceProvider _serviceProvider; + private TenantManager _tenantManager; + private UserManager _userManager; + private Global _global; + private FilesIntegration _filesIntegration; + private CurrencyInfo _defaultCurrency; + private TenantUtil _tenantUtil; + private DaoFactory _daoFactory; + private UserDbContext _userDbContext; + private DisplayUserSettingsHelper _displayUserSettings; + + #region Constructor + + public ReportDao(DbContextManager dbContextManager, + DbContextManager dbUserContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + FilesIntegration filesIntegration, + IOptionsMonitor logger, + ICache ascCache, + TenantUtil tenantUtil, + SettingsManager settingsManager, + Global global, + UserManager userManager, + IServiceProvider serviceProvider, + CurrencyProvider currencyProvider, + DaoFactory daoFactory, + DisplayUserSettingsHelper displayUserSettingsHelper, + IMapper mapper) : + base(dbContextManager, + tenantManager, + securityContext, + logger, + ascCache, + mapper) + { + _tenantUtil = tenantUtil; + + _filesIntegration = filesIntegration; + _global = global; + _userManager = userManager; + _tenantManager = tenantManager; + _serviceProvider = serviceProvider; + _daoFactory = daoFactory; + + var crmSettings = settingsManager.Load(); + + _userDbContext = dbUserContextManager.Get(CrmConstants.DatabaseId); + + _defaultCurrency = currencyProvider.Get(crmSettings.DefaultCurrency); + + _displayUserSettings = displayUserSettingsHelper; + + } + + + #endregion + + + + #region Common Methods + + private void GetTimePeriod(ReportTimePeriod timePeriod, out DateTime fromDate, out DateTime toDate) + { + var now = _tenantUtil.DateTimeNow().Date; + + var diff = (int)now.DayOfWeek - (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek; + if (diff < 0) + { + diff += 7; + } + + var quarter = (now.Month + 2) / 3; + + var year = now.Year; + + switch (timePeriod) + { + case ReportTimePeriod.Today: + fromDate = now; + toDate = now.AddDays(1).AddSeconds(-1); + break; + case ReportTimePeriod.Yesterday: + fromDate = now.AddDays(-1); + toDate = now.AddSeconds(-1); + break; + case ReportTimePeriod.Tomorrow: + fromDate = now.AddDays(1); + toDate = now.AddDays(2).AddSeconds(-1); + break; + case ReportTimePeriod.CurrentWeek: + fromDate = now.AddDays(-1 * diff); + toDate = now.AddDays(-1 * diff + 7).AddSeconds(-1); + break; + case ReportTimePeriod.PreviousWeek: + fromDate = now.AddDays(-1 * diff - 7); + toDate = now.AddDays(-1 * diff).AddSeconds(-1); + break; + case ReportTimePeriod.NextWeek: + fromDate = now.AddDays(-1 * diff + 7); + toDate = now.AddDays(-1 * diff + 14).AddSeconds(-1); + break; + case ReportTimePeriod.CurrentMonth: + fromDate = new DateTime(now.Year, now.Month, 1); + toDate = new DateTime(now.Year, now.Month, 1).AddMonths(1).AddSeconds(-1); + break; + case ReportTimePeriod.PreviousMonth: + toDate = new DateTime(now.Year, now.Month, 1).AddSeconds(-1); + fromDate = new DateTime(toDate.Year, toDate.Month, 1); + break; + case ReportTimePeriod.NextMonth: + fromDate = new DateTime(now.Year, now.Month, 1).AddMonths(1); + toDate = new DateTime(now.Year, now.Month, 1).AddMonths(2).AddSeconds(-1); + break; + case ReportTimePeriod.CurrentQuarter: + fromDate = new DateTime(now.Year, quarter * 3 - 2, 1); + toDate = new DateTime(now.Year, fromDate.Month, 1).AddMonths(3).AddSeconds(-1); + break; + case ReportTimePeriod.PreviousQuarter: + quarter--; + if (quarter == 0) + { + year--; + quarter = 4; + } + fromDate = new DateTime(year, quarter * 3 - 2, 1); + toDate = new DateTime(year, fromDate.Month, 1).AddMonths(3).AddSeconds(-1); + break; + case ReportTimePeriod.NextQuarter: + quarter++; + if (quarter == 5) + { + year++; + quarter = 1; + } + fromDate = new DateTime(year, quarter * 3 - 2, 1); + toDate = new DateTime(year, fromDate.Month, 1).AddMonths(3).AddSeconds(-1); + break; + case ReportTimePeriod.CurrentYear: + fromDate = new DateTime(now.Year, 1, 1); + toDate = new DateTime(now.Year, 1, 1).AddYears(1).AddSeconds(-1); + break; + case ReportTimePeriod.PreviousYear: + toDate = new DateTime(now.Year, 1, 1).AddSeconds(-1); + fromDate = new DateTime(toDate.Year, 1, 1); + break; + case ReportTimePeriod.NextYear: + fromDate = new DateTime(now.Year, 1, 1).AddYears(1); + toDate = new DateTime(now.Year, 1, 1).AddYears(2).AddSeconds(-1); + break; + case ReportTimePeriod.DuringAllTime: + fromDate = DateTime.MinValue; + toDate = DateTime.MaxValue; + break; + default: + fromDate = DateTime.MinValue; + toDate = DateTime.MinValue; + break; + } + } + + private string GetTimePeriodText(ReportTimePeriod timePeriod) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + switch (timePeriod) + { + case ReportTimePeriod.Today: + case ReportTimePeriod.Yesterday: + case ReportTimePeriod.Tomorrow: + return fromDate.ToShortDateString(); + case ReportTimePeriod.CurrentWeek: + case ReportTimePeriod.PreviousWeek: + case ReportTimePeriod.NextWeek: + return string.Format("{0}-{1}", fromDate.ToShortDateString(), toDate.ToShortDateString()); + case ReportTimePeriod.CurrentMonth: + case ReportTimePeriod.PreviousMonth: + case ReportTimePeriod.NextMonth: + return fromDate.ToString("Y"); + case ReportTimePeriod.CurrentQuarter: + case ReportTimePeriod.PreviousQuarter: + case ReportTimePeriod.NextQuarter: + return string.Format("{0}-{1}", fromDate.ToString("Y"), toDate.ToString("Y")); + case ReportTimePeriod.CurrentYear: + case ReportTimePeriod.PreviousYear: + case ReportTimePeriod.NextYear: + return fromDate.Year.ToString(CultureInfo.InvariantCulture); + case ReportTimePeriod.DuringAllTime: + return CRMReportResource.DuringAllTime; + default: + return string.Empty; + } + } + + public List GetMissingRates(string defaultCurrency) + { + var existingRatesQuery = CrmDbContext.CurrencyRate + .Where(x => x.ToCurrency == defaultCurrency) + .Select(x => x.FromCurrency).Distinct().ToList(); + + var missingRatesQuery = Query(CrmDbContext.Deals) + .Where(x => x.BidCurrency != defaultCurrency && !existingRatesQuery.Contains(x.BidCurrency)) + .Select(x => x.BidCurrency) + .Distinct(); + + return missingRatesQuery.ToList(); + } + + #endregion + + #region Report Files + + public List> SaveSampleReportFiles() + { + var result = new List>(); + + var storeTemplate = _global.GetStoreTemplate(); + + if (storeTemplate == null) return result; + + var culture = _userManager.GetUsers(_securityContext.CurrentAccount.ID).GetCulture() ?? + _tenantManager.GetCurrentTenant().GetCulture(); + + var path = culture + "/"; + + if (!storeTemplate.IsDirectory(path)) + { + path = "default/"; + if (!storeTemplate.IsDirectory(path)) return result; + } + + foreach (var filePath in storeTemplate.ListFilesRelative("", path, "*", false).Select(x => path + x)) + { + using (var stream = storeTemplate.GetReadStream("", filePath)) + { + + var document = _serviceProvider.GetService>(); + + document.Title = Path.GetFileName(filePath); + document.FolderID = _daoFactory.GetFileDao().GetRoot(); + document.ContentLength = stream.Length; + + + var file = _daoFactory.GetFileDao().SaveFile(document, stream); + + SaveFile((int)file.ID, -1); + + result.Add(file); + } + } + + return result; + } + + public List> GetFiles() + { + return GetFiles(_securityContext.CurrentAccount.ID); + } + + public List> GetFiles(Guid userId) + { + var filedao = _filesIntegration.DaoFactory.GetFileDao(); + + var fileIds = Query(CrmDbContext.ReportFile).Where(x => x.CreateBy == userId).Select(x => x.FileId).ToArray(); + + return fileIds.Length > 0 ? filedao.GetFiles(fileIds) : new List>(); + + } + + public List GetFileIds(Guid userId) + { + return Query(CrmDbContext.ReportFile) + .Where(x => x.CreateBy == userId).Select(x => x.FileId).ToList(); + } + + public Files.Core.File GetFile(int fileid) + { + return GetFile(fileid, _securityContext.CurrentAccount.ID); + } + + public Files.Core.File GetFile(int fileid, Guid userId) + { + var exist = Query(CrmDbContext.ReportFile) + .Any(x => x.CreateBy == userId && x.FileId == fileid); + + var filedao = _filesIntegration.DaoFactory.GetFileDao(); + + return exist ? filedao.GetFile(fileid) : null; + + } + + public void DeleteFile(int fileid) + { + var itemToDelete = Query(CrmDbContext.ReportFile).Where(x => x.FileId == fileid && x.CreateBy == _securityContext.CurrentAccount.ID); + + CrmDbContext.Remove(itemToDelete); + CrmDbContext.SaveChanges(); + + var filedao = _filesIntegration.DaoFactory.GetFileDao(); + + filedao.DeleteFile(fileid); + } + + public void DeleteFiles(Guid userId) + { + var fileIds = GetFileIds(userId); + + var itemToDelete = Query(CrmDbContext.ReportFile).Where(x => x.CreateBy == _securityContext.CurrentAccount.ID); + + CrmDbContext.Remove(itemToDelete); + CrmDbContext.SaveChanges(); + + var filedao = _filesIntegration.DaoFactory.GetFileDao(); + + foreach (var fileId in fileIds) + { + filedao.DeleteFile(fileId); + } + + } + + public void SaveFile(int fileId, int reportType) + { + + var itemToInsert = new DbReportFile + { + FileId = fileId, + ReportType = (ReportType)reportType, + CreateOn = _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow()), + CreateBy = _securityContext.CurrentAccount.ID, + TenantId = TenantID + }; + + CrmDbContext.Add(itemToInsert); + CrmDbContext.SaveChanges(); + } + + #endregion + + + #region SalesByManagersReport + + public bool CheckSalesByManagersReportData(ReportTimePeriod timePeriod, Guid[] managers) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + + return Query(CrmDbContext.Deals).Join(Query(CrmDbContext.DealMilestones), + x => x.DealMilestoneId, + y => y.Id, + (x, y) => new { x, y }) + .Where(x => x.y.Status == DealMilestoneStatus.ClosedAndWon) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.x.ResponsibleId) : true) + .Where(x => x.x.ActualCloseDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.x.ActualCloseDate <= _tenantUtil.DateTimeToUtc(toDate)) + .Any(); + } + + public object GetSalesByManagersReportData(ReportTimePeriod timePeriod, Guid[] managers, string defaultCurrency) + { + var reportData = BuildSalesByManagersReport(timePeriod, managers, defaultCurrency); + + return reportData == null || !reportData.Any() ? null : GenerateReportData(timePeriod, reportData); + } + + private List BuildSalesByManagersReport(ReportTimePeriod timePeriod, Guid[] managers, string defaultCurrency) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + Func exp; + + switch (timePeriod) + { + case ReportTimePeriod.Today: + case ReportTimePeriod.Yesterday: + exp = x => x ?? x.Value.Date.AddHours(x.Value.Hour); + + break; + case ReportTimePeriod.CurrentWeek: + case ReportTimePeriod.PreviousWeek: + case ReportTimePeriod.CurrentMonth: + case ReportTimePeriod.PreviousMonth: + exp = x => x ?? x.Value.Date; + + break; + case ReportTimePeriod.CurrentQuarter: + case ReportTimePeriod.PreviousQuarter: + case ReportTimePeriod.CurrentYear: + case ReportTimePeriod.PreviousYear: + exp = x => x ?? x.Value.Date.AddDays(-(x.Value.Day - 1)); + + break; + default: + return null; + } + + var result = Query(CrmDbContext.Deals) + .GroupJoin(Query(CrmDbContext.CurrencyRate), + x => x.BidCurrency, + y => y.FromCurrency, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x, CurrencyRate = y }) + .GroupJoin(Query(CrmDbContext.DealMilestones), + x => x.Deal.DealMilestoneId, + y => y.Id, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x.Deal, CurrencyRate = x.x.CurrencyRate, DealMilestone = y }) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Deal.ResponsibleId) : true) + .Where(x => x.Deal.ActualCloseDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.Deal.ActualCloseDate <= _tenantUtil.DateTimeToUtc(toDate)) + .Where(x => x.DealMilestone.Status == DealMilestoneStatus.ClosedAndWon) + .GroupBy(x => new { x.Deal.ResponsibleId, x.Deal.ActualCloseDate, x.DealMilestone.Status }) + .Select(x => new + { + responsible_id = x.Key.ResponsibleId, + status = x.Key.Status, + count = x.Count(), + deals_value = x.Sum(x => x.Deal.BidType == 0 ? x.Deal.BidValue * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate) : + x.Deal.BidValue * (x.Deal.PerPeriodValue == 0 ? 1.0m : Convert.ToDecimal(x.Deal.PerPeriodValue)) * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate)), + close_date = exp.Invoke(x.Key.ActualCloseDate) + }).ToList() + .ConvertAll(x => new SalesByManager + { + UserId = x.responsible_id, + UserName = _displayUserSettings.GetFullUserName(x.responsible_id), + Value = x.deals_value, + Date = x.close_date == DateTime.MinValue ? DateTime.MinValue : _tenantUtil.DateTimeFromUtc(x.close_date) + }); + + return result; + } + + private object GenerateReportData(ReportTimePeriod timePeriod, List data) + { + switch (timePeriod) + { + case ReportTimePeriod.Today: + case ReportTimePeriod.Yesterday: + return GenerateReportDataByHours(timePeriod, data); + case ReportTimePeriod.CurrentWeek: + case ReportTimePeriod.PreviousWeek: + case ReportTimePeriod.CurrentMonth: + case ReportTimePeriod.PreviousMonth: + return GenerateReportDataByDays(timePeriod, data); + case ReportTimePeriod.CurrentQuarter: + case ReportTimePeriod.PreviousQuarter: + case ReportTimePeriod.CurrentYear: + case ReportTimePeriod.PreviousYear: + return GenerateReportByMonths(timePeriod, data); + default: + return null; + } + } + + private object GenerateReportDataByHours(ReportTimePeriod timePeriod, List data) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + var res = new Dictionary>(); + + var users = data.Select(x => x.UserId).Distinct().ToList(); + + foreach (var userId in users) + { + var date = fromDate; + + while (date < toDate) + { + if (res.ContainsKey(userId)) + { + res[userId].Add(date, 0); + } + else + { + res.Add(userId, new Dictionary { { date, 0 } }); + } + + date = date.AddHours(1); + } + } + + foreach (var item in data) + { + var itemDate = new DateTime(item.Date.Year, item.Date.Month, item.Date.Day, item.Date.Hour, 0, 0); + + if (itemDate < res[item.UserId].First().Key) + itemDate = res[item.UserId].First().Key; + + if (itemDate > res[item.UserId].Last().Key) + itemDate = res[item.UserId].Last().Key; + + res[item.UserId][itemDate] += item.Value; + } + + var body = new List>(); + + foreach (var resItem in res) + { + var bodyItem = new List + { + data.First(x => x.UserId == resItem.Key).UserName + }; + + bodyItem.AddRange(resItem.Value.Select(x => new { format = "0.00", value = x.Value.ToString(CultureInfo.InvariantCulture) })); + + body.Add(bodyItem); + } + + var head = new List(); + + foreach (var key in res.First().Value.Keys) + { + head.Add(new { format = "H:mm", value = key.ToShortTimeString() }); + } + + return new + { + resource = new + { + manager = CRMReportResource.Manager, + summary = CRMReportResource.Sum, + total = CRMReportResource.Total, + dateRangeLabel = CRMReportResource.TimePeriod + ":", + dateRangeValue = GetTimePeriodText(timePeriod), + sheetName = CRMReportResource.SalesByManagersReport, + header = CRMReportResource.SalesByManagersReport, + header1 = CRMReportResource.SalesByHour + ", " + _defaultCurrency.Symbol, + header2 = CRMReportResource.TotalSalesByManagers + ", " + _defaultCurrency.Symbol, + chartName1 = CRMReportResource.SalesByHour + ", " + _defaultCurrency.Symbol, + chartName2 = CRMReportResource.TotalSalesByManagers + ", " + _defaultCurrency.Symbol + }, + thead = head, + tbody = body + }; + } + + private object GenerateReportDataByDays(ReportTimePeriod timePeriod, List data) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + var res = new Dictionary>(); + + var users = data.Select(x => x.UserId).Distinct().ToList(); + + foreach (var userId in users) + { + var date = fromDate; + + while (date < toDate) + { + if (res.ContainsKey(userId)) + { + res[userId].Add(date, 0); + } + else + { + res.Add(userId, new Dictionary { { date, 0 } }); + } + + date = date.AddDays(1); + } + } + + foreach (var item in data) + { + var itemDate = new DateTime(item.Date.Year, item.Date.Month, item.Date.Day); + + if (itemDate < res[item.UserId].First().Key) + itemDate = res[item.UserId].First().Key; + + if (itemDate > res[item.UserId].Last().Key) + itemDate = res[item.UserId].Last().Key; + + res[item.UserId][itemDate] += item.Value; + } + + var body = new List>(); + + foreach (var resItem in res) + { + var bodyItem = new List + { + data.First(x => x.UserId == resItem.Key).UserName + }; + + bodyItem.AddRange(resItem.Value.Select(x => new { format = "0.00", value = x.Value.ToString(CultureInfo.InvariantCulture) })); + + body.Add(bodyItem); + } + + var head = new List(); + var separator = CultureInfo.CurrentCulture.DateTimeFormat.DateSeparator.ToCharArray(); + var pattern = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern.Replace("yyyy", string.Empty).Trim(separator); + + foreach (var key in res.First().Value.Keys) + { + head.Add(new { format = pattern, value = key.ToString(ShortDateFormat, CultureInfo.InvariantCulture) }); + } + + return new + { + resource = new + { + manager = CRMReportResource.Manager, + summary = CRMReportResource.Sum, + total = CRMReportResource.Total, + dateRangeLabel = CRMReportResource.TimePeriod + ":", + dateRangeValue = GetTimePeriodText(timePeriod), + sheetName = CRMReportResource.SalesByManagersReport, + header = CRMReportResource.SalesByManagersReport, + header1 = CRMReportResource.SalesByDay + ", " + _defaultCurrency.Symbol, + header2 = CRMReportResource.TotalSalesByManagers + ", " + _defaultCurrency.Symbol, + chartName1 = CRMReportResource.SalesByDay + ", " + _defaultCurrency.Symbol, + chartName2 = CRMReportResource.TotalSalesByManagers + ", " + _defaultCurrency.Symbol + }, + thead = head, + tbody = body + }; + } + + private object GenerateReportByMonths(ReportTimePeriod timePeriod, List data) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + var res = new Dictionary>(); + + var users = data.Select(x => x.UserId).Distinct().ToList(); + + foreach (var userId in users) + { + var date = fromDate; + + while (date < toDate) + { + if (res.ContainsKey(userId)) + { + res[userId].Add(date, 0); + } + else + { + res.Add(userId, new Dictionary { { date, 0 } }); + } + + date = date.AddMonths(1); + } + } + + foreach (var item in data) + { + var itemDate = new DateTime(item.Date.Year, item.Date.Month, 1); + + if (itemDate < res[item.UserId].First().Key) + itemDate = res[item.UserId].First().Key; + + if (itemDate > res[item.UserId].Last().Key) + itemDate = res[item.UserId].Last().Key; + + res[item.UserId][itemDate] += item.Value; + } + + var body = new List>(); + + foreach (var resItem in res) + { + var bodyItem = new List + { + data.First(x => x.UserId == resItem.Key).UserName + }; + + bodyItem.AddRange(resItem.Value.Select(x => new { format = "0.00", value = x.Value.ToString(CultureInfo.InvariantCulture) })); + + body.Add(bodyItem); + } + + var head = new List(); + + foreach (var key in res.First().Value.Keys) + { + head.Add(new { format = "MMM-yy", value = key.ToString(ShortDateFormat, CultureInfo.InvariantCulture) }); + } + + return new + { + resource = new + { + manager = CRMReportResource.Manager, + summary = CRMReportResource.Sum, + total = CRMReportResource.Total, + dateRangeLabel = CRMReportResource.TimePeriod + ":", + dateRangeValue = GetTimePeriodText(timePeriod), + sheetName = CRMReportResource.SalesByManagersReport, + header = CRMReportResource.SalesByManagersReport, + header1 = CRMReportResource.SalesByMonth + ", " + _defaultCurrency.Symbol, + header2 = CRMReportResource.TotalSalesByManagers + ", " + _defaultCurrency.Symbol, + chartName1 = CRMReportResource.SalesByMonth + ", " + _defaultCurrency.Symbol, + chartName2 = CRMReportResource.TotalSalesByManagers + ", " + _defaultCurrency.Symbol + }, + thead = head, + tbody = body + }; + } + + #endregion + + + #region SalesForecastReport + + public bool CheckSalesForecastReportData(ReportTimePeriod timePeriod, Guid[] managers) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + return Query(CrmDbContext.Deals).Join(Query(CrmDbContext.DealMilestones), + x => x.DealMilestoneId, + y => y.Id, + (x, y) => new { x, y }) + .Where(x => x.y.Status == DealMilestoneStatus.Open) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.x.ResponsibleId) : true) + .Where(x => x.x.ExpectedCloseDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.x.ActualCloseDate <= _tenantUtil.DateTimeToUtc(toDate)) + .Any(); + } + + public object GetSalesForecastReportData(ReportTimePeriod timePeriod, Guid[] managers, string defaultCurrency) + { + var reportData = BuildSalesForecastReport(timePeriod, managers, defaultCurrency); + + return reportData == null || !reportData.Any() ? null : GenerateReportData(timePeriod, reportData); + } + + private List BuildSalesForecastReport(ReportTimePeriod timePeriod, Guid[] managers, string defaultCurrency) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + Func exp; + + switch (timePeriod) + { + case ReportTimePeriod.CurrentWeek: + case ReportTimePeriod.NextWeek: + case ReportTimePeriod.CurrentMonth: + case ReportTimePeriod.NextMonth: + exp = x => x.Date; + break; + case ReportTimePeriod.CurrentQuarter: + case ReportTimePeriod.NextQuarter: + case ReportTimePeriod.CurrentYear: + case ReportTimePeriod.NextYear: + exp = x => x.Date.AddDays(-(x.Day - 1)); + break; + default: + return null; + } + + var result = Query(CrmDbContext.Deals) + .GroupJoin(Query(CrmDbContext.CurrencyRate), + x => x.BidCurrency, + y => y.FromCurrency, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x, CurrencyRate = y }) + .GroupJoin(Query(CrmDbContext.DealMilestones), + x => x.Deal.DealMilestoneId, + y => y.Id, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x.Deal, CurrencyRate = x.x.CurrencyRate, DealMilestone = y }) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Deal.ResponsibleId) : true) + .Where(x => x.Deal.ExpectedCloseDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.Deal.ExpectedCloseDate <= _tenantUtil.DateTimeToUtc(toDate)) + .Where(x => x.DealMilestone.Status == DealMilestoneStatus.ClosedAndWon) + .GroupBy(x => new { x.Deal.ExpectedCloseDate }) + .Select(x => new + { + deals_value = x.Sum(x => x.Deal.BidType == 0 ? x.Deal.BidValue * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate) : + x.Deal.BidValue * (x.Deal.PerPeriodValue == 0 ? 1.0m : Convert.ToDecimal(x.Deal.PerPeriodValue)) * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate)), + + value_with_probability = x.Sum(x => x.Deal.BidType == 0 ? x.Deal.BidValue * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate) * x.Deal.DealMilestoneProbability / 100.0m : + x.Deal.BidValue * (x.Deal.PerPeriodValue == 0 ? 1.0m : Convert.ToDecimal(x.Deal.PerPeriodValue)) * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate) * x.Deal.DealMilestoneProbability / 100.0m), + close_date = exp.Invoke(x.Key.ExpectedCloseDate) + }).ToList() + .ConvertAll(x => new SalesForecast + { + Value = x.deals_value, + ValueWithProbability = x.value_with_probability, + Date = x.close_date == DateTime.MinValue ? DateTime.MinValue : _tenantUtil.DateTimeFromUtc(x.close_date) + }); + + + return result; + } + + private SalesForecast ToSalesForecast(object[] row) + { + return new SalesForecast + { + Value = Convert.ToDecimal(row[0]), + ValueWithProbability = Convert.ToDecimal(row[1]), + Date = Convert.ToDateTime(row[2]) == DateTime.MinValue ? DateTime.MinValue : _tenantUtil.DateTimeFromUtc(Convert.ToDateTime(row[2])) + }; + } + + private object GenerateReportData(ReportTimePeriod timePeriod, List data) + { + switch (timePeriod) + { + case ReportTimePeriod.CurrentWeek: + case ReportTimePeriod.NextWeek: + case ReportTimePeriod.CurrentMonth: + case ReportTimePeriod.NextMonth: + return GenerateReportDataByDays(timePeriod, data); + case ReportTimePeriod.CurrentQuarter: + case ReportTimePeriod.NextQuarter: + case ReportTimePeriod.CurrentYear: + case ReportTimePeriod.NextYear: + return GenerateReportByMonths(timePeriod, data); + default: + return null; + } + } + + private object GenerateReportDataByDays(ReportTimePeriod timePeriod, List data) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + var res = new Dictionary>(); + + var date = fromDate; + + while (date < toDate) + { + res.Add(date, new Tuple(0, 0)); + date = date.AddDays(1); + } + + foreach (var item in data) + { + var key = new DateTime(item.Date.Year, item.Date.Month, item.Date.Day); + + if (key < res.First().Key) + key = res.First().Key; + + if (key > res.Last().Key) + key = res.Last().Key; + + res[key] = new Tuple(res[key].Item1 + item.ValueWithProbability, + res[key].Item2 + item.Value); + } + + var body = new List>(); + var separator = CultureInfo.CurrentCulture.DateTimeFormat.DateSeparator.ToCharArray(); + var pattern = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern.Replace("yyyy", string.Empty).Trim(separator); + + foreach (var resItem in res) + { + var bodyItem = new List + { + new {format = pattern, value = resItem.Key.ToString(ShortDateFormat, CultureInfo.InvariantCulture)}, + new {format = "0.00", value = resItem.Value.Item1.ToString(CultureInfo.InvariantCulture)}, + new {format = "0.00", value = resItem.Value.Item2.ToString(CultureInfo.InvariantCulture)} + }; + + body.Add(bodyItem); + } + + var head = new List + { + CRMReportResource.Day, + CRMReportResource.WithRespectToProbability, + CRMReportResource.IfAllOpportunitiesWon + }; + + return new + { + resource = new + { + total = CRMReportResource.Total, + dateRangeLabel = CRMReportResource.TimePeriod + ":", + dateRangeValue = GetTimePeriodText(timePeriod), + sheetName = CRMReportResource.SalesForecastReport, + header = CRMReportResource.SalesForecastReport, + header1 = CRMReportResource.SalesForecastReport + ", " + _defaultCurrency.Symbol, + chartName = CRMReportResource.SalesForecastReport + ", " + _defaultCurrency.Symbol + }, + thead = head, + tbody = body + }; + } + + private object GenerateReportByMonths(ReportTimePeriod timePeriod, List data) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + var res = new Dictionary>(); + + var date = fromDate; + + while (date < toDate) + { + res.Add(date, new Tuple(0, 0)); + date = date.AddMonths(1); + } + + foreach (var item in data) + { + var key = new DateTime(item.Date.Year, item.Date.Month, 1); + + if (key < res.First().Key) + key = res.First().Key; + + if (key > res.Last().Key) + key = res.Last().Key; + + res[key] = new Tuple(res[key].Item1 + item.ValueWithProbability, + res[key].Item2 + item.Value); + } + + var body = new List>(); + + foreach (var resItem in res) + { + var bodyItem = new List + { + new {format = "MMM-yy", value = resItem.Key.ToString(ShortDateFormat, CultureInfo.InvariantCulture)}, + new {format = "0.00", value = resItem.Value.Item1.ToString(CultureInfo.InvariantCulture)}, + new {format = "0.00", value = resItem.Value.Item2.ToString(CultureInfo.InvariantCulture)} + }; + + body.Add(bodyItem); + } + + var head = new List + { + CRMReportResource.Month, + CRMReportResource.WithRespectToProbability, + CRMReportResource.IfAllOpportunitiesWon + }; + + return new + { + resource = new + { + total = CRMReportResource.Total, + dateRangeLabel = CRMReportResource.TimePeriod + ":", + dateRangeValue = GetTimePeriodText(timePeriod), + sheetName = CRMReportResource.SalesForecastReport, + header = CRMReportResource.SalesForecastReport, + header1 = CRMReportResource.SalesForecastReport + ", " + _defaultCurrency.Symbol, + chartName = CRMReportResource.SalesForecastReport + ", " + _defaultCurrency.Symbol + }, + thead = head, + tbody = body + }; + } + + #endregion + + + #region SalesFunnelReport + + public bool CheckSalesFunnelReportData(ReportTimePeriod timePeriod, Guid[] managers) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + return Query(CrmDbContext.Deals) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.ResponsibleId) : true) + .Where(x => x.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .Any(); + } + + public object GetSalesFunnelReportData(ReportTimePeriod timePeriod, Guid[] managers, string defaultCurrency) + { + var reportData = BuildSalesFunnelReport(timePeriod, managers, defaultCurrency); + + return reportData == null || !reportData.Any() ? null : GenerateReportData(timePeriod, reportData); + } + + private List BuildSalesFunnelReport(ReportTimePeriod timePeriod, Guid[] managers, string defaultCurrency) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + var result = Query(CrmDbContext.DealMilestones) + .GroupJoin(Query(CrmDbContext.Deals), + x => x.Id, + y => y.DealMilestoneId, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = y, DealMilestone = x.x }) + .GroupJoin(Query(CrmDbContext.CurrencyRate), + x => x.Deal.BidCurrency, + y => y.FromCurrency, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x.Deal, DealMilestone = x.x.DealMilestone, CurrencyRate = y }) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Deal.ResponsibleId) : true) + .Where(x => x.Deal.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.Deal.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .Where(x => x.DealMilestone.Status == DealMilestoneStatus.Open) + .GroupBy(x => new { x.DealMilestone.Id, x.DealMilestone.Title, x.DealMilestone.Status }) + .Select(x => new + { + status = x.Key.Status, + title = x.Key.Title, + deals_count = x.Count(), + deals_value = x.Sum(x => x.Deal.BidType == 0 ? x.Deal.BidValue * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate) : + x.Deal.BidValue * (x.Deal.PerPeriodValue == 0 ? 1.0m : Convert.ToDecimal(x.Deal.PerPeriodValue)) * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate)), + deals_duration = x.Average(x => x.DealMilestone.Status == DealMilestoneStatus.ClosedAndWon ? (x.Deal.ActualCloseDate - x.Deal.CreateOn).Value.Days : 0) + }) + .ToList() + .ConvertAll(x => + new SalesFunnel + { + Status = x.status, + Title = x.title, + Count = x.deals_count, + Value = x.deals_value, + Duration = Convert.ToInt32(x.deals_duration) + }); + + return result; + } + + private object GenerateReportData(ReportTimePeriod timePeriod, List data) + { + var totalCount = data.Sum(x => x.Count); + + if (totalCount == 0) return null; + + var totalBudget = data.Sum(x => x.Value); + + var closed = data.Where(x => x.Status == DealMilestoneStatus.ClosedAndWon).ToList(); + + var reportData = data.Select(item => new List + { + item.Title, + item.Status, + item.Count, + item.Value + }).ToList(); + + return new + { + resource = new + { + header = CRMReportResource.SalesFunnelReport, + sheetName = CRMReportResource.SalesFunnelReport, + dateRangeLabel = CRMReportResource.TimePeriod + ":", + dateRangeValue = GetTimePeriodText(timePeriod), + + chartName = CRMReportResource.SalesFunnelByCount, + chartName1 = CRMReportResource.SalesFunnelByBudget + ", " + _defaultCurrency.Symbol, + chartName2 = CRMReportResource.DealsCount, + chartName3 = CRMReportResource.DealsBudget + ", " + _defaultCurrency.Symbol, + + totalCountLabel = CRMReportResource.TotalDealsCount, + totalCountValue = totalCount, + + totalBudgetLabel = CRMReportResource.TotalDealsBudget + ", " + _defaultCurrency.Symbol, + totalBudgetValue = totalBudget, + + averageBidLabel = CRMReportResource.AverageDealsBudget + ", " + _defaultCurrency.Symbol, + averageBidValue = totalBudget / totalCount, + + averageDurationLabel = CRMReportResource.AverageDealsDuration, + averageDurationValue = closed.Sum(x => x.Duration) / closed.Count, + + header1 = CRMReportResource.ByCount, + header2 = CRMReportResource.ByBudget + ", " + _defaultCurrency.Symbol, + + stage = CRMReportResource.Stage, + count = CRMReportResource.Count, + budget = CRMReportResource.Budget, + conversion = CRMReportResource.Conversion, + + deals = CRMDealResource.Deals, + status0 = DealMilestoneStatus.Open.ToLocalizedString(), + status1 = DealMilestoneStatus.ClosedAndWon.ToLocalizedString(), + status2 = DealMilestoneStatus.ClosedAndLost.ToLocalizedString() + }, + data = reportData + }; + } + + #endregion + + + #region WorkloadByContactsReport + + public bool CheckWorkloadByContactsReportData(ReportTimePeriod timePeriod, Guid[] managers) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + return Query(CrmDbContext.Contacts) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.CreateBy) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .Any(); + } + + public object GetWorkloadByContactsReportData(ReportTimePeriod timePeriod, Guid[] managers) + { + var reportData = BuildWorkloadByContactsReport(timePeriod, managers); + + return reportData == null || !reportData.Any() ? null : GenerateReportData(timePeriod, reportData); + } + + private List BuildWorkloadByContactsReport(ReportTimePeriod timePeriod, Guid[] managers) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + var result = Query(CrmDbContext.Contacts) + .GroupJoin(Query(CrmDbContext.ListItem), + x => x.ContactTypeId, + y => y.Id, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Contact = x.x, ListItem = y }) + .GroupJoin(Query(CrmDbContext.Deals), + x => x.Contact.Id, + y => y.ContactId, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Contact = x.x.Contact, ListItem = x.x.ListItem, Deal = y }) + .Where(x => x.ListItem.ListType == ListType.ContactType) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Contact.CreateBy) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.Contact.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.Contact.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .GroupBy(x => new { x.Contact.CreateBy, x.ListItem.Id, x.ListItem.Title }) + .Select(x => new + { + create_by = x.Key.CreateBy, + id = x.Key.Id, + title = x.Key.Title, + total = x.Count(x => x.Contact.Id > 0), + with_deals = x.Count(x => x.Deal.Id > 0) + }) + .OrderBy(x => x.title) + .ToList() + .ConvertAll(x => new WorkloadByContacts + { + UserId = x.create_by, + UserName = _displayUserSettings.GetFullUserName(x.create_by), + CategoryId = x.id, + CategoryName = x.title, + Count = x.total, + WithDeals = x.with_deals + }); + + // .OrderBy("i.sort_order, i.title", true); + + return result; + } + + private object GenerateReportData(ReportTimePeriod timePeriod, List reportData) + { + return new + { + resource = new + { + header = CRMReportResource.WorkloadByContactsReport, + sheetName = CRMReportResource.WorkloadByContactsReport, + dateRangeLabel = CRMReportResource.TimePeriod + ":", + dateRangeValue = GetTimePeriodText(timePeriod), + + header1 = CRMReportResource.NewContacts, + header2 = CRMReportResource.NewContactsWithAndWithoutDeals, + + manager = CRMReportResource.Manager, + total = CRMReportResource.Total, + + noSet = CRMCommonResource.NoSet, + withDeals = CRMReportResource.ContactsWithDeals, + withouthDeals = CRMReportResource.ContactsWithoutDeals, + }, + data = reportData + }; + } + + #endregion + + + #region WorkloadByTasksReport + + public bool CheckWorkloadByTasksReportData(ReportTimePeriod timePeriod, Guid[] managers) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + var sqlNewTasksQuery = Query(CrmDbContext.Tasks) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.ResponsibleId) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .Any(); + + var sqlClosedTasksQuery = Query(CrmDbContext.Tasks) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.ResponsibleId) : true) + .Where(x => x.IsClosed) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.LastModifedOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.LastModifedOn <= _tenantUtil.DateTimeToUtc(toDate)) + .Any(); + + + + var sqlOverdueTasksQuery = Query(CrmDbContext.Tasks) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.ResponsibleId) : true) + .Where(x => x.IsClosed) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.Deadline >= _tenantUtil.DateTimeToUtc(fromDate) && x.LastModifedOn <= _tenantUtil.DateTimeToUtc(toDate)) + .Where(x => (!x.IsClosed && x.Deadline < _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow())) || + (x.IsClosed && x.LastModifedOn > x.Deadline)) + .Any(); + + return sqlNewTasksQuery || + sqlClosedTasksQuery || + sqlOverdueTasksQuery; + } + + public object GetWorkloadByTasksReportData(ReportTimePeriod timePeriod, Guid[] managers) + { + var reportData = BuildWorkloadByTasksReport(timePeriod, managers); + + if (reportData == null || !reportData.Any()) return null; + + var hasData = reportData.Any(item => item.Value.Count > 0); + + return hasData ? GenerateReportData(timePeriod, reportData) : null; + } + + private Dictionary> BuildWorkloadByTasksReport(ReportTimePeriod timePeriod, Guid[] managers) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + var sqlNewTasksQuery = Query(CrmDbContext.Tasks) + .GroupJoin(Query(CrmDbContext.ListItem), + x => x.CategoryId, + y => y.Id, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Task = x.x, ListItem = y }) + .Where(x => x.ListItem.ListType == ListType.TaskCategory) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Task.ResponsibleId) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.Task.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.Task.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .OrderBy(x => x.ListItem.SortOrder) + .GroupBy(x => new { x.ListItem.Id, x.ListItem.Title, x.Task.ResponsibleId }) + .Select(x => new + { + id = x.Key.Id, + title = x.Key.Title, + responsibleId = x.Key.ResponsibleId, + count = x.Count() + }) + .ToList() + .ConvertAll(x => new WorkloadByTasks + { + CategoryId = x.id, + CategoryName = x.title, + UserId = x.responsibleId, + UserName = _displayUserSettings.GetFullUserName(x.responsibleId), + Count = x.count + }); + + var sqlClosedTasksQuery = Query(CrmDbContext.Tasks) + .GroupJoin(Query(CrmDbContext.ListItem), + x => x.CategoryId, + y => y.Id, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Task = x.x, ListItem = y }) + .Where(x => x.ListItem.ListType == ListType.TaskCategory) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Task.ResponsibleId) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.Task.LastModifedOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.Task.LastModifedOn <= _tenantUtil.DateTimeToUtc(toDate)) + .Where(x => x.Task.IsClosed) + .OrderBy(x => x.ListItem.SortOrder) + .GroupBy(x => new { x.ListItem.Id, x.ListItem.Title, x.Task.ResponsibleId }) + .Select(x => new + { + id = x.Key.Id, + title = x.Key.Title, + responsibleId = x.Key.ResponsibleId, + count = x.Count() + }) + .ToList() + .ConvertAll(x => new WorkloadByTasks + { + CategoryId = x.id, + CategoryName = x.title, + UserId = x.responsibleId, + UserName = _displayUserSettings.GetFullUserName(x.responsibleId), + Count = x.count + }); + + var sqlOverdueTasksQuery = Query(CrmDbContext.Tasks) + .GroupJoin(Query(CrmDbContext.ListItem), + x => x.CategoryId, + y => y.Id, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Task = x.x, ListItem = y }) + .Where(x => x.ListItem.ListType == ListType.TaskCategory) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Task.ResponsibleId) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.Task.Deadline >= _tenantUtil.DateTimeToUtc(fromDate) && x.Task.Deadline <= _tenantUtil.DateTimeToUtc(toDate)) + .Where(x => (!x.Task.IsClosed && x.Task.Deadline < _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow())) || (x.Task.IsClosed && x.Task.LastModifedOn > x.Task.Deadline)) + .OrderBy(x => x.ListItem.SortOrder) + .GroupBy(x => new { x.ListItem.Id, x.ListItem.Title, x.Task.ResponsibleId }) + .Select(x => new + { + id = x.Key.Id, + title = x.Key.Title, + responsibleId = x.Key.ResponsibleId, + count = x.Count() + }) + .ToList() + .ConvertAll(x => new WorkloadByTasks + { + CategoryId = x.id, + CategoryName = x.title, + UserId = x.responsibleId, + UserName = _displayUserSettings.GetFullUserName(x.responsibleId), + Count = x.count + }); + + return new Dictionary> { + {"Created", sqlNewTasksQuery}, + {"Closed", sqlClosedTasksQuery}, + {"Overdue", sqlOverdueTasksQuery} + }; + } + + private object GenerateReportData(ReportTimePeriod timePeriod, Dictionary> reportData) + { + return new + { + resource = new + { + header = CRMReportResource.WorkloadByTasksReport, + sheetName = CRMReportResource.WorkloadByTasksReport, + dateRangeLabel = CRMReportResource.TimePeriod + ":", + dateRangeValue = GetTimePeriodText(timePeriod), + + header1 = CRMReportResource.ClosedTasks, + header2 = CRMReportResource.NewTasks, + header3 = CRMReportResource.OverdueTasks, + + manager = CRMReportResource.Manager, + total = CRMReportResource.Total + }, + data = reportData + }; + } + + #endregion + + + #region WorkloadByDealsReport + + public bool CheckWorkloadByDealsReportData(ReportTimePeriod timePeriod, Guid[] managers) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + return Query(CrmDbContext.Deals) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.ResponsibleId) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : (x.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) || + (x.ActualCloseDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.ActualCloseDate <= _tenantUtil.DateTimeToUtc(toDate))) + .Any(); + } + + public object GetWorkloadByDealsReportData(ReportTimePeriod timePeriod, Guid[] managers, string defaultCurrency) + { + var reportData = BuildWorkloadByDealsReport(timePeriod, managers, defaultCurrency); + + return reportData == null || !reportData.Any() ? null : GenerateReportData(timePeriod, reportData); + } + + private List BuildWorkloadByDealsReport(ReportTimePeriod timePeriod, Guid[] managers, string defaultCurrency) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + var result = Query(CrmDbContext.Deals) + .GroupJoin(Query(CrmDbContext.CurrencyRate), + x => x.BidCurrency, + y => y.FromCurrency, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x, CurrencyRate = y }) + .GroupJoin(Query(CrmDbContext.DealMilestones), + x => x.Deal.DealMilestoneId, + y => y.Id, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x.Deal, CurrencyRate = x.x.CurrencyRate, DealMilestone = y }) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Deal.ResponsibleId) : true) + .Where(x => x.Deal.ActualCloseDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.Deal.ActualCloseDate <= _tenantUtil.DateTimeToUtc(toDate)) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : (x.Deal.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.Deal.CreateOn <= _tenantUtil.DateTimeToUtc(toDate) || + x.Deal.ActualCloseDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.Deal.ActualCloseDate <= _tenantUtil.DateTimeToUtc(toDate))) + .GroupBy(x => new { x.Deal.ResponsibleId, x.DealMilestone.Status }) + .Select(x => new + { + responsible_id = x.Key.ResponsibleId, + status = x.Key.Status, + count = x.Count(), + deals_value = x.Sum(x => x.Deal.BidType == 0 ? x.Deal.BidValue * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate) : + x.Deal.BidValue * (x.Deal.PerPeriodValue == 0 ? 1.0m : Convert.ToDecimal(x.Deal.PerPeriodValue)) * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate)) + }).ToList() + .ConvertAll(x => new WorkloadByDeals + { + UserId = x.responsible_id, + UserName = _displayUserSettings.GetFullUserName(x.responsible_id), + Status = x.status, + Count = x.count, + Value = x.deals_value + }); + + return result; + } + + private object GenerateReportData(ReportTimePeriod timePeriod, List data) + { + var reportData = data.Select(item => new List + { + item.UserId, + item.UserName, + (int)item.Status, + item.Count, + item.Value + }).ToList(); + + return new + { + resource = new + { + header = CRMReportResource.WorkloadByDealsReport, + sheetName = CRMReportResource.WorkloadByDealsReport, + dateRangeLabel = CRMReportResource.TimePeriod + ":", + dateRangeValue = GetTimePeriodText(timePeriod), + + chartName = CRMReportResource.DealsCount, + chartName1 = CRMReportResource.DealsBudget + ", " + _defaultCurrency.Symbol, + + header1 = CRMReportResource.ByCount, + header2 = CRMReportResource.ByBudget + ", " + _defaultCurrency.Symbol, + + manager = CRMReportResource.Manager, + total = CRMReportResource.Total, + + status0 = CRMReportResource.New, + status1 = CRMReportResource.Won, + status2 = CRMReportResource.Lost + }, + data = reportData + }; + } + + #endregion + + + #region WorkloadByInvoicesReport + + public bool CheckWorkloadByInvoicesReportData(ReportTimePeriod timePeriod, Guid[] managers) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + return Query(CrmDbContext.Invoices) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.CreateBy) : true) + .Where(x => (x.Status != InvoiceStatus.Draft && (timePeriod == ReportTimePeriod.DuringAllTime ? true : x.IssueDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.IssueDate <= _tenantUtil.DateTimeToUtc(toDate))) || + (x.Status == InvoiceStatus.Paid && (timePeriod == ReportTimePeriod.DuringAllTime ? true : x.LastModifedOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.LastModifedOn <= _tenantUtil.DateTimeToUtc(toDate))) || + (x.Status == InvoiceStatus.Rejected && (timePeriod == ReportTimePeriod.DuringAllTime ? true : x.LastModifedOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.LastModifedOn <= _tenantUtil.DateTimeToUtc(toDate))) || + ((timePeriod == ReportTimePeriod.DuringAllTime ? true : x.DueDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.DueDate <= _tenantUtil.DateTimeToUtc(toDate))) && + (x.Status == InvoiceStatus.Sent && x.DueDate < _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow()) || x.Status == InvoiceStatus.Paid && x.LastModifedOn > x.DueDate)) + .Any(); + } + + public object GetWorkloadByInvoicesReportData(ReportTimePeriod timePeriod, Guid[] managers) + { + var reportData = BuildWorkloadByInvoicesReport(timePeriod, managers); + + if (reportData == null || !reportData.Any()) return null; + + var hasData = reportData.Any(item => item.SentCount > 0 || item.PaidCount > 0 || item.RejectedCount > 0 || item.OverdueCount > 0); + + return hasData ? GenerateReportData(timePeriod, reportData) : null; + } + + private List BuildWorkloadByInvoicesReport(ReportTimePeriod timePeriod, Guid[] managers) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + var result = Query(CrmDbContext.Invoices) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.CreateBy) : true) + .GroupBy(x => x.CreateBy) + .Select(x => new + { + createBy = x.Key, + sent = x.Sum(x => x.Status != InvoiceStatus.Draft && (timePeriod == ReportTimePeriod.DuringAllTime ? true : x.IssueDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.IssueDate <= _tenantUtil.DateTimeToUtc(toDate)) ? 1 : 0), + paid = x.Sum(x => x.Status == InvoiceStatus.Paid && (timePeriod == ReportTimePeriod.DuringAllTime ? true : x.LastModifedOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.LastModifedOn <= _tenantUtil.DateTimeToUtc(toDate)) ? 1 : 0), + rejected = x.Sum(x => x.Status == InvoiceStatus.Rejected && (timePeriod == ReportTimePeriod.DuringAllTime ? true : x.LastModifedOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.LastModifedOn <= _tenantUtil.DateTimeToUtc(toDate)) ? 1 : 0), + overdue = x.Sum(x => (timePeriod == ReportTimePeriod.DuringAllTime ? true : x.DueDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.DueDate <= _tenantUtil.DateTimeToUtc(toDate)) && ((x.Status == InvoiceStatus.Sent && x.DueDate < _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow())) || (x.Status == InvoiceStatus.Paid && x.LastModifedOn > x.DueDate)) ? 1 : 0) + }) + .ToList() + .ConvertAll(x => new WorkloadByInvoices + { + UserId = x.createBy, + UserName = _displayUserSettings.GetFullUserName(x.createBy), + SentCount = x.sent, + PaidCount = x.paid, + RejectedCount = x.rejected, + OverdueCount = x.overdue + }); + + return result; + } + + + private object GenerateReportData(ReportTimePeriod timePeriod, List reportData) + { + return new + { + resource = new + { + header = CRMReportResource.WorkloadByInvoicesReport, + sheetName = CRMReportResource.WorkloadByInvoicesReport, + dateRangeLabel = CRMReportResource.TimePeriod + ":", + dateRangeValue = GetTimePeriodText(timePeriod), + + chartName = CRMReportResource.BilledInvoices, + chartName1 = CRMInvoiceResource.Invoices, + + header1 = CRMInvoiceResource.Invoices, + + manager = CRMReportResource.Manager, + total = CRMReportResource.Total, + + billed = CRMReportResource.Billed, + paid = CRMReportResource.Paid, + rejected = CRMReportResource.Rejected, + overdue = CRMReportResource.Overdue + }, + data = reportData + }; + } + + #endregion + + + #region GetWorkloadByViopReport + + public bool CheckWorkloadByViopReportData(ReportTimePeriod timePeriod, Guid[] managers) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + return Query(CrmDbContext.VoipCalls) + .Where(x => x.ParentCallId == "") + .Where(x => managers != null && managers.Any() ? managers.ToList().Contains(x.AnsweredBy) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? + true : + x.DialDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.DialDate <= _tenantUtil.DateTimeToUtc(toDate)) + .Any(); + } + + public object GetWorkloadByViopReportData(ReportTimePeriod timePeriod, Guid[] managers) + { + var reportData = BuildWorkloadByViopReport(timePeriod, managers); + + return reportData == null || !reportData.Any() ? null : GenerateReportData(timePeriod, reportData); + } + + private List BuildWorkloadByViopReport(ReportTimePeriod timePeriod, Guid[] managers) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + var result = Query(CrmDbContext.VoipCalls) + .Where(x => x.ParentCallId == "") + .Where(x => managers != null && managers.Any() ? managers.Contains(x.AnsweredBy) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.DialDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.DialDate <= _tenantUtil.DateTimeToUtc(toDate)) + .GroupBy(x => new { x.AnsweredBy, x.Status }) + .Select(x => new + { + answered_by = x.Key.AnsweredBy, + status = x.Key.Status, + calls_count = x.Count(), + duration = x.Sum(x => x.DialDuration) + }) + .ToList() + .ConvertAll(x => new WorkloadByViop + { + UserId = x.answered_by, + UserName = _displayUserSettings.GetFullUserName(x.answered_by), + Status = x.status, + Count = x.calls_count, + Duration = x.duration ?? x.duration.Value + }); + + return result; + } + + private WorkloadByViop ToWorkloadByViop(object[] row) + { + return new WorkloadByViop + { + UserId = string.IsNullOrEmpty(Convert.ToString(row[0])) ? Guid.Empty : new Guid(Convert.ToString(row[0])), + UserName = Convert.ToString(row[1] ?? string.Empty), + Status = (VoipCallStatus)Convert.ToInt32(row[2] ?? 0), + Count = Convert.ToInt32(row[3]), + Duration = Convert.ToInt32(row[4]) + }; + } + + private object GenerateReportData(ReportTimePeriod timePeriod, List data) + { + var reportData = data.Select(item => new List + { + item.UserId, + item.UserName, + (int) item.Status, + item.Count, + new {format = TimeFormat, value = SecondsToTimeFormat(item.Duration)} + }).ToList(); + + return new + { + resource = new + { + header = CRMReportResource.WorkloadByVoipReport, + sheetName = CRMReportResource.WorkloadByVoipReport, + dateRangeLabel = CRMReportResource.TimePeriod + ":", + dateRangeValue = GetTimePeriodText(timePeriod), + + chartName = CRMReportResource.CallsCount, + chartName1 = CRMReportResource.CallsDuration, + + header1 = CRMReportResource.CallsCount, + header2 = CRMReportResource.CallsDuration, + + manager = CRMReportResource.Manager, + total = CRMReportResource.Total, + + incoming = CRMReportResource.Incoming, + outcoming = CRMReportResource.Outcoming, + + timeFormat = TimeFormat + }, + data = reportData + }; + } + + private string SecondsToTimeFormat(int duration) + { + var timeSpan = TimeSpan.FromSeconds(duration); + + return string.Format("{0}:{1}:{2}", + ((timeSpan.TotalHours < 10 ? "0" : "") + (int)timeSpan.TotalHours), + ((timeSpan.Minutes < 10 ? "0" : "") + timeSpan.Minutes), + ((timeSpan.Seconds < 10 ? "0" : "") + timeSpan.Seconds)); + } + + #endregion + + + #region SummaryForThePeriodReport + + public bool CheckSummaryForThePeriodReportData(ReportTimePeriod timePeriod, Guid[] managers) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + var newDealsSqlQuery = Query(CrmDbContext.Deals) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.ResponsibleId) : true) + .Where(x => x.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .Any(); + + var closedDealsSqlQuery = Query(CrmDbContext.Deals) + .Join(Query(CrmDbContext.DealMilestones), + x => x.DealMilestoneId, + y => y.Id, + (x, y) => new { x, y }) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.x.ResponsibleId) : true) + .Where(x => x.x.ActualCloseDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.x.ActualCloseDate <= _tenantUtil.DateTimeToUtc(toDate)) + .Where(x => x.y.Status != DealMilestoneStatus.Open) + .Any(); + + var overdueDealsSqlQuery = Query(CrmDbContext.Deals) + .Join(Query(CrmDbContext.DealMilestones), + x => x.DealMilestoneId, + y => y.Id, + (x, y) => new { x, y }) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.x.ResponsibleId) : true) + .Where(x => x.x.ExpectedCloseDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.x.ExpectedCloseDate <= _tenantUtil.DateTimeToUtc(toDate)) + .Where(x => (x.y.Status == DealMilestoneStatus.Open && x.x.ExpectedCloseDate < _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow())) || + (x.y.Status == DealMilestoneStatus.ClosedAndWon && x.x.ActualCloseDate > x.x.ExpectedCloseDate)) + .Any(); + + var invoicesSqlQuery = Query(CrmDbContext.Invoices) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.CreateBy) : true) + .Where(x => x.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .Any(); + + var contactsSqlQuery = Query(CrmDbContext.Contacts) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.CreateBy) : true) + .Where(x => x.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .Any(); + + + var tasksSqlQuery = Query(CrmDbContext.Tasks) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.ResponsibleId) : true) + .Where(x => x.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .Any(); + + + var voipSqlQuery = Query(CrmDbContext.VoipCalls) + .Where(x => x.ParentCallId == "") + .Where(x => managers != null && managers.Any() ? managers.Contains(x.AnsweredBy) : true) + .Where(x => x.DialDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.DialDate <= _tenantUtil.DateTimeToUtc(toDate)) + .Any(); + + return newDealsSqlQuery || + closedDealsSqlQuery || + overdueDealsSqlQuery || + invoicesSqlQuery || + contactsSqlQuery || + tasksSqlQuery || + voipSqlQuery; + } + + public object GetSummaryForThePeriodReportData(ReportTimePeriod timePeriod, Guid[] managers, string defaultCurrency) + { + var reportData = BuildSummaryForThePeriodReport(timePeriod, managers, defaultCurrency); + + if (reportData == null) return null; + + return GenerateSummaryForThePeriodReportData(timePeriod, reportData); + } + + private object BuildSummaryForThePeriodReport(ReportTimePeriod timePeriod, Guid[] managers, string defaultCurrency) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + var newDealsSqlQuery = Query(CrmDbContext.Deals) + .GroupJoin(Query(CrmDbContext.CurrencyRate), + x => x.BidCurrency, + y => y.FromCurrency, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x, CurrencyRate = y }) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Deal.ResponsibleId) : true) + .Where(x => x.Deal.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.Deal.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .GroupBy(x => x.Deal.Id) + .Select(x => new + { + count = x.Count(), + deals_value = x.Sum(x => x.Deal.BidType == 0 ? x.Deal.BidValue * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate) : + x.Deal.BidValue * (x.Deal.PerPeriodValue == 0 ? 1.0m : Convert.ToDecimal(x.Deal.PerPeriodValue)) * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate)) + }).ToList(); + + var wonDealsSqlQuery = Query(CrmDbContext.Deals) + .GroupJoin(Query(CrmDbContext.CurrencyRate), + x => x.BidCurrency, + y => y.FromCurrency, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x, CurrencyRate = y }) + .GroupJoin(Query(CrmDbContext.DealMilestones), + x => x.Deal.DealMilestoneId, + y => y.Id, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x.Deal, CurrencyRate = x.x.CurrencyRate, DealMilestone = y }) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Deal.ResponsibleId) : true) + .Where(x => x.Deal.ActualCloseDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.Deal.ActualCloseDate <= _tenantUtil.DateTimeToUtc(toDate)) + .Where(x => x.DealMilestone.Status == DealMilestoneStatus.ClosedAndWon) + .GroupBy(x => x.Deal.Id) + .Select(x => new + { + count = x.Count(), + deals_value = x.Sum(x => x.Deal.BidType == 0 ? x.Deal.BidValue * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate) : + x.Deal.BidValue * (x.Deal.PerPeriodValue == 0 ? 1.0m : Convert.ToDecimal(x.Deal.PerPeriodValue)) * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate)) + }).ToList(); + + var lostDealsSqlQuery = Query(CrmDbContext.Deals) + .GroupJoin(Query(CrmDbContext.CurrencyRate), + x => x.BidCurrency, + y => y.FromCurrency, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x, CurrencyRate = y }) + .GroupJoin(Query(CrmDbContext.DealMilestones), + x => x.Deal.DealMilestoneId, + y => y.Id, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x.Deal, CurrencyRate = x.x.CurrencyRate, DealMilestone = y }) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Deal.ResponsibleId) : true) + .Where(x => x.Deal.ActualCloseDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.Deal.ActualCloseDate <= _tenantUtil.DateTimeToUtc(toDate)) + .Where(x => x.DealMilestone.Status == DealMilestoneStatus.ClosedAndLost) + .GroupBy(x => x.Deal.Id) + .Select(x => new + { + count = x.Count(), + deals_value = x.Sum(x => x.Deal.BidType == 0 ? x.Deal.BidValue * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate) : + x.Deal.BidValue * (x.Deal.PerPeriodValue == 0 ? 1.0m : Convert.ToDecimal(x.Deal.PerPeriodValue)) * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate)) + }).ToList(); + + + var overdueDealsSqlQuery = Query(CrmDbContext.Deals) + .GroupJoin(Query(CrmDbContext.CurrencyRate), + x => x.BidCurrency, + y => y.FromCurrency, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x, CurrencyRate = y }) + .GroupJoin(Query(CrmDbContext.DealMilestones), + x => x.Deal.DealMilestoneId, + y => y.Id, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x.Deal, CurrencyRate = x.x.CurrencyRate, DealMilestone = y }) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Deal.ResponsibleId) : true) + .Where(x => x.Deal.ExpectedCloseDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.Deal.ExpectedCloseDate <= _tenantUtil.DateTimeToUtc(toDate)) + .Where(x => (x.DealMilestone.Status == DealMilestoneStatus.Open && x.Deal.ExpectedCloseDate < _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow())) || + (x.DealMilestone.Status == DealMilestoneStatus.ClosedAndWon && x.Deal.ActualCloseDate > x.Deal.ExpectedCloseDate)) + .GroupBy(x => x.Deal.Id) + .Select(x => new + { + count = x.Count(), + deals_value = x.Sum(x => x.Deal.BidType == 0 ? x.Deal.BidValue * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate) : + x.Deal.BidValue * (x.Deal.PerPeriodValue == 0 ? 1.0m : x.Deal.PerPeriodValue) * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate)) + }).ToList(); + + var invoicesSqlQuery = Query(CrmDbContext.Invoices) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.CreateBy) : true) + .Where(x => x.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .GroupBy(x => x.Status) + .Select(x => new + { + sent = x.Sum(x => x.Status != InvoiceStatus.Draft ? 1 : 0), + paid = x.Sum(x => x.Status == InvoiceStatus.Paid ? 1 : 0), + rejected = x.Sum(x => x.Status == InvoiceStatus.Rejected ? 1 : 0), + overdue = x.Sum(x => (x.Status == InvoiceStatus.Sent && x.DueDate < _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow())) || + (x.Status == InvoiceStatus.Paid && x.LastModifedOn > x.DueDate) + ? 1 : 0) + }).ToList(); + + var contactsSqlQuery = Query(CrmDbContext.Contacts) + .GroupJoin(Query(CrmDbContext.ListItem), + x => x.ContactTypeId, + y => y.Id, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Contact = x.x, ListItem = y }) + .Where(x => x.ListItem.ListType == ListType.ContactType) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Contact.CreateBy) : true) + .Where(x => x.Contact.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.Contact.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .GroupBy(x => new { x.ListItem.Title }) + .Select(x => new + { + title = x.Key.Title, + count = x.Count() + }) + .OrderBy(x => x.title) + .ToList(); + + var tasksSqlQuery = Query(CrmDbContext.Tasks) + .GroupJoin(Query(CrmDbContext.ListItem), + x => x.CategoryId, + y => y.Id, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Task = x.x, ListItem = y }) + .Where(x => x.ListItem.ListType == ListType.TaskCategory) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Task.ResponsibleId) : true) + .Where(x => x.Task.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.Task.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .GroupBy(x => new { x.ListItem.Title }) + .Select(x => new + { + + title = x.Key.Title, + sum1 = x.Sum(x => x.Task.IsClosed && x.Task.LastModifedOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.Task.LastModifedOn <= _tenantUtil.DateTimeToUtc(toDate) ? 1 : 0), + sum2 = x.Sum(x => (!x.Task.IsClosed && x.Task.Deadline < _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow())) || (x.Task.IsClosed && x.Task.LastModifedOn > x.Task.Deadline) ? 1 : 0), + count = x.Count() + }) + .OrderBy(x => x.title) + .ToList(); + // .OrderBy("i.sort_order, i.title", true); + + var voipSqlQuery = Query(CrmDbContext.VoipCalls) + .Where(x => String.IsNullOrEmpty(x.ParentCallId)) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.AnsweredBy) : true) + .Where(x => x.DialDate >= _tenantUtil.DateTimeToUtc(fromDate) && x.DialDate <= _tenantUtil.DateTimeToUtc(toDate)) + .GroupBy(x => x.Status) + .Select(x => new + { + status = x.Key, + calls_count = x.Count(), + duration = x.Sum(x => x.DialDuration) + }) + .ToList(); + return new + { + DealsInfo = new + { + Created = newDealsSqlQuery, + Won = wonDealsSqlQuery, + Lost = lostDealsSqlQuery, + Overdue = overdueDealsSqlQuery + }, + InvoicesInfo = invoicesSqlQuery, + ContactsInfo = contactsSqlQuery, + TasksInfo = tasksSqlQuery, + VoipInfo = voipSqlQuery + }; + } + + private object GenerateSummaryForThePeriodReportData(ReportTimePeriod timePeriod, object reportData) + { + return new + { + resource = new + { + header = CRMReportResource.SummaryForThePeriodReport, + sheetName = CRMReportResource.SummaryForThePeriodReport, + dateRangeLabel = CRMReportResource.TimePeriod + ":", + dateRangeValue = GetTimePeriodText(timePeriod), + chartName = CRMReportResource.DealsByBudget + ", " + _defaultCurrency.Symbol, + chartName1 = CRMReportResource.DealsByCount, + chartName2 = CRMReportResource.ContactsByType, + chartName3 = CRMReportResource.TasksForThePeriod, + chartName4 = CRMReportResource.InvoicesForThePeriod, + chartName5 = CRMReportResource.CallsForThePeriod, + header1 = CRMDealResource.Deals, + header2 = CRMContactResource.Contacts, + header3 = CRMTaskResource.Tasks, + header4 = CRMInvoiceResource.Invoices, + header5 = CRMReportResource.Calls, + byBudget = CRMReportResource.ByBudget, + currency = _defaultCurrency.Symbol, + byCount = CRMReportResource.ByCount, + item = CRMReportResource.Item, + type = CRMReportResource.Type, + won = CRMReportResource.Won, + lost = CRMReportResource.Lost, + created = CRMReportResource.Created, + closed = CRMReportResource.Closed, + overdue = CRMReportResource.Overdue, + notSpecified = CRMCommonResource.NoSet, + total = CRMReportResource.Total, + status = CRMReportResource.Status, + billed = CRMReportResource.Billed, + paid = CRMReportResource.Paid, + rejected = CRMReportResource.Rejected, + count = CRMReportResource.Count, + duration = CRMReportResource.Duration, + incoming = CRMReportResource.Incoming, + outcoming = CRMReportResource.Outcoming, + missed = CRMReportResource.MissedCount, + timeFormat = TimeFormat + }, + data = reportData + }; + } + + #endregion + + + #region SummaryAtThisMomentReport + + public bool CheckSummaryAtThisMomentReportData(ReportTimePeriod timePeriod, Guid[] managers) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + var dealsSqlQuery = Query(CrmDbContext.Deals) + .Join(CrmDbContext.DealMilestones, + x => x.DealMilestoneId, + y => y.Id, + (x, y) => new { x, y }) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.x.ResponsibleId) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.x.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.x.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .Where(x => x.y.Status == DealMilestoneStatus.Open) + .Any(); + + + var contactsSqlQuery = Query(CrmDbContext.Contacts) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.CreateBy) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .Any(); + + var tasksSqlQuery = Query(CrmDbContext.Tasks) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.ResponsibleId) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .Any(); + + var invoicesSqlQuery = Query(CrmDbContext.Invoices) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.CreateBy) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .Any(); + + return dealsSqlQuery || + contactsSqlQuery || + tasksSqlQuery || + invoicesSqlQuery; + + } + + public object GetSummaryAtThisMomentReportData(ReportTimePeriod timePeriod, Guid[] managers, string defaultCurrency) + { + var reportData = BuildSummaryAtThisMomentReport(timePeriod, managers, defaultCurrency); + + if (reportData == null) return null; + + return GenerateSummaryAtThisMomentReportData(timePeriod, reportData); + } + + private object BuildSummaryAtThisMomentReport(ReportTimePeriod timePeriod, Guid[] managers, string defaultCurrency) + { + DateTime fromDate; + DateTime toDate; + + GetTimePeriod(timePeriod, out fromDate, out toDate); + + var openDealsSqlQuery = Query(CrmDbContext.Deals) + .GroupJoin(Query(CrmDbContext.CurrencyRate), + x => x.BidCurrency, + y => y.FromCurrency, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x, CurrencyRate = y }) + .GroupJoin(Query(CrmDbContext.DealMilestones), + x => x.Deal.DealMilestoneId, + y => y.Id, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x.Deal, CurrencyRate = x.x.CurrencyRate, DealMilestone = y }) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Deal.ResponsibleId) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.Deal.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.Deal.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .Where(x => x.DealMilestone.Status == DealMilestoneStatus.Open) + .GroupBy(x => x.Deal.Id) + .Select(x => new + { + count = x.Count(), + deals_value = x.Sum(x => x.Deal.BidType == 0 ? x.Deal.BidValue * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate) : + x.Deal.BidValue * (x.Deal.PerPeriodValue == 0 ? 1.0m : Convert.ToDecimal(x.Deal.PerPeriodValue)) * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate)) + }).ToList(); + + var overdueDealsSqlQuery = Query(CrmDbContext.Deals) + .GroupJoin(Query(CrmDbContext.CurrencyRate), + x => x.BidCurrency, + y => y.FromCurrency, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x, CurrencyRate = y }) + .GroupJoin(Query(CrmDbContext.DealMilestones), + x => x.Deal.DealMilestoneId, + y => y.Id, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x.Deal, CurrencyRate = x.x.CurrencyRate, DealMilestone = y }) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Deal.ResponsibleId) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.Deal.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.Deal.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .Where(x => x.DealMilestone.Status == DealMilestoneStatus.Open) + .Where(x => x.Deal.ExpectedCloseDate < _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow())) + .GroupBy(x => x.Deal.Id) + .Select(x => new + { + count = x.Count(), + deals_value = x.Sum(x => x.Deal.BidType == 0 ? x.Deal.BidValue * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate) : + x.Deal.BidValue * (x.Deal.PerPeriodValue == 0 ? 1.0m : Convert.ToDecimal(x.Deal.PerPeriodValue)) * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate)) + }).ToList(); + + + var nearDealsSqlQuery = Query(CrmDbContext.Deals) + .GroupJoin(Query(CrmDbContext.CurrencyRate), + x => x.BidCurrency, + y => y.FromCurrency, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x, CurrencyRate = y }) + .GroupJoin(Query(CrmDbContext.DealMilestones), + x => x.Deal.DealMilestoneId, + y => y.Id, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x.Deal, CurrencyRate = x.x.CurrencyRate, DealMilestone = y }) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Deal.ResponsibleId) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.Deal.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.Deal.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .Where(x => x.DealMilestone.Status == DealMilestoneStatus.Open) + .Where(x => x.Deal.ExpectedCloseDate >= _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow()) && x.Deal.ExpectedCloseDate <= _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow().AddDays(30))) + .GroupBy(x => x.Deal.Id) + .Select(x => new + { + count = x.Count(), + deals_value = x.Sum(x => x.Deal.BidType == 0 ? x.Deal.BidValue * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate) : + x.Deal.BidValue * (x.Deal.PerPeriodValue == 0 ? 1.0m : Convert.ToDecimal(x.Deal.PerPeriodValue)) * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate)) + }).ToList(); + + var dealsByStageSqlQuery = Query(CrmDbContext.DealMilestones) + .GroupJoin(Query(CrmDbContext.Deals), + x => x.Id, + y => y.DealMilestoneId, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = y, DealMilestone = x.x }) + .GroupJoin(Query(CrmDbContext.CurrencyRate), + x => x.Deal.BidCurrency, + y => y.FromCurrency, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Deal = x.x.Deal, DealMilestone = x.x.DealMilestone, CurrencyRate = y }) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Deal.ResponsibleId) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.Deal.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.Deal.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .Where(x => x.DealMilestone.Status == DealMilestoneStatus.Open) + .GroupBy(x => new { x.DealMilestone.Id, x.DealMilestone.Title }) + .Select(x => new + { + title = x.Key.Title, + deals_count = x.Count(), + deals_value = x.Sum(x => x.Deal.BidType == 0 ? x.Deal.BidValue * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate) : + x.Deal.BidValue * (x.Deal.PerPeriodValue == 0 ? 1.0m : Convert.ToDecimal(x.Deal.PerPeriodValue)) * (x.Deal.BidCurrency == defaultCurrency ? 1.0m : x.CurrencyRate.Rate)) + }).ToList(); + + var contactsByTypeSqlQuery = Query(CrmDbContext.Contacts) + .GroupJoin(Query(CrmDbContext.ListItem), + x => x.ContactTypeId, + y => y.Id, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Contact = x.x, ListItem = y }) + .Where(x => x.ListItem.ListType == ListType.ContactType) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Contact.CreateBy) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.Contact.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.Contact.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .GroupBy(x => new { x.ListItem.Title }) + .Select(x => new + { + title = x.Key.Title, + count = x.Count() + }) + .OrderBy(x => x.title) + .ToList(); + + var contactsByStageSqlQuery = Query(CrmDbContext.Contacts) + .GroupJoin(Query(CrmDbContext.ListItem), + x => x.StatusId, + y => y.Id, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Contact = x.x, ListItem = y }) + .Where(x => x.ListItem.ListType == ListType.ContactStatus) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Contact.CreateBy) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.Contact.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.Contact.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .GroupBy(x => new { x.ListItem.Title }) + .Select(x => new + { + title = x.Key.Title, + count = x.Count() + }) + .OrderBy(x => x.title) + .ToList(); + + var tasksSqlQuery = Query(CrmDbContext.Tasks) + .GroupJoin(Query(CrmDbContext.ListItem), + x => x.CategoryId, + y => y.Id, + (x, y) => new { x, y }) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { Task = x.x, ListItem = y }) + .Where(x => x.ListItem.ListType == ListType.TaskCategory) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.Task.ResponsibleId) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.Task.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.Task.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .GroupBy(x => new { x.ListItem.Title }) + .Select(x => new + { + + title = x.Key.Title, + sum1 = x.Sum(x => !x.Task.IsClosed ? 1 : 0), + sum2 = x.Sum(x => (!x.Task.IsClosed && x.Task.Deadline < _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow())) ? 1 : 0), + count = x.Count() + }) + .OrderBy(x => x.title) + .ToList(); + + + var invoicesSqlQuery = Query(CrmDbContext.Invoices) + .Where(x => managers != null && managers.Any() ? managers.Contains(x.CreateBy) : true) + .Where(x => timePeriod == ReportTimePeriod.DuringAllTime ? true : x.CreateOn >= _tenantUtil.DateTimeToUtc(fromDate) && x.CreateOn <= _tenantUtil.DateTimeToUtc(toDate)) + .GroupBy(x => x.Status) + .Select(x => new + { + sent = x.Sum(x => x.Status == InvoiceStatus.Sent ? 1 : 0), + overdue = x.Sum(x => (x.Status == InvoiceStatus.Sent && x.DueDate < _tenantUtil.DateTimeToUtc(_tenantUtil.DateTimeNow())) ? 1 : 0) + }).ToList(); + + + return new + { + DealsInfo = new + { + Open = openDealsSqlQuery, + Overdue = overdueDealsSqlQuery, + Near = nearDealsSqlQuery, + ByStage = dealsByStageSqlQuery + }, + ContactsInfo = new + { + ByType = contactsByTypeSqlQuery, + ByStage = contactsByStageSqlQuery + }, + TasksInfo = tasksSqlQuery, + InvoicesInfo = invoicesSqlQuery + }; + } + + private object GenerateSummaryAtThisMomentReportData(ReportTimePeriod timePeriod, object reportData) + { + return new + { + resource = new + { + header = CRMReportResource.SummaryAtThisMomentReport, + sheetName = CRMReportResource.SummaryAtThisMomentReport, + dateRangeLabel = CRMReportResource.TimePeriod + ":", + dateRangeValue = GetTimePeriodText(timePeriod), + chartName = CRMReportResource.DealsByStatus + ", " + _defaultCurrency.Symbol, + chartName1 = CRMReportResource.DealsByStage + ", " + _defaultCurrency.Symbol, + chartName2 = CRMReportResource.ContactsByType, + chartName3 = CRMReportResource.ContactsByStage, + chartName4 = CRMReportResource.TasksByStatus, + chartName5 = CRMReportResource.InvoicesByStatus, + header1 = CRMDealResource.Deals, + header2 = CRMContactResource.Contacts, + header3 = CRMTaskResource.Tasks, + header4 = CRMInvoiceResource.Invoices, + budget = CRMReportResource.Budget + ", " + _defaultCurrency.Symbol, + count = CRMReportResource.Count, + open = CRMReportResource.Opened, + overdue = CRMReportResource.Overdue, + near = CRMReportResource.Near, + stage = CRMReportResource.Stage, + temperature = CRMContactResource.ContactStage, + type = CRMReportResource.Type, + total = CRMReportResource.Total, + billed = CRMReportResource.Billed, + notSpecified = CRMCommonResource.NoSet, + status = CRMReportResource.Status, + }, + data = reportData + }; + } + + #endregion + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Dao/SearchDao.cs b/products/ASC.CRM/Server/Core/Dao/SearchDao.cs new file mode 100644 index 00000000000..46bd00c7961 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/SearchDao.cs @@ -0,0 +1,475 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Common.Utils; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.Core.Tenants; +using ASC.CRM.Classes; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.Web.Core.ModuleManagement.Common; +using ASC.Web.Core.Utility.Skins; +using ASC.Web.CRM; +using ASC.Web.CRM.Configuration; +using ASC.Web.CRM.Core.Search; + +using AutoMapper; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.CRM.Core.Dao +{ + [Scope] + public class SearchDao : AbstractDao + { + private Dictionary> _findedIDs; + + //TODO: setting _fullTextSearchEnable field + private readonly bool _fullTextSearchEnable; + + + private DaoFactory DaoFactory { get; set; } + + public SearchDao(DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + CrmSecurity crmSecurity, + TenantUtil tenantUtil, + PathProvider pathProvider, + FactoryIndexerTask tasksDtoIndexer, + FactoryIndexerInvoice invoicesDtoIndexer, + IOptionsMonitor logger, + ICache ascCache, + WebImageSupplier webImageSupplier, + BundleSearch bundleSearch, + IMapper mapper + ) : + base(dbContextManager, + tenantManager, + securityContext, + logger, + ascCache, + mapper) + { + FactoryIndexerTask = tasksDtoIndexer; + FactoryIndexerInvoice = invoicesDtoIndexer; + CRMSecurity = crmSecurity; + TenantUtil = tenantUtil; + PathProvider = pathProvider; + WebImageSupplier = webImageSupplier; + BundleSearch = bundleSearch; + } + + + public BundleSearch BundleSearch { get; } + + public WebImageSupplier WebImageSupplier { get; } + + public TenantUtil TenantUtil { get; } + public PathProvider PathProvider { get; } + public FactoryIndexerTask FactoryIndexerTask { get; } + public FactoryIndexerInvoice FactoryIndexerInvoice { get; } + public CrmSecurity CRMSecurity { get; } + + public SearchResultItem[] Search(String searchText) + { + var keywords = searchText.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) + .ToArray(); + + if (keywords.Length == 0) return new List().ToArray(); + + if (_fullTextSearchEnable) + { + _findedIDs = new Dictionary>(); + + List casesId; + if (BundleSearch.TrySelectCase(searchText, out casesId)) + { + _findedIDs.Add(EntityType.Case, casesId); + } + + List contactsId; + if (BundleSearch.TrySelectContact(searchText, out contactsId)) + { + _findedIDs.Add(EntityType.Contact, contactsId); + } + + List dealsId; + if (BundleSearch.TrySelectOpportunity(searchText, out dealsId)) + { + _findedIDs.Add(EntityType.Opportunity, dealsId); + } + + List tasksId; + + if (FactoryIndexerTask.TrySelectIds(r => r.MatchAll(searchText), out tasksId)) + { + _findedIDs.Add(EntityType.Task, tasksId); + } + + List invoicesId; + + if (FactoryIndexerInvoice.TrySelectIds(r => r.MatchAll(searchText), out invoicesId)) + { + _findedIDs.Add(EntityType.Invoice, invoicesId); + } + } + else + { + _findedIDs = SearchByCustomFields(keywords) + .Union(SearchByRelationshipEvent(keywords)) + .Union(SearchByContactInfos(keywords)) + .ToLookup(pair => pair.Key, pair => pair.Value) + .ToDictionary(group => group.Key, group => group.First()); + } + + return GetSearchResultItems(keywords); + } + + private Dictionary> SearchByRelationshipEvent(String[] keywords) + { + var sqlQuery = Query(CrmDbContext.RelationshipEvent); + + if (keywords.Length > 0) + { + foreach (var k in keywords) + { + sqlQuery = sqlQuery.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Content, k + "%")); + } + } + + return sqlQuery.GroupBy(x => x.EntityType) + .ToDictionary(x => x.Key, x => x.Select(y => y.EntityId > 0 ? y.EntityId : y.ContactId)); + } + + private Dictionary> SearchByCustomFields(String[] keywords) + { + var sqlQuery = Query(CrmDbContext.FieldValue); + + if (keywords.Length > 0) + { + foreach (var k in keywords) + { + sqlQuery = sqlQuery.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Value, k + "%")); + } + } + + return sqlQuery.GroupBy(x => x.EntityType) + .ToDictionary(x => x.Key, x => x.Select(x => x.EntityId)); + } + + private Dictionary> SearchByContactInfos(String[] keywords) + { + var sqlQuery = Query(CrmDbContext.ContactsInfo); + + if (keywords.Length > 0) + { + foreach (var k in keywords) + { + sqlQuery = sqlQuery.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Data, k + "%")); + } + } + + return new Dictionary> { { EntityType.Contact, sqlQuery.Select(x => x.ContactId).Distinct() } }; + } + + private bool IncludeToSearch(EntityType entityType) + { + return _findedIDs.ContainsKey(entityType); + } + + private SearchResultItem[] GetSearchResultItems(String[] keywords) + { + var result = new List(); + + if (IncludeToSearch(EntityType.Task)) + { + var sqlQuery = Query(CrmDbContext.Tasks); + + if (_findedIDs.ContainsKey(EntityType.Task)) + { + sqlQuery = sqlQuery.Where(x => _findedIDs[EntityType.Task].Contains(x.Id)); + } + else + { + if (keywords.Length > 0) + { + foreach (var k in keywords) + { + sqlQuery = sqlQuery.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Title, k + "%") || + Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Description, k + "%")); + } + } + } + + sqlQuery.ToList().ForEach(x => + { + if (!CRMSecurity.CanAccessTo(new Task { ID = x.Id })) return; + + result.Add(new SearchResultItem + { + Name = x.Title, + Description = HtmlUtil.GetText(x.Description, 120), + URL = PathProvider.BaseAbsolutePath, + Date = TenantUtil.DateTimeFromUtc(x.CreateOn), + Additional = new Dictionary + { { "imageRef", WebImageSupplier.GetAbsoluteWebPath("tasks_widget.png", ProductEntryPoint.ID) }, + {"relativeInfo", GetPath( + x.ContactId, + x.EntityId, + x.EntityType)}, + {"typeInfo", EntityType.Task.ToLocalizedString()} + } + }); + }); + } + + if (IncludeToSearch(EntityType.Opportunity)) + { + var sqlQuery = Query(CrmDbContext.Deals); + + if (_findedIDs.ContainsKey(EntityType.Opportunity)) + { + sqlQuery = sqlQuery.Where(x => _findedIDs[EntityType.Opportunity].Contains(x.Id)); + } + else + { + if (keywords.Length > 0) + { + foreach (var k in keywords) + { + sqlQuery = sqlQuery.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Title, k + "%") || + Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Description, k + "%")); + } + } + } + + + sqlQuery.ToList().ForEach(x => + { + if (!CRMSecurity.CanAccessTo(new Deal { ID = x.Id })) return; + + result.Add(new SearchResultItem + { + Name = x.Title, + Description = HtmlUtil.GetText(x.Description, 120), + URL = string.Concat(PathProvider.BaseAbsolutePath, string.Format("deals.aspx?id={0}", x.Id)), + Date = TenantUtil.DateTimeFromUtc(x.CreateOn), + Additional = new Dictionary + { { "imageRef", WebImageSupplier.GetAbsoluteWebPath("deal_widget.png", ProductEntryPoint.ID) }, + {"relativeInfo", GetPath( + x.ContactId, + 0, + 0)}, + {"typeInfo", EntityType.Opportunity.ToLocalizedString()} + } + }); + + }); + } + + + if (IncludeToSearch(EntityType.Contact)) + { + var sqlQuery = Query(CrmDbContext.Contacts); + + if (_findedIDs.ContainsKey(EntityType.Contact)) + { + sqlQuery = sqlQuery.Where(x => _findedIDs[EntityType.Contact].Contains(x.Id)); + } + else + { + if (keywords.Length > 0) + { + foreach (var k in keywords) + { + sqlQuery = sqlQuery.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.FirstName, k + "%") || + Microsoft.EntityFrameworkCore.EF.Functions.Like(x.LastName, k + "%") || + Microsoft.EntityFrameworkCore.EF.Functions.Like(x.CompanyName, k + "%") || + Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Title, k + "%") || + Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Notes, k + "%") + ); + } + } + } + + sqlQuery.ToList().ForEach(x => + { + if (x.IsCompany) + { + if (!CRMSecurity.CanAccessTo(new Company { ID = x.Id })) return; + } + else + { + if (!CRMSecurity.CanAccessTo(new Person { ID = x.Id })) return; + } + + result.Add(new SearchResultItem + { + Name = x.IsCompany ? x.CompanyName : String.Format("{0} {1}", x.FirstName, x.LastName), + Description = HtmlUtil.GetText(x.Notes, 120), + URL = String.Concat(PathProvider.BaseAbsolutePath, String.Format("default.aspx?id={0}", x.Id)), + Date = TenantUtil.DateTimeFromUtc(x.CreateOn), + Additional = new Dictionary + { { "imageRef", WebImageSupplier.GetAbsoluteWebPath(x.IsCompany ? "companies_widget.png" : "people_widget.png", ProductEntryPoint.ID) }, + {"relativeInfo", GetPath( + 0, + 0, + 0)}, + {"typeInfo", EntityType.Contact.ToLocalizedString()} + } + }); + }); + } + + if (IncludeToSearch(EntityType.Case)) + { + var sqlQuery = Query(CrmDbContext.Cases); + + if (_findedIDs.ContainsKey(EntityType.Case)) + { + sqlQuery = sqlQuery.Where(x => _findedIDs[EntityType.Case].Contains(x.Id)); + } + else + { + if (keywords.Length > 0) + { + foreach (var k in keywords) + { + sqlQuery = sqlQuery.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Title, k + "%")); + } + } + } + + sqlQuery.ToList().ForEach(x => + { + if (!CRMSecurity.CanAccessTo(new Cases { ID = x.Id })) return; + + result.Add(new SearchResultItem + { + Name = x.Title, + Description = String.Empty, + URL = String.Concat(PathProvider.BaseAbsolutePath, String.Format("cases.aspx?id={0}", x.Id)), + Date = TenantUtil.DateTimeFromUtc(x.CreateOn), + Additional = new Dictionary + { { "imageRef", WebImageSupplier.GetAbsoluteWebPath("cases_widget.png", ProductEntryPoint.ID) }, + {"relativeInfo", GetPath( + 0, + 0, + 0)}, + {"typeInfo", EntityType.Case.ToLocalizedString()} + } + }); + + }); + + } + + + if (IncludeToSearch(EntityType.Invoice)) + { + var sqlQuery = Query(CrmDbContext.Invoices); + + if (_findedIDs.ContainsKey(EntityType.Invoice)) + { + sqlQuery = sqlQuery.Where(x => _findedIDs[EntityType.Invoice].Contains(x.Id)); + } + else + { + if (keywords.Length > 0) + { + foreach (var k in keywords) + { + sqlQuery = sqlQuery.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Number, k + "%") || + Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Description, k + "%")); + } + } + } + + sqlQuery.ToList().ForEach(x => + { + if (!CRMSecurity.CanAccessTo(new Invoice { ID = x.Id })) return; + + result.Add(new SearchResultItem + { + Name = x.Number, + Description = String.Empty, + URL = String.Concat(PathProvider.BaseAbsolutePath, String.Format("invoices.aspx?id={0}", x.Id)), + Date = TenantUtil.DateTimeFromUtc(x.CreateOn), + Additional = new Dictionary + { { "imageRef", WebImageSupplier.GetAbsoluteWebPath("invoices_widget.png", ProductEntryPoint.ID) }, + {"relativeInfo", GetPath( + x.ContactId, + x.EntityId, + x.EntityType)}, + {"typeInfo", EntityType.Invoice.ToLocalizedString()} + } + }); + + }); + } + + return result.ToArray(); + } + + private String GetPath(int contactID, int entityID, EntityType entityType) + { + + if (contactID == 0) return String.Empty; + + if (entityID == 0) + return DaoFactory.GetContactDao().GetByID(contactID).GetTitle(); + + switch (entityType) + { + case EntityType.Company: + case EntityType.Person: + case EntityType.Contact: + var contact = DaoFactory.GetContactDao().GetByID(contactID); + return contact == null ? string.Empty : contact.GetTitle(); + case EntityType.Opportunity: + var opportunity = DaoFactory.GetDealDao().GetByID(entityID); + return opportunity == null ? string.Empty : opportunity.Title; + case EntityType.Case: + var @case = DaoFactory.GetCasesDao().GetByID(entityID); + return @case == null ? string.Empty : @case.Title; + default: + throw new ArgumentException(); + } + } + } + +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Dao/TagDao.cs b/products/ASC.CRM/Server/Core/Dao/TagDao.cs new file mode 100644 index 00000000000..f76a084f838 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/TagDao.cs @@ -0,0 +1,441 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; + +using AutoMapper; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.CRM.Core.Dao +{ + [Scope] + public class TagDao : AbstractDao + { + public TagDao(DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + IOptionsMonitor logger, + ICache ascCache, + IMapper mapper) : + base(dbContextManager, + tenantManager, + securityContext, + logger, + ascCache, + mapper) + { + + } + + public bool IsExist(EntityType entityType, String tagName) + { + if (String.IsNullOrEmpty(tagName)) + throw new ArgumentNullException(tagName); + + return IsExistInDb(entityType, tagName); + } + + private bool IsExistInDb(EntityType entityType, String tagName) + { + return Query(CrmDbContext.Tags) + .Where(x => x.EntityType == entityType && String.Compare(x.Title, tagName, true) == 0) + .Any(); + } + + private int GetTagId(EntityType entityType, String tagName) + { + return Query(CrmDbContext.Tags) + .Where(x => x.EntityType == entityType && String.Compare(x.Title, tagName, true) == 0) + .Select(x => x.Id) + .SingleOrDefault(); + } + + public String[] GetAllTags(EntityType entityType) + { + return CrmDbContext.Tags + .Where(x => x.EntityType == entityType) + .OrderBy(x => x.Title) + .Select(x => x.Title) + .ToArray(); + } + + public List> GetAllTags() + { + return Query(CrmDbContext.Tags) + .OrderBy(x => x.Title) + .Select(x => new KeyValuePair(x.EntityType, x.Title)).ToList(); + } + + public String GetTagsLinkCountJSON(EntityType entityType) + { + int[] tags = GetTagsLinkCount(entityType).ToArray(); + + return JsonSerializer.Serialize(tags); + } + + public IEnumerable GetTagsLinkCount(EntityType entityType) + { + return Query(CrmDbContext.Tags) + .Join(CrmDbContext.EntityTags, + x => x.Id, + y => y.TagId, + (x, y) => new { x, y }) + .Where(x => x.x.EntityType == entityType) + .GroupBy(x => x.x.Id) + .Select(x => x.Count()); + } + + + public Dictionary> GetEntitiesTags(EntityType entityType) + { + return CrmDbContext.EntityTags.Join(Query(CrmDbContext.Tags), + x => x.TagId, + y => y.Id, + (x, y) => new { x, y }) + .Where(x => x.x.EntityType == entityType) + .Select(x => new { EntityId = x.x.EntityId, Title = x.y.Title }) + .ToList() + .GroupBy(x => x.EntityId) + .ToDictionary(x => x.Key, x => x.ToList().ConvertAll(t => t.Title)); + } + + public String[] GetEntityTags(EntityType entityType, int entityID) + { + return Query(CrmDbContext.Tags) + .Join(CrmDbContext.EntityTags, + x => x.Id, + y => y.TagId, + (x, y) => new + { + x, + y + + }) + .Where(x => x.y.EntityId == entityID && x.y.EntityType == entityType) + .Select(x => x.x.Title) + .ToArray(); + } + + public string[] GetUnusedTags(EntityType entityType) + { + return Query(CrmDbContext.Tags) + .Join(CrmDbContext.EntityTags.DefaultIfEmpty(), + x => x.Id, + y => y.TagId, + (x, y) => new { x, y }) + .Where(x => x.y == null && x.x.EntityType == entityType) + .Select(x => x.x.Title) + .ToArray(); + } + + public bool CanDeleteTag(EntityType entityType, String tagName) + { + return Query(CrmDbContext.Tags) + .Where(x => string.Compare(x.Title, tagName, true) == 0 && x.EntityType == entityType) + .Select(x => x.Id) + .SingleOrDefault() != 0; + } + + public void DeleteTag(EntityType entityType, String tagName) + { + if (!CanDeleteTag(entityType, tagName)) throw new ArgumentException(); + + DeleteTagFromEntity(entityType, 0, tagName); + } + + public void DeleteTagFromEntity(EntityType entityType, int entityID, String tagName) + { + var tagID = Query(CrmDbContext.Tags) + .Where(x => String.Compare(x.Title, tagName, true) == 0 && x.EntityType == entityType) + .Select(x => x.Id) + .SingleOrDefault(); + + if (tagID == 0) return; + + var itemsToRemove = CrmDbContext.EntityTags.Where(x => x.EntityType == entityType && x.TagId == tagID); + + if (entityID > 0) + itemsToRemove = itemsToRemove.Where(x => x.EntityId == entityID); + + CrmDbContext.RemoveRange(itemsToRemove); + + if (entityID == 0) + CrmDbContext.Tags.RemoveRange(Query(CrmDbContext.Tags).Where(x => x.Id == tagID && x.EntityType == entityType)); + + CrmDbContext.SaveChanges(); + } + + public void DeleteAllTagsFromEntity(EntityType entityType, int entityID) + { + if (entityID <= 0) return; + + var itemsToRemove = CrmDbContext.EntityTags.Where(x => x.EntityType == entityType && x.EntityId == entityID); + + CrmDbContext.EntityTags.RemoveRange(itemsToRemove); + + CrmDbContext.SaveChanges(); + } + + public void DeleteUnusedTags(EntityType entityType) + { + if (!_supportedEntityType.Contains(entityType)) + throw new ArgumentException(); + + var itemToDelete = Query(CrmDbContext.Tags) + .Join(CrmDbContext.EntityTags.DefaultIfEmpty(), + x => x.Id, + y => y.TagId, + (x, y) => new { x, y }) + .Where(x => x.x.EntityType == entityType && x.y == null).Select(x => x.x).ToList(); + + CrmDbContext.RemoveRange(itemToDelete); + + CrmDbContext.SaveChanges(); + } + + public int AddTag(EntityType entityType, String tagName, bool returnExisted = false) + { + tagName = CorrectTag(tagName); + + if (String.IsNullOrEmpty(tagName)) + throw new ArgumentNullException(CRMErrorsResource.TagNameNotSet); + + var existedTagId = GetTagId(entityType, tagName); + + if (existedTagId > 0) + { + if (returnExisted) + return existedTagId; + + throw new ArgumentException(CRMErrorsResource.TagNameBusy); + } + return AddTagInDb(entityType, tagName); + } + + private int AddTagInDb(EntityType entityType, String tagName) + { + var dbTag = new DbTag + { + Title = tagName, + EntityType = entityType + }; + + CrmDbContext.Tags.Add(dbTag); + + CrmDbContext.SaveChanges(); + + return dbTag.Id; + } + + + public Dictionary GetAndAddTags(EntityType entityType, String[] tags) + { + tags = tags.Select(CorrectTag).Where(t => !string.IsNullOrWhiteSpace(t)).ToArray(); + + using var tx = CrmDbContext.Database.BeginTransaction(); + + var tagNamesAndIds = Query(CrmDbContext.Tags) + .Where(x => tags.Contains(x.Title) && x.EntityType == entityType) + .Select(x => new { x.Id, x.Title }) + .ToDictionary(x => x.Title, x => x.Id); + + var tagsForCreate = tags.Where(t => !tagNamesAndIds.ContainsKey(t)); + + foreach (var tagName in tagsForCreate) + { + tagNamesAndIds.Add(tagName, AddTagInDb(entityType, tagName)); + } + + tx.Commit(); + + return tagNamesAndIds; + } + + private int AddTagToEntityInDb(EntityType entityType, int entityID, String tagName) + { + tagName = CorrectTag(tagName); + + if (String.IsNullOrEmpty(tagName) || entityID == 0) + throw new ArgumentException(); + + var tagID = Query(CrmDbContext.Tags) + .Where(x => String.Compare(x.Title, tagName, true) == 0 && x.EntityType == entityType) + .Select(x => x.Id).SingleOrDefault(); + + if (tagID == 0) + tagID = AddTagInDb(entityType, tagName); + + CrmDbContext.EntityTags.Add(new DbEntityTag + { + EntityId = entityID, + EntityType = entityType, + TagId = tagID + }); + + CrmDbContext.SaveChanges(); + + return tagID; + } + + public int AddTagToEntity(EntityType entityType, int entityID, String tagName) + { + return AddTagToEntityInDb(entityType, entityID, tagName); + } + + public int AddTagToContacts(int[] contactID, String tagName) + { + tagName = CorrectTag(tagName); + + if (String.IsNullOrEmpty(tagName) || contactID == null || contactID.Length == 0) + throw new ArgumentException(); + + using var tx = CrmDbContext.Database.BeginTransaction(); + + var tagID = Query(CrmDbContext.Tags) + .Where(x => String.Compare(x.Title, tagName, true) == 0 && x.EntityType == EntityType.Contact) + .Select(x => x.Id).SingleOrDefault(); + + if (tagID == 0) + tagID = AddTagInDb(EntityType.Contact, tagName); + + foreach (var id in contactID) + { + CrmDbContext.EntityTags.Add(new DbEntityTag + { + EntityId = id, + EntityType = EntityType.Contact, + TagId = tagID + }); + } + + CrmDbContext.SaveChanges(); + + tx.Commit(); + + return tagID; + } + + public int DeleteTagFromContacts(int[] contactID, String tagName) + { + if (String.IsNullOrEmpty(tagName) || contactID == null || contactID.Length == 0) + throw new ArgumentException(); + + using var tx = CrmDbContext.Database.BeginTransaction(); + + var tagID = Query(CrmDbContext.Tags) + .Where(x => String.Compare(x.Title, tagName, true) == 0 && x.EntityType == EntityType.Contact) + .Select(x => x.Id) + .SingleOrDefault(); + + if (tagID == 0) + throw new ArgumentException(); + + var itemsToRemove = CrmDbContext + .EntityTags + .Where(x => x.EntityType == EntityType.Contact && + x.TagId == tagID && + contactID.Contains(x.EntityId)); + + CrmDbContext.EntityTags.RemoveRange(itemsToRemove); + + CrmDbContext.SaveChanges(); + + tx.Commit(); + + return tagID; + } + + public void SetTagToEntity(EntityType entityType, int entityID, String[] tags) + { + using var tx = CrmDbContext.Database.BeginTransaction(); + + var itemsToDelete = CrmDbContext.EntityTags.Where(x => x.EntityId == entityID && x.EntityType == entityType); + + CrmDbContext.EntityTags.RemoveRange(itemsToDelete); + + foreach (var tagName in tags.Where(t => !string.IsNullOrWhiteSpace(t))) + { + AddTagToEntityInDb(entityType, entityID, tagName); + } + + CrmDbContext.SaveChanges(); + + tx.Commit(); + } + + private void AddTagToEntityInDb(EntityType entityType, int entityID, int tagID) + { + CrmDbContext.EntityTags.Add(new DbEntityTag + { + EntityId = entityID, + EntityType = entityType, + TagId = tagID + }); + + CrmDbContext.SaveChanges(); + } + + public void AddTagToEntity(EntityType entityType, int entityID, int[] tagIDs) + { + using var tx = CrmDbContext.Database.BeginTransaction(); + + foreach (var tagID in tagIDs) + { + AddTagToEntityInDb(entityType, entityID, tagID); + } + + CrmDbContext.SaveChanges(); + + tx.Commit(); + } + + private static string CorrectTag(string tag) + { + return tag == null + ? null + : tag.Trim() + .Replace("\r\n", string.Empty) + .Replace("\n", string.Empty) + .Replace("\r", string.Empty); + } + + } + +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Dao/TaskDao.cs b/products/ASC.CRM/Server/Core/Dao/TaskDao.cs new file mode 100644 index 00000000000..aeb91cb32b8 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/TaskDao.cs @@ -0,0 +1,994 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text.RegularExpressions; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.Core.Tenants; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.ElasticSearch; +using ASC.Web.CRM.Core.Search; + +using AutoMapper; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.CRM.Core.Dao +{ + [Scope] + public class TaskDao : AbstractDao + { + private readonly UserDbContext _userDbContext; + private readonly FactoryIndexerTask _factoryIndexer; + private readonly TenantUtil _tenantUtil; + private readonly CrmSecurity _crmSecurity; + + public TaskDao(DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + CrmSecurity crmSecurity, + TenantUtil tenantUtil, + FactoryIndexerTask factoryIndexer, + IOptionsMonitor logger, + ICache ascCache, + DbContextManager userDbContext, + IMapper mapper) : + base(dbContextManager, + tenantManager, + securityContext, + logger, + ascCache, + mapper) + { + _crmSecurity = crmSecurity; + _tenantUtil = tenantUtil; + _factoryIndexer = factoryIndexer; + _userDbContext = userDbContext.Value; + _mapper = mapper; + } + + public void OpenTask(int id) + { + var dbEntity = CrmDbContext.Tasks.Find(id); + + if (dbEntity == null) + throw new ArgumentException(); + + if (!dbEntity.IsClosed) return; + if (dbEntity.TenantId != TenantID) return; + + var entity = _mapper.Map(dbEntity); + + _crmSecurity.DemandEdit(entity); + + dbEntity.IsClosed = false; + + CrmDbContext.SaveChanges(); + } + + public void CloseTask(int id) + { + var dbEntity = CrmDbContext.Tasks.Find(id); + + if (dbEntity == null) + throw new ArgumentException(); + + if (dbEntity.IsClosed) return; + + var entity = _mapper.Map(dbEntity); + + _crmSecurity.DemandEdit(entity); + + dbEntity.IsClosed = true; + + CrmDbContext.SaveChanges(); + } + + public Task GetByID(int id) + { + var dbEntity = CrmDbContext.Tasks.Find(id); + + if (dbEntity.TenantId != TenantID) return null; + + var entity = _mapper.Map(dbEntity); + + _crmSecurity.DemandAccessTo(entity); + + return entity; + } + + public List GetTasks(EntityType entityType, int entityID, bool? onlyActiveTask) + { + return GetTasks(String.Empty, Guid.Empty, 0, onlyActiveTask, DateTime.MinValue, DateTime.MinValue, + entityType, entityID, 0, 0, null); + } + public int GetAllTasksCount() + { + return Query(CrmDbContext.Tasks).Count(); + } + + public List GetAllTasks() + { + var dbTasks = Query(CrmDbContext.Tasks) + .OrderBy(x => x.Deadline) + .OrderBy(x => x.Title) + .AsNoTracking() + .ToList(); + + return _mapper.Map, List>(dbTasks) + .FindAll(_crmSecurity.CanAccessTo); + } + + public void ExecAlert(IEnumerable ids) + { + if (!ids.Any()) return; + + foreach (var id in ids) + { + var dbEntity = CrmDbContext.Tasks.Find(id); + + if (dbEntity.TenantId != TenantID) + throw new Exception("Get data from another tenant"); + + dbEntity.ExecAlert = 1; + } + + CrmDbContext.SaveChanges(); + } + + public List GetInfoForReminder(DateTime scheduleDate) + { + return Query(CrmDbContext.Tasks).Where(x => + x.IsClosed == false && + x.AlertValue != 0 && + x.ExecAlert == 0 && + (x.Deadline.AddMinutes(-x.AlertValue) >= scheduleDate.AddHours(-1) && x.Deadline.AddMinutes(-x.AlertValue) <= scheduleDate.AddHours(-1)) + ) + .Select(x => new object[] { x.TenantId, x.Id, x.Deadline, x.AlertValue, x.ResponsibleId }) + .ToList(); + } + + public List GetTasks( + String searchText, + Guid responsibleID, + int categoryID, + bool? isClosed, + DateTime fromDate, + DateTime toDate, + EntityType entityType, + int entityID, + int from, + int count, + OrderBy orderBy) + { + + if (_crmSecurity.IsAdmin) + return GetCrudeTasks( + searchText, + responsibleID, + categoryID, + isClosed, + fromDate, + toDate, + entityType, + entityID, + from, + count, + orderBy); + + + var crudeTasks = GetCrudeTasks( + searchText, + responsibleID, + categoryID, + isClosed, + fromDate, + toDate, + entityType, + entityID, + 0, + from + count, + orderBy); + + if (crudeTasks.Count == 0) return crudeTasks; + + if (crudeTasks.Count < from + count) return _crmSecurity.FilterRead(crudeTasks).Skip(from).ToList(); + + var result = _crmSecurity.FilterRead(crudeTasks).ToList(); + + if (result.Count == crudeTasks.Count) return result.Skip(from).ToList(); + + var localCount = count; + var localFrom = from + count; + + while (true) + { + crudeTasks = GetCrudeTasks( + searchText, + responsibleID, + categoryID, + isClosed, + fromDate, + toDate, + entityType, + entityID, + localFrom, + localCount, + orderBy); + + if (crudeTasks.Count == 0) break; + + result.AddRange(_crmSecurity.FilterRead(crudeTasks)); + + if ((result.Count >= count + from) || (crudeTasks.Count < localCount)) break; + + localFrom += localCount; + localCount = localCount * 2; + } + + return result.Skip(from).Take(count).ToList(); + } + + + private List GetCrudeTasks( + String searchText, + Guid responsibleID, + int categoryID, + bool? isClosed, + DateTime fromDate, + DateTime toDate, + EntityType entityType, + int entityID, + int from, + int count, + OrderBy orderBy) + { + + var sqlQuery = GetDbTaskByFilters(responsibleID, categoryID, isClosed, fromDate, toDate, entityType, entityID, from, count, orderBy); + + if (!string.IsNullOrEmpty(searchText)) + { + searchText = searchText.Trim(); + + var keywords = searchText.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) + .ToArray(); + + if (keywords.Length > 0) + { + List tasksIds; + + if (!_factoryIndexer.TrySelectIds(s => s.MatchAll(searchText), out tasksIds)) + { + foreach (var k in keywords) + { + sqlQuery = sqlQuery.Where(x => Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Title, k + "%") || + Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Description, k + "%")); + } + } + else + { + if (tasksIds.Any()) + { + sqlQuery = sqlQuery.Where(x => tasksIds.Contains(x.Id)); + } + else + { + return new List(); + } + } + + } + } + + return _mapper.Map, List>(sqlQuery.ToList()); + } + + public int GetTasksCount( + String searchText, + Guid responsibleId, + int categoryId, + bool? isClosed, + DateTime fromDate, + DateTime toDate, + EntityType entityType, + int entityId) + { + + int result = 0; + + _logger.DebugFormat("Starting GetTasksCount: {0}", DateTime.Now.ToString()); + + var cacheKey = TenantID.ToString(CultureInfo.InvariantCulture) + + "tasks" + + _securityContext.CurrentAccount.ID.ToString() + + searchText + + responsibleId + + categoryId + + fromDate.ToString(CultureInfo.InvariantCulture) + + toDate.ToString(CultureInfo.InvariantCulture) + + (int)entityType + + entityId; + + if (!String.IsNullOrEmpty(_cache.Get(cacheKey))) + { + _logger.DebugFormat("End GetTasksCount: {0}. From cache", DateTime.Now.ToString()); + + return Convert.ToInt32(_cache.Get(cacheKey)); + } + + + if (!String.IsNullOrEmpty(searchText)) + { + var tasks = GetCrudeTasks(searchText, + responsibleId, + categoryId, + isClosed, + fromDate, + toDate, + entityType, + entityId, + 0, + 0, + null); + + if (_crmSecurity.IsAdmin) + result = tasks.Count(); + else + result = _crmSecurity.FilterRead(tasks).Count(); + } + else + { + if (_crmSecurity.IsAdmin) + { + result = GetDbTaskByFilters( + responsibleId, + categoryId, + isClosed, + fromDate, + toDate, + entityType, + entityId, + 0, + 0, + null).Count(); + } + else + { + throw new NotImplementedException(); + //var taskIds = new List(); + + //// PrivateTask + + + //// count tasks without entityId and only open contacts + //taskIds = GetDbTaskByFilters(responsibleId, + // categoryId, + // isClosed, + // fromDate, + // toDate, + // entityType, + // entityId, + // 0, + // 0, + // null).GroupJoin(Query(CRMDbContext.Contacts), + // x => x.ContactId, + // y => y.Id, + // (x, y) => new { x, y }) + // .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { x.x, y }) + // .Where(x => + + // x.y == null || + // x.y.IsShared == null || + // x.y.IsShared.Value == ShareType.Read || + // x.y.IsShared.Value == ShareType.ReadWrite) + // .Select(x => x.x.Id) + // .ToList(); + + //Logger.DebugFormat("End GetTasksCount: {0}. count tasks without entityId and only open contacts", DateTime.Now.ToString()); + + //var idsFromAcl = CoreDbContext.Acl.Where(x => x.Tenant == TenantID && + // x.Action == CRMSecurity._actionRead.ID && + // x.Subject == SecurityContext.CurrentAccount.ID && + // (Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Object, typeof(Company).FullName + "%") || + // Microsoft.EntityFrameworkCore.EF.Functions.Like(x.Object, typeof(Person).FullName + "%"))) + // .Select(x => Convert.ToInt32(x.Object.Split('|', StringSplitOptions.None)[1])) + // .ToList(); + + //Query(CRMDbContext.Contacts).GroupBy(x => x.Id).Select(x => x.) + //taskIds.AddRange(GetDbTaskByFilters(responsibleId, + // categoryId, + // isClosed, + // fromDate, + // toDate, + // entityType, + // entityId, + // 0, + // 0, + // null).GroupJoin(CRMDbContext.Contacts, + // x => x.ContactId, + // y => y.Id, + // (x, y) => new { x, y }) + // .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { x.x, y }) + // .Where(x => x.y != null && + // x.y.IsShared == 0 && + // idsFromAcl.Contains(x.y.Id)) + // .Select(x => x.x.Id) + // .ToList()); + + //Logger.DebugFormat("End GetTasksCount: {0}. count tasks with entityId and only close contacts", DateTime.Now.ToString()); + + + //sqlQuery = Query("crm_task tbl_tsk") + // .Select("tbl_tsk.id") + // .InnerJoin("core_acl tbl_cl", Exp.EqColumns("tbl_tsk.tenant_id", "tbl_cl.tenant") & + // Exp.Eq("tbl_cl.subject", SecurityContext.CurrentAccount.ID.ToString()) & + // Exp.Eq("tbl_cl.action", CRMSecurity._actionRead.ID.ToString()) & + // Exp.EqColumns("tbl_cl.object", "CONCAT('ASC.CRM.Core.Entities.Deal|', tbl_tsk.entity_id)")) + // .Where(!Exp.Eq("tbl_tsk.entity_id", 0) & Exp.Eq("tbl_tsk.entity_type", (int)EntityType.Opportunity) & Exp.Eq("tbl_tsk.contact_id", 0)); + + //sqlQuery = GetDbTaskByFilters(responsibleId, + // categoryId, + // isClosed, + // fromDate, + // toDate, + // entityType, + // entityId, + // 0, + // 0, + // null); + + //// count tasks with entityId and without contact + //taskIds.AddRange(Db.ExecuteList(sqlQuery).Select(item => Convert.ToInt32(item[0])).ToList()); + + //Logger.DebugFormat("End GetTasksCount: {0}. count tasks with entityId and without contact", DateTime.Now.ToString()); + + //sqlQuery = Query("crm_task tbl_tsk") + // .Select("tbl_tsk.id") + // .InnerJoin("core_acl tbl_cl", Exp.EqColumns("tbl_tsk.tenant_id", "tbl_cl.tenant") & + // Exp.Eq("tbl_cl.subject", SecurityContext.CurrentAccount.ID.ToString()) & + // Exp.Eq("tbl_cl.action", CRMSecurity._actionRead.ID.ToString()) & + // Exp.EqColumns("tbl_cl.object", "CONCAT('ASC.CRM.Core.Entities.Cases|', tbl_tsk.entity_id)")) + // .Where(!Exp.Eq("tbl_tsk.entity_id", 0) & Exp.Eq("tbl_tsk.entity_type", (int)EntityType.Case) & Exp.Eq("tbl_tsk.contact_id", 0)); + + //sqlQuery = GetDbTaskByFilters(responsibleId, + // categoryId, + // isClosed, + // fromDate, + // toDate, + // entityType, + // entityId, + // 0, + // 0, + // null); + + //// count tasks with entityId and without contact + //taskIds.AddRange(Db.ExecuteList(sqlQuery).Select(item => Convert.ToInt32(item[0])).ToList()); + + //result = taskIds.Distinct().Count(); + + //Logger.DebugFormat("End GetTasksCount: {0}. count tasks with entityId and without contact", DateTime.Now.ToString()); + + //Logger.Debug("Finish"); + + } + } + + + if (result > 0) + { + _cache.Insert(cacheKey, result, TimeSpan.FromMinutes(1)); + } + + return result; + } + + private IQueryable GetDbTaskByFilters( + Guid responsibleID, + int categoryID, + bool? isClosed, + DateTime fromDate, + DateTime toDate, + EntityType entityType, + int entityID, + int from, + int count, + OrderBy orderBy) + { + var sqlQuery = Query(CrmDbContext.Tasks).AsNoTracking(); + + if (responsibleID != Guid.Empty) + sqlQuery = sqlQuery.Where(x => x.ResponsibleId == responsibleID); + + if (entityID > 0) + switch (entityType) + { + case EntityType.Contact: + var isCompany = Query(CrmDbContext.Contacts).Where(x => x.Id == entityID).Select(x => x.IsCompany).Single(); + + if (isCompany) + return GetDbTaskByFilters(responsibleID, categoryID, isClosed, fromDate, toDate, EntityType.Company, entityID, from, count, orderBy); + else + return GetDbTaskByFilters(responsibleID, categoryID, isClosed, fromDate, toDate, EntityType.Person, entityID, from, count, orderBy); + + case EntityType.Person: + sqlQuery = sqlQuery.Where(x => x.ContactId == entityID); + break; + case EntityType.Company: + + var personIDs = GetRelativeToEntity(entityID, EntityType.Person, null).ToList(); + + if (personIDs.Count == 0) + { + sqlQuery = sqlQuery.Where(x => x.ContactId == entityID); + } + else + { + personIDs.Add(entityID); + sqlQuery = sqlQuery.Where(x => personIDs.Contains(x.ContactId)); + } + + break; + case EntityType.Case: + case EntityType.Opportunity: + sqlQuery = sqlQuery.Where(x => x.EntityId == entityID && x.EntityType == entityType); + break; + } + + + + if (isClosed.HasValue) + { + sqlQuery = sqlQuery.Where(x => x.IsClosed == isClosed); + } + + if (categoryID > 0) + { + sqlQuery = sqlQuery.Where(x => x.CategoryId == categoryID); + } + + if (fromDate != DateTime.MinValue && toDate != DateTime.MinValue) + { + sqlQuery = sqlQuery.Where(x => x.Deadline >= _tenantUtil.DateTimeToUtc(fromDate) && x.Deadline <= _tenantUtil.DateTimeToUtc(toDate)); + } + else if (fromDate != DateTime.MinValue) + { + sqlQuery = sqlQuery.Where(x => x.Deadline > _tenantUtil.DateTimeToUtc(fromDate)); + } + else if (toDate != DateTime.MinValue) + { + sqlQuery = sqlQuery.Where(x => x.Deadline < _tenantUtil.DateTimeToUtc(toDate)); + } + + if (0 < from && from < int.MaxValue) + sqlQuery = sqlQuery.Skip(from); + + if (0 < count && count < int.MaxValue) + sqlQuery = sqlQuery.Take(count); + + sqlQuery = sqlQuery.OrderBy(x => x.IsClosed); + + if (orderBy != null && Enum.IsDefined(typeof(TaskSortedByType), orderBy.SortedBy)) + { + switch ((TaskSortedByType)orderBy.SortedBy) + { + case TaskSortedByType.Title: + { + if (orderBy.IsAsc) + sqlQuery = sqlQuery.OrderBy(x => x.Title) + .ThenBy(x => x.Deadline); + else + sqlQuery = sqlQuery.OrderByDescending(x => x.Title) + .ThenBy(x => x.Deadline); + } + + break; + case TaskSortedByType.DeadLine: + { + if (orderBy.IsAsc) + sqlQuery = sqlQuery.OrderBy(x => x.Deadline) + .ThenBy(x => x.Title); + else + sqlQuery = sqlQuery.OrderByDescending(x => x.Deadline) + .ThenBy(x => x.Title); + } + break; + case TaskSortedByType.Category: + { + if (orderBy.IsAsc) + sqlQuery = sqlQuery.OrderBy(x => x.CategoryId) + .ThenBy(x => x.Deadline) + .ThenBy(x => x.Title); + else + sqlQuery = sqlQuery.OrderByDescending(x => x.CategoryId) + .ThenBy(x => x.Deadline) + .ThenBy(x => x.Title); + } + + break; + case TaskSortedByType.ContactManager: + { + sqlQuery = sqlQuery.GroupJoin(_userDbContext.Users.Where(x => x.Tenant == TenantID), + x => x.ResponsibleId, + y => y.Id, + (x, y) => new { x, y } + ) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { x.x, y }) + .OrderBy("x.y.LastName", orderBy.IsAsc) + .OrderBy("x.y.FirstName", orderBy.IsAsc) + .OrderBy(x => x.x.Deadline) + .OrderBy(x => x.x.Title) + .Select(x => x.x); + } + + break; + case TaskSortedByType.Contact: + { + var subSqlQuery = sqlQuery.GroupJoin(CrmDbContext.Contacts, + x => x.ContactId, + y => y.Id, + (x, y) => new { x, y } + ) + .SelectMany(x => x.y.DefaultIfEmpty(), (x, y) => new { x.x, y }); + + if (orderBy.IsAsc) + { + subSqlQuery = subSqlQuery.OrderBy(x => x.y != null ? x.y.DisplayName : "") + .ThenBy(x => x.x.Deadline) + .ThenBy(x => x.x.Title); + } + else + { + subSqlQuery = subSqlQuery.OrderByDescending(x => x.y != null ? x.y.DisplayName : "") + .ThenBy(x => x.x.Deadline) + .ThenBy(x => x.x.Title); + + } + + sqlQuery = subSqlQuery.Select(x => x.x); + + break; + } + } + } + else + { + sqlQuery = sqlQuery.OrderBy(x => x.Deadline) + .ThenBy(x => x.Title); + } + + return sqlQuery; + + } + + public Dictionary GetNearestTask(int[] contactID) + { + return Query(CrmDbContext.Tasks) + .Where(x => contactID.Contains(x.ContactId) && !x.IsClosed) + .GroupBy(x => x.ContactId) + .ToDictionary(x => x.Key, y => _mapper.Map(y.Single(x => x.Id == y.Min(x => x.Id)))); + } + + public IEnumerable GetResponsibles(int categoryID) + { + var sqlQuery = Query(CrmDbContext.Tasks); + + if (0 < categoryID) + sqlQuery = sqlQuery.Where(x => x.CategoryId == categoryID); + + return sqlQuery.GroupBy(x => x.ResponsibleId).Select(x => x.Key).ToList(); + } + + public Dictionary GetTasksCount(int[] contactID) + { + return Query(CrmDbContext.Tasks) + .Where(x => contactID.Contains(x.ContactId)) + .GroupBy(x => x.ContactId) + .Select(x => new { Key = x.Key, Value = x.Count() }) + .ToDictionary(x => x.Key, x => x.Value); + } + + public int GetTasksCount(int contactID) + { + var result = GetTasksCount(new[] { contactID }); + + if (result.Count == 0) return 0; + + return result[contactID]; + } + + public Dictionary HaveLateTask(int[] contactID) + { + return Query(CrmDbContext.Tasks) + .Where(x => contactID.Contains(x.ContactId) && !x.IsClosed && x.Deadline <= DateTime.UtcNow) + .GroupBy(x => x.ContactId) + .Select(x => new { Key = x.Key, Value = x.Count() }) + .ToDictionary(x => x.Key, x => x.Value > 0); + } + + public bool HaveLateTask(int contactID) + { + var result = HaveLateTask(new[] { contactID }); + + if (result.Count == 0) return false; + + return result[contactID]; + } + + public Task SaveOrUpdateTask(Task newTask) + { + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "tasks.*")); + + return SaveOrUpdateTaskInDb(newTask); + } + + public Task[] SaveOrUpdateTaskList(List newTasks) + { + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "tasks.*")); + + var result = new List(); + + foreach (var newTask in newTasks) + { + result.Add(SaveOrUpdateTaskInDb(newTask)); + } + + return result.ToArray(); + } + + private Task SaveOrUpdateTaskInDb(Task newTask) + { + if (string.IsNullOrEmpty(newTask.Title) || + newTask.DeadLine == DateTime.MinValue || + newTask.CategoryID <= 0) + throw new ArgumentException(); + + var dbEntity = new DbTask + { + Id = newTask.ID, + Title = newTask.Title, + Description = newTask.Description, + Deadline = _tenantUtil.DateTimeToUtc(newTask.DeadLine), + ResponsibleId = newTask.ResponsibleID, + ContactId = newTask.ContactID, + EntityType = newTask.EntityType, + EntityId = newTask.EntityID, + IsClosed = newTask.IsClosed, + CategoryId = newTask.CategoryID, + CreateOn = newTask.CreateOn == DateTime.MinValue ? DateTime.UtcNow : newTask.CreateOn, + CreateBy = newTask.CreateBy == Guid.Empty ? _securityContext.CurrentAccount.ID : newTask.CreateBy, + LastModifedOn = DateTime.UtcNow, + LastModifedBy = _securityContext.CurrentAccount.ID, + AlertValue = newTask.AlertValue, + ExecAlert = 0, + TenantId = TenantID + }; + + CrmDbContext.Update(dbEntity); + CrmDbContext.SaveChanges(); + + _factoryIndexer.Index(dbEntity); + + return _mapper.Map(dbEntity); + } + + public int SaveTask(Task newTask) + { + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "tasks.*")); + + return SaveTaskInDb(newTask); + } + + private int SaveTaskInDb(Task newTask) + { + if (String.IsNullOrEmpty(newTask.Title) || newTask.DeadLine == DateTime.MinValue || + newTask.CategoryID == 0) + throw new ArgumentException(); + + var dbTask = new DbTask + { + Title = newTask.Title, + Description = newTask.Description, + Deadline = _tenantUtil.DateTimeToUtc(newTask.DeadLine), + ResponsibleId = newTask.ResponsibleID, + ContactId = newTask.ContactID, + EntityType = newTask.EntityType, + EntityId = newTask.EntityID, + IsClosed = newTask.IsClosed, + CategoryId = newTask.CategoryID, + CreateOn = newTask.CreateOn == DateTime.MinValue ? DateTime.UtcNow : newTask.CreateOn, + CreateBy = _securityContext.CurrentAccount.ID, + LastModifedOn = newTask.CreateOn == DateTime.MinValue ? DateTime.UtcNow : newTask.CreateOn, + LastModifedBy = _securityContext.CurrentAccount.ID, + AlertValue = newTask.AlertValue, + TenantId = TenantID + }; + + CrmDbContext.Tasks.Add(dbTask); + + CrmDbContext.SaveChanges(); + + newTask.ID = dbTask.Id; + + _factoryIndexer.Index(dbTask); + + return newTask.ID; + } + + public int[] SaveTaskList(List items) + { + using var tx = CrmDbContext.Database.BeginTransaction(); + + var result = new List(); + + foreach (var item in items) + { + result.Add(SaveTaskInDb(item)); + } + + CrmDbContext.SaveChanges(); + + tx.Commit(); + + return result.ToArray(); + } + + public void DeleteTask(int id) + { + var task = GetByID(id); + + if (task == null) return; + + _crmSecurity.DemandEdit(task); + + var dbEntity = CrmDbContext.Tasks.Find(id); + + _factoryIndexer.Delete(dbEntity); + + CrmDbContext.Tasks.Remove(dbEntity); + + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "tasks.*")); + + } + + public List CreateByTemplate(List templateItems, EntityType entityType, int entityID) + { + if (templateItems == null || templateItems.Count == 0) return new List(); + + var result = new List(); + + using var tx = CrmDbContext.Database.BeginTransaction(); + + foreach (var templateItem in templateItems) + { + var task = new Task + { + ResponsibleID = templateItem.ResponsibleID, + Description = templateItem.Description, + DeadLine = _tenantUtil.DateTimeNow().AddTicks(templateItem.Offset.Ticks), + CategoryID = templateItem.CategoryID, + Title = templateItem.Title, + CreateOn = _tenantUtil.DateTimeNow(), + CreateBy = templateItem.CreateBy + }; + + switch (entityType) + { + case EntityType.Contact: + case EntityType.Person: + case EntityType.Company: + task.ContactID = entityID; + break; + case EntityType.Opportunity: + task.EntityType = EntityType.Opportunity; + task.EntityID = entityID; + break; + case EntityType.Case: + task.EntityType = EntityType.Case; + task.EntityID = entityID; + break; + default: + throw new NotImplementedException(); + } + + task = SaveOrUpdateTask(task); + + result.Add(task); + + CrmDbContext.TaskTemplateTask.Add(new DbTaskTemplateTask + { + TaskId = task.ID, + TaskTemplateId = templateItem.ID, + TenantId = TenantID + }); + } + + tx.Commit(); + + CrmDbContext.SaveChanges(); + + return result; + } + + public void ReassignTasksResponsible(Guid fromUserId, Guid toUserId) + { + var dbEntities = Query(CrmDbContext.Tasks) + .Where(x => x.ResponsibleId == fromUserId) + .ToList(); + + foreach (var dbEntity in dbEntities) + { + dbEntity.ResponsibleId = toUserId; + } + + CrmDbContext.SaveChanges(); + } + + /// + /// Test method + /// + /// + /// + public void SetTaskCreationDate(int id, DateTime creationDate) + { + var dbEntity = CrmDbContext.Tasks.Find(id); + + dbEntity.LastModifedOn = _tenantUtil.DateTimeToUtc(creationDate); + + CrmDbContext.SaveChanges(); + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "tasks.*")); + } + + /// + /// Test method + /// + /// + /// + public void SetTaskLastModifedDate(int id, DateTime lastModifedDate) + { + var dbEntity = CrmDbContext.Tasks.Find(id); + + dbEntity.LastModifedOn = _tenantUtil.DateTimeToUtc(lastModifedDate); + + CrmDbContext.SaveChanges(); + + // Delete relative keys + _cache.Remove(new Regex(TenantID.ToString(CultureInfo.InvariantCulture) + "tasks.*")); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Dao/TaskTemplateContainerDao.cs b/products/ASC.CRM/Server/Core/Dao/TaskTemplateContainerDao.cs new file mode 100644 index 00000000000..1f632a2828d --- /dev/null +++ b/products/ASC.CRM/Server/Core/Dao/TaskTemplateContainerDao.cs @@ -0,0 +1,236 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; + +using AutoMapper; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.CRM.Core.Dao +{ + [Scope] + public class TaskTemplateContainerDao : AbstractDao + { + public TaskTemplateContainerDao( + DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + IOptionsMonitor logger, + ICache cache, + IMapper mapper + ) + : base(dbContextManager, + tenantManager, + securityContext, + logger, + cache, + mapper) + { + + } + + public int SaveOrUpdate(TaskTemplateContainer item) + { + var dbEntity = new DbTaskTemplateContainer + { + Id = item.ID, + Title = item.Title, + EntityType = item.EntityType, + CreateOn = item.CreateOn == DateTime.MinValue ? DateTime.UtcNow : item.CreateOn, + CreateBy = item.CreateBy == Guid.Empty ? _securityContext.CurrentAccount.ID : item.CreateBy, + LastModifedOn = DateTime.UtcNow, + LastModifedBy = _securityContext.CurrentAccount.ID, + TenantId = TenantID + }; + + CrmDbContext.Update(dbEntity); + CrmDbContext.SaveChanges(); + + return dbEntity.Id; + } + + public void Delete(int id) + { + var dbEntity = CrmDbContext.TaskTemplateContainer.Find(id); + + CrmDbContext.TaskTemplateContainer.Remove(dbEntity); + + CrmDbContext.SaveChanges(); + } + + public TaskTemplateContainer GetByID(int id) + { + var dbEntity = CrmDbContext.TaskTemplateContainer.Find(id); + + if (dbEntity.TenantId != TenantID) return null; + + return _mapper.Map(dbEntity); + } + + public List GetItems(EntityType entityType) + { + if (!_supportedEntityType.Contains(entityType)) + throw new ArgumentException("", entityType.ToString()); + + var dbEntities = Query(CrmDbContext.TaskTemplateContainer) + .AsNoTracking() + .Where(x => x.EntityType == entityType) + .ToList(); + + return _mapper.Map, List>(dbEntities); + } + } + + [Scope] + public class TaskTemplateDao : AbstractDao + { + public TaskTemplateDao(DbContextManager dbContextManager, + TenantManager tenantManager, + SecurityContext securityContext, + IOptionsMonitor logger, + ICache cache, + IMapper mapper) + : base(dbContextManager, + tenantManager, + securityContext, + logger, + cache, + mapper) + { + + } + + public int SaveOrUpdate(TaskTemplate item) + { + var dbEntity = new DbTaskTemplate + { + Id = item.ID, + Title = item.Title, + CategoryId = item.CategoryID, + Description = item.Description, + ResponsibleId = item.ResponsibleID, + IsNotify = item.isNotify, + Offset = item.Offset.Ticks, + DeadLineIsFixed = item.DeadLineIsFixed, + ContainerId = item.ContainerID, + CreateOn = item.CreateOn == DateTime.MinValue ? DateTime.UtcNow : item.CreateOn, + CreateBy = item.CreateBy == Guid.Empty ? _securityContext.CurrentAccount.ID : item.CreateBy, + LastModifedOn = DateTime.UtcNow, + LastModifedBy = _securityContext.CurrentAccount.ID, + SortOrder = item.SortOrder, + TenantId = TenantID + }; + + CrmDbContext.Update(dbEntity); + CrmDbContext.SaveChanges(); + + return dbEntity.Id; + + } + + public TaskTemplate GetNext(int taskID) + { + var sqlResult = Query(CrmDbContext.TaskTemplateTask) + .Join(Query(CrmDbContext.TaskTemplates), + x => x.TaskTemplateId, + y => y.Id, + (x, y) => new { x, y } + ).Where(x => x.x.TaskId == taskID) + .Select(x => new { x.y.ContainerId, x.y.SortOrder }) + .SingleOrDefault(); + + if (sqlResult == null) return null; + + var dbEntity = Query(CrmDbContext.TaskTemplates) + .FirstOrDefault(x => x.ContainerId == sqlResult.ContainerId && + x.SortOrder > sqlResult.SortOrder && !x.DeadLineIsFixed); + + var result = _mapper.Map(dbEntity); + + var dbEntityToDelete = CrmDbContext.TaskTemplateTask.Find(taskID); + + if (dbEntityToDelete.TenantId != TenantID) return null; + + CrmDbContext.Remove(dbEntityToDelete); + CrmDbContext.SaveChanges(); + + return result; + } + + public List GetAll() + { + var dbEntities = Query(CrmDbContext.TaskTemplates) + .AsNoTracking() + .OrderBy(x => x.SortOrder) + .ToList(); + + return _mapper.Map, List>(dbEntities); + } + + public List GetList(int containerID) + { + var dbEntities = Query(CrmDbContext.TaskTemplates) + .AsNoTracking() + .Where(x => x.ContainerId == containerID) + .OrderBy(x => x.SortOrder) + .ToList(); + + return _mapper.Map, List>(dbEntities); + } + + public TaskTemplate GetByID(int id) + { + var dbEntity = CrmDbContext.TaskTemplates.Find(id); + + if (dbEntity.TenantId != TenantID) return null; + + return _mapper.Map(dbEntity); + } + + public virtual void Delete(int id) + { + var dbEntity = CrmDbContext.TaskTemplateContainer.Find(id); + + if (dbEntity.TenantId != TenantID) return; + + CrmDbContext.Remove(dbEntity); + + CrmDbContext.SaveChanges(); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/DomainObject.cs b/products/ASC.CRM/Server/Core/DomainObject.cs new file mode 100644 index 00000000000..7dccd101317 --- /dev/null +++ b/products/ASC.CRM/Server/Core/DomainObject.cs @@ -0,0 +1,64 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System.Text.Json.Serialization; + +#endregion + +namespace ASC.CRM.Core +{ + public class DomainObject + { + + [JsonPropertyName("id")] + public virtual int ID + { + get; + set; + } + + public override int GetHashCode() + { + return (GetType().FullName + "|" + ID.GetHashCode()).GetHashCode(); + } + + public override bool Equals(object obj) + { + var compareTo = obj as DomainObject; + return compareTo != null && ( + (!IsTransient() && !compareTo.IsTransient() && ID.Equals(compareTo.ID)) || + ((IsTransient() || compareTo.IsTransient()) && GetHashCode().Equals(compareTo.GetHashCode()))); + } + + private bool IsTransient() + { + return ID.Equals(default(int)); + } + + } +} diff --git a/products/ASC.CRM/Server/Core/EF/CRMDbContext.cs b/products/ASC.CRM/Server/Core/EF/CRMDbContext.cs new file mode 100644 index 00000000000..fea8586c832 --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/CRMDbContext.cs @@ -0,0 +1,475 @@ +using ASC.Common; +using ASC.Core.Common.EF; +using ASC.Core.Common.EF.Model; + +using Microsoft.EntityFrameworkCore; + +namespace ASC.CRM.Core.EF +{ + public partial class CrmDbContext : BaseDbContext + { + public virtual DbSet Cases { get; set; } + public virtual DbSet Contacts { get; set; } + public virtual DbSet ContactsInfo { get; set; } + public virtual DbSet CurrencyInfo { get; set; } + public virtual DbSet CurrencyRate { get; set; } + public virtual DbSet Deals { get; set; } + public virtual DbSet DealMilestones { get; set; } + public virtual DbSet EntityContact { get; set; } + public virtual DbSet EntityTags { get; set; } + public virtual DbSet FieldDescription { get; set; } + public virtual DbSet FieldValue { get; set; } + public virtual DbSet Invoices { get; set; } + public virtual DbSet InvoiceItem { get; set; } + public virtual DbSet InvoiceLine { get; set; } + public virtual DbSet InvoiceTax { get; set; } + public virtual DbSet ListItem { get; set; } + public virtual DbSet OrganisationLogo { get; set; } + public virtual DbSet Projects { get; set; } + public virtual DbSet RelationshipEvent { get; set; } + public virtual DbSet ReportFile { get; set; } + public virtual DbSet Tags { get; set; } + public virtual DbSet Tasks { get; set; } + public virtual DbSet TaskTemplates { get; set; } + public virtual DbSet TaskTemplateContainer { get; set; } + public virtual DbSet TaskTemplateTask { get; set; } + public virtual DbSet VoipCalls { get; set; } + public virtual DbSet VoipNumber { get; set; } + public virtual DbSet Tenants { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + ModelBuilderWrapper.From(modelBuilder, Provider) + .AddDbFieldValue() + .AddDbContact() + .AddDbContactInfo() + .AddDbCase() + .AddDbRelationshipEvent() + .AddDbDeal() + .AddDbTask() + .AddDbTenant(); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Abbreviation) + .HasName("PRIMARY"); + + entity.Property(e => e.Abbreviation) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.CultureName) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.ResourceKey) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Symbol) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.FromCurrency) + .HasDatabaseName("from_currency"); + + entity.HasIndex(e => e.TenantId) + .HasDatabaseName("tenant_id"); + + entity.Property(e => e.CreateBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.FromCurrency) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.LastModifedBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.ToCurrency) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + + ; + + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.TenantId) + .HasDatabaseName("tenant_id"); + + entity.Property(e => e.Color) + .HasDefaultValueSql("'0'") + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Description) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Title) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => new { e.EntityId, e.EntityType, e.ContactId }) + .HasName("PRIMARY"); + + entity.HasIndex(e => e.ContactId) + .HasDatabaseName("IX_Contact"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => new { e.EntityId, e.EntityType, e.TagId }) + .HasName("PRIMARY"); + + entity.HasIndex(e => e.TagId) + .HasDatabaseName("tag_id"); + }); + + modelBuilder.Entity(entity => + { + entity.HasIndex(e => new { e.TenantId, e.EntityType, e.SortOrder }) + .HasDatabaseName("entity_type"); + + entity.Property(e => e.Label) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Mask) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + + + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.TenantId) + .HasDatabaseName("tenant_id"); + + entity.Property(e => e.ConsigneeId).HasDefaultValueSql("'-1'"); + + entity.Property(e => e.ContactId).HasDefaultValueSql("'-1'"); + + entity.Property(e => e.CreateBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Currency) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Description) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.ExchangeRate).HasDefaultValueSql("'1.00'"); + + entity.Property(e => e.FileId).HasDefaultValueSql("'-1'"); + + entity.Property(e => e.JsonData) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Language) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.LastModifedBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Number) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.PurchaseOrderNumber) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Status).HasDefaultValueSql("'1'"); + + entity.Property(e => e.Terms) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.TenantId) + .HasDatabaseName("tenant_id"); + + entity.Property(e => e.CreateBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Currency) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Description) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.LastModifedBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.StockKeepingUnit) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Title) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.TenantId) + .HasDatabaseName("tenant_id"); + + entity.Property(e => e.Description) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.TenantId) + .HasDatabaseName("tenant_id"); + + entity.Property(e => e.CreateBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Description) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.LastModifedBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Name) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + + modelBuilder.Entity(entity => + { + entity.HasIndex(e => new { e.TenantId, e.ListType }) + .HasDatabaseName("list_type"); + + entity.Property(e => e.AdditionalParams) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Color) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Description) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Title) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.TenantId) + .HasDatabaseName("tenant_id"); + + entity.Property(e => e.Content) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.CreateBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => new { e.TenantId, e.ContactId, e.ProjectId }) + .HasName("PRIMARY"); + + entity.HasIndex(e => new { e.TenantId, e.ContactId }) + .HasDatabaseName("contact_id"); + + entity.HasIndex(e => new { e.TenantId, e.ProjectId }) + .HasDatabaseName("project_id"); + }); + + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.FileId) + .HasName("PRIMARY"); + + entity.HasIndex(e => e.CreateBy) + .HasDatabaseName("create_by"); + + entity.HasIndex(e => e.CreateOn) + .HasDatabaseName("create_on"); + + entity.HasIndex(e => e.TenantId) + .HasDatabaseName("tenant_id"); + + entity.Property(e => e.CreateBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.TenantId) + .HasDatabaseName("tenant_id"); + + entity.Property(e => e.Title) + .HasCharSet("utf8") + .UseCollation("utf8_bin"); + }); + + + modelBuilder.Entity(entity => + { + entity.HasIndex(e => new { e.TenantId, e.ContainerId }) + .HasDatabaseName("template_id"); + + entity.Property(e => e.CreateBy) + .HasDefaultValueSql("'00000000-0000-0000-0000-000000000000'") + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Description) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.LastModifedBy) + .HasDefaultValueSql("'00000000-0000-0000-0000-000000000000'") + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.ResponsibleId) + .HasDefaultValueSql("'00000000-0000-0000-0000-000000000000'") + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Title) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + + modelBuilder.Entity(entity => + { + entity.HasIndex(e => new { e.TenantId, e.EntityType }) + .HasDatabaseName("entity_type"); + + entity.Property(e => e.CreateBy) + .HasDefaultValueSql("'0'") + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.LastModifedBy) + .HasDefaultValueSql("'0'") + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Title) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => new { e.TenantId, e.TaskId, e.TaskTemplateId }) + .HasName("PRIMARY"); + }); + + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.TenantId) + .HasDatabaseName("tenant_id"); + + entity.HasIndex(e => new { e.ParentCallId, e.TenantId }) + .HasDatabaseName("parent_call_id"); + + entity.Property(e => e.Id) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.AnsweredBy) + .HasDefaultValueSql("'00000000-0000-0000-0000-000000000000'") + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.NumberFrom) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.NumberTo) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.ParentCallId) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.RecordSid) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.RecordUrl) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.TenantId) + .HasDatabaseName("tenant_id"); + + entity.Property(e => e.Id) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Alias) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Number) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Settings) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + + OnModelCreatingPartial(modelBuilder); + } + + partial void OnModelCreatingPartial(ModelBuilder modelBuilder); + } + + + public static class CrmDbContextExtention + { + public static DIHelper AddCRMDbContextService(this DIHelper services) + { + return services.AddDbContextManagerService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/CrmDbContextSeed.cs b/products/ASC.CRM/Server/Core/EF/CrmDbContextSeed.cs new file mode 100644 index 00000000000..5fb8c452df5 --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/CrmDbContextSeed.cs @@ -0,0 +1,193 @@ +using System; + +using ASC.Core; +using ASC.Core.Common.Settings; +using ASC.Core.Configuration; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; +using ASC.Web.CRM.Classes; + +namespace ASC.CRM.Core.EF +{ + public static class CrmDbContextSeed + { + public static void SeedInitPortalData(SettingsManager settingsManager, + DaoFactory daoFactory, + CoreConfiguration coreConfiguration) + { + var tenantSettings = settingsManager.Load(); + + if (!tenantSettings.IsConfiguredPortal) + { + // Task Category + var listItemDao = daoFactory.GetListItemDao(); + + listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_Call, "task_category_call.png")); + listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_Deal, "task_category_deal.png")); + listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_Demo, "task_category_demo.png")); + listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_Email, "task_category_email.png")); + listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_Fax, "task_category_fax.png")); + listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_FollowUP, "task_category_follow_up.png")); + listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_Lunch, "task_category_lunch.png")); + listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_Meeting, "task_category_meeting.png")); + listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_Note, "task_category_note.png")); + listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_Ship, "task_category_ship.png")); + listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_SocialNetworks, "task_category_social_networks.png")); + listItemDao.CreateItem(ListType.TaskCategory, new ListItem(CRMTaskResource.TaskCategory_ThankYou, "task_category_thank_you.png")); + + // Deal Milestone New + var milestoneDao = daoFactory.GetDealMilestoneDao(); + + milestoneDao.Create(new DealMilestone + { + Title = CRMDealResource.DealMilestone_InitialContact_Title, + Description = CRMDealResource.DealMilestone_InitialContact_Description, + Probability = 1, + Color = "#e795c1", + Status = DealMilestoneStatus.Open + }); + + milestoneDao.Create(new DealMilestone + { + Title = CRMDealResource.DealMilestone_Preapproach_Title, + Description = CRMDealResource.DealMilestone_Preapproach_Description, + Probability = 2, + Color = "#df7895", + Status = DealMilestoneStatus.Open + }); + + milestoneDao.Create(new DealMilestone + { + Title = CRMDealResource.DealMilestone_Suspect_Title, + Description = CRMDealResource.DealMilestone_Suspect_Description, + Probability = 3, + Color = "#f48454", + SortOrder = 1, + Status = DealMilestoneStatus.Open + }); + + milestoneDao.Create(new DealMilestone + { + Title = CRMDealResource.DealMilestone_Champion_Title, + Description = CRMDealResource.DealMilestone_Champion_Description, + Probability = 20, + Color = "#b58fd6", + SortOrder = 2, + Status = DealMilestoneStatus.Open + }); + + milestoneDao.Create(new DealMilestone + { + Title = CRMDealResource.DealMilestone_Opportunity_Title, + Description = CRMDealResource.DealMilestone_Opportunity_Description, + Probability = 50, + Color = "#d28cc8", + SortOrder = 3, + Status = DealMilestoneStatus.Open + }); + + milestoneDao.Create(new DealMilestone + { + Title = CRMDealResource.DealMilestone_Prospect_Title, + Description = CRMDealResource.DealMilestone_Prospect_Description, + Probability = 75, + Color = "#ffb45e", + SortOrder = 4, + Status = DealMilestoneStatus.Open + }); + + milestoneDao.Create(new DealMilestone + { + Title = CRMDealResource.DealMilestone_Verbal_Title, + Description = CRMDealResource.DealMilestone_Verbal_Description, + Probability = 90, + Color = "#ffd267", + SortOrder = 5, + Status = DealMilestoneStatus.Open + }); + + milestoneDao.Create(new DealMilestone + { + Title = CRMDealResource.DealMilestone_Won_Title, + Description = CRMDealResource.DealMilestone_Won_Description, + Probability = 100, + Color = "#6bbd72", + SortOrder = 6, + Status = DealMilestoneStatus.ClosedAndWon + }); + + milestoneDao.Create(new DealMilestone + { + Title = CRMDealResource.DealMilestone_Lost_Title, + Description = CRMDealResource.DealMilestone_Lost_Description, + Probability = 0, + Color = "#f2a9be", + SortOrder = 7, + Status = DealMilestoneStatus.ClosedAndLost + }); + + // Contact Status + listItemDao.CreateItem(ListType.ContactStatus, new ListItem { Title = CRMContactResource.ContactStatus_Cold, Color = "#8a98d8", SortOrder = 1 }); + listItemDao.CreateItem(ListType.ContactStatus, new ListItem { Title = CRMContactResource.ContactStatus_Warm, Color = "#ffd267", SortOrder = 2 }); + listItemDao.CreateItem(ListType.ContactStatus, new ListItem { Title = CRMContactResource.ContactStatus_Hot, Color = "#df7895", SortOrder = 3 }); + // Contact Type + listItemDao.CreateItem(ListType.ContactType, new ListItem { Title = CRMContactResource.ContactType_Client, SortOrder = 1 }); + listItemDao.CreateItem(ListType.ContactType, new ListItem { Title = CRMContactResource.ContactType_Supplier, SortOrder = 2 }); + listItemDao.CreateItem(ListType.ContactType, new ListItem { Title = CRMContactResource.ContactType_Partner, SortOrder = 3 }); + listItemDao.CreateItem(ListType.ContactType, new ListItem { Title = CRMContactResource.ContactType_Competitor, SortOrder = 4 }); + + // History Category + listItemDao.CreateItem(ListType.HistoryCategory, new ListItem(CRMCommonResource.HistoryCategory_Note, "event_category_note.png")); + listItemDao.CreateItem(ListType.HistoryCategory, new ListItem(CRMCommonResource.HistoryCategory_Email, "event_category_email.png")); + listItemDao.CreateItem(ListType.HistoryCategory, new ListItem(CRMCommonResource.HistoryCategory_Call, "event_category_call.png")); + listItemDao.CreateItem(ListType.HistoryCategory, new ListItem(CRMCommonResource.HistoryCategory_Meeting, "event_category_meeting.png")); + + // Tags + daoFactory.GetTagDao().AddTag(EntityType.Contact, CRMContactResource.Lead, true); + daoFactory.GetTagDao().AddTag(EntityType.Contact, CRMContactResource.Customer, true); + daoFactory.GetTagDao().AddTag(EntityType.Contact, CRMContactResource.Supplier, true); + daoFactory.GetTagDao().AddTag(EntityType.Contact, CRMContactResource.Staff, true); + + tenantSettings.WebFormKey = Guid.NewGuid(); + tenantSettings.IsConfiguredPortal = true; + + if (!settingsManager.Save(tenantSettings)) + { + throw new Exception("not save CRMSettings"); + } + } + + if (!tenantSettings.IsConfiguredSmtp) + { + var smtp = settingsManager.Load().SMTPServerSettingOld; + + if (smtp != null && coreConfiguration.SmtpSettings.IsDefaultSettings) + { + var newSettings = new SmtpSettings(smtp.Host, smtp.Port, smtp.SenderEmailAddress, + smtp.SenderDisplayName) + { + EnableSSL = smtp.EnableSSL, + EnableAuth = smtp.RequiredHostAuthentication, + }; + + if (!string.IsNullOrEmpty(smtp.HostLogin) && !string.IsNullOrEmpty(smtp.HostPassword)) + { + newSettings.SetCredentials(smtp.HostLogin, smtp.HostPassword); + } + + coreConfiguration.SmtpSettings = newSettings; + + } + + tenantSettings.IsConfiguredSmtp = true; + + if (!settingsManager.Save(tenantSettings)) + { + throw new Exception("not save CRMSettings"); + } + } + } + } +} diff --git a/products/ASC.CRM/Server/Core/EF/DbCase.cs b/products/ASC.CRM/Server/Core/EF/DbCase.cs new file mode 100644 index 00000000000..813a2fad149 --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbCase.cs @@ -0,0 +1,98 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq.Expressions; + +using ASC.Core.Common.EF; +using ASC.Core.Common.EF.Model; +using ASC.ElasticSearch; +using ASC.ElasticSearch.Core; + +using Microsoft.EntityFrameworkCore; + +using Nest; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_case")] + public class DbCase : IDbCrm, ISearchItem + { + public int Id { get; set; } + + [Required] + [Text(Analyzer = "whitespacecustom")] + public string Title { get; set; } + + [Column("is_closed")] + public bool IsClosed { get; set; } + + [Required] + [Column("create_by", TypeName = "char(38)")] + public Guid CreateBy { get; set; } + + [Column("create_on", TypeName = "datetime")] + public DateTime CreateOn { get; set; } + + [Column("tenant_id", TypeName = "int(11)")] + public int TenantId { get; set; } + + [Column("last_modifed_on", TypeName = "datetime")] + public DateTime? LastModifedOn { get; set; } + + [Column("last_modifed_by", TypeName = "char(38)")] + public Guid? LastModifedBy { get; set; } + + [NotMapped] + [Ignore] + public string IndexName + { + get + { + return "crm_case"; + } + } + + public Expression> GetSearchContentFields(SearchSettingsHelper searchSettings) + { + return (a) => new[] { Title }; + } + } + + public static class DbCaseExtension + { + public static ModelBuilderWrapper AddDbCase(this ModelBuilderWrapper modelBuilder) + { + modelBuilder + .Add(MySqlAddDbCase, Provider.MySql); + + return modelBuilder; + } + private static void MySqlAddDbCase(this ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.CreateOn) + .HasDatabaseName("create_on"); + + entity.HasIndex(e => e.LastModifedOn) + .HasDatabaseName("last_modifed_on"); + + entity.HasIndex(e => e.TenantId) + .HasDatabaseName("tenant_id"); + + entity.Property(e => e.CreateBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.LastModifedBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Title) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + } + } + +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbContact.cs b/products/ASC.CRM/Server/Core/EF/DbContact.cs new file mode 100644 index 00000000000..6a35bf18d9e --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbContact.cs @@ -0,0 +1,169 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq.Expressions; + +using ASC.Core.Common.EF; +using ASC.Core.Common.EF.Model; +using ASC.CRM.Core.Enums; +using ASC.ElasticSearch; +using ASC.ElasticSearch.Core; + +using Microsoft.EntityFrameworkCore; + +using Nest; + +namespace ASC.CRM.Core.EF +{ + [ElasticsearchType(RelationName = "crm_contact")] + [Table("crm_contact")] + public partial class DbContact : IDbCrm, ISearchItem + { + public int Id { get; set; } + + [Column("is_company")] + public bool IsCompany { get; set; } + + public string Notes { get; set; } + + [Text(Analyzer = "whitespacecustom")] + public string Title { get; set; } + + [Column("first_name", TypeName = "varchar(255)")] + public string FirstName { get; set; } + + [Column("last_name", TypeName = "varchar(255)")] + public string LastName { get; set; } + + [Column("company_name", TypeName = "varchar(255)")] + public string CompanyName { get; set; } + + [Column("industry", TypeName = "varchar(255)")] + public string Industry { get; set; } + + [Column("status_id", TypeName = "int(11)")] + public int StatusId { get; set; } + + [Column("company_id", TypeName = "int(11)")] + public int CompanyId { get; set; } + + [Column("contact_type_id", TypeName = "int(11)")] + public int ContactTypeId { get; set; } + + [Required] + [Column("create_by", TypeName = "char(38)")] + public Guid CreateBy { get; set; } + + [Column("create_on", TypeName = "datetime")] + public DateTime CreateOn { get; set; } + + [Column("last_modifed_on", TypeName = "datetime")] + public DateTime? LastModifedOn { get; set; } + + [Column("last_modifed_by", TypeName = "char(38)")] + public Guid LastModifedBy { get; set; } + + [Column("display_name", TypeName = "varchar(255)")] + public string DisplayName { get; set; } + + [Column("is_shared", TypeName = "tinyint(4)")] + public ShareType? IsShared { get; set; } + + [Column("currency", TypeName = "varchar(3)")] + public string Currency { get; set; } + + [Column("tenant_id", TypeName = "int(11)")] + public int TenantId { get; set; } + + [NotMapped] + [Ignore] + public string IndexName + { + get + { + return "crm_contact"; + } + } + + public Expression> GetSearchContentFields(SearchSettingsHelper searchSettings) + { + return (a) => new[] { Title }; + } + } + + public static class DbContactExtension + { + public static ModelBuilderWrapper AddDbContact(this ModelBuilderWrapper modelBuilder) + { + modelBuilder + .Add(MySqlAddDbContact, Provider.MySql) + .Add(PgSqlAddDbContact, Provider.Postgre); + + return modelBuilder; + } + + private static void MySqlAddDbContact(this ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.CreateOn) + .HasDatabaseName("create_on"); + + entity.HasIndex(e => new { e.LastModifedOn, e.TenantId }) + .HasDatabaseName("last_modifed_on"); + + entity.HasIndex(e => new { e.TenantId, e.CompanyId }) + .HasDatabaseName("company_id"); + + entity.HasIndex(e => new { e.TenantId, e.DisplayName }) + .HasDatabaseName("display_name"); + + entity.Property(e => e.CompanyName) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.CreateBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Currency) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.DisplayName) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.FirstName) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Industry) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.LastModifedBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.LastName) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Notes) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Title) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + } + + private static void PgSqlAddDbContact(this ModelBuilder modelBuilder) + { + throw new NotImplementedException(); + } + + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbContactInfo.cs b/products/ASC.CRM/Server/Core/EF/DbContactInfo.cs new file mode 100644 index 00000000000..6cdf3e138c4 --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbContactInfo.cs @@ -0,0 +1,99 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq.Expressions; + +using ASC.Core.Common.EF; +using ASC.Core.Common.EF.Model; +using ASC.CRM.Core.Enums; +using ASC.ElasticSearch; +using ASC.ElasticSearch.Core; + +using Microsoft.EntityFrameworkCore; + +using Nest; + +namespace ASC.CRM.Core.EF +{ + [ElasticsearchType(RelationName = "crm_contact_info")] + [Table("crm_contact_info")] + public sealed class DbContactInfo : IDbCrm, ISearchItem + { + public int Id { get; set; } + + [Required] + [Text(Analyzer = "whitespacecustom")] + public string Data { get; set; } + + [Column("category", TypeName = "int(255)")] + public int Category { get; set; } + + [Column("tenant_id", TypeName = "int(255)")] + public int TenantId { get; set; } + + [Column("is_primary", TypeName = "tinyint(4)")] + public bool IsPrimary { get; set; } + + [Column("contact_id", TypeName = "int(11)")] + public int ContactId { get; set; } + + [Column("type", TypeName = "int(255)")] + public ContactInfoType Type { get; set; } + + [Column("last_modifed_on", TypeName = "datetime")] + public DateTime? LastModifedOn { get; set; } + + [Column("last_modifed_by", TypeName = "char(38)")] + public Guid LastModifedBy { get; set; } + + [NotMapped] + [Ignore] + public string IndexName + { + get + { + return "crm_field_value"; + } + } + + public Expression> GetSearchContentFields(SearchSettingsHelper searchSettings) + { + return (a) => new[] { Data }; + } + } + + public static class DbContactInfoExtension + { + public static ModelBuilderWrapper AddDbContactInfo(this ModelBuilderWrapper modelBuilder) + { + modelBuilder + .Add(MySqlAddDbContactInfo, Provider.MySql); + + return modelBuilder; + } + + private static void MySqlAddDbContactInfo(this ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.LastModifedOn) + .HasDatabaseName("last_modifed_on"); + + entity.HasIndex(e => new { e.TenantId, e.ContactId }) + .HasDatabaseName("IX_Contact"); + + entity.Property(e => e.Data) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.LastModifedBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + } + } + + + + +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbCurrencyInfo.cs b/products/ASC.CRM/Server/Core/EF/DbCurrencyInfo.cs new file mode 100644 index 00000000000..3df94f761ca --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbCurrencyInfo.cs @@ -0,0 +1,41 @@ +// This file has been auto generated by EF Core Power Tools. +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +using ASC.Core.Common.EF; +using ASC.Core.Common.EF.Model; + +using Microsoft.EntityFrameworkCore; + +using Nest; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_currency_info")] + public sealed class DbCurrencyInfo + { + [Required] + [Column("resource_key", TypeName = "varchar(255)")] + public string ResourceKey { get; set; } + + [Key] + [Column("abbreviation", TypeName = "varchar(255)")] + public string Abbreviation { get; set; } + + [Required] + [Column("symbol", TypeName = "varchar(255)")] + public string Symbol { get; set; } + + [Required] + [Column("culture_name", TypeName = "varchar(255)")] + public string CultureName { get; set; } + + [Column("is_convertable", TypeName = "tinyint(4)")] + public bool IsConvertable { get; set; } + + [Column("is_basic", TypeName = "tinyint(4)")] + public bool IsBasic { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbCurrencyRate.cs b/products/ASC.CRM/Server/Core/EF/DbCurrencyRate.cs new file mode 100644 index 00000000000..6fae35bc348 --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbCurrencyRate.cs @@ -0,0 +1,37 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_currency_rate")] + public partial class DbCurrencyRate : IDbCrm + { + [Key] + [Column("id", TypeName = "int(11)")] + public int Id { get; set; } + + [Required] + [Column("from_currency", TypeName = "varchar(255)")] + public string FromCurrency { get; set; } + + [Required] + [Column("to_currency", TypeName = "varchar(255)")] + public string ToCurrency { get; set; } + + [Column("rate", TypeName = "decimal(10,2)")] + public decimal Rate { get; set; } + [Required] + [Column("create_by", TypeName = "char(38)")] + public Guid CreateBy { get; set; } + [Column("create_on", TypeName = "datetime")] + public DateTime CreateOn { get; set; } + [Column("tenant_id", TypeName = "int(11)")] + public int TenantId { get; set; } + [Column("last_modifed_by", TypeName = "char(38)")] + public Guid? LastModifedBy { get; set; } + + [Column("last_modifed_on", TypeName = "datetime")] + public DateTime? LastModifedOn { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbDeal.cs b/products/ASC.CRM/Server/Core/EF/DbDeal.cs new file mode 100644 index 00000000000..c2cac665c9a --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbDeal.cs @@ -0,0 +1,176 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using System; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq.Expressions; + +using ASC.Core.Common.EF; +using ASC.Core.Common.EF.Model; +using ASC.CRM.Core.Enums; +using ASC.ElasticSearch; +using ASC.ElasticSearch.Core; + +using Microsoft.EntityFrameworkCore; + +using Nest; + +namespace ASC.CRM.Core.EF +{ + [ElasticsearchType(RelationName = "crm_deal")] + [Table("crm_deal")] + public class DbDeal : IDbCrm, ISearchItem + { + public int Id { get; set; } + + [Text(Analyzer = "whitespacecustom")] + public string Title { get; set; } + + [Text(Analyzer = "whitespacecustom")] + public string Description { get; set; } + + [Column("responsible_id")] + public Guid ResponsibleId { get; set; } + + [Column("contact_id")] + public int ContactId { get; set; } + + [Column("create_on")] + public DateTime CreateOn { get; set; } + + [Column("create_by")] + public Guid CreateBy { get; set; } + + [Column("bid_currency")] + public string BidCurrency { get; set; } + + [Column("bid_value")] + public decimal BidValue { get; set; } + + [Column("bid_type")] + public BidType BidType { get; set; } + + [Column("deal_milestone_id")] + public int DealMilestoneId { get; set; } + + [Column("tenant_id")] + public int TenantId { get; set; } + + [Column("expected_close_date")] + public DateTime ExpectedCloseDate { get; set; } + + [Column("per_period_value")] + public int PerPeriodValue { get; set; } + + [Column("deal_milestone_probability")] + public int DealMilestoneProbability { get; set; } + + [Column("last_modifed_on")] + public DateTime? LastModifedOn { get; set; } + + [Column("last_modifed_by")] + public Guid LastModifedBy { get; set; } + + [Column("actual_close_date")] + public DateTime? ActualCloseDate { get; set; } + + [NotMapped] + [Ignore] + public string IndexName + { + get + { + return "crm_deal"; + } + } + + public Expression> GetSearchContentFields(SearchSettingsHelper searchSettings) + { + return (a) => new[] { Title, Description }; + } + } + + public static class DbDealExtension + { + public static ModelBuilderWrapper AddDbDeal(this ModelBuilderWrapper modelBuilder) + { + modelBuilder + .Add(MySqlAddDbDeal, Provider.MySql) + .Add(PgSqlAddDbDeal, Provider.Postgre); + + return modelBuilder; + } + private static void MySqlAddDbDeal(this ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.Property(x => x.Id) + .ValueGeneratedOnAdd(); + + entity.Property(e => e.Title) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Description) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.ResponsibleId) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.HasIndex(e => new { e.TenantId, e.ContactId }) + .HasDatabaseName("contact_id"); + + entity.HasIndex(e => e.CreateOn) + .HasDatabaseName("create_on"); + + entity.Property(e => e.CreateBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.BidCurrency) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.HasIndex(e => e.LastModifedOn) + .HasDatabaseName("last_modifed_on"); + + entity.HasIndex(e => e.DealMilestoneId) + .HasDatabaseName("deal_milestone_id"); + + entity.Property(e => e.LastModifedBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + } + + private static void PgSqlAddDbDeal(this ModelBuilder modelBuilder) + { + throw new NotImplementedException(); + } + + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbDealMilestone.cs b/products/ASC.CRM/Server/Core/EF/DbDealMilestone.cs new file mode 100644 index 00000000000..1c557c67174 --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbDealMilestone.cs @@ -0,0 +1,40 @@ +// This file has been auto generated by EF Core Power Tools. +using ASC.CRM.Core.Enums; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_deal_milestone")] + public partial class DbDealMilestone : IDbCrm + { + [Key] + [Column("id", TypeName = "int(10)")] + public int Id { get; set; } + + [Required] + [Column("color", TypeName = "varchar(50)")] + public string Color { get; set; } + + [Column("sort_order", TypeName = "int(10)")] + public int SortOrder { get; set; } + + [Required] + [Column("title", TypeName = "varchar(250)")] + public string Title { get; set; } + + [Column("description", TypeName = "text")] + public string Description { get; set; } + + [Column("probability", TypeName = "int(10)")] + public int Probability { get; set; } + + [Column("status", TypeName = "int(10)")] + public DealMilestoneStatus Status { get; set; } + + [Column("tenant_id", TypeName = "int(10)")] + public int TenantId { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbEntityContact.cs b/products/ASC.CRM/Server/Core/EF/DbEntityContact.cs new file mode 100644 index 00000000000..eeedd5ede5f --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbEntityContact.cs @@ -0,0 +1,25 @@ + +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +using ASC.CRM.Core.Enums; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_entity_contact")] + public partial class DbEntityContact + { + [Key] + [Column("entity_id", TypeName = "int(11)")] + public int EntityId { get; set; } + + [Key] + [Column("entity_type", TypeName = "int(11)")] + public EntityType EntityType { get; set; } + + [Key] + [Column("contact_id", TypeName = "int(11)")] + public int ContactId { get; set; } + } + +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbEntityTag.cs b/products/ASC.CRM/Server/Core/EF/DbEntityTag.cs new file mode 100644 index 00000000000..b797098a23b --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbEntityTag.cs @@ -0,0 +1,27 @@ +// This file has been auto generated by EF Core Power Tools. +using ASC.CRM.Core.Enums; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_entity_tag")] + public partial class DbEntityTag + { + [Key] + [Column("tag_id", TypeName = "int(11)")] + public int TagId { get; set; } + + [Key] + [Column("entity_id", TypeName = "int(11)")] + public int EntityId { get; set; } + + [Key] + [Column("entity_type", TypeName = "int(10)")] + public EntityType EntityType { get; set; } + } + +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbFieldDescription.cs b/products/ASC.CRM/Server/Core/EF/DbFieldDescription.cs new file mode 100644 index 00000000000..31146395869 --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbFieldDescription.cs @@ -0,0 +1,36 @@ +// This file has been auto generated by EF Core Power Tools. +using ASC.CRM.Core.Enums; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_field_description")] + public partial class DbFieldDescription : IDbCrm + { + [Key] + [Column("id", TypeName = "int(11)")] + public int Id { get; set; } + + [Column("tenant_id", TypeName = "int(11)")] + public int TenantId { get; set; } + + [Required] + [Column("label", TypeName = "varchar(255)")] + public string Label { get; set; } + + [Column("type", TypeName = "int(11)")] + public CustomFieldType Type { get; set; } + + [Column("sort_order", TypeName = "int(11)")] + public int SortOrder { get; set; } + + [Column("mask", TypeName = "text")] + public string Mask { get; set; } + + [Column("entity_type", TypeName = "int(255)")] + public EntityType EntityType { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbFieldValue.cs b/products/ASC.CRM/Server/Core/EF/DbFieldValue.cs new file mode 100644 index 00000000000..c9af6b6fa76 --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbFieldValue.cs @@ -0,0 +1,100 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq.Expressions; + +using ASC.Core.Common.EF; +using ASC.Core.Common.EF.Model; +using ASC.CRM.Core.Enums; +using ASC.ElasticSearch; +using ASC.ElasticSearch.Core; + +using Microsoft.EntityFrameworkCore; + +using Nest; + +namespace ASC.CRM.Core.EF +{ + [ElasticsearchType(RelationName = "crm_field_value")] + [Table("crm_field_value")] + public partial class DbFieldValue : IDbCrm, ISearchItem + { + public int Id { get; set; } + + [Text(Analyzer = "whitespacecustom")] + public string Value { get; set; } + + [Column("entity_id", TypeName = "int(11)")] + public int EntityId { get; set; } + + [Column("tenant_id", TypeName = "int(11)")] + public int TenantId { get; set; } + + [Column("field_id", TypeName = "int(11)")] + public int FieldId { get; set; } + + [Column("entity_type", TypeName = "int(10)")] + public EntityType EntityType { get; set; } + + [Column("last_modifed_on", TypeName = "datetime")] + public DateTime? LastModifedOn { get; set; } + + [Column("last_modifed_by", TypeName = "char(38)")] + public Guid LastModifedBy { get; set; } + + [NotMapped] + [Ignore] + public string IndexName + { + get + { + return "crm_field_value"; + } + } + + public Expression> GetSearchContentFields(SearchSettingsHelper searchSettings) + { + return (a) => new[] { Value }; + } + } + + public static class DbFieldValueExtension + { + public static ModelBuilderWrapper AddDbFieldValue(this ModelBuilderWrapper modelBuilder) + { + modelBuilder + .Add(MySqlAddDbDeal, Provider.MySql) + .Add(PgSqlAddDbDeal, Provider.Postgre); + + return modelBuilder; + } + + private static void MySqlAddDbDeal(this ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.FieldId) + .HasDatabaseName("field_id"); + + entity.HasIndex(e => e.LastModifedOn) + .HasDatabaseName("last_modifed_on"); + + entity.HasIndex(e => new { e.TenantId, e.EntityId, e.EntityType, e.FieldId }) + .HasDatabaseName("tenant_id"); + + entity.Property(e => e.LastModifedBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Value) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + } + + private static void PgSqlAddDbDeal(this ModelBuilder modelBuilder) + { + throw new NotImplementedException(); + } + + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbInvoice.cs b/products/ASC.CRM/Server/Core/EF/DbInvoice.cs new file mode 100644 index 00000000000..9505c5a7869 --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbInvoice.cs @@ -0,0 +1,107 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq.Expressions; + +using ASC.CRM.Core.Enums; +using ASC.ElasticSearch; +using ASC.ElasticSearch.Core; + +using Nest; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_invoice")] + public class DbInvoice : IDbCrm, ISearchItem + { + [Key] + [Column("id", TypeName = "int(11)")] + public int Id { get; set; } + + [Column("status", TypeName = "int(11)")] + public InvoiceStatus Status { get; set; } + + [Required] + [Column("number", TypeName = "varchar(255)")] + public string Number { get; set; } + + [Column("issue_date", TypeName = "datetime")] + public DateTime IssueDate { get; set; } + + [Column("template_type", TypeName = "int(11)")] + public InvoiceTemplateType TemplateType { get; set; } + + [Column("contact_id", TypeName = "int(11)")] + public int ContactId { get; set; } + + [Column("consignee_id", TypeName = "int(11)")] + public int ConsigneeId { get; set; } + + [Column("entity_type", TypeName = "int(11)")] + public EntityType EntityType { get; set; } + + [Column("entity_id", TypeName = "int(11)")] + public int EntityId { get; set; } + + [Column("due_date", TypeName = "datetime")] + public DateTime DueDate { get; set; } + + [Required] + [Column("language", TypeName = "varchar(255)")] + public string Language { get; set; } + + [Required] + [Column("currency", TypeName = "varchar(255)")] + public string Currency { get; set; } + + [Column("exchange_rate", TypeName = "decimal(10,2)")] + public decimal ExchangeRate { get; set; } + + [Required] + [Column("purchase_order_number", TypeName = "varchar(255)")] + public string PurchaseOrderNumber { get; set; } + + [Column("terms", TypeName = "text")] + public string Terms { get; set; } + + [Column("description", TypeName = "text")] + public string Description { get; set; } + + [Column("json_data", TypeName = "text")] + public string JsonData { get; set; } + + [Column("file_id", TypeName = "int(11)")] + public int FileId { get; set; } + + [Column("create_on", TypeName = "datetime")] + public DateTime CreateOn { get; set; } + + [Required] + [Column("create_by", TypeName = "char(38)")] + public Guid CreateBy { get; set; } + + [Column("last_modifed_on", TypeName = "datetime")] + public DateTime? LastModifedOn { get; set; } + + [Column("last_modifed_by", TypeName = "char(38)")] + public Guid LastModifedBy { get; set; } + + [Column("tenant_id", TypeName = "int(11)")] + public int TenantId { get; set; } + + [NotMapped] + [Ignore] + public string IndexName + { + get + { + return "crm_deal"; + } + } + + public Expression> GetSearchContentFields(SearchSettingsHelper searchSettings) + { + return (a) => new[] { Description }; + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbInvoiceItem.cs b/products/ASC.CRM/Server/Core/EF/DbInvoiceItem.cs new file mode 100644 index 00000000000..254fad4a5fd --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbInvoiceItem.cs @@ -0,0 +1,53 @@ +// This file has been auto generated by EF Core Power Tools. +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_invoice_item")] + public partial class DbInvoiceItem : IDbCrm + { + [Key] + [Column("id", TypeName = "int(11)")] + public int Id { get; set; } + [Required] + [Column("title", TypeName = "varchar(255)")] + public string Title { get; set; } + [Required] + [Column("description", TypeName = "text")] + public string Description { get; set; } + [Required] + [Column("stock_keeping_unit", TypeName = "varchar(255)")] + public string StockKeepingUnit { get; set; } + [Column("price", TypeName = "decimal(10,2)")] + public decimal Price { get; set; } + [Column("stock_quantity", TypeName = "decimal(10,2)")] + public decimal StockQuantity { get; set; } + [Column("track_inventory", TypeName = "tinyint(4)")] + public bool TrackInventory { get; set; } + [Column("invoice_tax1_id", TypeName = "int(11)")] + public int InvoiceTax1Id { get; set; } + [Column("invoice_tax2_id", TypeName = "int(11)")] + public int InvoiceTax2Id { get; set; } + [Required] + [Column("currency", TypeName = "varchar(255)")] + public string Currency { get; set; } + + [Column("create_on", TypeName = "datetime")] + public DateTime CreateOn { get; set; } + + [Required] + [Column("create_by", TypeName = "char(38)")] + public Guid CreateBy { get; set; } + + [Column("last_modifed_on", TypeName = "datetime")] + public DateTime? LastModifedOn { get; set; } + + [Column("last_modifed_by", TypeName = "char(38)")] + public Guid LastModifedBy { get; set; } + [Column("tenant_id", TypeName = "int(11)")] + public int TenantId { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbInvoiceLine.cs b/products/ASC.CRM/Server/Core/EF/DbInvoiceLine.cs new file mode 100644 index 00000000000..00684fd352b --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbInvoiceLine.cs @@ -0,0 +1,37 @@ +// This file has been auto generated by EF Core Power Tools. +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_invoice_line")] + public partial class DbInvoiceLine : IDbCrm + { + [Key] + [Column("id", TypeName = "int(11)")] + public int Id { get; set; } + [Column("invoice_id", TypeName = "int(11)")] + public int InvoiceId { get; set; } + [Column("invoice_item_id", TypeName = "int(11)")] + public int InvoiceItemId { get; set; } + [Column("invoice_tax1_id", TypeName = "int(11)")] + public int InvoiceTax1Id { get; set; } + [Column("invoice_tax2_id", TypeName = "int(11)")] + public int InvoiceTax2Id { get; set; } + [Required] + [Column("description", TypeName = "text")] + public string Description { get; set; } + [Column("quantity", TypeName = "decimal(10,2)")] + public decimal Quantity { get; set; } + [Column("price", TypeName = "decimal(10,2)")] + public decimal Price { get; set; } + [Column("discount", TypeName = "decimal(10,2)")] + public decimal Discount { get; set; } + [Column("sort_order", TypeName = "int(11)")] + public int SortOrder { get; set; } + [Column("tenant_id", TypeName = "int(11)")] + public int TenantId { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbInvoiceTax.cs b/products/ASC.CRM/Server/Core/EF/DbInvoiceTax.cs new file mode 100644 index 00000000000..bcfef3bd752 --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbInvoiceTax.cs @@ -0,0 +1,43 @@ +// This file has been auto generated by EF Core Power Tools. +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_invoice_tax")] + public partial class DbInvoiceTax : IDbCrm + { + [Key] + [Column("id", TypeName = "int(11)")] + public int Id { get; set; } + + [Required] + [Column("name", TypeName = "varchar(255)")] + public string Name { get; set; } + + [Required] + [Column("description", TypeName = "text")] + public string Description { get; set; } + + [Column("rate", TypeName = "decimal(10,2)")] + public decimal Rate { get; set; } + + [Column("create_on", TypeName = "datetime")] + public DateTime CreateOn { get; set; } + + [Required] + [Column("create_by", TypeName = "char(38)")] + public Guid CreateBy { get; set; } + + [Column("last_modifed_on", TypeName = "datetime")] + public DateTime? LastModifedOn { get; set; } + + [Column("last_modifed_by", TypeName = "char(38)")] + public Guid? LastModifedBy { get; set; } + + [Column("tenant_id", TypeName = "int(11)")] + public int TenantId { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbListItem.cs b/products/ASC.CRM/Server/Core/EF/DbListItem.cs new file mode 100644 index 00000000000..9b221f05a62 --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbListItem.cs @@ -0,0 +1,37 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +using ASC.CRM.Core.Enums; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_list_item")] + public partial class DbListItem : IDbCrm + { + [Key] + [Column("id", TypeName = "int(11)")] + public int Id { get; set; } + + [Required] + [Column("title", TypeName = "varchar(255)")] + public string Title { get; set; } + + [Column("sort_order", TypeName = "int(11)")] + public int SortOrder { get; set; } + + [Column("color", TypeName = "varchar(255)")] + public string Color { get; set; } + + [Column("additional_params", TypeName = "varchar(255)")] + public string AdditionalParams { get; set; } + + [Column("tenant_id", TypeName = "int(11)")] + public int TenantId { get; set; } + + [Column("list_type", TypeName = "int(255)")] + public ListType ListType { get; set; } + + [Column("description", TypeName = "varchar(255)")] + public string Description { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbOrganisationLogo.cs b/products/ASC.CRM/Server/Core/EF/DbOrganisationLogo.cs new file mode 100644 index 00000000000..ab5c889c243 --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbOrganisationLogo.cs @@ -0,0 +1,26 @@ +// This file has been auto generated by EF Core Power Tools. +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_organisation_logo")] + public partial class DbOrganisationLogo : IDbCrm + { + [Key] + [Column("id", TypeName = "int(11)")] + public int Id { get; set; } + [Required] + [Column("content", TypeName = "mediumtext")] + public string Content { get; set; } + [Required] + [Column("create_by", TypeName = "char(38)")] + public string CreateBy { get; set; } + [Column("create_on", TypeName = "datetime")] + public DateTime CreateOn { get; set; } + [Column("tenant_id", TypeName = "int(11)")] + public int TenantId { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbProjects.cs b/products/ASC.CRM/Server/Core/EF/DbProjects.cs new file mode 100644 index 00000000000..bed9c2a6458 --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbProjects.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_projects")] + public partial class DbProjects : IDbCrm + { + [Key] + [Column("project_id", TypeName = "int(10)")] + public int ProjectId { get; set; } + + [Key] + [Column("contact_id", TypeName = "int(10)")] + public int ContactId { get; set; } + + [Key] + [Column("tenant_id", TypeName = "int(10)")] + public int TenantId { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbRelationshipEvent.cs b/products/ASC.CRM/Server/Core/EF/DbRelationshipEvent.cs new file mode 100644 index 00000000000..82195e71bd8 --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbRelationshipEvent.cs @@ -0,0 +1,122 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq.Expressions; + +using ASC.Core.Common.EF; +using ASC.Core.Common.EF.Model; +using ASC.CRM.Core.Enums; +using ASC.ElasticSearch; +using ASC.ElasticSearch.Core; + +using Microsoft.EntityFrameworkCore; + +using Nest; + +namespace ASC.CRM.Core.EF +{ + [ElasticsearchType(RelationName = "crm_relationship_event")] + [Table("crm_relationship_event")] + public class DbRelationshipEvent : IDbCrm, ISearchItem + { + [Key] + [Column("id")] + public int Id { get; set; } + + [Column("contact_id")] + public int ContactId { get; set; } + + [Column("content")] + [Text(Analyzer = "whitespacecustom")] + public string Content { get; set; } + + [Required] + [Column("create_by", TypeName = "char(38)")] + public Guid CreateBy { get; set; } + + [Column("create_on", TypeName = "datetime")] + public DateTime CreateOn { get; set; } + + [Column("tenant_id", TypeName = "int(11)")] + public int TenantId { get; set; } + + [Column("entity_type", TypeName = "int(11)")] + public EntityType EntityType { get; set; } + + [Column("entity_id", TypeName = "int(11)")] + public int EntityId { get; set; } + + [Column("category_id", TypeName = "int(11)")] + public int CategoryId { get; set; } + + [Column("last_modifed_by", TypeName = "char(38)")] + public Guid? LastModifedBy { get; set; } + + [Column("last_modifed_on", TypeName = "datetime")] + public DateTime? LastModifedOn { get; set; } + + [Column("have_files", TypeName = "int(11)")] + public bool HaveFiles { get; set; } + + [NotMapped] + [Ignore] + public string IndexName + { + get + { + return "crm_relationship_event"; + } + } + + public Expression> GetSearchContentFields(SearchSettingsHelper searchSettings) + { + return (a) => new[] { Content }; + } + } + + public static class DbRelationshipEventExtension + { + public static ModelBuilderWrapper AddDbRelationshipEvent(this ModelBuilderWrapper modelBuilder) + { + modelBuilder + .Add(MySqlAddDbRelationshipEvent, Provider.MySql) + .Add(PgSqlAddDbRelationshipEvent, Provider.Postgre); + + return modelBuilder; + } + private static void MySqlAddDbRelationshipEvent(this ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.HasIndex(e => e.ContactId) + .HasDatabaseName("IX_Contact"); + + entity.HasIndex(e => e.LastModifedOn) + .HasDatabaseName("last_modifed_on"); + + entity.HasIndex(e => e.TenantId) + .HasDatabaseName("tenant_id"); + + entity.HasIndex(e => new { e.EntityId, e.EntityType }) + .HasDatabaseName("IX_Entity"); + + entity.Property(e => e.Content) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.CreateBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.LastModifedBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + } + + private static void PgSqlAddDbRelationshipEvent(this ModelBuilder modelBuilder) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbReportFile.cs b/products/ASC.CRM/Server/Core/EF/DbReportFile.cs new file mode 100644 index 00000000000..c7d60ba9861 --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbReportFile.cs @@ -0,0 +1,30 @@ +// This file has been auto generated by EF Core Power Tools. +using ASC.CRM.Core.Enums; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_report_file")] + public partial class DbReportFile : IDbCrm + { + [Key] + [Column("file_id", TypeName = "int(11)")] + public int FileId { get; set; } + + [Column("report_type", TypeName = "int(11)")] + public ReportType ReportType { get; set; } + + [Column("create_on", TypeName = "datetime")] + public DateTime CreateOn { get; set; } + + [Required] + [Column("create_by", TypeName = "char(38)")] + public Guid CreateBy { get; set; } + + [Column("tenant_id", TypeName = "int(11)")] + public int TenantId { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbTag.cs b/products/ASC.CRM/Server/Core/EF/DbTag.cs new file mode 100644 index 00000000000..36ae65b2bb8 --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbTag.cs @@ -0,0 +1,25 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +using ASC.CRM.Core.Enums; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_tag")] + public partial class DbTag : IDbCrm + { + [Key] + [Column("id", TypeName = "int(11)")] + public int Id { get; set; } + + [Required] + [Column("title", TypeName = "varchar(255)")] + public string Title { get; set; } + + [Column("tenant_id", TypeName = "int(11)")] + public int TenantId { get; set; } + + [Column("entity_type", TypeName = "int(11)")] + public EntityType EntityType { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbTask.cs b/products/ASC.CRM/Server/Core/EF/DbTask.cs new file mode 100644 index 00000000000..58ea383b4ed --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbTask.cs @@ -0,0 +1,147 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq.Expressions; + +using ASC.Core.Common.EF; +using ASC.Core.Common.EF.Model; +using ASC.CRM.Core.Enums; +using ASC.ElasticSearch; +using ASC.ElasticSearch.Core; + +using Microsoft.EntityFrameworkCore; + +using Nest; + +namespace ASC.CRM.Core.EF +{ + [ElasticsearchType(RelationName = "crm_task")] + [Table("crm_task")] + public partial class DbTask : IDbCrm, ISearchItem + { + public int Id { get; set; } + + [Required] + [Text(Analyzer = "whitespacecustom")] + public string Title { get; set; } + + [Text(Analyzer = "whitespacecustom")] + public string Description { get; set; } + + [Column("deadline", TypeName = "datetime")] + public DateTime Deadline { get; set; } + + [Required] + [Column("responsible_id", TypeName = "char(38)")] + public Guid ResponsibleId { get; set; } + + [Column("contact_id", TypeName = "int(11)")] + public int ContactId { get; set; } + + [Column("is_closed", TypeName = "int(1)")] + public bool IsClosed { get; set; } + + [Column("tenant_id", TypeName = "int(11)")] + public int TenantId { get; set; } + + [Column("entity_type", TypeName = "int(11)")] + public EntityType EntityType { get; set; } + + [Column("entity_id", TypeName = "int(11)")] + public int EntityId { get; set; } + + [Column("category_id", TypeName = "int(11)")] + public int CategoryId { get; set; } + + [Column("create_on", TypeName = "datetime")] + public DateTime CreateOn { get; set; } + + [Required] + [Column("create_by", TypeName = "char(38)")] + public Guid CreateBy { get; set; } + + [Column("last_modifed_on", TypeName = "datetime")] + public DateTime? LastModifedOn { get; set; } + + [Column("last_modifed_by", TypeName = "char(38)")] + public Guid? LastModifedBy { get; set; } + + [Column("alert_value", TypeName = "int(10)")] + public int AlertValue { get; set; } + + [Column("exec_alert", TypeName = "int(10)")] + public int ExecAlert { get; set; } + + [NotMapped] + [Ignore] + public string IndexName + { + get => "crm_task"; + } + + public Expression> GetSearchContentFields(SearchSettingsHelper searchSettings) + { + return (a) => new[] { Title, Description }; + } + } + + public static class DbTaskExtension + { + public static ModelBuilderWrapper AddDbTask(this ModelBuilderWrapper modelBuilder) + { + modelBuilder + .Add(MySqlAddDbTask, Provider.MySql); + + return modelBuilder; + } + + public static void MySqlAddDbTask(this ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.ToTable("crm_task"); + + entity.HasIndex(e => e.CreateOn) + .HasDatabaseName("create_on"); + + entity.HasIndex(e => e.Deadline) + .HasDatabaseName("deadline"); + + entity.HasIndex(e => e.LastModifedOn) + .HasDatabaseName("last_modifed_on"); + + entity.HasIndex(e => new { e.TenantId, e.ContactId }) + .HasDatabaseName("IX_Contact"); + + entity.HasIndex(e => new { e.TenantId, e.ResponsibleId }) + .HasDatabaseName("responsible_id"); + + entity.HasIndex(e => new { e.TenantId, e.EntityId, e.EntityType }) + .HasDatabaseName("IX_Entity"); + + entity.Property(e => e.ContactId).HasDefaultValueSql("'-1'"); + + entity.Property(e => e.CreateBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Description) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.LastModifedBy) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.ResponsibleId) + .HasDefaultValueSql("'00000000-0000-0000-0000-000000000000'") + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + + entity.Property(e => e.Title) + .HasCharSet("utf8") + .UseCollation("utf8_general_ci"); + }); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbTaskTemplate.cs b/products/ASC.CRM/Server/Core/EF/DbTaskTemplate.cs new file mode 100644 index 00000000000..b92bcbac057 --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbTaskTemplate.cs @@ -0,0 +1,62 @@ +// This file has been auto generated by EF Core Power Tools. +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_task_template")] + public partial class DbTaskTemplate : IDbCrm + { + [Key] + [Column("id", TypeName = "int(10)")] + public int Id { get; set; } + + [Column("create_on", TypeName = "datetime")] + public DateTime CreateOn { get; set; } + + [Required] + [Column("create_by", TypeName = "char(38)")] + public Guid CreateBy { get; set; } + + [Column("last_modifed_on", TypeName = "datetime")] + public DateTime LastModifedOn { get; set; } + + [Required] + [Column("last_modifed_by", TypeName = "char(38)")] + public Guid LastModifedBy { get; set; } + + [Required] + [Column("title", TypeName = "varchar(255)")] + public string Title { get; set; } + + [Column("category_id", TypeName = "int(10)")] + public int CategoryId { get; set; } + + [Column("description", TypeName = "tinytext")] + public string Description { get; set; } + + [Required] + [Column("responsible_id", TypeName = "char(38)")] + public Guid ResponsibleId { get; set; } + + [Column("is_notify", TypeName = "tinyint(4)")] + public bool IsNotify { get; set; } + + [Column("offset", TypeName = "bigint(20)")] + public long Offset { get; set; } + + [Column("sort_order", TypeName = "int(11)")] + public int SortOrder { get; set; } + + [Column("deadLine_is_fixed", TypeName = "tinyint(4)")] + public bool DeadLineIsFixed { get; set; } + + [Column("tenant_id", TypeName = "int(10)")] + public int TenantId { get; set; } + + [Column("container_id", TypeName = "int(10)")] + public int ContainerId { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbTaskTemplateContainer.cs b/products/ASC.CRM/Server/Core/EF/DbTaskTemplateContainer.cs new file mode 100644 index 00000000000..dcec0776eeb --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbTaskTemplateContainer.cs @@ -0,0 +1,41 @@ +// This file has been auto generated by EF Core Power Tools. +using ASC.CRM.Core.Enums; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_task_template_container")] + public partial class DbTaskTemplateContainer : IDbCrm + { + [Key] + [Column("id", TypeName = "int(10)")] + public int Id { get; set; } + + [Required] + [Column("title", TypeName = "varchar(256)")] + public string Title { get; set; } + + [Column("entity_type", TypeName = "int(10)")] + public EntityType EntityType { get; set; } + + [Column("tenant_id", TypeName = "int(10)")] + public int TenantId { get; set; } + + [Column("create_on", TypeName = "datetime")] + public DateTime CreateOn { get; set; } + + [Required] + [Column("create_by", TypeName = "char(38)")] + public Guid CreateBy { get; set; } + + [Column("last_modifed_on", TypeName = "datetime")] + public DateTime LastModifedOn { get; set; } + + [Required] + [Column("last_modifed_by", TypeName = "char(38)")] + public Guid LastModifedBy { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbTaskTemplateTask.cs b/products/ASC.CRM/Server/Core/EF/DbTaskTemplateTask.cs new file mode 100644 index 00000000000..9cf84ee691c --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbTaskTemplateTask.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_task_template_task")] + public partial class DbTaskTemplateTask : IDbCrm + { + [Key] + [Column("task_id", TypeName = "int(10)")] + public int TaskId { get; set; } + + [Key] + [Column("task_template_id", TypeName = "int(10)")] + public int TaskTemplateId { get; set; } + + [Key] + [Column("tenant_id", TypeName = "int(10)")] + public int TenantId { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbVoipCalls.cs b/products/ASC.CRM/Server/Core/EF/DbVoipCalls.cs new file mode 100644 index 00000000000..70764feb123 --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbVoipCalls.cs @@ -0,0 +1,50 @@ +// This file has been auto generated by EF Core Power Tools. +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +using ASC.VoipService; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_voip_calls")] + public partial class DbVoipCalls : IDbCrm + { + [Key] + [Column("id", TypeName = "varchar(50)")] + public string Id { get; set; } + [Required] + [Column("parent_call_id", TypeName = "varchar(50)")] + public string ParentCallId { get; set; } + [Required] + [Column("number_from", TypeName = "varchar(50)")] + public string NumberFrom { get; set; } + [Required] + [Column("number_to", TypeName = "varchar(50)")] + public string NumberTo { get; set; } + [Column("status", TypeName = "int(10)")] + public VoipCallStatus Status { get; set; } + [Required] + [Column("answered_by", TypeName = "varchar(50)")] + public Guid AnsweredBy { get; set; } + [Column("dial_date", TypeName = "datetime")] + public DateTime? DialDate { get; set; } + [Column("dial_duration", TypeName = "int(11)")] + public int? DialDuration { get; set; } + [Column("record_sid", TypeName = "varchar(50)")] + public string RecordSid { get; set; } + [Column("record_url", TypeName = "text")] + public string RecordUrl { get; set; } + [Column("record_duration", TypeName = "int(11)")] + public int? RecordDuration { get; set; } + [Column("record_price", TypeName = "decimal(10,4)")] + public decimal RecordPrice { get; set; } + [Column("contact_id", TypeName = "int(10)")] + public int? ContactId { get; set; } + [Column("price", TypeName = "decimal(10,4)")] + public decimal? Price { get; set; } + [Column("tenant_id", TypeName = "int(10)")] + public int TenantId { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/DbVoipNumber.cs b/products/ASC.CRM/Server/Core/EF/DbVoipNumber.cs new file mode 100644 index 00000000000..d0df468511c --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/DbVoipNumber.cs @@ -0,0 +1,25 @@ +// This file has been auto generated by EF Core Power Tools. +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ASC.CRM.Core.EF +{ + [Table("crm_voip_number")] + public partial class DbVoipNumber + { + [Key] + [Column("id", TypeName = "varchar(50)")] + public string Id { get; set; } + [Required] + [Column("number", TypeName = "varchar(50)")] + public string Number { get; set; } + [Column("alias", TypeName = "varchar(255)")] + public string Alias { get; set; } + [Column("settings", TypeName = "text")] + public string Settings { get; set; } + [Column("tenant_id", TypeName = "int(10)")] + public int TenantId { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/EF/IDbCrm.cs b/products/ASC.CRM/Server/Core/EF/IDbCrm.cs new file mode 100644 index 00000000000..0a0e9d354cb --- /dev/null +++ b/products/ASC.CRM/Server/Core/EF/IDbCrm.cs @@ -0,0 +1,12 @@ +namespace ASC.CRM.Core.EF +{ + public interface IDbCrm + { + public int TenantId { get; set; } + } + + public interface IDbSearch + { + public string Title { get; set; } + } +} diff --git a/products/ASC.CRM/Server/Core/Entities/Cases.cs b/products/ASC.CRM/Server/Core/Entities/Cases.cs new file mode 100644 index 00000000000..ad120838355 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/Cases.cs @@ -0,0 +1,58 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; + +using ASC.Common.Mapping; +using ASC.Common.Security; +using ASC.CRM.Core.EF; + +namespace ASC.CRM.Core.Entities +{ + public class Cases : DomainObject, ISecurityObjectId, IMapFrom + { + public Guid CreateBy { get; set; } + public DateTime CreateOn { get; set; } + public Guid? LastModifedBy { get; set; } + public DateTime? LastModifedOn { get; set; } + public String Title { get; set; } + public bool IsClosed { get; set; } + public HashSet Members { get; set; } + // TODO: to finish the field + public DateTime ClosedDate { get; set; } + + public object SecurityId + { + get { return ID; } + } + + public Type ObjectType + { + get { return GetType(); } + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Entities/Comment.cs b/products/ASC.CRM/Server/Core/Entities/Comment.cs new file mode 100644 index 00000000000..7ad5e47e78c --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/Comment.cs @@ -0,0 +1,49 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System; + +#endregion + +namespace ASC.CRM.Core.Entities +{ + public class Comment + { + public Guid Parent { get; set; } + public string Content { get; set; } + public bool Inactive { get; set; } + public String TargetUniqID { get; set; } + public Guid CreateBy { get; set; } + public DateTime CreateOn { get; set; } + public override int GetHashCode() + { + return (GetType().FullName + "|" + Content + "|" + CreateBy.GetHashCode() + "|" + Parent.GetHashCode()).GetHashCode(); + } + + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Entities/Contact.cs b/products/ASC.CRM/Server/Core/Entities/Contact.cs new file mode 100644 index 00000000000..6f03c0fc4e5 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/Contact.cs @@ -0,0 +1,133 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +using ASC.Common.Security; +using ASC.CRM.Core.Enums; + +namespace ASC.CRM.Core.Entities +{ + [Serializable] + public class Person : Contact + { + public Person() + { + FirstName = String.Empty; + LastName = String.Empty; + CompanyID = 0; + JobTitle = String.Empty; + } + + public String FirstName { get; set; } + public String LastName { get; set; } + public int CompanyID { get; set; } + public String JobTitle { get; set; } + } + + [Serializable] + public class Company : Contact + { + public Company() + { + CompanyName = String.Empty; + } + + public String CompanyName { get; set; } + } + + public static class ContactExtension + { + public static String GetTitle(this Contact contact) + { + if (contact == null) + return String.Empty; + + if (contact is Company) + { + var company = (Company)contact; + + return company.CompanyName; + } + + var people = (Person)contact; + + return String.Format("{0} {1}", people.FirstName, people.LastName); + } + + //public static String RenderLinkForCard(this Contact contact) + //{ + // var isCompany = contact is Company; + // var popupID = Guid.NewGuid(); + + // return !CRMSecurity.CanAccessTo(contact) ? + // String.Format(@"{0}", GetTitle(contact).HtmlEncode()) : + // String.Format(@" + // {4} + // ", + // isCompany ? "crm-companyInfoCardLink" : "crm-peopleInfoCardLink", + // UrlConstant.ID, contact != null ? contact.ID : 0, + // isCompany ? String.Empty : String.Format("&{0}=people", UrlConstant.Type), + // GetTitle(contact).HtmlEncode(), popupID); + //} + } + + [Serializable] + public abstract class Contact : DomainObject, ISecurityObjectId + { + protected Contact() + { + About = String.Empty; + Industry = String.Empty; + StatusID = 0; + ContactTypeID = 0; + ShareType = ShareType.None; + } + + public Guid CreateBy { get; set; } + public DateTime CreateOn { get; set; } + public Guid? LastModifedBy { get; set; } + public DateTime? LastModifedOn { get; set; } + public String About { get; set; } + public String Industry { get; set; } + public int StatusID { get; set; } + public int ContactTypeID { get; set; } + + public ShareType ShareType { get; set; } + + public string Currency { get; set; } + + public object SecurityId + { + get { return ID; } + } + + public Type ObjectType + { + get { return GetType(); } + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Entities/ContactInfo.cs b/products/ASC.CRM/Server/Core/Entities/ContactInfo.cs new file mode 100644 index 00000000000..9e20e630d55 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/ContactInfo.cs @@ -0,0 +1,82 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +using ASC.Common.Mapping; +using ASC.CRM.Classes; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Enums; + +namespace ASC.CRM.Core +{ + public class ContactInfo : DomainObject, IMapFrom + { + public int ContactID { get; set; } + public ContactInfoType InfoType { get; set; } + public int Category { get; set; } + public String Data { get; set; } + public bool IsPrimary { get; set; } + public static int GetDefaultCategory(ContactInfoType infoTypeEnum) + { + switch (infoTypeEnum) + { + case ContactInfoType.Phone: + return (int)PhoneCategory.Work; + case ContactInfoType.Address: + return (int)AddressCategory.Work; + default: + return (int)ContactInfoBaseCategory.Work; + } + } + + public String CategoryToString() + { + switch (InfoType) + { + case ContactInfoType.Phone: + return ((PhoneCategory)Category).ToLocalizedString(); + case ContactInfoType.Address: + return ((AddressCategory)Category).ToLocalizedString(); + default: + return ((ContactInfoBaseCategory)Category).ToLocalizedString(); + } + } + + public static Type GetCategory(ContactInfoType infoType) + { + switch (infoType) + { + case ContactInfoType.Phone: + return typeof(PhoneCategory); + case ContactInfoType.Address: + return typeof(AddressCategory); + default: + return typeof(ContactInfoBaseCategory); + } + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Entities/CurrencyInfo.cs b/products/ASC.CRM/Server/Core/Entities/CurrencyInfo.cs new file mode 100644 index 00000000000..63375828526 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/CurrencyInfo.cs @@ -0,0 +1,95 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +using ASC.Common.Mapping; +using ASC.CRM.Core.EF; +using ASC.CRM.Resources; + +using AutoMapper; + +namespace ASC.CRM.Core +{ + public class CurrencyInfo : IMapFrom + { + private String _resourceKey; + + public CurrencyInfo() + { + + } + public CurrencyInfo(string resourceKey, string abbreviation, string symbol, string cultureName, bool isConvertable, bool isBasic) + { + _resourceKey = resourceKey; + Symbol = symbol; + Abbreviation = abbreviation; + CultureName = cultureName; + IsConvertable = isConvertable; + IsBasic = isBasic; + } + + + public string Symbol { get; set; } + public string Abbreviation { get; set; } + public string CultureName { get; set; } + public bool IsConvertable { get; set; } + public bool IsBasic { get; set; } + + public String Title + { + get + { + if (String.IsNullOrEmpty(_resourceKey)) + return String.Empty; + + return CRMCommonResource.ResourceManager.GetString(_resourceKey); + } + } + + public override bool Equals(object obj) + { + var ci = obj as CurrencyInfo; + return ci != null && string.Compare(Title, ci.Title, true) == 0; + } + + public override int GetHashCode() + { + return ToString().GetHashCode(); + } + + public override string ToString() + { + return string.Concat(Abbreviation, "-", Title); + } + + public void Mapping(Profile profile) + { + profile.CreateMap() + .ForMember(dest => dest._resourceKey, opt => opt.MapFrom(src => src.ResourceKey)); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Entities/CurrencyRate.cs b/products/ASC.CRM/Server/Core/Entities/CurrencyRate.cs new file mode 100644 index 00000000000..65927c35443 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/CurrencyRate.cs @@ -0,0 +1,44 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +using ASC.Common.Mapping; +using ASC.CRM.Core.EF; + +namespace ASC.CRM.Core +{ + public class CurrencyRate : DomainObject, IMapFrom + { + public string FromCurrency { get; set; } + public string ToCurrency { get; set; } + public decimal Rate { get; set; } + public Guid CreateBy { get; set; } + public DateTime CreateOn { get; set; } + public Guid? LastModifedBy { get; set; } + public DateTime? LastModifedOn { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Entities/CustomField.cs b/products/ASC.CRM/Server/Core/Entities/CustomField.cs new file mode 100644 index 00000000000..07170bc34ac --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/CustomField.cs @@ -0,0 +1,54 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System; + +using ASC.Common.Mapping; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Enums; + +#endregion + +namespace ASC.CRM.Core.Entities +{ + public class CustomField : DomainObject, IMapFrom + { + public EntityType EntityType { get; set; } + public int EntityID { get; set; } + public String Label { get; set; } + public String Value { get; set; } + public CustomFieldType Type { get; set; } + public int SortOrder { get; set; } + public String Mask { get; set; } + + public override int GetHashCode() + { + return string.Format("{0}|{1}|{2}|{3}|{4}", GetType().FullName, ID, EntityID, Label, (int)Type).GetHashCode(); + } + } +} diff --git a/products/ASC.CRM/Server/Core/Entities/Deal.cs b/products/ASC.CRM/Server/Core/Entities/Deal.cs new file mode 100644 index 00000000000..e48f87a4745 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/Deal.cs @@ -0,0 +1,108 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Text.Json.Serialization; + +using ASC.Common.Mapping; +using ASC.Common.Security; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Enums; + +namespace ASC.CRM.Core.Entities +{ + public class Deal : DomainObject, ISecurityObjectId, IMapFrom + { + public Guid CreateBy { get; set; } + public DateTime CreateOn { get; set; } + public Guid? LastModifedBy { get; set; } + public DateTime? LastModifedOn { get; set; } + + [JsonPropertyName("contact_id")] + public int ContactID { get; set; } + public Contact Contact { get; set; } + public string Title { get; set; } + public string Description { get; set; } + + [JsonPropertyName("responsible_id")] + public Guid ResponsibleID { get; set; } + + [JsonPropertyName("bid_type")] + public BidType BidType { get; set; } + + [JsonPropertyName("bid_value")] + public decimal BidValue { get; set; } + + [JsonPropertyName("bid_currency")] + public string BidCurrency { get; set; } + + [JsonPropertyName("per_period_value")] + public int PerPeriodValue { get; set; } + + [JsonPropertyName("deal_milestone")] + public int DealMilestoneID { get; set; } + + [JsonPropertyName("deal_milestone_probability")] + public int DealMilestoneProbability { get; set; } + + [JsonPropertyName("actual_close_date")] + public DateTime ActualCloseDate { get; set; } + + //[DataMember(Name = "actual_close_date")] + //private String ActualCloseDateStr + //{ + // get + // { + // return ActualCloseDate.Date == DateTime.MinValue.Date + // ? string.Empty : ActualCloseDate.ToString(DateTimeExtension.DateFormatPattern); + // } + // set { ; } + //} + + [JsonPropertyName("expected_close_date")] + public DateTime ExpectedCloseDate { get; set; } + + //private String ExpectedCloseDateStr + //{ + // get + // { + // return ExpectedCloseDate.Date == DateTime.MinValue.Date + // ? string.Empty : ExpectedCloseDate.ToString(DateTimeExtension.DateFormatPattern); + // } + // set { ; } + //} + + public object SecurityId + { + get { return ID; } + } + + public Type ObjectType + { + get { return GetType(); } + } + } +} diff --git a/products/ASC.CRM/Server/Core/Entities/DealMilestone.cs b/products/ASC.CRM/Server/Core/Entities/DealMilestone.cs new file mode 100644 index 00000000000..8a1abcd7e79 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/DealMilestone.cs @@ -0,0 +1,51 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System; +using System.Text.Json.Serialization; + +using ASC.Common.Mapping; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Enums; + +#endregion + +namespace ASC.CRM.Core.Entities +{ + public class DealMilestone : DomainObject, IMapFrom + { + public String Title { get; set; } + public String Description { get; set; } + public String Color { get; set; } + + [JsonPropertyName("sort_order")] + public int SortOrder { get; set; } + public int Probability { get; set; } + public DealMilestoneStatus Status { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Entities/Filter.cs b/products/ASC.CRM/Server/Core/Entities/Filter.cs new file mode 100644 index 00000000000..b220ca1598b --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/Filter.cs @@ -0,0 +1,572 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json; + +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Enums; +using ASC.Web.CRM.Classes; + +using EnumExtension = ASC.CRM.Classes.EnumExtension; + +#endregion + +namespace ASC.CRM.Core.Entities +{ + public abstract class FilterObject + { + public string SortBy { get; set; } + public string SortOrder { get; set; } + public string FilterValue { get; set; } + + public bool IsAsc + { + get + { + return !String.IsNullOrEmpty(SortOrder) && SortOrder != "descending"; + } + } + + public abstract ICollection GetItemsByFilter(DaoFactory daofactory); + } + + public class CasesFilterObject : FilterObject + { + public bool? IsClosed { get; set; } + public List Tags { get; set; } + + public CasesFilterObject() + { + SortBy = "title"; + SortOrder = "ascending"; + } + + public CasesFilterObject(string base64String) + { + if (string.IsNullOrEmpty(base64String)) return; + + var json = Encoding.UTF8.GetString(Convert.FromBase64String(base64String)); + + var jsonArray = json.Split(';'); + + foreach (var filterItem in jsonArray) + { + var filterObj = JsonDocument.Parse(filterItem).RootElement; + + var paramString = filterObj.GetProperty("params").GetString(); + + if (string.IsNullOrEmpty(paramString)) continue; + + var filterParam = JsonDocument.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(paramString))).RootElement; + + switch (filterObj.GetProperty("id").GetString()) + { + case "sorter": + SortBy = filterParam.GetProperty("id").GetString(); + SortOrder = filterParam.GetProperty("sortOrder").GetString(); + break; + + case "text": + FilterValue = filterParam.GetProperty("value").GetString(); + break; + + case "closed": + case "opened": + IsClosed = filterParam.GetProperty("value").GetBoolean(); + break; + + case "tags": + Tags = filterParam.GetProperty("value").EnumerateArray().Select(x => x.GetString()).ToList(); + break; + } + } + } + + public override ICollection GetItemsByFilter(DaoFactory daofactory) + { + SortedByType sortBy; + if (!EnumExtension.TryParse(SortBy, true, out sortBy)) + { + sortBy = SortedByType.Title; + } + + return daofactory.GetCasesDao().GetCases( + FilterValue, + 0, + IsClosed, + Tags, + 0, 0, + new OrderBy(sortBy, IsAsc)); + } + } + + public class TaskFilterObject : FilterObject + { + public int CategoryId { get; set; } + public int ContactId { get; set; } + public Guid ResponsibleId { get; set; } + public bool? IsClosed { get; set; } + public DateTime FromDate { get; set; } + public DateTime ToDate { get; set; } + + public TaskFilterObject() + { + IsClosed = null; + FromDate = DateTime.MinValue; + ToDate = DateTime.MinValue; + SortBy = "deadline"; + SortOrder = "ascending"; + } + + public TaskFilterObject(string base64String) + { + if (string.IsNullOrEmpty(base64String)) return; + + var json = Encoding.UTF8.GetString(Convert.FromBase64String(base64String)); + + var jsonArray = json.Split(';'); + + foreach (var filterItem in jsonArray) + { + var filterObj = JsonDocument.Parse(filterItem).RootElement; + + var paramString = filterObj.GetProperty("params").GetString(); + + if (string.IsNullOrEmpty(paramString)) continue; + + var filterParam = Global.JObjectParseWithDateAsString(Encoding.UTF8.GetString(Convert.FromBase64String(paramString))).RootElement; + + switch (filterObj.GetProperty("id").GetString()) + { + case "sorter": + SortBy = filterParam.GetProperty("id").GetString(); + SortOrder = filterParam.GetProperty("sortOrder").GetString(); + break; + + case "text": + FilterValue = filterParam.GetProperty("value").GetString(); + break; + + case "my": + case "responsibleID": + ResponsibleId = filterParam.GetProperty("value").GetGuid(); + break; + + case "overdue": + case "today": + case "theNext": + var valueString = filterParam.GetProperty("value").GetString(); + var fromToArray = JsonDocument.Parse(valueString) + .RootElement + .EnumerateArray() + .Select(x => x.GetString()) + .ToList(); + + if (fromToArray.Count != 2) continue; + + FromDate = !String.IsNullOrEmpty(fromToArray[0]) + ? Global.ApiDateTimeParse(fromToArray[0]) : DateTime.MinValue; + ToDate = !String.IsNullOrEmpty(fromToArray[1]) + ? Global.ApiDateTimeParse(fromToArray[1]) : DateTime.MinValue; + break; + + case "fromToDate": + FromDate = filterParam.GetProperty("from").GetDateTime(); + ToDate = (filterParam.GetProperty("to").GetDateTime()).AddDays(1).AddSeconds(-1); + break; + + case "categoryID": + CategoryId = filterParam.GetProperty("value").GetInt32(); + break; + + case "openTask": + case "closedTask": + IsClosed = filterParam.GetProperty("value").GetBoolean(); + break; + + case "contactID": + ContactId = filterParam.GetProperty("id").GetInt32(); + break; + } + } + } + + public override ICollection GetItemsByFilter(DaoFactory daofactory) + { + TaskSortedByType sortBy; + if (!EnumExtension.TryParse(SortBy, true, out sortBy)) + { + sortBy = TaskSortedByType.DeadLine; + } + + return daofactory.GetTaskDao().GetTasks( + FilterValue, + ResponsibleId, + CategoryId, + IsClosed, + FromDate, + ToDate, + ContactId > 0 ? EntityType.Contact : EntityType.Any, + ContactId, + 0, 0, + new OrderBy(sortBy, IsAsc)); + } + } + + public class DealFilterObject : FilterObject + { + public Guid ResponsibleId { get; set; } + public String StageType { get; set; } + public int OpportunityStageId { get; set; } + public DateTime FromDate { get; set; } + public DateTime ToDate { get; set; } + public int ContactId { get; set; } + public bool? ContactAlsoIsParticipant { get; set; } + public List Tags { get; set; } + + public DealFilterObject() + { + ContactAlsoIsParticipant = null; + FromDate = DateTime.MinValue; + ToDate = DateTime.MinValue; + SortBy = "stage"; + SortOrder = "ascending"; + } + + public DealFilterObject(string base64String) + { + if (string.IsNullOrEmpty(base64String)) return; + + var json = Encoding.UTF8.GetString(Convert.FromBase64String(base64String)); + + var jsonArray = json.Split(';'); + + foreach (var filterItem in jsonArray) + { + var filterObj = JsonDocument.Parse(filterItem).RootElement; + + var paramString = filterObj.GetProperty("params").GetString(); + + if (string.IsNullOrEmpty(paramString)) continue; + + var filterParam = Global.JObjectParseWithDateAsString(Encoding.UTF8.GetString(Convert.FromBase64String(paramString))).RootElement; + + switch (filterObj.GetProperty("id").GetString()) + { + case "sorter": + SortBy = filterParam.GetProperty("id").GetString(); + SortOrder = filterParam.GetProperty("sortOrder").GetString(); + break; + + case "text": + FilterValue = filterParam.GetProperty("value").GetString(); + break; + + case "my": + case "responsibleID": + ResponsibleId = filterParam.GetProperty("value").GetGuid(); + + break; + case "stageTypeOpen": + case "stageTypeClosedAndWon": + case "stageTypeClosedAndLost": + StageType = filterParam.GetProperty("value").GetString(); + break; + case "opportunityStagesID": + OpportunityStageId = filterParam.GetProperty("value").GetInt32(); + break; + case "lastMonth": + case "yesterday": + case "today": + case "thisMonth": + var valueString = filterParam.GetProperty("value").GetString(); + var fromToArray = JsonDocument.Parse(valueString) + .RootElement + .EnumerateArray() + .Select(x => x.GetString()) + .ToList(); + + if (fromToArray.Count != 2) continue; + + FromDate = Global.ApiDateTimeParse(fromToArray[0]); + ToDate = Global.ApiDateTimeParse(fromToArray[1]); + break; + + case "fromToDate": + FromDate = Global.ApiDateTimeParse(filterParam.GetProperty("from").GetString()); + ToDate = Global.ApiDateTimeParse(filterParam.GetProperty("to").GetString()); + break; + + case "participantID": + ContactId = filterParam.GetProperty("id").GetInt32(); + ContactAlsoIsParticipant = true; + break; + + case "contactID": + ContactId = filterParam.GetProperty("id").GetInt32(); + ContactAlsoIsParticipant = false; + break; + + case "tags": + Tags = filterParam.GetProperty("value").EnumerateArray().Select(x => x.GetString()).ToList(); + break; + } + } + + } + + public override ICollection GetItemsByFilter(DaoFactory daofactory) + { + DealSortedByType sortBy; + EnumExtension.TryParse(SortBy, true, out sortBy); + + DealMilestoneStatus? stageType = null; + DealMilestoneStatus stage; + if (EnumExtension.TryParse(StageType, true, out stage)) + { + stageType = stage; + } + + return daofactory.GetDealDao().GetDeals( + FilterValue, + ResponsibleId, + OpportunityStageId, + Tags, + ContactId, + stageType, + ContactAlsoIsParticipant, + FromDate, + ToDate, + 0, 0, + new OrderBy(sortBy, IsAsc)); + } + } + + public class ContactFilterObject : FilterObject + { + public List Tags { get; set; } + public string ContactListView { get; set; } + public int ContactStage { get; set; } + public int ContactType { get; set; } + public Guid? ResponsibleId { get; set; } + public bool? IsShared { get; set; } + public DateTime FromDate { get; set; } + public DateTime ToDate { get; set; } + + public ContactFilterObject() + { + FromDate = DateTime.MinValue; + ToDate = DateTime.MinValue; + ResponsibleId = null; + ContactStage = -1; + ContactType = -1; + SortBy = "created"; + SortOrder = "descending"; + } + + public ContactFilterObject(string base64String) + { + ContactStage = -1; + ContactType = -1; + + if (string.IsNullOrEmpty(base64String)) return; + + var json = Encoding.UTF8.GetString(Convert.FromBase64String(base64String)); + + var jsonArray = json.Split(';'); + + foreach (var filterItem in jsonArray) + { + var filterObj = JsonDocument.Parse(filterItem).RootElement; + + var paramString = filterObj.GetProperty("params").GetString(); + + if (string.IsNullOrEmpty(paramString)) continue; + + var filterParam = Global.JObjectParseWithDateAsString(Encoding.UTF8.GetString(Convert.FromBase64String(paramString))).RootElement; + + switch (filterObj.GetProperty("id").GetString()) + { + case "sorter": + SortBy = filterParam.GetProperty("id").GetString(); + SortOrder = filterParam.GetProperty("sortOrder").GetString(); + break; + + case "text": + FilterValue = filterParam.GetProperty("value").GetString(); + break; + + case "my": + case "responsibleID": + case "noresponsible": + ResponsibleId = filterParam.GetProperty("value").GetGuid(); + break; + + case "tags": + Tags = filterParam.GetProperty("value").EnumerateArray().Select(x => x.GetString()).ToList(); + break; + + case "withopportunity": + case "person": + case "company": + ContactListView = filterParam.GetProperty("value").GetString(); + break; + + case "contactType": + ContactType = filterParam.GetProperty("value").GetInt32(); + break; + + case "contactStage": + ContactStage = filterParam.GetProperty("value").GetInt32(); + break; + + case "lastMonth": + case "yesterday": + case "today": + case "thisMonth": + var valueString = filterParam.GetProperty("value").GetString(); + var fromToArray = JsonDocument.Parse(valueString) + .RootElement + .EnumerateArray() + .Select(x => x.GetString()) + .ToList(); + + if (fromToArray.Count != 2) continue; + + FromDate = Global.ApiDateTimeParse(fromToArray[0]); + ToDate = Global.ApiDateTimeParse(fromToArray[1]); + break; + + case "fromToDate": + FromDate = Global.ApiDateTimeParse(filterParam.GetProperty("from").GetString()); + ToDate = Global.ApiDateTimeParse(filterParam.GetProperty("to").GetString()); + break; + + case "restricted": + case "shared": + IsShared = filterParam.GetProperty("value").GetBoolean(); + break; + } + } + } + + public override ICollection GetItemsByFilter(DaoFactory daofactory) + { + ContactSortedByType sortBy; + if (!EnumExtension.TryParse(SortBy, true, out sortBy)) + { + sortBy = ContactSortedByType.Created; + } + + ContactListViewType contactListViewType; + EnumExtension.TryParse(ContactListView, true, out contactListViewType); + + return daofactory.GetContactDao().GetContacts( + FilterValue, + Tags, + ContactStage, + ContactType, + contactListViewType, + FromDate, + ToDate, + 0, + 0, + new OrderBy(sortBy, IsAsc), + ResponsibleId, + IsShared); + } + }; + + public class InvoiceItemFilterObject : FilterObject + { + public bool? InventoryStock { get; set; } + + public InvoiceItemFilterObject() + { + InventoryStock = null; + SortBy = "name"; + SortOrder = "ascending"; + } + + public InvoiceItemFilterObject(string base64String) + { + if (string.IsNullOrEmpty(base64String)) return; + + var json = Encoding.UTF8.GetString(Convert.FromBase64String(base64String)); + + var jsonArray = json.Split(';'); + + foreach (var filterItem in jsonArray) + { + var filterObj = JsonDocument.Parse(filterItem).RootElement; + + var paramString = filterObj.GetProperty("params").GetString(); + + if (string.IsNullOrEmpty(paramString)) continue; + + var filterParam = JsonDocument.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(paramString))).RootElement; + + switch (filterObj.GetProperty("id").GetString()) + { + case "sorter": + SortBy = filterParam.GetProperty("id").GetString(); + SortOrder = filterParam.GetProperty("sortOrder").GetString(); + break; + + case "text": + FilterValue = filterParam.GetProperty("value").GetString(); + break; + + case "withInventoryStock": + case "withoutInventoryStock": + InventoryStock = filterParam.GetProperty("value").GetBoolean(); + break; + } + } + + } + + public override ICollection GetItemsByFilter(DaoFactory daofactory) + { + InvoiceItemSortedByType sortBy; + EnumExtension.TryParse(SortBy, true, out sortBy); + + return daofactory.GetInvoiceItemDao().GetInvoiceItems( + FilterValue, + 0, + InventoryStock, + 0, 0, + new OrderBy(sortBy, IsAsc)); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Entities/Invoice.cs b/products/ASC.CRM/Server/Core/Entities/Invoice.cs new file mode 100644 index 00000000000..0d60ca42263 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/Invoice.cs @@ -0,0 +1,120 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; + +using ASC.Common.Mapping; +using ASC.Common.Security; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Enums; +using ASC.Files.Core; + + +namespace ASC.CRM.Core.Entities +{ + public class Invoice : DomainObject, ISecurityObjectId, IMapFrom + { + public InvoiceStatus Status { get; set; } + public string Number { get; set; } + public DateTime IssueDate { get; set; } + public InvoiceTemplateType TemplateType { get; set; } + public int ContactID { get; set; } + public int ConsigneeID { get; set; } + public EntityType EntityType { get; set; } + public int EntityID { get; set; } + public DateTime DueDate { get; set; } + public string Language { get; set; } + public string Currency { get; set; } + public decimal ExchangeRate { get; set; } + public string PurchaseOrderNumber { get; set; } + public string Terms { get; set; } + public string Description { get; set; } + public string JsonData { get; set; } + public int FileID { get; set; } + public DateTime CreateOn { get; set; } + public Guid CreateBy { get; set; } + public DateTime? LastModifedOn { get; set; } + public Guid? LastModifedBy { get; set; } + public object SecurityId + { + get { return ID; } + } + + public Type ObjectType + { + get { return GetType(); } + } + + public List GetInvoiceLines(DaoFactory daoFactory) + { + return daoFactory.GetInvoiceLineDao().GetInvoiceLines(ID); + } + + public File GetInvoiceFile(DaoFactory daoFactory) + { + return daoFactory.GetFileDao().GetFile(FileID, 0); + } + + public decimal GetInvoiceCost(DaoFactory daoFactory) + { + var lines = GetInvoiceLines(daoFactory); + decimal cost = 0; + foreach (var line in lines) + { + var linePrice = Math.Round(line.Price * line.Quantity, 2); + var lineDiscount = Math.Round(linePrice * line.Discount / 100, 2); + + linePrice = linePrice - lineDiscount; + + decimal lineTax1 = 0; + if (line.InvoiceTax1ID > 0) + { + var tax1 = daoFactory.GetInvoiceTaxDao().GetByID(line.InvoiceTax1ID); + if (tax1 != null) + { + lineTax1 = Math.Round(linePrice * tax1.Rate / 100, 2); + } + } + + decimal lineTax2 = 0; + if (line.InvoiceTax2ID > 0) + { + var tax2 = daoFactory.GetInvoiceTaxDao().GetByID(line.InvoiceTax2ID); + if (tax2 != null) + { + lineTax2 = Math.Round(linePrice * tax2.Rate / 100, 2); + } + } + + cost += linePrice + lineTax1 + lineTax2; + } + + return Math.Round(cost, 2); + } + } +} diff --git a/products/ASC.CRM/Server/Core/Entities/InvoiceItem.cs b/products/ASC.CRM/Server/Core/Entities/InvoiceItem.cs new file mode 100644 index 00000000000..e473b11afb7 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/InvoiceItem.cs @@ -0,0 +1,59 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +using ASC.Common.Mapping; +using ASC.Common.Security; +using ASC.CRM.Core.EF; + +namespace ASC.CRM.Core.Entities +{ + public class InvoiceItem : DomainObject, ISecurityObjectId, IMapFrom + { + public string Title { get; set; } + public string Description { get; set; } + public string StockKeepingUnit { get; set; } + public decimal Price { get; set; } + public decimal StockQuantity { get; set; } + public bool TrackInventory { get; set; } + public int InvoiceTax1ID { get; set; } + public int InvoiceTax2ID { get; set; } + public string Currency { get; set; } + public DateTime CreateOn { get; set; } + public Guid CreateBy { get; set; } + public DateTime? LastModifedOn { get; set; } + public Guid? LastModifedBy { get; set; } + public object SecurityId + { + get { return ID; } + } + public Type ObjectType + { + get { return GetType(); } + } + } +} diff --git a/products/ASC.CRM/Server/Core/Entities/InvoiceLine.cs b/products/ASC.CRM/Server/Core/Entities/InvoiceLine.cs new file mode 100644 index 00000000000..e67a4f8d723 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/InvoiceLine.cs @@ -0,0 +1,44 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using ASC.Common.Mapping; +using ASC.CRM.Core.EF; + +namespace ASC.CRM.Core.Entities +{ + public class InvoiceLine : DomainObject, IMapFrom + { + public int InvoiceID { get; set; } + public int InvoiceItemID { get; set; } + public int InvoiceTax1ID { get; set; } + public int InvoiceTax2ID { get; set; } + public int SortOrder { get; set; } + public string Description { get; set; } + public decimal Quantity { get; set; } + public decimal Price { get; set; } + public decimal Discount { get; set; } + } +} + diff --git a/products/ASC.CRM/Server/Core/Entities/InvoiceTax.cs b/products/ASC.CRM/Server/Core/Entities/InvoiceTax.cs new file mode 100644 index 00000000000..65267f9c4d0 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/InvoiceTax.cs @@ -0,0 +1,61 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +using ASC.Common.Mapping; +using ASC.Common.Security; +using ASC.CRM.Core.EF; + +using AutoMapper; + +namespace ASC.CRM.Core.Entities +{ + public class InvoiceTax : DomainObject, ISecurityObjectId, IMapFrom + { + public string Name { get; set; } + public string Description { get; set; } + public decimal Rate { get; set; } + public DateTime CreateOn { get; set; } + public Guid CreateBy { get; set; } + public DateTime? LastModifedOn { get; set; } + public Guid? LastModifedBy { get; set; } + + public object SecurityId + { + get { return ID; } + } + + public Type ObjectType + { + get { return GetType(); } + } + public void Mapping(Profile profile) + { + profile.CreateMap(); + } + } +} diff --git a/products/ASC.CRM/Server/Core/Entities/ListItem.cs b/products/ASC.CRM/Server/Core/Entities/ListItem.cs new file mode 100644 index 00000000000..89d0cc999ff --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/ListItem.cs @@ -0,0 +1,61 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Text.Json.Serialization; + +using ASC.Common.Mapping; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Enums; + +namespace ASC.CRM.Core.Entities +{ + public class ListItem : DomainObject, IMapFrom + { + public ListItem() + { + + } + + public ListItem(string title, string addparams) + { + Title = title; + AdditionalParams = addparams; + } + + public string Title { get; set; } + public string Description { get; set; } + public string Color { get; set; } + + [JsonPropertyName("sort_order")] + public int SortOrder { get; set; } + + [JsonPropertyName("additional_params")] + public string AdditionalParams { get; set; } + + [JsonPropertyName("list_type")] + public ListType? ListType { get; set; } + } +} diff --git a/products/ASC.CRM/Server/Core/Entities/ListItemHistory.cs b/products/ASC.CRM/Server/Core/Entities/ListItemHistory.cs new file mode 100644 index 00000000000..7006dbcf53a --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/ListItemHistory.cs @@ -0,0 +1,41 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +using ASC.CRM.Core.Enums; + +namespace ASC.CRM.Core.Entities +{ + public class ListItemHistory : DomainObject + { + public int EntityID { get; set; } + public EntityType EntityType { get; set; } + public int StatusID { get; set; } + public DateTime ModifedOn { get; set; } + public Guid ModifedBy { get; set; } + } +} diff --git a/products/ASC.CRM/Server/Core/Entities/OrderBy.cs b/products/ASC.CRM/Server/Core/Entities/OrderBy.cs new file mode 100644 index 00000000000..b1a039b43a1 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/OrderBy.cs @@ -0,0 +1,53 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +namespace ASC.CRM.Core.Entities +{ + public class OrderBy + { + + public bool IsAsc + { + get; + set; + } + + + public Enum SortedBy + { + get; + set; + } + + public OrderBy(Enum sortedByType, bool isAsc) + { + IsAsc = isAsc; + SortedBy = sortedByType; + } + } +} diff --git a/products/ASC.CRM/Server/Core/Entities/RelationshipEvent.cs b/products/ASC.CRM/Server/Core/Entities/RelationshipEvent.cs new file mode 100644 index 00000000000..6928857c61a --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/RelationshipEvent.cs @@ -0,0 +1,61 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System; + +using ASC.Common.Mapping; +using ASC.Common.Security; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Enums; +#endregion + +namespace ASC.CRM.Core.Entities +{ + public class RelationshipEvent : DomainObject, ISecurityObjectId, IMapFrom + { + public Guid CreateBy { get; set; } + public DateTime CreateOn { get; set; } + public Guid? LastModifedBy { get; set; } + public DateTime? LastModifedOn { get; set; } + public String Content { get; set; } + public int ContactID { get; set; } + public EntityType EntityType { get; set; } + public int EntityID { get; set; } + public int CategoryID { get; set; } + + public object SecurityId + { + get { return ID; } + } + + public Type ObjectType + { + get { return GetType(); } + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Entities/Report.cs b/products/ASC.CRM/Server/Core/Entities/Report.cs new file mode 100644 index 00000000000..dd9af482ecf --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/Report.cs @@ -0,0 +1,108 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System; + +using ASC.CRM.Core.Enums; +using ASC.VoipService; + +#endregion + +namespace ASC.CRM.Core.Entities +{ + public class SalesByManager + { + public Guid UserId { get; set; } + public string UserName { get; set; } + public decimal Value { get; set; } + public DateTime Date { get; set; } + } + + public class SalesForecast + { + public decimal Value { get; set; } + public decimal ValueWithProbability { get; set; } + public DateTime Date { get; set; } + } + + public class SalesFunnel + { + public DealMilestoneStatus Status { get; set; } + public string Title { get; set; } + public int Count { get; set; } + public decimal Value { get; set; } + public int Duration { get; set; } + } + + public class WorkloadByDeals + { + public Guid UserId { get; set; } + public string UserName { get; set; } + public DealMilestoneStatus Status { get; set; } + public int Count { get; set; } + public decimal Value { get; set; } + } + + public class WorkloadByTasks + { + public int CategoryId { get; set; } + public string CategoryName { get; set; } + public Guid UserId { get; set; } + public string UserName { get; set; } + public int Count { get; set; } + } + + public class WorkloadByInvoices + { + public Guid UserId { get; set; } + public string UserName { get; set; } + public int SentCount { get; set; } + public int PaidCount { get; set; } + public int RejectedCount { get; set; } + public int OverdueCount { get; set; } + } + + public class WorkloadByViop + { + public Guid UserId { get; set; } + public string UserName { get; set; } + public VoipCallStatus Status { get; set; } + public int Count { get; set; } + public int Duration { get; set; } + } + + public class WorkloadByContacts + { + public int CategoryId { get; set; } + public string CategoryName { get; set; } + public Guid UserId { get; set; } + public string UserName { get; set; } + public int Count { get; set; } + public int WithDeals { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Entities/Task.cs b/products/ASC.CRM/Server/Core/Entities/Task.cs new file mode 100644 index 00000000000..54cd2e3628e --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/Task.cs @@ -0,0 +1,73 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Usings + +using System; + +using ASC.Common.Mapping; +using ASC.Common.Security; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Enums; + +using AutoMapper; + +#endregion + +namespace ASC.CRM.Core.Entities +{ + public class Task : DomainObject, ISecurityObjectId, IMapFrom + { + public Guid CreateBy { get; set; } + public DateTime CreateOn { get; set; } + public Guid? LastModifedBy { get; set; } + public DateTime? LastModifedOn { get; set; } + public int ContactID { get; set; } + public Contact Contact { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public DateTime DeadLine { get; set; } + public Guid ResponsibleID { get; set; } + public bool IsClosed { get; set; } + public int CategoryID { get; set; } + public EntityType EntityType { get; set; } + public int EntityID { get; set; } + public int AlertValue { get; set; } + public object SecurityId + { + get { return ID; } + } + public Type ObjectType + { + get { return GetType(); } + } + + public void Mapping(Profile profile) + { + profile.CreateMap(); + } + } +} diff --git a/products/ASC.CRM/Server/Core/Entities/TaskTemplate.cs b/products/ASC.CRM/Server/Core/Entities/TaskTemplate.cs new file mode 100644 index 00000000000..44fa0a73d11 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/TaskTemplate.cs @@ -0,0 +1,60 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System; + +using ASC.Common.Mapping; +using ASC.CRM.Core.EF; + +using AutoMapper; + +#endregion + +namespace ASC.CRM.Core.Entities +{ + public class TaskTemplate : DomainObject, IMapFrom + { + public int ContainerID { get; set; } + public String Title { get; set; } + public String Description { get; set; } + public Guid ResponsibleID { get; set; } + public int CategoryID { get; set; } + public bool isNotify { get; set; } + public TimeSpan Offset { get; set; } + public bool DeadLineIsFixed { get; set; } + public Guid CreateBy { get; set; } + public DateTime CreateOn { get; set; } + public int SortOrder { get; set; } + public void Mapping(Profile profile) + { + profile.CreateMap() + .ForMember(dest => dest.Offset, opt => opt.MapFrom(src => TimeSpan.FromTicks(src.Offset))); + + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Entities/TaskTemplateContainer.cs b/products/ASC.CRM/Server/Core/Entities/TaskTemplateContainer.cs new file mode 100644 index 00000000000..8f3bd47a0e7 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Entities/TaskTemplateContainer.cs @@ -0,0 +1,48 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System; + +using ASC.Common.Mapping; +using ASC.CRM.Core.EF; +using ASC.CRM.Core.Enums; + +#endregion + +namespace ASC.CRM.Core.Entities +{ + public class TaskTemplateContainer : DomainObject, IMapFrom + { + public String Title { get; set; } + public EntityType EntityType { get; set; } + public Guid CreateBy { get; set; } + public DateTime CreateOn { get; set; } + + + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Enums/BidTypeEnum.cs b/products/ASC.CRM/Server/Core/Enums/BidTypeEnum.cs new file mode 100644 index 00000000000..3a2c9a8ff3f --- /dev/null +++ b/products/ASC.CRM/Server/Core/Enums/BidTypeEnum.cs @@ -0,0 +1,39 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +namespace ASC.CRM.Core.Enums +{ + public enum BidType + { + FixedBid = 0, + PerHour = 1, + PerDay = 2, + PerWeek = 3, + PerMonth = 4, + PerYear = 5 + + } +} diff --git a/products/ASC.CRM/Server/Core/Enums/CommunicationTypeEnum.cs b/products/ASC.CRM/Server/Core/Enums/CommunicationTypeEnum.cs new file mode 100644 index 00000000000..321dd5d35f0 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Enums/CommunicationTypeEnum.cs @@ -0,0 +1,37 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +namespace ASC.CRM.Core.Enums +{ + public enum CommunicationType + { + + Email = 0, + Telephone = 1, + Meeting = 2 + + } +} diff --git a/products/ASC.CRM/Server/Core/Enums/ContactInfoCategoryEnum.cs b/products/ASC.CRM/Server/Core/Enums/ContactInfoCategoryEnum.cs new file mode 100644 index 00000000000..4c811c94da4 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Enums/ContactInfoCategoryEnum.cs @@ -0,0 +1,72 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.ComponentModel; + +using ASC.CRM.Classes; + +namespace ASC.CRM.Core.Enums +{ + [TypeConverter(typeof(LocalizedEnumConverter))] + public enum ContactInfoBaseCategory + { + Home, + Work, + Other + } + + [TypeConverter(typeof(LocalizedEnumConverter))] + public enum PhoneCategory + { + Home, + Work, + Mobile, + Fax, + Direct, + Other + } + + [TypeConverter(typeof(LocalizedEnumConverter))] + public enum AddressPart + { + Street, + City, + State, + Zip, + Country + } + + [TypeConverter(typeof(LocalizedEnumConverter))] + public enum AddressCategory + { + Home, + Postal, + Office, + Billing, + Other, + Work + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Enums/ContactInfoTypeEnum.cs b/products/ASC.CRM/Server/Core/Enums/ContactInfoTypeEnum.cs new file mode 100644 index 00000000000..c1acf721274 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Enums/ContactInfoTypeEnum.cs @@ -0,0 +1,56 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.ComponentModel; + +using ASC.CRM.Classes; + + +namespace ASC.CRM.Core.Enums +{ + [TypeConverter(typeof(LocalizedEnumConverter))] + public enum ContactInfoType + { + Phone, + Email, + Website, + Skype, + Twitter, + LinkedIn, + Facebook, + Address, + LiveJournal, + MySpace, + GMail, + Blogger, + Yahoo, + MSN, + ICQ, + Jabber, + AIM, + VK + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Enums/ContactListViewTypeEnum.cs b/products/ASC.CRM/Server/Core/Enums/ContactListViewTypeEnum.cs new file mode 100644 index 00000000000..48ac35b3491 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Enums/ContactListViewTypeEnum.cs @@ -0,0 +1,49 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.ComponentModel; + +using ASC.CRM.Classes; + +namespace ASC.CRM.Core.Enums +{ + [TypeConverter(typeof(LocalizedEnumConverter))] + public enum ContactListViewType + { + All, + Company, + Person, + WithOpportunity + } + +} + + + + + + + diff --git a/products/ASC.CRM/Server/Core/Enums/ContactSelectorTypeEnum.cs b/products/ASC.CRM/Server/Core/Enums/ContactSelectorTypeEnum.cs new file mode 100644 index 00000000000..cb065ae7d1e --- /dev/null +++ b/products/ASC.CRM/Server/Core/Enums/ContactSelectorTypeEnum.cs @@ -0,0 +1,43 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +namespace ASC.CRM.Core.Enums +{ + public enum ContactSelectorTypeEnum + { + All = -1, + Companies = 0, + Persons = 1, + PersonsWithoutCompany = 2, + CompaniesAndPersonsWithoutCompany = 3 + } +} + + + + + + + diff --git a/products/ASC.CRM/Server/Core/Enums/CustomFieldTypeEnum.cs b/products/ASC.CRM/Server/Core/Enums/CustomFieldTypeEnum.cs new file mode 100644 index 00000000000..67bc03245b3 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Enums/CustomFieldTypeEnum.cs @@ -0,0 +1,43 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.ComponentModel; + +using ASC.CRM.Classes; + +namespace ASC.CRM.Core.Enums +{ + [TypeConverter(typeof(LocalizedEnumConverter))] + public enum CustomFieldType + { + TextField = 0, + TextArea = 1, + SelectBox = 2, + CheckBox = 3, + Heading = 4, + Date = 5 + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Enums/DealMilestoneStatus.cs b/products/ASC.CRM/Server/Core/Enums/DealMilestoneStatus.cs new file mode 100644 index 00000000000..e7f505b1257 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Enums/DealMilestoneStatus.cs @@ -0,0 +1,45 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System.ComponentModel; + +using ASC.CRM.Classes; + +#endregion + +namespace ASC.CRM.Core.Enums +{ + [TypeConverter(typeof(LocalizedEnumConverter))] + public enum DealMilestoneStatus + { + Open = 0, + ClosedAndWon = 1, + ClosedAndLost = 2 + } + +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Enums/EntityTypeEnum.cs b/products/ASC.CRM/Server/Core/Enums/EntityTypeEnum.cs new file mode 100644 index 00000000000..7fa846e55c5 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Enums/EntityTypeEnum.cs @@ -0,0 +1,47 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.ComponentModel; + +using ASC.CRM.Classes; + +namespace ASC.CRM.Core.Enums +{ + [TypeConverter(typeof(LocalizedEnumConverter))] + public enum EntityType + { + Any = -1, + Contact = 0, + Opportunity = 1, + RelationshipEvent = 2, + Task = 3, + Company = 4, + Person = 5, + File = 6, + Case = 7, + Invoice = 8 + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Enums/InvoiceStatus.cs b/products/ASC.CRM/Server/Core/Enums/InvoiceStatus.cs new file mode 100644 index 00000000000..7390696dbd3 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Enums/InvoiceStatus.cs @@ -0,0 +1,43 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.ComponentModel; + +using ASC.CRM.Classes; + +namespace ASC.CRM.Core.Enums +{ + [TypeConverter(typeof(LocalizedEnumConverter))] + public enum InvoiceStatus + { + Draft = 1, + Sent = 2, + Rejected = 3, + Paid = 4, + Archived = 5 + } +} + diff --git a/products/ASC.CRM/Server/Core/Enums/InvoiceTemplateTypeEnum.cs b/products/ASC.CRM/Server/Core/Enums/InvoiceTemplateTypeEnum.cs new file mode 100644 index 00000000000..c0da1517d76 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Enums/InvoiceTemplateTypeEnum.cs @@ -0,0 +1,46 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.ComponentModel; + +using ASC.CRM.Classes; + +namespace ASC.CRM.Core.Enums +{ + [TypeConverter(typeof(LocalizedEnumConverter))] + public enum InvoiceTemplateType + { + Eur = 0, + Rus = 1 + } + + public enum InvoiceActionType + { + Create, + Edit, + Duplicate + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Enums/ListTypeEnum.cs b/products/ASC.CRM/Server/Core/Enums/ListTypeEnum.cs new file mode 100644 index 00000000000..7da612c994b --- /dev/null +++ b/products/ASC.CRM/Server/Core/Enums/ListTypeEnum.cs @@ -0,0 +1,53 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System.ComponentModel; + +using ASC.CRM.Classes; + +#endregion + +namespace ASC.CRM.Core.Enums +{ + public enum ListType + { + ContactStatus = 1, + TaskCategory = 2, + HistoryCategory = 3, + ContactType = 4 + } + + [TypeConverter(typeof(LocalizedEnumConverter))] + public enum HistoryCategorySystem + { + TaskClosed = -1, + FilesUpload = -2, + MailMessage = -3 + } + +} diff --git a/products/ASC.CRM/Server/Core/Enums/ReportTimePeriodEnum.cs b/products/ASC.CRM/Server/Core/Enums/ReportTimePeriodEnum.cs new file mode 100644 index 00000000000..15946bf56ed --- /dev/null +++ b/products/ASC.CRM/Server/Core/Enums/ReportTimePeriodEnum.cs @@ -0,0 +1,53 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +namespace ASC.CRM.Core.Enums +{ + public enum ReportTimePeriod + { + Today, + Yesterday, + Tomorrow, + + CurrentWeek, + PreviousWeek, + NextWeek, + + CurrentMonth, + PreviousMonth, + NextMonth, + + CurrentQuarter, + PreviousQuarter, + NextQuarter, + + CurrentYear, + PreviousYear, + NextYear, + + DuringAllTime + } +} diff --git a/products/ASC.CRM/Server/Core/Enums/ReportTypeEnum.cs b/products/ASC.CRM/Server/Core/Enums/ReportTypeEnum.cs new file mode 100644 index 00000000000..8294e51b598 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Enums/ReportTypeEnum.cs @@ -0,0 +1,42 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +namespace ASC.CRM.Core.Enums +{ + public enum ReportType + { + SalesByManagers, + SalesForecast, + SalesFunnel, + WorkloadByContacts, + WorkloadByTasks, + WorkloadByDeals, + WorkloadByInvoices, + WorkloadByVoip, + SummaryForThePeriod, + SummaryAtThisMoment + } +} diff --git a/products/ASC.CRM/Server/Core/Enums/ShareType.cs b/products/ASC.CRM/Server/Core/Enums/ShareType.cs new file mode 100644 index 00000000000..e3e12977b63 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Enums/ShareType.cs @@ -0,0 +1,37 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + + + +namespace ASC.CRM.Core.Enums +{ + public enum ShareType + { + None, + ReadWrite, + Read + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Enums/SortedByTypeEnum.cs b/products/ASC.CRM/Server/Core/Enums/SortedByTypeEnum.cs new file mode 100644 index 00000000000..9080993068b --- /dev/null +++ b/products/ASC.CRM/Server/Core/Enums/SortedByTypeEnum.cs @@ -0,0 +1,91 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +namespace ASC.CRM.Core.Enums +{ + + public enum TaskSortedByType + { + Title, + Category, + DeadLine, + Contact, + ContactManager + } + + public enum DealSortedByType + { + Title, + Responsible, + Stage, + BidValue, + DateAndTime + } + + public enum RelationshipEventByType + { + Created, + CreateBy, + Category, + Content + } + + public enum ContactSortedByType + { + DisplayName, + ContactType, + Created, + FirstName, + LastName, + History + } + + public enum SortedByType + { + DateAndTime, + Title, + CreateBy + } + + public enum InvoiceSortedByType + { + Number, + IssueDate, + Contact, + DueDate, + Status + } + + public enum InvoiceItemSortedByType + { + Name, + Price, + Quantity, + SKU, + Created + } + +} diff --git a/products/ASC.CRM/Server/Core/Search/BundleSearch.cs b/products/ASC.CRM/Server/Core/Search/BundleSearch.cs new file mode 100644 index 00000000000..648e6893e6b --- /dev/null +++ b/products/ASC.CRM/Server/Core/Search/BundleSearch.cs @@ -0,0 +1,169 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.CRM.Core.EF; + +namespace ASC.Web.CRM.Core.Search +{ + [Scope] + public class BundleSearch + { + private readonly FactoryIndexerContact _factoryIndexerContact; + private readonly FactoryIndexerContactInfo _factoryIndexerContactInfo; + private readonly FactoryIndexerFieldValue _factoryIndexerFieldValue; + private readonly FactoryIndexerEvents _factoryIndexerEvents; + private readonly FactoryIndexerDeal _factoryIndexerDeal; + private readonly FactoryIndexerTask _factoryIndexerTask; + private readonly FactoryIndexerCase _factoryIndexerCase; + private readonly FactoryIndexerInvoice _factoryIndexerInvoice; + + public BundleSearch(FactoryIndexerContact factoryIndexerContact, + FactoryIndexerContactInfo factoryIndexerContactInfo, + FactoryIndexerFieldValue factoryIndexerFieldValue, + FactoryIndexerEvents factoryIndexerEvents, + FactoryIndexerDeal factoryIndexerDeal, + FactoryIndexerTask factoryIndexerTask, + FactoryIndexerCase factoryIndexerCase, + FactoryIndexerInvoice factoryIndexerInvoice) + { + _factoryIndexerContact = factoryIndexerContact; + _factoryIndexerContactInfo = factoryIndexerContactInfo; + _factoryIndexerFieldValue = factoryIndexerFieldValue; + _factoryIndexerEvents = factoryIndexerEvents; + _factoryIndexerDeal = factoryIndexerDeal; + _factoryIndexerTask = factoryIndexerTask; + _factoryIndexerCase = factoryIndexerCase; + _factoryIndexerInvoice = factoryIndexerInvoice; + } + + public bool TrySelectCase(string text, out List result) + { + var success = false; + result = new List(); + + List casesId; + + if (_factoryIndexerCase.TrySelectIds(s => s.MatchAll(text), out casesId)) + { + result.AddRange(casesId); + success = true; + } + + IReadOnlyCollection casesCustom; + if (_factoryIndexerFieldValue.TrySelect(s => s.MatchAll(text).Where(r => (int)r.EntityType, 7), out casesCustom)) + { + result.AddRange(casesCustom.Select(r => r.EntityId).ToList()); + success = true; + } + + IReadOnlyCollection events; + + if (!_factoryIndexerEvents.TrySelect(s => s.MatchAll(text).Where(r => (int)r.EntityType, 7).Gt(r => r.EntityId, 0), out events)) + { + result.AddRange(events.Select(r => r.EntityId).ToList()); + success = true; + } + + return success; + } + + public bool TrySelectContact(string text, out List result) + { + var success = false; + result = new List(); + + List contactsId; + + if (_factoryIndexerContact.TrySelectIds(s => s.MatchAll(text), out contactsId)) + { + result.AddRange(contactsId); + success = true; + } + + IReadOnlyCollection infos; + + if (_factoryIndexerContactInfo.TrySelect(s => s.MatchAll(text), out infos)) + { + result.AddRange(infos.Select(r => r.ContactId).ToList()); + success = true; + } + + IReadOnlyCollection personCustom; + + if (_factoryIndexerFieldValue.TrySelect(s => s.MatchAll(text).In(r => r.EntityType, new[] { 0, 4, 5 }), out personCustom)) + { + result.AddRange(personCustom.Select(r => r.EntityId).ToList()); + success = true; + } + + IReadOnlyCollection events; + + if (_factoryIndexerEvents.TrySelect(s => s.MatchAll(text).Gt(r => r.ContactId, 0), out events)) + { + result.AddRange(events.Select(r => r.ContactId).ToList()); + success = true; + } + + return success; + } + + public bool TrySelectOpportunity(string text, out List result) + { + var success = false; + result = new List(); + + List dealsId; + + if (_factoryIndexerDeal.TrySelectIds(s => s.MatchAll(text), out dealsId)) + { + result.AddRange(dealsId); + success = true; + } + + IReadOnlyCollection casesCustom; + + if (_factoryIndexerFieldValue.TrySelect(s => s.MatchAll(text).Where(r => (int)r.EntityType, 1), out casesCustom)) + { + result.AddRange(casesCustom.Select(r => r.EntityId).ToList()); + success = true; + } + + IReadOnlyCollection events; + + if (!_factoryIndexerEvents.TrySelect(s => s.MatchAll(text).Where(r => (int)r.EntityType, 1).Gt(r => r.EntityId, 0), out events)) + { + result.AddRange(events.Select(r => r.EntityId).ToList()); + success = true; + } + + return success; + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Search/FactoryIndexerCase.cs b/products/ASC.CRM/Server/Core/Search/FactoryIndexerCase.cs new file mode 100644 index 00000000000..039847b7990 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Search/FactoryIndexerCase.cs @@ -0,0 +1,136 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.EF; +using ASC.ElasticSearch; +using ASC.ElasticSearch.Core; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.Web.CRM.Core.Search +{ + [Scope] + public sealed class FactoryIndexerCase : FactoryIndexer + { + public FactoryIndexerCase( + IOptionsMonitor options, + TenantManager tenantManager, + SearchSettingsHelper searchSettingsHelper, + FactoryIndexer factoryIndexer, + BaseIndexer baseIndexer, + IServiceProvider serviceProvider, + DaoFactory daoFactory, + ICache cache) + : base(options, tenantManager, searchSettingsHelper, factoryIndexer, baseIndexer, serviceProvider, cache) + { + DaoFactory = daoFactory; + } + + public DaoFactory DaoFactory { get; } + + public override void IndexAll() + { + var entityDao = DaoFactory.GetCasesDao(); + + IQueryable GetBaseQuery(DateTime lastIndexed) => + entityDao.CrmDbContext.Cases + .Where(r => r.LastModifedOn >= lastIndexed) + .Join(entityDao.CrmDbContext.Tenants, r => r.TenantId, r => r.Id, (f, t) => new { DbEntity = f, DbTenant = t }) + .Where(r => r.DbTenant.Status == ASC.Core.Tenants.TenantStatus.Active) + .Select(r => r.DbEntity); + + (int, int, int) getCount(DateTime lastIndexed) + { + var q = GetBaseQuery(lastIndexed); + + var count = q.Count(); + var min = count > 0 ? q.Min(r => r.Id) : 0; + var max = count > 0 ? q.Max(r => r.Id) : 0; + + return (count, max, min); + } + + List getData(long start, long stop, DateTime lastIndexed) => + GetBaseQuery(lastIndexed) + .Where(r => r.Id >= start && r.Id <= stop) + .ToList(); + + List getIds(DateTime lastIndexed) + { + long start = 0; + + var result = new List(); + + while (true) + { + var id = GetBaseQuery(lastIndexed) + .AsNoTracking() + .Where(r => r.Id >= start) + .OrderBy(x => x.Id) + .Skip(BaseIndexer.QueryLimit) + .Select(x => x.Id) + .FirstOrDefault(); + + if (id != 0) + { + start = id; + result.Add(id); + } + else + { + break; + } + } + + return result; + } + + try + { + foreach (var data in Indexer.IndexAll(getCount, getIds, getData)) + { + Index(data); + } + } + catch (Exception e) + { + Logger.Error(e); + + throw; + } + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Search/FactoryIndexerContact.cs b/products/ASC.CRM/Server/Core/Search/FactoryIndexerContact.cs new file mode 100644 index 00000000000..d906c5726f9 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Search/FactoryIndexerContact.cs @@ -0,0 +1,134 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.EF; +using ASC.ElasticSearch; +using ASC.ElasticSearch.Core; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.Web.CRM.Core.Search +{ + [Scope] + public sealed class FactoryIndexerContact : FactoryIndexer + { + public FactoryIndexerContact( + IOptionsMonitor options, + TenantManager tenantManager, + SearchSettingsHelper searchSettingsHelper, + FactoryIndexer factoryIndexer, + BaseIndexer baseIndexer, + IServiceProvider serviceProvider, + DaoFactory daoFactory, + ICache cache) + : base(options, tenantManager, searchSettingsHelper, factoryIndexer, baseIndexer, serviceProvider, cache) + { + DaoFactory = daoFactory; + } + + public DaoFactory DaoFactory { get; } + + public override void IndexAll() + { + var entityDao = DaoFactory.GetContactDao(); + + IQueryable GetBaseQuery(DateTime lastIndexed) => + entityDao.CrmDbContext.Contacts + .Where(r => r.LastModifedOn >= lastIndexed) + .Join(entityDao.CrmDbContext.Tenants, r => r.TenantId, r => r.Id, (f, t) => new { DbEntity = f, DbTenant = t }) + .Where(r => r.DbTenant.Status == ASC.Core.Tenants.TenantStatus.Active) + .Select(r => r.DbEntity); + + (int, int, int) getCount(DateTime lastIndexed) + { + var q = GetBaseQuery(lastIndexed); + + var count = q.Count(); + var min = count > 0 ? q.Min(r => r.Id) : 0; + var max = count > 0 ? q.Max(r => r.Id) : 0; + + return (count, max, min); + } + + List getData(long start, long stop, DateTime lastIndexed) => + GetBaseQuery(lastIndexed) + .Where(r => r.Id >= start && r.Id <= stop) + .ToList(); + + List getIds(DateTime lastIndexed) + { + long start = 0; + + var result = new List(); + + while (true) + { + var id = GetBaseQuery(lastIndexed) + .AsNoTracking() + .Where(r => r.Id >= start) + .OrderBy(x => x.Id) + .Skip(BaseIndexer.QueryLimit) + .Select(x => x.Id) + .FirstOrDefault(); + + if (id != 0) + { + start = id; + result.Add(id); + } + else + { + break; + } + } + + return result; + } + try + { + foreach (var data in Indexer.IndexAll(getCount, getIds, getData)) + { + Index(data); + } + } + catch (Exception e) + { + Logger.Error(e); + throw; + } + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Search/FactoryIndexerContactInfo.cs b/products/ASC.CRM/Server/Core/Search/FactoryIndexerContactInfo.cs new file mode 100644 index 00000000000..569494840e2 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Search/FactoryIndexerContactInfo.cs @@ -0,0 +1,134 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.EF; +using ASC.ElasticSearch; +using ASC.ElasticSearch.Core; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.Web.CRM.Core.Search +{ + [Scope] + public sealed class FactoryIndexerContactInfo : FactoryIndexer + { + public FactoryIndexerContactInfo( + IOptionsMonitor options, + TenantManager tenantManager, + SearchSettingsHelper searchSettingsHelper, + FactoryIndexer factoryIndexer, + BaseIndexer baseIndexer, + IServiceProvider serviceProvider, + DaoFactory daoFactory, + ICache cache) + : base(options, tenantManager, searchSettingsHelper, factoryIndexer, baseIndexer, serviceProvider, cache) + { + DaoFactory = daoFactory; + } + + public DaoFactory DaoFactory { get; } + + public override void IndexAll() + { + var entityDao = DaoFactory.GetContactInfoDao(); + + IQueryable GetBaseQuery(DateTime lastIndexed) => + entityDao.CrmDbContext.ContactsInfo + .Where(r => r.LastModifedOn >= lastIndexed) + .Join(entityDao.CrmDbContext.Tenants, r => r.TenantId, r => r.Id, (f, t) => new { DbEntity = f, DbTenant = t }) + .Where(r => r.DbTenant.Status == ASC.Core.Tenants.TenantStatus.Active) + .Select(r => r.DbEntity); + + (int, int, int) getCount(DateTime lastIndexed) + { + var q = GetBaseQuery(lastIndexed); + + var count = q.Count(); + var min = count > 0 ? q.Min(r => r.Id) : 0; + var max = count > 0 ? q.Max(r => r.Id) : 0; + + return (count, max, min); + } + + List getData(long start, long stop, DateTime lastIndexed) => + GetBaseQuery(lastIndexed) + .Where(r => r.Id >= start && r.Id <= stop) + .ToList(); + + List getIds(DateTime lastIndexed) + { + long start = 0; + + var result = new List(); + + while (true) + { + var id = GetBaseQuery(lastIndexed) + .AsNoTracking() + .Where(r => r.Id >= start) + .OrderBy(x => x.Id) + .Skip(BaseIndexer.QueryLimit) + .Select(x => x.Id) + .FirstOrDefault(); + + if (id != 0) + { + start = id; + result.Add(id); + } + else + { + break; + } + } + + return result; + } + try + { + foreach (var data in Indexer.IndexAll(getCount, getIds, getData)) + { + Index(data); + } + } + catch (Exception e) + { + Logger.Error(e); + throw; + } + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Search/FactoryIndexerDeal.cs b/products/ASC.CRM/Server/Core/Search/FactoryIndexerDeal.cs new file mode 100644 index 00000000000..a891a61665b --- /dev/null +++ b/products/ASC.CRM/Server/Core/Search/FactoryIndexerDeal.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.EF; +using ASC.ElasticSearch; +using ASC.ElasticSearch.Core; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.Web.CRM.Core.Search +{ + [Scope] + public sealed class FactoryIndexerDeal : FactoryIndexer + { + public FactoryIndexerDeal( + IOptionsMonitor options, + TenantManager tenantManager, + SearchSettingsHelper searchSettingsHelper, + FactoryIndexer factoryIndexer, + BaseIndexer baseIndexer, + IServiceProvider serviceProvider, + DaoFactory daoFactory, + ICache cache) + : base(options, tenantManager, searchSettingsHelper, factoryIndexer, baseIndexer, serviceProvider, cache) + { + DaoFactory = daoFactory; + } + + public DaoFactory DaoFactory { get; } + + public override void IndexAll() + { + var entityDao = DaoFactory.GetDealDao(); + + IQueryable GetBaseQuery(DateTime lastIndexed) => + entityDao.CrmDbContext.Deals + .Where(r => r.LastModifedOn >= lastIndexed) + .Join(entityDao.CrmDbContext.Tenants, r => r.TenantId, r => r.Id, (f, t) => new { DbEntity = f, DbTenant = t }) + .Where(r => r.DbTenant.Status == ASC.Core.Tenants.TenantStatus.Active) + .Select(r => r.DbEntity); + + (int, int, int) getCount(DateTime lastIndexed) + { + var q = GetBaseQuery(lastIndexed); + + var count = q.Count(); + var min = count > 0 ? q.Min(r => r.Id) : 0; + var max = count > 0 ? q.Max(r => r.Id) : 0; + + return (count, max, min); + } + + List getData(long start, long stop, DateTime lastIndexed) => + GetBaseQuery(lastIndexed) + .Where(r => r.Id >= start && r.Id <= stop) + .ToList(); + + List getIds(DateTime lastIndexed) + { + long start = 0; + + var result = new List(); + + while (true) + { + var id = GetBaseQuery(lastIndexed) + .AsNoTracking() + .Where(r => r.Id >= start) + .OrderBy(x => x.Id) + .Skip(BaseIndexer.QueryLimit) + .Select(x => x.Id) + .FirstOrDefault(); + + if (id != 0) + { + start = id; + result.Add(id); + } + else + { + break; + } + } + + return result; + } + try + { + foreach (var data in Indexer.IndexAll(getCount, getIds, getData)) + { + Index(data); + } + } + catch (Exception e) + { + Logger.Error(e); + throw; + } + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Search/FactoryIndexerEvents.cs b/products/ASC.CRM/Server/Core/Search/FactoryIndexerEvents.cs new file mode 100644 index 00000000000..3a3352a4ff5 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Search/FactoryIndexerEvents.cs @@ -0,0 +1,133 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.EF; +using ASC.ElasticSearch; +using ASC.ElasticSearch.Core; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.Web.CRM.Core.Search +{ + [Scope] + public sealed class FactoryIndexerEvents : FactoryIndexer + { + public FactoryIndexerEvents( + IOptionsMonitor options, + TenantManager tenantManager, + SearchSettingsHelper searchSettingsHelper, + FactoryIndexer factoryIndexer, + BaseIndexer baseIndexer, + IServiceProvider serviceProvider, + DaoFactory daoFactory, + ICache cache) + : base(options, tenantManager, searchSettingsHelper, factoryIndexer, baseIndexer, serviceProvider, cache) + { + DaoFactory = daoFactory; + } + + public DaoFactory DaoFactory { get; } + + public override void IndexAll() + { + var entityDao = DaoFactory.GetRelationshipEventDao(); + + IQueryable GetBaseQuery(DateTime lastIndexed) => + entityDao.CrmDbContext.RelationshipEvent + .Where(r => r.LastModifedOn >= lastIndexed) + .Join(entityDao.CrmDbContext.Tenants, r => r.TenantId, r => r.Id, (f, t) => new { DbEntity = f, DbTenant = t }) + .Where(r => r.DbTenant.Status == ASC.Core.Tenants.TenantStatus.Active) + .Select(r => r.DbEntity); + + (int, int, int) getCount(DateTime lastIndexed) + { + var q = GetBaseQuery(lastIndexed); + + var count = q.Count(); + var min = count > 0 ? q.Min(r => r.Id) : 0; + var max = count > 0 ? q.Max(r => r.Id) : 0; + + return (count, max, min); + } + + List getData(long start, long stop, DateTime lastIndexed) => + GetBaseQuery(lastIndexed) + .Where(r => r.Id >= start && r.Id <= stop) + .ToList(); + + List getIds(DateTime lastIndexed) + { + long start = 0; + + var result = new List(); + + while (true) + { + var id = GetBaseQuery(lastIndexed) + .AsNoTracking() + .Where(r => r.Id >= start) + .OrderBy(x => x.Id) + .Skip(BaseIndexer.QueryLimit) + .Select(x => x.Id) + .FirstOrDefault(); + + if (id != 0) + { + start = id; + result.Add(id); + } + else + { + break; + } + } + + return result; + } + try + { + foreach (var data in Indexer.IndexAll(getCount, getIds, getData)) + { + Index(data); + } + } + catch (Exception e) + { + Logger.Error(e); + throw; + } + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Search/FactoryIndexerFieldValue.cs b/products/ASC.CRM/Server/Core/Search/FactoryIndexerFieldValue.cs new file mode 100644 index 00000000000..abab0749690 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Search/FactoryIndexerFieldValue.cs @@ -0,0 +1,136 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.EF; +using ASC.ElasticSearch; +using ASC.ElasticSearch.Core; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.Web.CRM.Core.Search +{ + [Scope] + public sealed class FactoryIndexerFieldValue : FactoryIndexer + { + public FactoryIndexerFieldValue( + IOptionsMonitor options, + TenantManager tenantManager, + SearchSettingsHelper searchSettingsHelper, + FactoryIndexer factoryIndexer, + BaseIndexer baseIndexer, + IServiceProvider serviceProvider, + DaoFactory daoFactory, + ICache cache) + : base(options, tenantManager, searchSettingsHelper, factoryIndexer, baseIndexer, serviceProvider, cache) + { + DaoFactory = daoFactory; + } + + public DaoFactory DaoFactory { get; } + + public override void IndexAll() + { + var entityDao = DaoFactory.GetCustomFieldDao(); + + IQueryable GetBaseQuery(DateTime lastIndexed) => + entityDao.CrmDbContext.FieldValue + .Where(r => r.LastModifedOn >= lastIndexed) + .Join(entityDao.CrmDbContext.Tenants, r => r.TenantId, r => r.Id, (f, t) => new { DbEntity = f, DbTenant = t }) + .Where(r => r.DbTenant.Status == ASC.Core.Tenants.TenantStatus.Active) + .Select(r => r.DbEntity); + + (int, int, int) getCount(DateTime lastIndexed) + { + var q = GetBaseQuery(lastIndexed); + + var count = q.Count(); + var min = count > 0 ? q.Min(r => r.Id) : 0; + var max = count > 0 ? q.Max(r => r.Id) : 0; + + return (count, max, min); + } + + List getData(long start, long stop, DateTime lastIndexed) => + GetBaseQuery(lastIndexed) + .Where(r => r.Id >= start && r.Id <= stop) + .ToList(); + + List getIds(DateTime lastIndexed) + { + long start = 0; + + var result = new List(); + + while (true) + { + var id = GetBaseQuery(lastIndexed) + .AsNoTracking() + .Where(r => r.Id >= start) + .OrderBy(x => x.Id) + .Skip(BaseIndexer.QueryLimit) + .Select(x => x.Id) + .FirstOrDefault(); + + if (id != 0) + { + start = id; + result.Add(id); + } + else + { + break; + } + } + + return result; + } + + try + { + foreach (var data in Indexer.IndexAll(getCount, getIds, getData)) + { + Index(data); + } + } + catch (Exception e) + { + Logger.Error(e); + throw; + } + } + + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Search/FactoryIndexerInvoice.cs b/products/ASC.CRM/Server/Core/Search/FactoryIndexerInvoice.cs new file mode 100644 index 00000000000..0170f1848f7 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Search/FactoryIndexerInvoice.cs @@ -0,0 +1,135 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.EF; +using ASC.ElasticSearch; +using ASC.ElasticSearch.Core; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.Web.CRM.Core.Search +{ + [Scope] + public sealed class FactoryIndexerInvoice : FactoryIndexer + { + public FactoryIndexerInvoice( + IOptionsMonitor options, + TenantManager tenantManager, + SearchSettingsHelper searchSettingsHelper, + FactoryIndexer factoryIndexer, + BaseIndexer baseIndexer, + IServiceProvider serviceProvider, + DaoFactory daoFactory, + ICache cache) + : base(options, tenantManager, searchSettingsHelper, factoryIndexer, baseIndexer, serviceProvider, cache) + { + DaoFactory = daoFactory; + } + + public DaoFactory DaoFactory { get; } + + public override void IndexAll() + { + var entityDao = DaoFactory.GetInvoiceDao(); + + IQueryable GetBaseQuery(DateTime lastIndexed) => + entityDao.CrmDbContext.Invoices + .Where(r => r.LastModifedOn >= lastIndexed) + .Join(entityDao.CrmDbContext.Tenants, r => r.TenantId, r => r.Id, (f, t) => new { DbEntity = f, DbTenant = t }) + .Where(r => r.DbTenant.Status == ASC.Core.Tenants.TenantStatus.Active) + .Select(r => r.DbEntity); + + (int, int, int) getCount(DateTime lastIndexed) + { + var q = GetBaseQuery(lastIndexed); + + var count = q.Count(); + var min = count > 0 ? q.Min(r => r.Id) : 0; + var max = count > 0 ? q.Max(r => r.Id) : 0; + + return (count, max, min); + } + + List getData(long start, long stop, DateTime lastIndexed) => + GetBaseQuery(lastIndexed) + .Where(r => r.Id >= start && r.Id <= stop) + .ToList(); + + List getIds(DateTime lastIndexed) + { + long start = 0; + + var result = new List(); + + while (true) + { + var id = GetBaseQuery(lastIndexed) + .AsNoTracking() + .Where(r => r.Id >= start) + .OrderBy(x => x.Id) + .Skip(BaseIndexer.QueryLimit) + .Select(x => x.Id) + .FirstOrDefault(); + + if (id != 0) + { + start = id; + result.Add(id); + } + else + { + break; + } + } + + return result; + } + + try + { + foreach (var data in Indexer.IndexAll(getCount, getIds, getData)) + { + Index(data); + } + } + catch (Exception e) + { + Logger.Error(e); + throw; + } + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Search/FactoryIndexerTask.cs b/products/ASC.CRM/Server/Core/Search/FactoryIndexerTask.cs new file mode 100644 index 00000000000..f9ccb649810 --- /dev/null +++ b/products/ASC.CRM/Server/Core/Search/FactoryIndexerTask.cs @@ -0,0 +1,135 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.EF; +using ASC.ElasticSearch; +using ASC.ElasticSearch.Core; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace ASC.Web.CRM.Core.Search +{ + [Scope] + public sealed class FactoryIndexerTask : FactoryIndexer + { + public FactoryIndexerTask( + IOptionsMonitor options, + TenantManager tenantManager, + SearchSettingsHelper searchSettingsHelper, + FactoryIndexer factoryIndexer, + BaseIndexer baseIndexer, + IServiceProvider serviceProvider, + DaoFactory daoFactory, + ICache cache) + : base(options, tenantManager, searchSettingsHelper, factoryIndexer, baseIndexer, serviceProvider, cache) + { + DaoFactory = daoFactory; + } + + public DaoFactory DaoFactory { get; } + + public override void IndexAll() + { + var entityDao = DaoFactory.GetTaskDao(); + + IQueryable GetBaseQuery(DateTime lastIndexed) => + entityDao.CrmDbContext.Tasks + .Where(r => r.LastModifedOn >= lastIndexed) + .Join(entityDao.CrmDbContext.Tenants, r => r.TenantId, r => r.Id, (f, t) => new { DbEntity = f, DbTenant = t }) + .Where(r => r.DbTenant.Status == ASC.Core.Tenants.TenantStatus.Active) + .Select(r => r.DbEntity); + + (int, int, int) getCount(DateTime lastIndexed) + { + var q = GetBaseQuery(lastIndexed); + + var count = q.Count(); + var min = count > 0 ? q.Min(r => r.Id) : 0; + var max = count > 0 ? q.Max(r => r.Id) : 0; + + return (count, max, min); + } + + List getData(long start, long stop, DateTime lastIndexed) => + GetBaseQuery(lastIndexed) + .Where(r => r.Id >= start && r.Id <= stop) + .ToList(); + + List getIds(DateTime lastIndexed) + { + long start = 0; + + var result = new List(); + + while (true) + { + var id = GetBaseQuery(lastIndexed) + .AsNoTracking() + .Where(r => r.Id >= start) + .OrderBy(x => x.Id) + .Skip(BaseIndexer.QueryLimit) + .Select(x => x.Id) + .FirstOrDefault(); + + if (id != 0) + { + start = id; + result.Add(id); + } + else + { + break; + } + } + + return result; + } + + try + { + foreach (var data in Indexer.IndexAll(getCount, getIds, getData)) + { + Index(data); + } + } + catch (Exception e) + { + Logger.Error(e); + throw; + } + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Security/CRMSecurityObjectProvider.cs b/products/ASC.CRM/Server/Core/Security/CRMSecurityObjectProvider.cs new file mode 100644 index 00000000000..7afa441282f --- /dev/null +++ b/products/ASC.CRM/Server/Core/Security/CRMSecurityObjectProvider.cs @@ -0,0 +1,122 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System; +using System.Collections.Generic; + +using ASC.Common.Security; +using ASC.Common.Security.Authorizing; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +#endregion + +namespace ASC.CRM.Core +{ + public class CrmSecurityObjectProvider : ISecurityObjectProvider + { + public ISecurityObjectId InheritFrom(ISecurityObjectId objectId) + { + int contactId; + int entityId; + EntityType entityType; + + if (objectId is Task) + { + var task = (Task)objectId; + + contactId = task.ContactID; + entityId = task.EntityID; + entityType = task.EntityType; + } + else if (objectId is RelationshipEvent) + { + var eventObj = (RelationshipEvent)objectId; + + contactId = eventObj.ContactID; + entityId = eventObj.EntityID; + entityType = eventObj.EntityType; + + } + else + { + return null; + } + + if (entityId == 0 && contactId == 0) return null; + + if (entityId == 0) + return new Company + { + ID = contactId, + CompanyName = "fakeCompany" + }; + + // return _daoFactory.GetContactDao().GetByID(contactId); + + switch (entityType) + { + + case EntityType.Opportunity: + return new Deal + { + ID = entityId, + Title = "fakeDeal" + }; + // return _daoFactory.GetDealDao().GetByID(entityId); + case EntityType.Case: + return new Cases + { + ID = entityId, + Title = "fakeCases" + }; + // return _daoFactory.GetCasesDao().GetByID(entityId); + } + + return null; + } + + public bool InheritSupported + { + get { return true; } + } + + public bool ObjectRolesSupported + { + get { return false; } + } + + public IEnumerable GetObjectRoles(ISubject account, ISecurityObjectId objectId, SecurityCallContext callContext) + { + + // Constants.Everyone + // if (_daoFactory.GetManagerDao().GetAll(false).Contains(ASC.Core.CoreContext.UserManager.GetUsers(account.ID))) + // return new Action[] + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Security/CRMSecutiry.cs b/products/ASC.CRM/Server/Core/Security/CRMSecutiry.cs new file mode 100644 index 00000000000..487a326667e --- /dev/null +++ b/products/ASC.CRM/Server/Core/Security/CRMSecutiry.cs @@ -0,0 +1,857 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security; + +using ASC.Common; +using ASC.Common.Security; +using ASC.Common.Security.Authorizing; +using ASC.Core; +using ASC.Core.Users; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; +using ASC.Files.Core; +using ASC.Files.Core.Security; +using ASC.Web.Core; +using ASC.Web.Core.Users; +using ASC.Web.CRM.Classes; +using ASC.Web.CRM.Configuration; + +using Autofac; + +using Action = ASC.Common.Security.Authorizing.Action; +using Constants = ASC.Core.Users.Constants; +using SecurityContext = ASC.Core.SecurityContext; + +namespace ASC.CRM.Core +{ + [Scope] + public class CrmSecurity + { + private readonly CurrencyProvider _currencyProvider; + private readonly PermissionContext _permissionContext; + private readonly WebItemSecurity _webItemSecurity; + private readonly DaoFactory _daoFactory; + private readonly SecurityContext _securityContext; + private readonly AuthorizationManager _authorizationManager; + private readonly DisplayUserSettingsHelper _displayUserSettingsHelper; + private readonly UserManager _userManager; + + public readonly IAction _actionRead = new Action(new Guid("{6F05C382-8BCA-4469-9424-C807A98C40D7}"), "", true, false); + + public CrmSecurity(SecurityContext securityContext, + AuthorizationManager authorizationManager, + UserManager userManager, + DisplayUserSettingsHelper displayUserSettingsHelper, + DaoFactory daoFactory, + WebItemSecurity webItemSecurity, + PermissionContext permissionContext, + CurrencyProvider currencyProvider, + TenantManager tenantManager) + { + _securityContext = securityContext; + _authorizationManager = authorizationManager; + _userManager = userManager; + _displayUserSettingsHelper = displayUserSettingsHelper; + _daoFactory = daoFactory; + _webItemSecurity = webItemSecurity; + _permissionContext = permissionContext; + _currencyProvider = currencyProvider; + } + + private ISecurityObjectProvider GetCRMSecurityProvider() + { + return new CrmSecurityObjectProvider(); + } + + public bool IsPrivate(ISecurityObjectId entity) + { + return GetAccessSubjectTo(entity).Any(); + } + + public bool CanAccessTo(ISecurityObjectId entity) + { + return CanAccessTo(entity, _securityContext.CurrentAccount.ID); + } + + public bool CanAccessTo(ISecurityObjectId entity, Guid userId) + { + return IsAdministrator(userId) || _permissionContext.CheckPermissions(entity, GetCRMSecurityProvider(), _actionRead); + } + + public void MakePublic(ISecurityObjectId entity) + { + SetAccessTo(entity, new List()); + } + + public IEnumerable GetPrivateItems(Type objectType) + { + if (IsAdmin) return new List(); + + return GetPrivateItems(objectType, Guid.Empty, true); + } + + private IEnumerable GetPrivateItems(Type objectType, Guid userId, bool withoutUser) + { + var query = _authorizationManager + .GetAces(userId, _actionRead.ID) + .Where( + item => + !String.IsNullOrEmpty(item.ObjectId) && + item.ObjectId.StartsWith(objectType.FullName)) + .GroupBy(item => item.ObjectId, item => item.SubjectId); + + if (withoutUser) + { + if (userId != Guid.Empty) + query = query.Where(item => !item.Contains(userId)); + else + query = query.Where(item => !item.Contains(_securityContext.CurrentAccount.ID)); + + } + + return query.Select(item => Convert.ToInt32(item.Key.Split(new[] { '|' })[1])); + } + + public IEnumerable GetContactsIdByManager(Guid userId) + { + return GetPrivateItems(typeof(Company), userId, false) + .Union(GetPrivateItems(typeof(Person), userId, false)); + } + + public int GetPrivateItemsCount(Type objectType) + { + if (IsAdmin) return 0; + + return GetPrivateItems(objectType).Count(); + } + + private Dictionary GetAccessSubjectTo(ISecurityObjectId entity, EmployeeStatus employeeStatus) + { + var allAces = _authorizationManager.GetAcesWithInherits(Guid.Empty, _actionRead.ID, entity, + GetCRMSecurityProvider()) + .Where(item => item.SubjectId != Constants.GroupEveryone.ID); + + var result = new Dictionary(); + + foreach (var azRecord in allAces) + { + if (!result.ContainsKey(azRecord.SubjectId)) + { + var userInfo = _userManager.GetUsers(azRecord.SubjectId); + var displayName = employeeStatus == EmployeeStatus.All || userInfo.Status == employeeStatus + ? userInfo.DisplayUserName(_displayUserSettingsHelper) + : Constants.LostUser.DisplayUserName(_displayUserSettingsHelper); + result.Add(azRecord.SubjectId, displayName); + } + } + return result; + } + + public Dictionary GetAccessSubjectTo(ISecurityObjectId entity) + { + return GetAccessSubjectTo(entity, EmployeeStatus.All); + } + + public List GetAccessSubjectGuidsTo(ISecurityObjectId entity) + { + var allAces = _authorizationManager.GetAcesWithInherits(Guid.Empty, _actionRead.ID, entity, + GetCRMSecurityProvider()) + .Where(item => item.SubjectId != Constants.GroupEveryone.ID); + + var result = new List(); + + foreach (var azRecord in allAces) + { + if (!result.Contains(azRecord.SubjectId)) + result.Add(azRecord.SubjectId); + } + return result; + } + + public void SetAccessTo(ISecurityObjectId entity, List subjectID) + { + + if (subjectID.Count == 0) + { + _authorizationManager.RemoveAllAces(entity); + return; + } + + var aces = _authorizationManager.GetAcesWithInherits(Guid.Empty, _actionRead.ID, entity, GetCRMSecurityProvider()); + foreach (var r in aces) + { + if (!subjectID.Contains(r.SubjectId) && (r.SubjectId != Constants.GroupEveryone.ID || r.Reaction != AceType.Allow)) + { + _authorizationManager.RemoveAce(r); + } + } + + var oldSubjects = aces.Select(r => r.SubjectId).ToList(); + + foreach (var s in subjectID) + { + if (!oldSubjects.Contains(s)) + { + _authorizationManager.AddAce(new AzRecord(s, _actionRead.ID, AceType.Allow, entity)); + } + } + + _authorizationManager.AddAce(new AzRecord(Constants.GroupEveryone.ID, _actionRead.ID, AceType.Deny, entity)); + } + + public void SetAccessTo(File file) + { + if (IsAdmin || file.CreateBy == _securityContext.CurrentAccount.ID || file.ModifiedBy == _securityContext.CurrentAccount.ID) + file.Access = FileShare.None; + else + file.Access = FileShare.Read; + } + + public void SetAccessTo(Deal deal, List subjectID) + { + if (IsAdmin || deal.CreateBy == _securityContext.CurrentAccount.ID) + { + SetAccessTo((ISecurityObjectId)deal, subjectID); + } + } + + public void SetAccessTo(Cases cases, List subjectID) + { + if (IsAdmin || cases.CreateBy == _securityContext.CurrentAccount.ID) + { + SetAccessTo((ISecurityObjectId)cases, subjectID); + } + } + + public bool CanAccessTo(RelationshipEvent relationshipEvent) + { + return CanAccessTo(relationshipEvent, _securityContext.CurrentAccount.ID); + } + + public bool CanAccessTo(RelationshipEvent relationshipEvent, Guid userId) + { + if (IsAdministrator(userId)) + return true; + + if (relationshipEvent.ContactID > 0) + { + var contactObj = _daoFactory.GetContactDao().GetByID(relationshipEvent.ContactID); + if (contactObj != null) return CanAccessTo(contactObj, userId); + } + + if (relationshipEvent.EntityType == EntityType.Case) + { + var caseObj = _daoFactory.GetCasesDao().GetByID(relationshipEvent.EntityID); + if (caseObj != null) return CanAccessTo(caseObj, userId); + } + + if (relationshipEvent.EntityType == EntityType.Opportunity) + { + var dealObj = _daoFactory.GetDealDao().GetByID(relationshipEvent.EntityID); + if (dealObj != null) return CanAccessTo(dealObj, userId); + } + + return false; + } + + public bool CanAccessTo(Contact contact) + { + return CanAccessTo(contact, _securityContext.CurrentAccount.ID); + } + + public bool CanAccessTo(Contact contact, Guid userId) + { + return contact.ShareType == ShareType.Read || + contact.ShareType == ShareType.ReadWrite || + IsAdministrator(userId) || + GetAccessSubjectTo(contact).ContainsKey(userId); + } + + public bool CanAccessTo(int contactID, EntityType entityType, ShareType? shareType, int companyID) + { + if (shareType.HasValue && (shareType.Value == ShareType.Read || shareType.Value == ShareType.ReadWrite) || IsAdmin) + { + return true; + } + if (entityType == EntityType.Company) + { + var fakeContact = new Company() { ID = contactID }; + return GetAccessSubjectTo(fakeContact).ContainsKey(_securityContext.CurrentAccount.ID); + } + else if (entityType == EntityType.Person) + { + var fakeContact = new Person() { ID = contactID, CompanyID = companyID }; + return GetAccessSubjectTo(fakeContact).ContainsKey(_securityContext.CurrentAccount.ID); + } + return false; + } + + public bool CanAccessTo(Task task) + { + return CanAccessTo(task, _securityContext.CurrentAccount.ID); + } + + public bool CanAccessTo(Task task, Guid userId) + { + if (IsAdministrator(userId) || task.ResponsibleID == userId || + (task.ContactID == 0 && task.EntityID == 0) || task.CreateBy == userId) + return true; + + if (task.ContactID > 0) + { + var contactObj = _daoFactory.GetContactDao().GetByID(task.ContactID); + if (contactObj != null) return CanAccessTo(contactObj, userId); + } + + if (task.EntityType == EntityType.Case) + { + var caseObj = _daoFactory.GetCasesDao().GetByID(task.EntityID); + if (caseObj != null) return CanAccessTo(caseObj, userId); + } + + if (task.EntityType == EntityType.Opportunity) + { + var dealObj = _daoFactory.GetDealDao().GetByID(task.EntityID); + if (dealObj != null) return CanAccessTo(dealObj, userId); + } + + return false; + + } + + public bool CanAccessTo(Invoice invoice) + { + return CanAccessTo(invoice, _securityContext.CurrentAccount.ID); + } + + public bool CanAccessTo(Invoice invoice, Guid userId) + { + if (IsAdministrator(userId) || invoice.CreateBy == userId) return true; + + if (invoice.ContactID > 0) + return CanAccessTo(_daoFactory.GetContactDao().GetByID(invoice.ContactID), userId); + + if (invoice.EntityType == EntityType.Opportunity) + return CanAccessTo(_daoFactory.GetDealDao().GetByID(invoice.EntityID), userId); + + return false; + + } + + public bool CanAccessTo(InvoiceTax invoiceTax) + { + return CanAccessTo(invoiceTax, _securityContext.CurrentAccount.ID); + } + + public bool CanAccessTo(InvoiceTax invoiceTax, Guid userId) + { + if (IsAdministrator(userId) || invoiceTax.CreateBy == userId) return true; + + return false; + } + + public bool CanEdit(File file) + { + if (!(IsAdmin || file.CreateBy == _securityContext.CurrentAccount.ID || file.ModifiedBy == _securityContext.CurrentAccount.ID)) + return false; + + if ((file.FileStatus & FileStatus.IsEditing) == FileStatus.IsEditing) + return false; + + return true; + } + + public bool CanEdit(Deal deal) + { + return (IsAdmin || deal.ResponsibleID == _securityContext.CurrentAccount.ID || deal.CreateBy == _securityContext.CurrentAccount.ID || + !IsPrivate(deal) || GetAccessSubjectTo(deal).ContainsKey(_securityContext.CurrentAccount.ID)); + } + + public bool CanEdit(RelationshipEvent relationshipEvent) + { + var userId = _securityContext.CurrentAccount.ID; + + if (IsAdmin) return true; + + + if (relationshipEvent.ContactID > 0) + { + var contactObj = _daoFactory.GetContactDao().GetByID(relationshipEvent.ContactID); + if (contactObj != null) + { + if (CanEdit(contactObj)) return true; + + return CanAccessTo(contactObj, userId) && relationshipEvent.CreateBy == userId; + } + } + + if (relationshipEvent.EntityType == EntityType.Case) + { + var caseObj = _daoFactory.GetCasesDao().GetByID(relationshipEvent.EntityID); + if (caseObj != null) + { + if (CanEdit(caseObj)) return true; + + return CanAccessTo(caseObj, userId) && relationshipEvent.CreateBy == userId; + } + } + + if (relationshipEvent.EntityType == EntityType.Opportunity) + { + var dealObj = _daoFactory.GetDealDao().GetByID(relationshipEvent.EntityID); + if (dealObj != null) + { + if (CanEdit(dealObj)) return true; + + return CanAccessTo(dealObj, userId) && relationshipEvent.CreateBy == userId; + } + } + + return false; + } + + public bool CanEdit(Contact contact) + { + return contact.ShareType == ShareType.ReadWrite || IsAdmin || GetAccessSubjectTo(contact).ContainsKey(_securityContext.CurrentAccount.ID); + } + + public bool CanEdit(Task task) + { + return (IsAdmin || task.ResponsibleID == _securityContext.CurrentAccount.ID || task.CreateBy == _securityContext.CurrentAccount.ID); + } + + public bool CanEdit(Cases cases) + { + return (IsAdmin || cases.CreateBy == _securityContext.CurrentAccount.ID || + !IsPrivate(cases) || GetAccessSubjectTo(cases).ContainsKey(_securityContext.CurrentAccount.ID)); + } + + public bool CanEdit(Invoice invoice) + { + return (IsAdmin || invoice.CreateBy == _securityContext.CurrentAccount.ID) && invoice.Status == InvoiceStatus.Draft; + } + + public bool CanEdit(InvoiceTax invoiceTax) + { + return IsAdmin; + } + + public bool CanEdit(InvoiceItem invoiceItem) + { + return IsAdmin; + } + + + public bool CanDelete(Contact contact) + { + return CanEdit(contact) && _daoFactory.GetContactDao().CanDelete(contact.ID); + + } + + public bool CanDelete(Invoice invoice) + { + return (IsAdmin || invoice.CreateBy == _securityContext.CurrentAccount.ID); + } + + public bool CanDelete(InvoiceItem invoiceItem) + { + return CanEdit(invoiceItem) && _daoFactory.GetInvoiceItemDao().CanDelete(invoiceItem.ID); + } + + public bool CanDelete(InvoiceTax invoiceTax) + { + return CanEdit(invoiceTax) && _daoFactory.GetInvoiceTaxDao().CanDelete(invoiceTax.ID); + + } + + public bool CanDelete(Deal deal) + { + return CanEdit(deal); + } + + public bool CanDelete(Cases cases) + { + return CanEdit(cases); + } + + public bool CanDelete(RelationshipEvent relationshipEvent) + { + return CanEdit(relationshipEvent); + } + + public bool IsPrivate(Contact contact) + { + return contact.ShareType == ShareType.None; + } + + public void DemandAccessTo(File file) + { + // if (!CanAccessTo((File)file)) CreateSecurityException(); + } + + public void DemandAccessTo(Deal deal) + { + if (!CanAccessTo(deal)) throw CreateSecurityException(); + } + + public void DemandAccessTo(RelationshipEvent relationshipEvent) + { + if (!CanAccessTo(relationshipEvent)) throw CreateSecurityException(); + } + + public void DemandAccessTo(Contact contact) + { + if (!CanAccessTo(contact)) throw CreateSecurityException(); + } + + public void DemandAccessTo(Task task) + { + if (!CanAccessTo(task)) throw CreateSecurityException(); + } + + public void DemandAccessTo(Cases cases) + { + if (!CanAccessTo(cases)) throw CreateSecurityException(); + } + + public void DemandAccessTo(Invoice invoice) + { + if (!CanAccessTo(invoice)) throw CreateSecurityException(); + } + + public void DemandAccessTo(InvoiceTax invoiceTax) + { + if (!CanAccessTo(invoiceTax)) throw CreateSecurityException(); + } + + public void DemandEdit(File file) + { + if (!CanEdit(file)) throw CreateSecurityException(); + } + + public void DemandEdit(Deal deal) + { + if (!CanEdit(deal)) throw CreateSecurityException(); + } + + public void DemandEdit(RelationshipEvent relationshipEvent) + { + if (!CanEdit(relationshipEvent)) throw CreateSecurityException(); + } + + public void DemandEdit(Contact contact) + { + if (!CanEdit(contact)) throw CreateSecurityException(); + } + + public void DemandEdit(Task task) + { + if (!CanEdit(task)) throw CreateSecurityException(); + } + + public void DemandEdit(Cases cases) + { + if (!CanEdit(cases)) throw CreateSecurityException(); + } + + public void DemandEdit(Invoice invoice) + { + if (!CanEdit(invoice)) throw CreateSecurityException(); + } + + public void DemandEdit(InvoiceTax invoiceTax) + { + if (!CanEdit(invoiceTax)) throw CreateSecurityException(); + } + + public void DemandEdit(InvoiceItem invoiceItem) + { + if (!CanEdit(invoiceItem)) throw CreateSecurityException(); + } + + public void DemandDelete(File file) + { + if (!CanEdit(file)) throw CreateSecurityException(); + } + + + public void DemandDelete(Contact contact) + { + if (!CanDelete(contact)) throw CreateSecurityException(); + } + + public void DemandDelete(Invoice invoice) + { + if (!CanDelete(invoice)) throw CreateSecurityException(); + } + + public void DemandDelete(Deal deal) + { + if (!CanDelete(deal)) throw CreateSecurityException(); + } + + public void DemandDelete(Cases cases) + { + if (!CanDelete(cases)) throw CreateSecurityException(); + } + + public void DemandDelete(InvoiceItem invoiceItem) + { + if (!CanDelete(invoiceItem)) throw CreateSecurityException(); + } + + public void DemandDelete(InvoiceTax invoiceTax) + { + if (!CanDelete(invoiceTax)) throw CreateSecurityException(); + } + + public void DemandDelete(RelationshipEvent relationshipEvent) + { + if (!CanDelete(relationshipEvent)) throw CreateSecurityException(); + } + + public void DemandCreateOrUpdate(RelationshipEvent relationshipEvent) + { + if (String.IsNullOrEmpty(relationshipEvent.Content) || relationshipEvent.CategoryID == 0 || (relationshipEvent.ContactID == 0 && relationshipEvent.EntityID == 0)) + throw new ArgumentException(); + + if (relationshipEvent.EntityID > 0 && relationshipEvent.EntityType != EntityType.Opportunity && relationshipEvent.EntityType != EntityType.Case) + throw new ArgumentException(); + + if (relationshipEvent.Content.Length > Global.MaxHistoryEventCharacters) + throw new ArgumentException(CRMErrorsResource.HistoryEventDataTooLong); + + if (!CanAccessTo(relationshipEvent)) throw CreateSecurityException(); + } + + public void DemandCreateOrUpdate(Deal deal) + { + if (string.IsNullOrEmpty(deal.Title) || deal.ResponsibleID == Guid.Empty || + deal.DealMilestoneID <= 0 || string.IsNullOrEmpty(deal.BidCurrency)) + throw new ArgumentException(); + + var listItem = _daoFactory.GetDealMilestoneDao().GetByID(deal.DealMilestoneID); + if (listItem == null) throw new ArgumentException(CRMErrorsResource.DealMilestoneNotFound); + + if (deal.ContactID != 0) + { + var contact = _daoFactory.GetContactDao().GetByID(deal.ContactID); + if (contact == null) throw new ArgumentException(); + + if (!CanAccessTo(contact)) throw new SecurityException(CRMErrorsResource.AccessDenied); + } + if (string.IsNullOrEmpty(deal.BidCurrency)) + { + throw new ArgumentException(); + } + else + { + if (_currencyProvider.Get(deal.BidCurrency.ToUpper()) == null) + { + throw new ArgumentException(); + } + } + } + + public void DemandCreateOrUpdate(InvoiceLine line, Invoice targetInvoice) + { + if (line.InvoiceID <= 0 || line.InvoiceItemID <= 0 || + line.Quantity < 0 || line.Price < 0 || line.Discount < 0 || line.Discount > 100 || + line.InvoiceTax1ID < 0 || line.InvoiceTax2ID < 0) + throw new ArgumentException(); + + if (targetInvoice == null || targetInvoice.ID != line.InvoiceID) throw new ArgumentException(); + if (!CanEdit(targetInvoice)) throw CreateSecurityException(); + + if (!_daoFactory.GetInvoiceItemDao().IsExist(line.InvoiceItemID)) + throw new ArgumentException(); + + if (line.InvoiceTax1ID > 0 && !_daoFactory.GetInvoiceTaxDao().IsExist(line.InvoiceTax1ID)) + throw new ArgumentException(); + + if (line.InvoiceTax2ID > 0 && !_daoFactory.GetInvoiceTaxDao().IsExist(line.InvoiceTax2ID)) + throw new ArgumentException(); + + } + + public void DemandCreateOrUpdate(Invoice invoice) + { + if (invoice.IssueDate == DateTime.MinValue || + invoice.ContactID <= 0 || + invoice.DueDate == DateTime.MinValue || + String.IsNullOrEmpty(invoice.Currency) || + invoice.ExchangeRate <= 0 || + String.IsNullOrEmpty(invoice.Terms)) + throw new ArgumentException(); + + var contact = _daoFactory.GetContactDao().GetByID(invoice.ContactID); + if (contact == null) throw new ArgumentException(); + if (!CanAccessTo(contact)) throw new SecurityException(CRMErrorsResource.AccessDenied); + + if (invoice.ConsigneeID != 0 && invoice.ConsigneeID != invoice.ContactID) + { + var consignee = _daoFactory.GetContactDao().GetByID(invoice.ConsigneeID); + if (consignee == null) throw new ArgumentException(); + if (!CanAccessTo(consignee)) throw new SecurityException(CRMErrorsResource.AccessDenied); + } + + if (invoice.EntityID != 0) + { + var deal = _daoFactory.GetDealDao().GetByID(invoice.EntityID); + if (deal == null) throw new ArgumentException(); + if (!CanAccessTo(deal)) throw new SecurityException(CRMErrorsResource.AccessDenied); + + var dealMembers = _daoFactory.GetDealDao().GetMembers(invoice.EntityID); + if (!dealMembers.Contains(invoice.ContactID)) + throw new ArgumentException(); + } + + if (_currencyProvider.Get(invoice.Currency.ToUpper()) == null) + { + throw new ArgumentException(); + } + } + + public Exception CreateSecurityException() + { + throw new SecurityException(CRMErrorsResource.AccessDenied); + } + + public bool IsAdmin + { + get + { + return IsAdministrator(_securityContext.CurrentAccount.ID); + } + } + + public bool IsAdministrator(Guid userId) + { + return _webItemSecurity.IsProductAdministrator(ProductEntryPoint.ID, userId); + } + + public IEnumerable FilterRead(IEnumerable tasks) + { + if (tasks == null || !tasks.Any()) return new List(); + + if (IsAdmin) return tasks; + + var result = tasks.ToList(); + var contactIDs = result + .Where(x => x.ResponsibleID != _securityContext.CurrentAccount.ID) + .Select(x => x.ContactID) + .Distinct() + .ToList(); + + if (contactIDs.Any()) + { + contactIDs = _daoFactory.GetContactDao() + .GetContacts(contactIDs.ToArray()) + .Select(x => x.ID) + .ToList(); + + result = result.Where(x => x.ContactID == 0 || contactIDs.Contains(x.ContactID) || x.ResponsibleID == _securityContext.CurrentAccount.ID).ToList(); + + if (!result.Any()) return Enumerable.Empty(); + } + + var casesIds = result.Where(x => x.EntityType == EntityType.Case && x.ResponsibleID != _securityContext.CurrentAccount.ID) + .Select(x => x.EntityID) + .Distinct() + .ToList(); + + if (casesIds.Any()) + { + casesIds = _daoFactory.GetCasesDao() + .GetCases(casesIds.ToArray()) + .Select(x => x.ID) + .ToList(); + + result = result.Where(x => x.EntityID == 0 || casesIds.Contains(x.EntityID) || x.ResponsibleID == _securityContext.CurrentAccount.ID).ToList(); + + if (!result.Any()) return Enumerable.Empty(); + } + + var dealsIds = result.Where(x => x.EntityType == EntityType.Opportunity && x.ResponsibleID != _securityContext.CurrentAccount.ID) + .Select(x => x.EntityID) + .Distinct() + .ToList(); + + if (dealsIds.Any()) + { + dealsIds = _daoFactory.GetDealDao() + .GetDeals(dealsIds.ToArray()) + .Select(x => x.ID) + .ToList(); + + result = result + .Where(x => x.EntityID == 0 || dealsIds.Contains(x.EntityID) || x.ResponsibleID == _securityContext.CurrentAccount.ID) + .ToList(); + + if (!result.Any()) return Enumerable.Empty(); + } + + return result; + + + } + + public IEnumerable FilterRead(IEnumerable invoices) + { + if (invoices == null || !invoices.Any()) return new List(); + + if (IsAdmin) return invoices; + + var result = invoices.ToList(); + var contactIDs = result.Select(x => x.ContactID).Distinct().ToList(); + + if (contactIDs.Any()) + { + contactIDs = _daoFactory.GetContactDao() + .GetContacts(contactIDs.ToArray()) + .Select(x => x.ID) + .ToList(); + + result = result.Where(x => x.ContactID == 0 || contactIDs.Contains(x.ContactID)).ToList(); + + if (!result.Any()) return Enumerable.Empty(); + } + + return result; + } + + public bool CanGoToFeed(Task task) + { + return IsAdmin || task.ResponsibleID == _securityContext.CurrentAccount.ID || task.CreateBy == _securityContext.CurrentAccount.ID; + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Core/Security/FileSecurityProvider.cs b/products/ASC.CRM/Server/Core/Security/FileSecurityProvider.cs new file mode 100644 index 00000000000..1e475e7908d --- /dev/null +++ b/products/ASC.CRM/Server/Core/Security/FileSecurityProvider.cs @@ -0,0 +1,154 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.CRM.Core.Dao; +using ASC.Files.Core; +using ASC.Files.Core.Security; +using ASC.Web.Files.Api; + +using Autofac; + +namespace ASC.CRM.Core +{ + public class FileSecurity : IFileSecurity + { + private readonly DaoFactory _daoFactory; + private readonly FilesIntegration _filesIntegration; + private readonly CrmSecurity _crmSecurity; + + public FileSecurity(FilesIntegration filesIntegration, + CrmSecurity crmSecurity, + DaoFactory daoFactory) + { + _filesIntegration = filesIntegration; + _crmSecurity = crmSecurity; + _daoFactory = daoFactory; + } + + + public bool CanCreate(FileEntry entry, Guid userId) + { + return true; + } + + public bool CanComment(FileEntry entry, Guid userId) + { + return CanEdit(entry, userId); + } + + public bool CanFillForms(FileEntry entry, Guid userId) + { + return CanEdit(entry, userId); + } + + public bool CanReview(FileEntry entry, Guid userId) + { + return CanEdit(entry, userId); + } + + public bool CanDelete(FileEntry entry, Guid userId) + { + return CanEdit(entry, userId); + } + + public bool CanEdit(FileEntry entry, Guid userId) + { + return + CanRead(entry, userId) && + entry.CreateBy == userId || entry.ModifiedBy == userId || _crmSecurity.IsAdministrator(userId); + } + + public bool CanRead(FileEntry entry, Guid userId) + { + if (entry.FileEntryType == FileEntryType.Folder) return false; + + var invoice = _daoFactory.GetInvoiceDao().GetByFileId(Convert.ToInt32(entry.ID)); + if (invoice != null) + return _crmSecurity.CanAccessTo(invoice, userId); + + var reportFile = _daoFactory.GetReportDao().GetFile(Convert.ToInt32(entry.ID), userId); + + if (reportFile != null) + return true; + + var tagDao = _filesIntegration.DaoFactory.GetTagDao(); + + var eventIds = tagDao.GetTags(entry.ID, FileEntryType.File, TagType.System) + .Where(x => x.TagName.StartsWith("RelationshipEvent_")) + .Select(x => Convert.ToInt32(x.TagName.Split(new[] { '_' })[1])) + .ToList(); + + if (!eventIds.Any()) return false; + + var eventItem = _daoFactory.GetRelationshipEventDao().GetByID(eventIds.First()); + + return _crmSecurity.CanAccessTo(eventItem, userId); + + } + + public IEnumerable WhoCanRead(FileEntry entry) + { + throw new NotImplementedException(); + } + + public bool CanCustomFilterEdit(FileEntry entry, Guid userId) + { + return CanEdit(entry, userId); + } + } + + [Scope] + public class FileSecurityProvider : IFileSecurityProvider + { + public FileSecurityProvider(FilesIntegration filesIntegration, + CrmSecurity crmSecurity, + DaoFactory daoFactory) + { + FilesIntegration = filesIntegration; + CRMSecurity = crmSecurity; + DaoFactory = daoFactory; + } + + public DaoFactory DaoFactory { get; } + public FilesIntegration FilesIntegration { get; } + public CrmSecurity CRMSecurity { get; } + + public IFileSecurity GetFileSecurity(string data) + { + return new FileSecurity(FilesIntegration, CRMSecurity, DaoFactory); + } + + public Dictionary GetFileSecurity(Dictionary data) + { + return data.ToDictionary, object, IFileSecurity>(d => d.Key, d => new FileSecurity(FilesIntegration, CRMSecurity, DaoFactory)); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/DocStore/default/ONLYOFFICE Sales funnel sample report.xlsx b/products/ASC.CRM/Server/DocStore/default/ONLYOFFICE Sales funnel sample report.xlsx new file mode 100644 index 00000000000..7be4510326e Binary files /dev/null and b/products/ASC.CRM/Server/DocStore/default/ONLYOFFICE Sales funnel sample report.xlsx differ diff --git a/products/ASC.CRM/Server/DocStore/default/ONLYOFFICE Summary for the moment sample report.xlsx b/products/ASC.CRM/Server/DocStore/default/ONLYOFFICE Summary for the moment sample report.xlsx new file mode 100644 index 00000000000..3c32291b635 Binary files /dev/null and b/products/ASC.CRM/Server/DocStore/default/ONLYOFFICE Summary for the moment sample report.xlsx differ diff --git a/products/ASC.CRM/Server/DocStore/default/ONLYOFFICE Summary for the period sample report.xlsx b/products/ASC.CRM/Server/DocStore/default/ONLYOFFICE Summary for the period sample report.xlsx new file mode 100644 index 00000000000..aff2e3d5a2f Binary files /dev/null and b/products/ASC.CRM/Server/DocStore/default/ONLYOFFICE Summary for the period sample report.xlsx differ diff --git a/products/ASC.CRM/Server/DocStore/ru-RU/ONLYOFFICE Sales funnel sample report.xlsx b/products/ASC.CRM/Server/DocStore/ru-RU/ONLYOFFICE Sales funnel sample report.xlsx new file mode 100644 index 00000000000..a4cf72d4df9 Binary files /dev/null and b/products/ASC.CRM/Server/DocStore/ru-RU/ONLYOFFICE Sales funnel sample report.xlsx differ diff --git a/products/ASC.CRM/Server/DocStore/ru-RU/ONLYOFFICE Summary for the moment sample report.xlsx b/products/ASC.CRM/Server/DocStore/ru-RU/ONLYOFFICE Summary for the moment sample report.xlsx new file mode 100644 index 00000000000..78a03b1d794 Binary files /dev/null and b/products/ASC.CRM/Server/DocStore/ru-RU/ONLYOFFICE Summary for the moment sample report.xlsx differ diff --git a/products/ASC.CRM/Server/DocStore/ru-RU/ONLYOFFICE Summary for the period sample report.xlsx b/products/ASC.CRM/Server/DocStore/ru-RU/ONLYOFFICE Summary for the period sample report.xlsx new file mode 100644 index 00000000000..0a8a248fc67 Binary files /dev/null and b/products/ASC.CRM/Server/DocStore/ru-RU/ONLYOFFICE Summary for the period sample report.xlsx differ diff --git a/products/ASC.CRM/Server/InvoiceTemplates/template.docx b/products/ASC.CRM/Server/InvoiceTemplates/template.docx new file mode 100644 index 00000000000..d3a35668b76 Binary files /dev/null and b/products/ASC.CRM/Server/InvoiceTemplates/template.docx differ diff --git a/products/ASC.CRM/Server/Mapping/TypeConverter/CasesDtoTypeConverter.cs b/products/ASC.CRM/Server/Mapping/TypeConverter/CasesDtoTypeConverter.cs new file mode 100644 index 00000000000..78ca48203b9 --- /dev/null +++ b/products/ASC.CRM/Server/Mapping/TypeConverter/CasesDtoTypeConverter.cs @@ -0,0 +1,111 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Api.Core; +using ASC.Common; +using ASC.Core.Users; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.Web.Api.Models; + +using AutoMapper; + +namespace ASC.CRM.Mapping +{ + [Scope] + public class CasesDtoTypeConverter : ITypeConverter + { + private readonly DaoFactory _daoFactory; + private readonly CrmSecurity _CRMSecurity; + private readonly ApiDateTimeHelper _apiDateTimeHelper; + private readonly EmployeeWraperHelper _employeeWraperHelper; + + public CasesDtoTypeConverter(ApiDateTimeHelper apiDateTimeHelper, + EmployeeWraperHelper employeeWraperHelper, + CrmSecurity crmSecurity, + DaoFactory daoFactory) + { + _apiDateTimeHelper = apiDateTimeHelper; + _employeeWraperHelper = employeeWraperHelper; + _CRMSecurity = crmSecurity; + _daoFactory = daoFactory; + } + + public CasesDto Convert(Cases source, CasesDto destination, ResolutionContext context) + { + if (destination != null) + throw new NotImplementedException(); + + var result = new CasesDto(); + + result.Title = source.Title; + result.IsClosed = source.IsClosed; + result.IsPrivate = _CRMSecurity.IsPrivate(source); + result.Created = _apiDateTimeHelper.Get(source.CreateOn); + result.CreateBy = _employeeWraperHelper.Get(source.CreateBy); + result.CanEdit = _CRMSecurity.CanEdit(source); + + if (result.IsPrivate) + { + result.AccessList = _CRMSecurity.GetAccessSubjectTo(source) + .SkipWhile(item => item.Key == Constants.GroupEveryone.ID) + .Select(item => _employeeWraperHelper.Get(item.Key)); + } + + result.CustomFields = _daoFactory + .GetCustomFieldDao() + .GetEnityFields(EntityType.Case, source.ID, false) + .ConvertAll(item => context.Mapper.Map(item)); + + result.Members = new List(); + + var memberIDs = _daoFactory.GetCasesDao().GetMembers(source.ID); + var membersList = _daoFactory.GetContactDao().GetContacts(memberIDs); + + var membersDtoList = new List(); + + foreach (var member in membersList) + { + if (member == null) continue; + + membersDtoList.Add(context.Mapper.Map(member)); + } + + result.Members = membersDtoList; + + return destination; + } + } +} + + diff --git a/products/ASC.CRM/Server/Mapping/TypeConverter/ContactDtoTypeConverter.cs b/products/ASC.CRM/Server/Mapping/TypeConverter/ContactDtoTypeConverter.cs new file mode 100644 index 00000000000..f1a6ef22622 --- /dev/null +++ b/products/ASC.CRM/Server/Mapping/TypeConverter/ContactDtoTypeConverter.cs @@ -0,0 +1,453 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Api.Core; +using ASC.Common; +using ASC.Common.Web; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.Web.Api.Models; +using ASC.Web.CRM; +using ASC.Web.CRM.Classes; + +using AutoMapper; + +using Contact = ASC.CRM.Core.Entities.Contact; + +namespace ASC.CRM.Mapping +{ + [Scope] + public class ContactDtoTypeConverter : ITypeConverter, + ITypeConverter, List>, + ITypeConverter, + ITypeConverter, + ITypeConverter, + ITypeConverter, + ITypeConverter + + + { + private readonly DaoFactory _daoFactory; + private readonly CrmSecurity _crmSecurity; + private readonly ApiDateTimeHelper _apiDateTimeHelper; + private readonly EmployeeWraperHelper _employeeDtoHelper; + private readonly CurrencyProvider _currencyProvider; + private readonly PathProvider _pathProvider; + + public ContactDtoTypeConverter(ApiDateTimeHelper apiDateTimeHelper, + EmployeeWraperHelper employeeWraperHelper, + CrmSecurity crmSecurity, + CurrencyProvider currencyProvider, + PathProvider pathProvider, + DaoFactory daoFactory) + { + _apiDateTimeHelper = apiDateTimeHelper; + _employeeDtoHelper = employeeWraperHelper; + _crmSecurity = crmSecurity; + _currencyProvider = currencyProvider; + _pathProvider = pathProvider; + _daoFactory = daoFactory; + } + + + + public ContactDto Convert(Contact source, ContactDto destination, ResolutionContext context) + { + ContactDto result; + + var person = source as Person; + + if (person != null) + { + var peopleDto = (PersonDto)context.Mapper.Map(source); + + peopleDto.FirstName = person.FirstName; + peopleDto.LastName = person.LastName; + peopleDto.Title = person.JobTitle; + + if (person.CompanyID > 0) + { + peopleDto.Company = context.Mapper.Map(_daoFactory.GetContactDao().GetByID(person.CompanyID)); + } + + result = peopleDto; + } + else + { + var company = source as Company; + + if (company != null) + { + result = (CompanyDto)context.Mapper.Map(source); + ((CompanyDto)result).CompanyName = company.CompanyName; + ((CompanyDto)result).PersonsCount = _daoFactory.GetContactDao().GetMembersCount(result.Id); + } + else throw new ArgumentException(); + } + + if (source.StatusID > 0) + { + var listItem = _daoFactory.GetListItemDao().GetByID(source.StatusID); + if (listItem == null) throw new ItemNotFoundException(); + + result.ContactStatus = new ContactStatusBaseDto(listItem); + } + + result.TaskCount = _daoFactory.GetTaskDao().GetTasksCount(source.ID); + result.HaveLateTasks = _daoFactory.GetTaskDao().HaveLateTask(source.ID); + + var contactInfos = new List(); + var addresses = new List
    (); + + var data = _daoFactory.GetContactInfoDao().GetList(source.ID, null, null, null); + + foreach (var contactInfo in data) + { + if (contactInfo.InfoType == ContactInfoType.Address) + { + addresses.Add(new Address(contactInfo)); + } + else + { + contactInfos.Add(context.Mapper.Map(contactInfo)); + } + } + + result.Addresses = addresses; + result.CommonData = contactInfos; + + result.CustomFields = _daoFactory.GetCustomFieldDao() + .GetEnityFields(source is Person ? EntityType.Person : EntityType.Company, source.ID, false) + .ConvertAll(item => context.Mapper.Map(item)); + return result; + } + + public ContactBaseDto Convert(Contact source, ContactBaseDto destination, ResolutionContext context) + { + ContactBaseDto result; + + if (source is Company) + result = new CompanyDto(); + else + result = new PersonDto(); + + result.Id = source.ID; + result.DisplayName = source.GetTitle(); + result.IsPrivate = _crmSecurity.IsPrivate(source); + result.IsShared = source.ShareType == ShareType.ReadWrite || source.ShareType == ShareType.Read; + result.ShareType = source.ShareType; + + result.Currency = !String.IsNullOrEmpty(source.Currency) ? + context.Mapper.Map(_currencyProvider.Get(source.Currency)) : null; + + + result.SmallFotoUrl = String.Format("{0}HttpHandlers/filehandler.ashx?action=contactphotoulr&cid={1}&isc={2}&ps=1", _pathProvider.BaseAbsolutePath, source.ID, source is Company).ToLower(); + result.MediumFotoUrl = String.Format("{0}HttpHandlers/filehandler.ashx?action=contactphotoulr&cid={1}&isc={2}&ps=2", _pathProvider.BaseAbsolutePath, source.ID, source is Company).ToLower(); + result.IsCompany = source is Company; + result.CanEdit = _crmSecurity.CanEdit(source); + result.CanDelete = _crmSecurity.CanDelete(source); + + if (result.IsPrivate) + { + result.AccessList = _crmSecurity.GetAccessSubjectTo(source) + .Select(item => _employeeDtoHelper.Get(item.Key)); + } + + return result; + } + + public PersonDto Convert(Person source, PersonDto destination, ResolutionContext context) + { + var result = (PersonDto)context.Mapper.Map(source); + + result.CreateBy = _employeeDtoHelper.Get(source.CreateBy); + result.Created = _apiDateTimeHelper.Get(source.CreateOn); + result.About = source.About; + result.Industry = source.Industry; + result.FirstName = source.FirstName; + result.LastName = source.LastName; + result.Title = source.JobTitle; + + return result; + } + + public CompanyDto Convert(Company source, CompanyDto destination, ResolutionContext context) + { + var result = (CompanyDto)context.Mapper.Map(source); + + result.CompanyName = source.CompanyName; + + result.CreateBy = _employeeDtoHelper.Get(source.CreateBy); + result.Created = _apiDateTimeHelper.Get(source.CreateOn); + result.About = source.About; + result.Industry = source.Industry; + + return result; + } + + public ContactBaseWithPhoneDto Convert(Contact source, ContactBaseWithPhoneDto destination, ResolutionContext context) + { + if (source == null) return null; + + if (source == null) return null; + + var contactBaseDto = context.Mapper.Map(source); + + var result = new ContactBaseWithPhoneDto + { + AccessList = contactBaseDto.AccessList, + CanDelete = contactBaseDto.CanDelete, + CanEdit = contactBaseDto.CanEdit, + Currency = contactBaseDto.Currency, + DisplayName = contactBaseDto.DisplayName, + Id = contactBaseDto.Id, + IsCompany = contactBaseDto.IsCompany, + IsPrivate = contactBaseDto.IsPrivate, + IsShared = contactBaseDto.IsShared, + MediumFotoUrl = contactBaseDto.MediumFotoUrl, + ShareType = contactBaseDto.ShareType, + SmallFotoUrl = contactBaseDto.SmallFotoUrl + }; + + var phone = _daoFactory.GetContactInfoDao().GetList(source.ID, ContactInfoType.Phone, null, true).FirstOrDefault(); + + result.Phone = context.Mapper.Map(phone); + + return result; + } + + public ContactBaseWithEmailDto Convert(Contact source, ContactBaseWithEmailDto destination, ResolutionContext context) + { + if (source == null) return null; + + var contactBaseDto = context.Mapper.Map(source); + + var result = new ContactBaseWithEmailDto + { + AccessList = contactBaseDto.AccessList, + CanDelete = contactBaseDto.CanDelete, + CanEdit = contactBaseDto.CanEdit, + Currency = contactBaseDto.Currency, + DisplayName = contactBaseDto.DisplayName, + Id = contactBaseDto.Id, + IsCompany = contactBaseDto.IsCompany, + IsPrivate = contactBaseDto.IsPrivate, + IsShared = contactBaseDto.IsShared, + MediumFotoUrl = contactBaseDto.MediumFotoUrl, + ShareType = contactBaseDto.ShareType, + SmallFotoUrl = contactBaseDto.SmallFotoUrl + }; + + var email = _daoFactory.GetContactInfoDao().GetList(source.ID, ContactInfoType.Email, null, true).FirstOrDefault(); + + result.Email = context.Mapper.Map(email); + + return result; + } + + public List Convert(List source, List destination, ResolutionContext context) + { + if (source.Count == 0) return new List(); + + var result = new List(); + + var personsIDs = new List(); + var companyIDs = new List(); + var contactIDs = new int[source.Count]; + + var peopleCompanyIDs = new List(); + var peopleCompanyList = new Dictionary(); + + + var contactDao = _daoFactory.GetContactDao(); + + + for (var index = 0; index < source.Count; index++) + { + var contact = source[index]; + + if (contact is Company) + { + companyIDs.Add(contact.ID); + } + else + { + var person = contact as Person; + if (person != null) + { + personsIDs.Add(person.ID); + + if (person.CompanyID > 0) + { + peopleCompanyIDs.Add(person.CompanyID); + } + } + } + + contactIDs[index] = source[index].ID; + } + + if (peopleCompanyIDs.Count > 0) + { + var tmpList = contactDao.GetContacts(peopleCompanyIDs.ToArray()).ConvertAll(item => context.Mapper.Map(item)); + var tmpListCanDelete = contactDao.CanDelete(tmpList.Select(item => item.Id).ToArray()); + + foreach (var contactBaseDtoQuick in tmpList) + { + contactBaseDtoQuick.CanDelete = contactBaseDtoQuick.CanEdit && tmpListCanDelete[contactBaseDtoQuick.Id]; + peopleCompanyList.Add(contactBaseDtoQuick.Id, contactBaseDtoQuick); + } + } + + var companiesMembersCount = contactDao.GetMembersCount(companyIDs.Distinct().ToArray()); + + var contactStatusIDs = source.Select(item => item.StatusID).Distinct().ToArray(); + var contactInfos = new Dictionary>(); + + var haveLateTask = _daoFactory.GetTaskDao().HaveLateTask(contactIDs); + var contactStatus = _daoFactory.GetListItemDao() + .GetItems(contactStatusIDs) + .ToDictionary(item => item.ID, item => new ContactStatusBaseDto(item)); + + var personsCustomFields = _daoFactory.GetCustomFieldDao().GetEnityFields(EntityType.Person, personsIDs.ToArray()); + var companyCustomFields = _daoFactory.GetCustomFieldDao().GetEnityFields(EntityType.Company, companyIDs.ToArray()); + + var customFields = personsCustomFields.Union(companyCustomFields) + .GroupBy(item => item.EntityID).ToDictionary(item => item.Key, item => item.Select(x => context.Mapper.Map(x))); + + var addresses = new Dictionary>(); + var taskCount = _daoFactory.GetTaskDao().GetTasksCount(contactIDs); + + var contactTags = _daoFactory.GetTagDao().GetEntitiesTags(EntityType.Contact); + + _daoFactory.GetContactInfoDao().GetAll(contactIDs).ForEach( + item => + { + if (item.InfoType == ContactInfoType.Address) + { + if (!addresses.ContainsKey(item.ContactID)) + addresses.Add(item.ContactID, new List
    { new Address(item) }); + else + addresses[item.ContactID].Add(new Address(item)); + } + else + { + if (!contactInfos.ContainsKey(item.ContactID)) + contactInfos.Add(item.ContactID, new List { context.Mapper.Map(item) }); + else + contactInfos[item.ContactID].Add(context.Mapper.Map(item)); + } + } + ); + + + foreach (var contact in source) + { + ContactDto contactDto; + + var person = contact as Person; + if (person != null) + { + var people = person; + + var peopleDto = context.Mapper.Map(people); + + if (people.CompanyID > 0 && peopleCompanyList.ContainsKey(people.CompanyID)) + { + peopleDto.Company = peopleCompanyList[people.CompanyID]; + } + + contactDto = peopleDto; + } + else + { + var company = contact as Company; + if (company != null) + { + contactDto = context.Mapper.Map(company); + + if (companiesMembersCount.ContainsKey(contactDto.Id)) + { + ((CompanyDto)contactDto).PersonsCount = companiesMembersCount[contactDto.Id]; + } + } + else + { + throw new ArgumentException(); + } + } + + if (contactTags.ContainsKey(contact.ID)) + { + contactDto.Tags = contactTags[contact.ID].OrderBy(x => x); + } + + if (addresses.ContainsKey(contact.ID)) + { + contactDto.Addresses = addresses[contact.ID]; + } + + contactDto.CommonData = contactInfos.ContainsKey(contact.ID) ? contactInfos[contact.ID] : new List(); + + if (contactStatus.ContainsKey(contact.StatusID)) + { + contactDto.ContactStatus = contactStatus[contact.StatusID]; + } + + contactDto.HaveLateTasks = haveLateTask.ContainsKey(contact.ID) && haveLateTask[contact.ID]; + + contactDto.CustomFields = customFields.ContainsKey(contact.ID) ? customFields[contact.ID] : new List(); + + contactDto.TaskCount = taskCount.ContainsKey(contact.ID) ? taskCount[contact.ID] : 0; + + result.Add(contactDto); + } + + #region CanDelete for main contacts + + if (result.Count > 0) + { + var resultListCanDelete = contactDao.CanDelete(result.Select(item => item.Id).ToArray()); + foreach (var contactBaseDtoQuick in result) + { + contactBaseDtoQuick.CanDelete = contactBaseDtoQuick.CanEdit && resultListCanDelete[contactBaseDtoQuick.Id]; + } + } + + #endregion + + return result; + } + } + +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Mapping/TypeConverter/CustomFieldDtoTypeConverter.cs b/products/ASC.CRM/Server/Mapping/TypeConverter/CustomFieldDtoTypeConverter.cs new file mode 100644 index 00000000000..d3fc8821d7d --- /dev/null +++ b/products/ASC.CRM/Server/Mapping/TypeConverter/CustomFieldDtoTypeConverter.cs @@ -0,0 +1,43 @@ +using System; + +using ASC.Common; +using ASC.CRM.ApiModels; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; + +using AutoMapper; + +namespace ASC.CRM.Mapping +{ + [Scope] + public class CustomFieldDtoTypeConverter : ITypeConverter + { + private readonly DaoFactory _daoFactory; + + public CustomFieldDtoTypeConverter(DaoFactory daoFactory) + { + _daoFactory = daoFactory; + + } + + public CustomFieldDto Convert(CustomField source, CustomFieldDto destination, ResolutionContext context) + { + if (destination != null) + throw new NotImplementedException(); + + var result = new CustomFieldDto + { + Id = source.ID, + EntityId = source.EntityID, + FieldType = source.Type, + FieldValue = source.Value, + Label = source.Label, + Mask = source.Mask, + Position = source.SortOrder, + RelativeItemsCount = _daoFactory.GetCustomFieldDao().GetContactLinkCount(source.EntityType, source.ID) + }; + + return result; + } + } +} diff --git a/products/ASC.CRM/Server/Mapping/TypeConverter/DealMilestoneDtoTypeConverter.cs b/products/ASC.CRM/Server/Mapping/TypeConverter/DealMilestoneDtoTypeConverter.cs new file mode 100644 index 00000000000..c4ce4658806 --- /dev/null +++ b/products/ASC.CRM/Server/Mapping/TypeConverter/DealMilestoneDtoTypeConverter.cs @@ -0,0 +1,59 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +using ASC.Common; +using ASC.CRM.ApiModels; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; + +using AutoMapper; + +namespace ASC.CRM.Mapping +{ + [Scope] + public sealed class DealMilestoneDtoTypeConverter : ITypeConverter + { + private readonly DaoFactory _daoFactory; + + public DealMilestoneDtoTypeConverter(DaoFactory daoFactory) + { + _daoFactory = daoFactory; + } + + public DealMilestoneDto Convert(DealMilestone source, DealMilestoneDto destination, ResolutionContext context) + { + if (destination != null) + throw new NotImplementedException(); + + return new DealMilestoneDto(source) + { + RelativeItemsCount = _daoFactory.GetDealMilestoneDao().GetRelativeItemsCount(source.ID) + }; + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Mapping/TypeConverter/InvoiceBaseDtoTypeConverter.cs b/products/ASC.CRM/Server/Mapping/TypeConverter/InvoiceBaseDtoTypeConverter.cs new file mode 100644 index 00000000000..584721a6bb2 --- /dev/null +++ b/products/ASC.CRM/Server/Mapping/TypeConverter/InvoiceBaseDtoTypeConverter.cs @@ -0,0 +1,119 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using System; + +using ASC.Api.Core; +using ASC.Common; +using ASC.Core.Common.Settings; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.Web.Api.Models; +using ASC.Web.CRM.Classes; + +using AutoMapper; + +namespace ASC.CRM.Mapping +{ + [Scope] + public class InvoiceBaseDtoTypeConverter : ITypeConverter + { + private readonly EntityDtoHelper _entityDtoHelper; + private readonly CurrencyProvider _currencyProvider; + private readonly SettingsManager _settingsManager; + private readonly CrmSecurity _crmSecurity; + private readonly ApiDateTimeHelper _apiDateTimeHelper; + private readonly EmployeeWraperHelper _employeeWraperHelper; + private readonly DaoFactory _daoFactory; + + public InvoiceBaseDtoTypeConverter(ApiDateTimeHelper apiDateTimeHelper, + EmployeeWraperHelper employeeWraperHelper, + CrmSecurity crmSecurity, + SettingsManager settingsManager, + CurrencyProvider currencyProvider, + DaoFactory daoFactory, + EntityDtoHelper entityDtoHelper) + { + _apiDateTimeHelper = apiDateTimeHelper; + _employeeWraperHelper = employeeWraperHelper; + _crmSecurity = crmSecurity; + _settingsManager = settingsManager; + _currencyProvider = currencyProvider; + _daoFactory = daoFactory; + _entityDtoHelper = entityDtoHelper; + } + + public InvoiceBaseDto Convert(Invoice source, InvoiceBaseDto destination, ResolutionContext context) + { + var crmSettings = _settingsManager.Load(); + var defaultCurrency = _currencyProvider.Get(crmSettings.DefaultCurrency); + + var result = new InvoiceBaseDto + { + Id = source.ID, + Status = context.Mapper.Map(source.Status), + Number = source.Number, + IssueDate = _apiDateTimeHelper.Get(source.IssueDate), + TemplateType = source.TemplateType, + DueDate = _apiDateTimeHelper.Get(source.DueDate), + Currency = !String.IsNullOrEmpty(source.Currency) ? + context.Mapper.Map(_currencyProvider.Get(source.Currency)) : + context.Mapper.Map(defaultCurrency), + ExchangeRate = source.ExchangeRate, + Language = source.Language, + PurchaseOrderNumber = source.PurchaseOrderNumber, + Terms = source.Terms, + Description = source.Description, + FileID = source.FileID, + CreateOn = _apiDateTimeHelper.Get(source.CreateOn), + CreateBy = _employeeWraperHelper.Get(source.CreateBy), + CanEdit = _crmSecurity.CanEdit(source), + CanDelete = _crmSecurity.CanDelete(source) + }; + + if (source.ContactID > 0) + { + result.Contact = context.Mapper.Map(_daoFactory.GetContactDao().GetByID(source.ContactID)); + } + + if (source.ConsigneeID > 0) + { + result.Consignee = context.Mapper.Map(_daoFactory.GetContactDao().GetByID(source.ConsigneeID)); + } + + if (source.EntityID > 0) + { + result.Entity = _entityDtoHelper.Get(source.EntityType, source.EntityID); + } + + result.Cost = source.GetInvoiceCost(_daoFactory); + + return result; + + } + } +} diff --git a/products/ASC.CRM/Server/Mapping/TypeConverter/InvoiceDtoTypeConverter.cs b/products/ASC.CRM/Server/Mapping/TypeConverter/InvoiceDtoTypeConverter.cs new file mode 100644 index 00000000000..da06050d2fc --- /dev/null +++ b/products/ASC.CRM/Server/Mapping/TypeConverter/InvoiceDtoTypeConverter.cs @@ -0,0 +1,122 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using System; +using System.Linq; + +using ASC.Api.Core; +using ASC.Common; +using ASC.Core.Common.Settings; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.Web.Api.Models; +using ASC.Web.CRM.Classes; + +using AutoMapper; + +namespace ASC.CRM.Mapping +{ + [Scope] + public class InvoiceDtoTypeConverter : ITypeConverter + { + private readonly DaoFactory _daoFactory; + private readonly CurrencyProvider _currencyProvider; + private readonly SettingsManager _settingsManager; + private readonly ApiDateTimeHelper _apiDateTimeHelper; + private readonly EmployeeWraperHelper _employeeWraperHelper; + private readonly CrmSecurity _crmSecurity; + private readonly EntityDtoHelper _entityDtoHelper; + + public InvoiceDtoTypeConverter(ApiDateTimeHelper apiDateTimeHelper, + EmployeeWraperHelper employeeWraperHelper, + CrmSecurity crmSecurity, + SettingsManager settingsManager, + CurrencyProvider currencyProvider, + DaoFactory daoFactory, + EntityDtoHelper entityDtoHelper) + { + _apiDateTimeHelper = apiDateTimeHelper; + _employeeWraperHelper = employeeWraperHelper; + _crmSecurity = crmSecurity; + _settingsManager = settingsManager; + _currencyProvider = currencyProvider; + _daoFactory = daoFactory; + _entityDtoHelper = entityDtoHelper; + } + + + public InvoiceDto Convert(Invoice source, InvoiceDto destination, ResolutionContext context) + { + var crmSettings = _settingsManager.Load(); + var defaultCurrency = _currencyProvider.Get(crmSettings.DefaultCurrency); + + var result = new InvoiceDto + { + Id = source.ID, + Status = context.Mapper.Map(source.Status), + Number = source.Number, + IssueDate = _apiDateTimeHelper.Get(source.IssueDate), + TemplateType = source.TemplateType, + DueDate = _apiDateTimeHelper.Get(source.DueDate), + Currency = !String.IsNullOrEmpty(source.Currency) ? + context.Mapper.Map(_currencyProvider.Get(source.Currency)) : + context.Mapper.Map(defaultCurrency), + ExchangeRate = source.ExchangeRate, + Language = source.Language, + PurchaseOrderNumber = source.PurchaseOrderNumber, + Terms = source.Terms, + Description = source.Description, + FileID = source.FileID, + CreateOn = _apiDateTimeHelper.Get(source.CreateOn), + CreateBy = _employeeWraperHelper.Get(source.CreateBy), + CanEdit = _crmSecurity.CanEdit(source), + CanDelete = _crmSecurity.CanDelete(source), + }; + + if (source.ContactID > 0) + { + result.Contact = context.Mapper.Map(_daoFactory.GetContactDao().GetByID(source.ContactID)); + } + + if (source.ConsigneeID > 0) + { + result.Consignee = context.Mapper.Map(_daoFactory.GetContactDao().GetByID(source.ConsigneeID)); + } + + if (source.EntityID > 0) + { + result.Entity = _entityDtoHelper.Get(source.EntityType, source.EntityID); + } + + result.Cost = source.GetInvoiceCost(_daoFactory); + + result.InvoiceLines = source.GetInvoiceLines(_daoFactory).Select(x => context.Mapper.Map(x)).ToList(); + + return result; + } + } +} diff --git a/products/ASC.CRM/Server/Mapping/TypeConverter/InvoiceItemDtoTypeConverter.cs b/products/ASC.CRM/Server/Mapping/TypeConverter/InvoiceItemDtoTypeConverter.cs new file mode 100644 index 00000000000..19800a0aa94 --- /dev/null +++ b/products/ASC.CRM/Server/Mapping/TypeConverter/InvoiceItemDtoTypeConverter.cs @@ -0,0 +1,110 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using System; + +using ASC.Api.Core; +using ASC.Common; +using ASC.Core.Common.Settings; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.Web.Api.Models; +using ASC.Web.CRM.Classes; + +using AutoMapper; + +namespace ASC.CRM.Mapping +{ + [Scope] + public class InvoiceItemDtoTypeConverter : ITypeConverter + { + private readonly DaoFactory _daoFactory; + private readonly CurrencyProvider _currencyProvider; + private readonly SettingsManager _settingsManager; + private readonly ApiDateTimeHelper _apiDateTimeHelper; + private readonly EmployeeWraperHelper _employeeWraperHelper; + private readonly CrmSecurity _crmSecurity; + + public InvoiceItemDtoTypeConverter(ApiDateTimeHelper apiDateTimeHelper, + EmployeeWraperHelper employeeWraperHelper, + CrmSecurity crmSecurity, + SettingsManager settingsManager, + CurrencyProvider currencyProvider, + DaoFactory daoFactory) + { + _apiDateTimeHelper = apiDateTimeHelper; + _employeeWraperHelper = employeeWraperHelper; + _crmSecurity = crmSecurity; + _settingsManager = settingsManager; + _currencyProvider = currencyProvider; + _daoFactory = daoFactory; + } + + public InvoiceItemDto Convert(InvoiceItem source, InvoiceItemDto destination, ResolutionContext context) + { + if (destination != null) + throw new NotImplementedException(); + + var crmSettings = _settingsManager.Load(); + var defaultCurrency = _currencyProvider.Get(crmSettings.DefaultCurrency); + + var result = new InvoiceItemDto + { + + Title = source.Title, + StockKeepingUnit = source.StockKeepingUnit, + Description = source.Description, + Price = source.Price, + StockQuantity = source.StockQuantity, + TrackInvenory = source.TrackInventory, + CreateOn = _apiDateTimeHelper.Get(source.CreateOn), + CreateBy = _employeeWraperHelper.Get(source.CreateBy), + Currency = !String.IsNullOrEmpty(source.Currency) ? + context.Mapper.Map(_currencyProvider.Get(source.Currency)) : + context.Mapper.Map(defaultCurrency), + CanEdit = _crmSecurity.CanEdit(source), + CanDelete = _crmSecurity.CanDelete(source) + }; + + if (source.InvoiceTax1ID > 0) + { + var invoiceTax1ID = _daoFactory.GetInvoiceTaxDao().GetByID(source.InvoiceTax1ID); + + result.InvoiceTax1 = context.Mapper.Map(invoiceTax1ID); + } + + if (source.InvoiceTax2ID > 0) + { + var invoiceTax2ID = _daoFactory.GetInvoiceTaxDao().GetByID(source.InvoiceTax2ID); + + result.InvoiceTax2 = context.Mapper.Map(invoiceTax2ID); + } + + return result; + } + } +} diff --git a/products/ASC.CRM/Server/Mapping/TypeConverter/InvoiceTaxDtoTypeConverter.cs b/products/ASC.CRM/Server/Mapping/TypeConverter/InvoiceTaxDtoTypeConverter.cs new file mode 100644 index 00000000000..e8a936d8c28 --- /dev/null +++ b/products/ASC.CRM/Server/Mapping/TypeConverter/InvoiceTaxDtoTypeConverter.cs @@ -0,0 +1,74 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using System; + +using ASC.Api.Core; +using ASC.Common; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Entities; +using ASC.Web.Api.Models; + +using AutoMapper; + +namespace ASC.CRM.Mapping +{ + [Scope] + public class InvoiceTaxDtoTypeConverter : ITypeConverter + { + private readonly ApiDateTimeHelper _apiDateTimeHelper; + private readonly EmployeeWraperHelper _employeeWraperHelper; + private readonly CrmSecurity _crmSecurity; + + public InvoiceTaxDtoTypeConverter(ApiDateTimeHelper apiDateTimeHelper, + EmployeeWraperHelper employeeWraperHelper, + CrmSecurity crmSecurity) + { + _apiDateTimeHelper = apiDateTimeHelper; + _employeeWraperHelper = employeeWraperHelper; + _crmSecurity = crmSecurity; + } + + public InvoiceTaxDto Convert(InvoiceTax source, InvoiceTaxDto destination, ResolutionContext context) + { + if (destination != null) + throw new NotImplementedException(); + + var result = new InvoiceTaxDto(); + + result.Id = source.ID; + result.Name = source.Name; + result.Description = source.Description; + result.Rate = source.Rate; + result.CreateOn = _apiDateTimeHelper.Get(source.CreateOn); + result.CreateBy = _employeeWraperHelper.Get(source.CreateBy); + result.CanEdit = _crmSecurity.CanEdit(source); + result.CanDelete = _crmSecurity.CanDelete(source); + + return result; + } + } +} diff --git a/products/ASC.CRM/Server/Mapping/TypeConverter/ListItemDtoTypeConverter.cs b/products/ASC.CRM/Server/Mapping/TypeConverter/ListItemDtoTypeConverter.cs new file mode 100644 index 00000000000..3cb52355551 --- /dev/null +++ b/products/ASC.CRM/Server/Mapping/TypeConverter/ListItemDtoTypeConverter.cs @@ -0,0 +1,121 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +using ASC.Common; +using ASC.CRM.ApiModels; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.Web.Core.Utility.Skins; +using ASC.Web.CRM.Configuration; + +using AutoMapper; + +namespace ASC.CRM.Mapping +{ + + [Scope] + public sealed class ListItemDtoTypeConverter : ITypeConverter, + ITypeConverter, + ITypeConverter, + ITypeConverter, + ITypeConverter + + { + private readonly WebImageSupplier _webImageSupplier; + private readonly DaoFactory _daoFactory; + + public ListItemDtoTypeConverter(WebImageSupplier webImageSupplier, + DaoFactory daoFactory) + { + _webImageSupplier = webImageSupplier; + _daoFactory = daoFactory; + } + + public TaskCategoryBaseDto Convert(ListItem source, TaskCategoryBaseDto destination, ResolutionContext context) + { + if (destination != null) + throw new NotImplementedException(); + + return new TaskCategoryDto(source) + { + ImagePath = _webImageSupplier.GetAbsoluteWebPath(source.AdditionalParams, ProductEntryPoint.ID) + }; + } + + public HistoryCategoryDto Convert(ListItem source, HistoryCategoryDto destination, ResolutionContext context) + { + if (destination != null) + throw new NotImplementedException(); + + var result = new HistoryCategoryDto(source); + + result.RelativeItemsCount = _daoFactory.GetListItemDao().GetRelativeItemsCount(ListType.HistoryCategory, source.ID); + + return result; + } + + public TaskCategoryDto Convert(ListItem source, TaskCategoryDto destination, ResolutionContext context) + { + if (destination != null) + throw new NotImplementedException(); + + var result = new TaskCategoryDto(source); + + result.RelativeItemsCount = _daoFactory.GetListItemDao().GetRelativeItemsCount(ListType.TaskCategory, source.ID); + + return result; + } + + public ContactStatusDto Convert(ListItem source, ContactStatusDto destination, ResolutionContext context) + { + if (destination != null) + throw new NotImplementedException(); + + var result = new ContactStatusDto(source); + + result.RelativeItemsCount = _daoFactory.GetListItemDao().GetRelativeItemsCount(ListType.ContactStatus, source.ID); + + return result; + + } + + public ContactTypeDto Convert(ListItem source, ContactTypeDto destination, ResolutionContext context) + { + if (destination != null) + throw new NotImplementedException(); + + var result = new ContactTypeDto(source); + + result.RelativeItemsCount = _daoFactory.GetListItemDao().GetRelativeItemsCount(ListType.ContactType, source.ID); + + return result; + + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Mapping/TypeConverter/OpportunityDtoTypeConverter.cs b/products/ASC.CRM/Server/Mapping/TypeConverter/OpportunityDtoTypeConverter.cs new file mode 100644 index 00000000000..564bfb42b41 --- /dev/null +++ b/products/ASC.CRM/Server/Mapping/TypeConverter/OpportunityDtoTypeConverter.cs @@ -0,0 +1,126 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Collections.Generic; +using System.Linq; + +using ASC.Api.Core; +using ASC.Common; +using ASC.Common.Web; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.Web.Api.Models; +using ASC.Web.CRM.Classes; + +using AutoMapper; + +namespace ASC.CRM.Mapping +{ + [Scope] + public class OpportunityDtoTypeConverter : ITypeConverter + { + private readonly CurrencyProvider _currencyProvider; + private readonly DaoFactory _daoFactory; + private readonly CrmSecurity _crmSecurity; + private readonly ApiDateTimeHelper _apiDateTimeHelper; + private readonly EmployeeWraperHelper _employeeWraperHelper; + + public OpportunityDtoTypeConverter(ApiDateTimeHelper apiDateTimeHelper, + EmployeeWraperHelper employeeWraperHelper, + CrmSecurity crmSecurity, + DaoFactory daoFactory, + CurrencyProvider currencyProvider) + { + _apiDateTimeHelper = apiDateTimeHelper; + _employeeWraperHelper = employeeWraperHelper; + _crmSecurity = crmSecurity; + _daoFactory = daoFactory; + _currencyProvider = currencyProvider; + } + + public OpportunityDto Convert(Deal source, OpportunityDto destination, ResolutionContext context) + { + var dealDto = new OpportunityDto + { + Id = source.ID, + CreateBy = _employeeWraperHelper.Get(source.CreateBy), + Created = _apiDateTimeHelper.Get(source.CreateOn), + Title = source.Title, + Description = source.Description, + Responsible = _employeeWraperHelper.Get(source.ResponsibleID), + BidType = source.BidType, + BidValue = source.BidValue, + PerPeriodValue = source.PerPeriodValue, + SuccessProbability = source.DealMilestoneProbability, + ActualCloseDate = _apiDateTimeHelper.Get(source.ActualCloseDate), + ExpectedCloseDate = _apiDateTimeHelper.Get(source.ExpectedCloseDate), + CanEdit = _crmSecurity.CanEdit(source) + }; + + if (source.ContactID > 0) + dealDto.Contact = context.Mapper.Map(_daoFactory.GetContactDao().GetByID(source.ContactID)); + + if (source.DealMilestoneID > 0) + { + var dealMilestone = _daoFactory.GetDealMilestoneDao().GetByID(source.DealMilestoneID); + + if (dealMilestone == null) + throw new ItemNotFoundException(); + + dealDto.Stage = new DealMilestoneBaseDto(dealMilestone); + } + + dealDto.AccessList = _crmSecurity.GetAccessSubjectTo(source) + .Select(item => _employeeWraperHelper.Get(item.Key)); + + dealDto.IsPrivate = _crmSecurity.IsPrivate(source); + + if (!string.IsNullOrEmpty(source.BidCurrency)) + dealDto.BidCurrency = context.Mapper.Map(_currencyProvider.Get(source.BidCurrency)); + + dealDto.CustomFields = _daoFactory.GetCustomFieldDao().GetEnityFields(EntityType.Opportunity, source.ID, false).ConvertAll(item => context.Mapper.Map(item)); + + dealDto.Members = new List(); + + var memberIDs = _daoFactory.GetDealDao().GetMembers(source.ID); + var membersList = _daoFactory.GetContactDao().GetContacts(memberIDs); + var membersDtoList = new List(); + + foreach (var member in membersList) + { + if (member == null) continue; + membersDtoList.Add(context.Mapper.Map(member)); + } + + dealDto.Members = membersDtoList; + + return dealDto; + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Mapping/TypeConverter/RelationshipEventDtoTypeConverter.cs b/products/ASC.CRM/Server/Mapping/TypeConverter/RelationshipEventDtoTypeConverter.cs new file mode 100644 index 00000000000..f7048be8933 --- /dev/null +++ b/products/ASC.CRM/Server/Mapping/TypeConverter/RelationshipEventDtoTypeConverter.cs @@ -0,0 +1,112 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Collections.Generic; + +using ASC.Api.Core; +using ASC.Api.Documents; +using ASC.Common; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.Web.Api.Models; + +using AutoMapper; + +namespace ASC.CRM.Mapping +{ + [Scope] + public class RelationshipEventDtoTypeConverter : ITypeConverter + { + private readonly FileWrapperHelper _fileWrapperHelper; + private readonly DaoFactory _daoFactory; + private readonly CrmSecurity _crmSecurity; + private readonly ApiDateTimeHelper _apiDateTimeHelper; + private readonly EmployeeWraperHelper _employeeWraperHelper; + private readonly EntityDtoHelper _entityDtoHelper; + + public RelationshipEventDtoTypeConverter( + ApiDateTimeHelper apiDateTimeHelper, + EmployeeWraperHelper employeeWraperHelper, + FileWrapperHelper fileWrapperHelper, + CrmSecurity crmSecurity, + DaoFactory daoFactory, + EntityDtoHelper entityDtoHelper) + { + _apiDateTimeHelper = apiDateTimeHelper; + _employeeWraperHelper = employeeWraperHelper; + _crmSecurity = crmSecurity; + _daoFactory = daoFactory; + _fileWrapperHelper = fileWrapperHelper; + _entityDtoHelper = entityDtoHelper; + } + + public RelationshipEventDto Convert(RelationshipEvent source, RelationshipEventDto destination, ResolutionContext context) + { + var result = new RelationshipEventDto + { + Id = source.ID, + CreateBy = _employeeWraperHelper.Get(source.CreateBy), + Created = _apiDateTimeHelper.Get(source.CreateOn), + Content = source.Content, + Files = new List>(), + CanEdit = _crmSecurity.CanEdit(source) + }; + + + var historyCategory = _daoFactory.GetListItemDao().GetByID(source.CategoryID); + + if (historyCategory != null) + { + result.Category = (HistoryCategoryBaseDto)context.Mapper.Map(historyCategory); + } + + if (source.EntityID > 0) + { + result.Entity = _entityDtoHelper.Get(source.EntityType, source.EntityID); + } + + result.Files = _daoFactory.GetRelationshipEventDao().GetFiles(source.ID).ConvertAll(file => _fileWrapperHelper.Get(file)); + + if (source.ContactID > 0) + { + var relativeContact = _daoFactory.GetContactDao().GetByID(source.ContactID); + + if (relativeContact != null) + { + result.Contact = context.Mapper.Map(relativeContact); + } + } + + result.CanEdit = _crmSecurity.CanAccessTo(source); + + return result; + + } + } + +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Mapping/TypeConverter/TaskDtoTypeConverter.cs b/products/ASC.CRM/Server/Mapping/TypeConverter/TaskDtoTypeConverter.cs new file mode 100644 index 00000000000..66ec0c4aeaa --- /dev/null +++ b/products/ASC.CRM/Server/Mapping/TypeConverter/TaskDtoTypeConverter.cs @@ -0,0 +1,122 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using System; + +using ASC.Api.Core; +using ASC.Common; +using ASC.CRM.ApiModels; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.Web.Api.Models; + +using AutoMapper; + +namespace ASC.CRM.Mapping +{ + [Scope] + public class TaskDtoTypeConverter : ITypeConverter, + ITypeConverter + { + private readonly CrmSecurity _crmSecurity; + private readonly ApiDateTimeHelper _apiDateTimeHelper; + private readonly EmployeeWraperHelper _employeeWraperHelper; + private readonly DaoFactory _daoFactory; + private readonly EntityDtoHelper _entityDtoHelper; + + public TaskDtoTypeConverter(ApiDateTimeHelper apiDateTimeHelper, + EmployeeWraperHelper employeeWraperHelper, + CrmSecurity crmSecurity, + DaoFactory daoFactory, + EntityDtoHelper entityDtoHelper) + { + _apiDateTimeHelper = apiDateTimeHelper; + _employeeWraperHelper = employeeWraperHelper; + _crmSecurity = crmSecurity; + _daoFactory = daoFactory; + _entityDtoHelper = entityDtoHelper; + } + + public TaskDto Convert(Task source, TaskDto destination, ResolutionContext context) + { + var result = new TaskDto + { + Title = source.Title, + Description = source.Description, + DeadLine = _apiDateTimeHelper.Get(source.DeadLine), + Responsible = _employeeWraperHelper.Get(source.ResponsibleID), + IsClosed = source.IsClosed, + AlertValue = source.AlertValue, + Created = _apiDateTimeHelper.Get(source.CreateOn) + }; + + if (source.CategoryID > 0) + { + var categoryItem = _daoFactory.GetListItemDao().GetByID(source.CategoryID); + + result.Category = new TaskCategoryDto(categoryItem) + { + RelativeItemsCount = _daoFactory.GetListItemDao().GetRelativeItemsCount(ListType.TaskCategory, categoryItem.ID) + }; + + } + + if (source.ContactID > 0) + { + result.Contact = context.Mapper.Map(_daoFactory.GetContactDao().GetByID(source.ContactID)); + } + + if (source.EntityID > 0) + { + + result.Entity = _entityDtoHelper.Get(source.EntityType, source.EntityID); + } + + result.CanEdit = _crmSecurity.CanEdit(source); + + return result; + } + + public TaskBaseDto Convert(Task source, TaskBaseDto destination, ResolutionContext context) + { + if (destination != null) + throw new NotImplementedException(); + + return new TaskBaseDto + { + Title = source.Title, + Description = source.Description, + DeadLine = _apiDateTimeHelper.Get(source.DeadLine), + Responsible = _employeeWraperHelper.Get(source.ResponsibleID), + IsClosed = source.IsClosed, + AlertValue = source.AlertValue + }; + } + } +} + + diff --git a/products/ASC.CRM/Server/Mapping/TypeConverter/VoipCallDtoTypeConverter.cs b/products/ASC.CRM/Server/Mapping/TypeConverter/VoipCallDtoTypeConverter.cs new file mode 100644 index 00000000000..04f72bbea7e --- /dev/null +++ b/products/ASC.CRM/Server/Mapping/TypeConverter/VoipCallDtoTypeConverter.cs @@ -0,0 +1,91 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using System; +using System.Linq; + +using ASC.Api.Core; +using ASC.Common; +using ASC.CRM.ApiModels; +using ASC.CRM.Core.Dao; +using ASC.VoipService; +using ASC.Web.Api.Models; + +using AutoMapper; + +namespace ASC.CRM.Mapping +{ + [Scope] + public class VoipCallDtoTypeConverter : ITypeConverter + { + private readonly ApiDateTimeHelper _apiDateTimeHelper; + private readonly EmployeeWraperHelper _employeeWraperHelper; + private readonly DaoFactory _daoFactory; + + public VoipCallDtoTypeConverter(ApiDateTimeHelper apiDateTimeHelper, + EmployeeWraperHelper employeeWraperHelper, + DaoFactory daoFactory) + { + _daoFactory = daoFactory; + _apiDateTimeHelper = apiDateTimeHelper; + _employeeWraperHelper = employeeWraperHelper; + } + + public VoipCallDto Convert(VoipCall source, VoipCallDto destination, ResolutionContext context) + { + if (destination != null) + throw new NotImplementedException(); + + var result = new VoipCallDto + { + Id = source.Id, + From = source.From, + To = source.To, + Status = source.Status, + AnsweredBy = _employeeWraperHelper.Get(source.AnsweredBy), + DialDate = _apiDateTimeHelper.Get(source.DialDate), + DialDuration = source.DialDuration, + Cost = source.Price + source.ChildCalls.Sum(r => r.Price) + source.VoipRecord.Price, + RecordUrl = source.VoipRecord.Uri, + RecordDuration = source.VoipRecord.Duration + }; + + if (source.ContactId > 0) +{ + result.Contact = context.Mapper.Map(_daoFactory.GetContactDao().GetByID(source.ContactId)); + } + + if (source.ChildCalls.Any()) + { + result.Calls = source.ChildCalls.Select(childCall => context.Mapper.Map(childCall)); + } + + + return result; + } + } +} + + diff --git a/products/ASC.CRM/Server/Middlewares/ContactPhotoHandlerMiddleware.cs b/products/ASC.CRM/Server/Middlewares/ContactPhotoHandlerMiddleware.cs new file mode 100644 index 00000000000..5afce5d56c8 --- /dev/null +++ b/products/ASC.CRM/Server/Middlewares/ContactPhotoHandlerMiddleware.cs @@ -0,0 +1,163 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + + +using System; +using System.Text.Json; + +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Resources; +using ASC.MessagingSystem; +using ASC.Web.Core; +using ASC.Web.Core.Files; +using ASC.Web.Core.Utility; +using ASC.Web.CRM.Classes; +using ASC.Web.CRM.Configuration; +using ASC.Web.Studio.Core; + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; + +namespace ASC.Web.CRM.HttpHandlers +{ + public class ContactPhotoHandlerMiddleware + { + public ContactPhotoHandlerMiddleware(RequestDelegate next) + { + _next = next; + } + + private readonly RequestDelegate _next; + + public async System.Threading.Tasks.Task Invoke(HttpContext context, + SetupInfo setupInfo, + CrmSecurity crmSecurity, + FileSizeComment fileSizeComment, + WebItemSecurity webItemSecurity, + MessageTarget messageTarget, + MessageService messageService, + DaoFactory daoFactory, + ContactPhotoManager contactPhotoManager) + { + + if (!webItemSecurity.IsAvailableForMe(ProductEntryPoint.ID)) + throw crmSecurity.CreateSecurityException(); + + context.Request.EnableBuffering(); + + var contactId = Convert.ToInt32(context.Request.Form["contactID"]); + + Contact contact = null; + + if (contactId != 0) + { + contact = daoFactory.GetContactDao().GetByID(contactId); + + if (!crmSecurity.CanEdit(contact)) + throw crmSecurity.CreateSecurityException(); + } + + var fileUploadResult = new FileUploadResult(); + + if (context.Request.Form.Files.Count == 0) + { + await context.Response.WriteAsync(JsonSerializer.Serialize(fileUploadResult)); + } + + var fileName = context.Request.Form.Files[0].FileName; + var contentLength = context.Request.Form.Files[0].Length; + + if (String.IsNullOrEmpty(fileName) || contentLength == 0) + throw new InvalidOperationException(CRMErrorsResource.InvalidFile); + + if (0 < setupInfo.MaxImageUploadSize && setupInfo.MaxImageUploadSize < contentLength) + { + fileUploadResult.Success = false; + fileUploadResult.Message = fileSizeComment.GetFileImageSizeNote(CRMCommonResource.ErrorMessage_UploadFileSize, false).HtmlEncode(); + + await context.Response.WriteAsync(JsonSerializer.Serialize(fileUploadResult)); + } + + if (FileUtility.GetFileTypeByFileName(fileName) != FileType.Image) + { + fileUploadResult.Success = false; + fileUploadResult.Message = CRMJSResource.ErrorMessage_NotImageSupportFormat.HtmlEncode(); + + await context.Response.WriteAsync(JsonSerializer.Serialize(fileUploadResult)); + } + + var uploadOnly = Convert.ToBoolean(context.Request.Form["uploadOnly"]); + var tmpDirName = Convert.ToString(context.Request.Form["tmpDirName"]); + + try + { + ContactPhotoManager.PhotoData photoData; + if (contactId != 0) + { + photoData = contactPhotoManager.UploadPhoto(context.Request.Form.Files[0].OpenReadStream(), contactId, uploadOnly); + } + else + { + if (String.IsNullOrEmpty(tmpDirName) || tmpDirName == "null") + { + tmpDirName = Guid.NewGuid().ToString(); + } + photoData = contactPhotoManager.UploadPhotoToTemp(context.Request.Form.Files[0].OpenReadStream(), tmpDirName); + } + + fileUploadResult.Success = true; + fileUploadResult.Data = photoData; + } + catch (Exception e) + { + fileUploadResult.Success = false; + fileUploadResult.Message = e.Message.HtmlEncode(); + + await context.Response.WriteAsync(JsonSerializer.Serialize(fileUploadResult)); + } + + if (contact != null) + { + var messageAction = contact is Company ? MessageAction.CompanyUpdatedPhoto : MessageAction.PersonUpdatedPhoto; + + messageService.Send(messageAction, messageTarget.Create(contact.ID), contact.GetTitle()); + + } + + await context.Response.WriteAsync(JsonSerializer.Serialize(fileUploadResult)); + } + } + + public static class ContactPhotoHandlerMiddlewareExtensions + { + public static IApplicationBuilder UseContactPhotoHandler(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Middlewares/FileHandlerMiddleware.cs b/products/ASC.CRM/Server/Middlewares/FileHandlerMiddleware.cs new file mode 100644 index 00000000000..23433df7bd0 --- /dev/null +++ b/products/ASC.CRM/Server/Middlewares/FileHandlerMiddleware.cs @@ -0,0 +1,117 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.IO; +using System.Threading.Tasks; + +using ASC.CRM.Resources; +using ASC.Web.CRM.Classes; + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; + +namespace ASC.Web.CRM.HttpHandlers +{ + public class FileHandlerMiddleware + { + private readonly RequestDelegate _next; + + public FileHandlerMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task Invoke(HttpContext context, + Global global, + ContactPhotoManager contactPhotoManager) + { + context.Request.EnableBuffering(); + + var action = context.Request.Query["action"]; + + switch (action) + { + case "contactphotoulr": + { + var contactId = Convert.ToInt32(context.Request.Query["cid"]); + var isCompany = Convert.ToBoolean(context.Request.Query["isc"]); + var photoSize = Convert.ToInt32(context.Request.Query["ps"]); + + string photoUrl; + + switch (photoSize) + { + case 1: + photoUrl = contactPhotoManager.GetSmallSizePhoto(contactId, isCompany); + break; + case 2: + photoUrl = contactPhotoManager.GetMediumSizePhoto(contactId, isCompany); + break; + case 3: + photoUrl = contactPhotoManager.GetBigSizePhoto(contactId, isCompany); + break; + default: + throw new Exception(CRMErrorsResource.ContactPhotoSizeUnknown); + } + + context.Response.Clear(); + + await context.Response.WriteAsync(photoUrl); + } + break; + case "mailmessage": + { + var messageId = Convert.ToInt32(context.Request.Query["message_id"]); + + var filePath = String.Format("folder_{0}/message_{1}.html", (messageId / 1000 + 1) * 1000, messageId); + + string messageContent = string.Empty; + + using (var streamReader = new StreamReader(global.GetStore().GetReadStream("mail_messages", filePath))) + { + messageContent = streamReader.ReadToEnd(); + } + + context.Response.Clear(); + + await context.Response.WriteAsync(messageContent); + } + break; + default: + throw new ArgumentException(String.Format("action='{0}' is not defined", action)); + } + } + } + + public static class FileHandlerMiddlewareExtensions + { + public static IApplicationBuilder UseFileHandler(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } + } +} diff --git a/products/ASC.CRM/Server/Middlewares/FileUploaderHandlerMiddleware.cs b/products/ASC.CRM/Server/Middlewares/FileUploaderHandlerMiddleware.cs new file mode 100644 index 00000000000..551241a10b4 --- /dev/null +++ b/products/ASC.CRM/Server/Middlewares/FileUploaderHandlerMiddleware.cs @@ -0,0 +1,107 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Text.Json; +using System.Threading.Tasks; + +using ASC.CRM.Core.Dao; +using ASC.CRM.Resources; +using ASC.Files.Core; +using ASC.Web.Core.Utility; +using ASC.Web.Studio.Core; +using ASC.Web.Studio.UserControls.Statistics; +using ASC.Web.Studio.Utility; + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace ASC.Web.CRM.HttpHandlers +{ + public class FileUploaderHandlerMiddleware + { + private readonly RequestDelegate _next; + + public FileUploaderHandlerMiddleware(RequestDelegate next) + { + _next = next; + } + public async Task InvokeAsync(HttpContext context, + SetupInfo setupInfo, + DaoFactory daoFactory, + FileSizeComment fileSizeComment, + IServiceProvider serviceProvider, + TenantExtra tenantExtra, + TenantStatisticsProvider tenantStatisticsProvider) + { + context.Request.EnableBuffering(); + + var fileUploadResult = new FileUploadResult(); + + if (context.Request.Form.Files.Count == 0) + { + await context.Response.WriteAsync(JsonSerializer.Serialize(fileUploadResult)); + } + + var fileName = context.Request.Form.Files[0].FileName; + var contentLength = context.Request.Form.Files[0].Length; + + if (String.IsNullOrEmpty(fileName) || contentLength == 0) + throw new InvalidOperationException(CRMErrorsResource.InvalidFile); + + if (0 < setupInfo.MaxUploadSize(tenantExtra, tenantStatisticsProvider) && setupInfo.MaxUploadSize(tenantExtra, tenantStatisticsProvider) < contentLength) + throw fileSizeComment.FileSizeException; + + fileName = fileName.LastIndexOf('\\') != -1 + ? fileName.Substring(fileName.LastIndexOf('\\') + 1) + : fileName; + + var document = serviceProvider.GetService>(); + + document.Title = fileName; + document.FolderID = daoFactory.GetFileDao().GetRoot(); + document.ContentLength = contentLength; + + document = daoFactory.GetFileDao().SaveFile(document, context.Request.Form.Files[0].OpenReadStream()); + + fileUploadResult.Data = document.ID; + fileUploadResult.FileName = document.Title; + fileUploadResult.FileURL = document.DownloadUrl; + fileUploadResult.Success = true; + + await context.Response.WriteAsync(JsonSerializer.Serialize(fileUploadResult)); + } + } + + public static class FileUploaderHandlerMiddlewareExtensions + { + public static IApplicationBuilder UseFileUploaderHandler(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Middlewares/ImportFileHandlerMiddleware.cs b/products/ASC.CRM/Server/Middlewares/ImportFileHandlerMiddleware.cs new file mode 100644 index 00000000000..1e495a831ba --- /dev/null +++ b/products/ASC.CRM/Server/Middlewares/ImportFileHandlerMiddleware.cs @@ -0,0 +1,96 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Threading.Tasks; + +using ASC.CRM.Core; +using ASC.Web.Core; +using ASC.Web.Core.Utility; +using ASC.Web.CRM.Classes; +using ASC.Web.CRM.Configuration; + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; + +namespace ASC.Web.CRM.HttpHandlers +{ + public class ImportFileHandlerMiddleware + { + private readonly RequestDelegate _next; + + public ImportFileHandlerMiddleware( + RequestDelegate next) + { + _next = next; + } + + public async Task Invoke(HttpContext context, + WebItemSecurity webItemSecurity, + CrmSecurity crmSecurity, + Global global, + ImportFromCSV importFromCSV) + { + if (!webItemSecurity.IsAvailableForMe(ProductEntryPoint.ID)) + throw crmSecurity.CreateSecurityException(); + + var fileUploadResult = new FileUploadResult(); + + if (context.Request.Form.Files.Count == 0) + { + await context.Response.WriteAsync(JsonSerializer.Serialize(fileUploadResult)); + } + + var fileName = context.Request.Form.Files[0].FileName; + var contentLength = context.Request.Form.Files[0].Length; + + String assignedPath; + + global.GetStore().SaveTemp("temp", out assignedPath, context.Request.Form.Files[0].OpenReadStream()); + + var jObject = importFromCSV.GetInfo(context.Request.Form.Files[0].OpenReadStream(), context.Request.Form["importSettings"]); + + var jsonDocumentAsDictionary = JsonSerializer.Deserialize>(jObject.ToString()); + + jsonDocumentAsDictionary.Add("assignedPath", assignedPath); + + fileUploadResult.Success = true; + fileUploadResult.Data = Global.EncodeTo64(JsonSerializer.Serialize(jsonDocumentAsDictionary)); + + await context.Response.WriteAsync(JsonSerializer.Serialize(fileUploadResult)); + } + } + + public static class ImportFileHandlerMiddlewareExtensions + { + public static IApplicationBuilder UseImportFileHandlerHandler(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Middlewares/OrganisationLogoHandlerMiddleware.cs b/products/ASC.CRM/Server/Middlewares/OrganisationLogoHandlerMiddleware.cs new file mode 100644 index 00000000000..78916654254 --- /dev/null +++ b/products/ASC.CRM/Server/Middlewares/OrganisationLogoHandlerMiddleware.cs @@ -0,0 +1,122 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Text.Json; + +using ASC.CRM.Core; +using ASC.CRM.Resources; +using ASC.Web.Core.Files; +using ASC.Web.Core.Utility; +using ASC.Web.CRM.Classes; +using ASC.Web.Studio.Core; + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; + +namespace ASC.Web.CRM.HttpHandlers +{ + public class OrganisationLogoHandlerMiddleware + { + private readonly RequestDelegate _next; + + public OrganisationLogoHandlerMiddleware( + RequestDelegate next + ) + { + _next = next; + } + + public async System.Threading.Tasks.Task Invoke(HttpContext context, + CrmSecurity crmSecurity, + SetupInfo setupInfo, + FileSizeComment fileSizeComment, + ContactPhotoManager contactPhotoManager, + OrganisationLogoManager organisationLogoManager) + { + context.Request.EnableBuffering(); + + if (!crmSecurity.IsAdmin) + throw crmSecurity.CreateSecurityException(); + + var fileUploadResult = new FileUploadResult(); + + if (context.Request.Form.Files.Count == 0) + { + await context.Response.WriteAsync(JsonSerializer.Serialize(fileUploadResult)); + } + + var fileName = context.Request.Form.Files[0].FileName; + var contentLength = context.Request.Form.Files[0].Length; + + if (String.IsNullOrEmpty(fileName) || contentLength == 0) + throw new InvalidOperationException(CRMErrorsResource.InvalidFile); + + if (0 < setupInfo.MaxImageUploadSize && setupInfo.MaxImageUploadSize < contentLength) + { + fileUploadResult.Success = false; + fileUploadResult.Message = fileSizeComment.GetFileImageSizeNote(CRMCommonResource.ErrorMessage_UploadFileSize, false).HtmlEncode(); + + await context.Response.WriteAsync(JsonSerializer.Serialize(fileUploadResult)); + } + + if (FileUtility.GetFileTypeByFileName(fileName) != FileType.Image) + { + fileUploadResult.Success = false; + fileUploadResult.Message = CRMJSResource.ErrorMessage_NotImageSupportFormat.HtmlEncode(); + + await context.Response.WriteAsync(JsonSerializer.Serialize(fileUploadResult)); + } + + try + { + var imageData = Global.ToByteArray(context.Request.Form.Files[0].OpenReadStream()); + var imageFormat = contactPhotoManager.CheckImgFormat(imageData); + var photoUri = organisationLogoManager.UploadLogo(imageData, imageFormat); + + fileUploadResult.Success = true; + fileUploadResult.Data = photoUri; + + await context.Response.WriteAsync(JsonSerializer.Serialize(fileUploadResult)); + } + catch (Exception exception) + { + fileUploadResult.Success = false; + fileUploadResult.Message = exception.Message.HtmlEncode(); + + await context.Response.WriteAsync(JsonSerializer.Serialize(fileUploadResult)); + } + } + } + + public static class OrganisationLogoHandlerMiddlewareExtensions + { + public static IApplicationBuilder UseOrganisationLogoHandler(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Middlewares/TenantConfigureMiddleware.cs b/products/ASC.CRM/Server/Middlewares/TenantConfigureMiddleware.cs new file mode 100644 index 00000000000..351c4b9b34d --- /dev/null +++ b/products/ASC.CRM/Server/Middlewares/TenantConfigureMiddleware.cs @@ -0,0 +1,31 @@ +using System.Threading.Tasks; + +using ASC.Core; +using ASC.Core.Common.Settings; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.EF; + +using Microsoft.AspNetCore.Http; + +namespace ASC.CRM.HttpHandlers +{ + public class TenantConfigureMiddleware + { + private readonly RequestDelegate _next; + + public TenantConfigureMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task InvokeAsync(HttpContext context, + DaoFactory daoFactory, + SettingsManager settingsManager, + CoreConfiguration coreConfiguration) + { + CrmDbContextSeed.SeedInitPortalData(settingsManager, daoFactory, coreConfiguration); + + await _next.Invoke(context); + } + } +} diff --git a/products/ASC.CRM/Server/Middlewares/WebToLeadFromHandlerMiddleware.cs b/products/ASC.CRM/Server/Middlewares/WebToLeadFromHandlerMiddleware.cs new file mode 100644 index 00000000000..f81b4103db8 --- /dev/null +++ b/products/ASC.CRM/Server/Middlewares/WebToLeadFromHandlerMiddleware.cs @@ -0,0 +1,448 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; +using System.Net; +using System.Text; +using System.Text.Json; +using System.Web; + +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.Settings; +using ASC.CRM.Classes; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; +using ASC.MessagingSystem; +using ASC.Web.Core; +using ASC.Web.CRM.Classes; +using ASC.Web.CRM.Configuration; +using ASC.Web.CRM.Services.NotifyService; + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; + +namespace ASC.Web.CRM.HttpHandlers +{ + public class WebToLeadFromHandlerMiddleware + { + private HttpContext _context; + private readonly RequestDelegate _next; + + public Global Global { get; set; } + public WebItemSecurity WebItemSecurity { get; set; } + public SecurityContext SecurityContext { get; set; } + public CrmSecurity CRMSecurity { get; set; } + public MessageTarget MessageTarget { get; set; } + public ILog Logger { get; set; } + public NotifyClient NotifyClient { get; set; } + public MessageService MessageService { get; set; } + public SettingsManager SettingsManager { get; set; } + public DaoFactory DaoFactory { get; set; } + public WebToLeadFromHandlerMiddleware( + RequestDelegate next +) + { + _next = next; + } + + private String GetValue(String propertyName) + { + return _context.Request.Form[propertyName]; + } + + private bool CheckPermission() + { + try + { + var webFromKey = GetValue("web_form_key"); + + if (String.IsNullOrEmpty(webFromKey)) + return false; + + var webFromKeyAsGuid = new Guid(webFromKey); + + var TenantSettings = SettingsManager.Load(); + + return TenantSettings.WebFormKey == webFromKeyAsGuid; + } + catch (Exception) + { + return false; + } + } + + public async System.Threading.Tasks.Task Invoke(HttpContext context, + WebItemSecurity webItemSecurity, + SecurityContext securityContext, + CrmSecurity crmSecurity, + MessageTarget messageTarget, + MessageService messageService, + Global global, + IOptionsMonitor logger, + NotifyClient notifyClient, + SettingsManager settingsManager, + DaoFactory daoFactory) + { + try + { + WebItemSecurity = webItemSecurity; + SecurityContext = securityContext; + CRMSecurity = crmSecurity; + MessageTarget = messageTarget; + MessageService = messageService; + Global = global; + Logger = logger.Get("ASC.CRM"); + NotifyClient = notifyClient; + SettingsManager = settingsManager; + + _context = context; + + SecurityContext.AuthenticateMe(ASC.Core.Configuration.Constants.CoreSystem); + + if (!CheckPermission()) + { + throw new Exception(CRMSettingResource.WebToLeadsForm_InvalidKeyException); + } + + var productInfo = WebItemSecurity.GetSecurityInfo(ProductEntryPoint.ID.ToString()); + if (!productInfo.Enabled) + { + throw new Exception(CRMCommonResource.CRMProductIsDisabled); + } + + Contact contact; + + var fieldCollector = new NameValueCollection(); + + var addressTemplate = new Dictionary(); + + foreach (String addressPartName in Enum.GetNames(typeof(AddressPart))) + addressTemplate.Add(addressPartName.ToLower(), ""); + + var addressTemplateStr = JsonSerializer.Serialize(addressTemplate); + + var isCompany = false; + + var isCompanyString = GetValue("is_company"); + var firstName = GetValue("firstName"); + var lastName = GetValue("lastName"); + var companyName = GetValue("companyName"); + + if (!String.IsNullOrEmpty(isCompanyString)) + { + if (!Boolean.TryParse(isCompanyString, out isCompany)) + { + throw new ArgumentException(); + } + } + else //old scheme + { + if (!String.IsNullOrEmpty(firstName)) + { + isCompany = false; + } + else if (!String.IsNullOrEmpty(companyName)) + { + isCompany = true; + } + else + { + throw new ArgumentException(); + } + } + + + if (isCompany) + { + contact = new Company(); + + ((Company)contact).CompanyName = companyName; + + fieldCollector.Add(CRMContactResource.CompanyName, companyName); + } + else + { + contact = new Person(); + + ((Person)contact).FirstName = firstName; + ((Person)contact).LastName = lastName; + ((Person)contact).JobTitle = GetValue("jobTitle"); + + fieldCollector.Add(CRMContactResource.FirstName, firstName); + fieldCollector.Add(CRMContactResource.LastName, lastName); + + if (!String.IsNullOrEmpty(GetValue("jobTitle"))) + fieldCollector.Add(CRMContactResource.JobTitle, ((Person)contact).JobTitle); + } + + contact.About = GetValue("about"); + + if (!String.IsNullOrEmpty(contact.About)) + fieldCollector.Add(CRMContactResource.About, contact.About); + + if (!String.IsNullOrEmpty(GetValue("is_shared"))) + { + contact.ShareType = Convert.ToBoolean(GetValue("is_shared")) + ? ShareType.ReadWrite + : ShareType.None; + } + else + { + contact.ShareType = (ShareType)(Convert.ToInt32(GetValue("share_type"))); + } + + contact.ID = daoFactory.GetContactDao().SaveContact(contact); + + var messageAction = contact is Company + ? MessageAction.CompanyCreatedWithWebForm + : MessageAction.PersonCreatedWithWebForm; + + MessageService.Send(MessageInitiator.System, messageAction, + MessageTarget.Create(contact.ID), contact.GetTitle()); + + var contactInfos = new List(); + + foreach (var key in _context.Request.Form.Keys) + { + if (key.StartsWith("customField_")) + { + var fieldID = Convert.ToInt32(key.Split(new[] { '_' })[1]); + String fieldValue = GetValue(key); + + if (String.IsNullOrEmpty(fieldValue)) continue; + + var customField = daoFactory.GetCustomFieldDao().GetFieldDescription(fieldID); + + if (customField == null || + !(customField.EntityType == EntityType.Contact || + customField.EntityType == EntityType.Company && isCompany || + customField.EntityType == EntityType.Person && !isCompany)) continue; + + if (customField.Type == CustomFieldType.CheckBox) + { + fieldValue = fieldValue == "on" || fieldValue == "true" ? "true" : "false"; + } + fieldCollector.Add(customField.Label, fieldValue); + + daoFactory.GetCustomFieldDao().SetFieldValue(isCompany ? EntityType.Company : EntityType.Person, contact.ID, fieldID, fieldValue); + } + else if (key.StartsWith("contactInfo_")) + { + var nameParts = key.Split(new[] { '_' }).Skip(1).ToList(); + var contactInfoType = (ContactInfoType)Enum.Parse(typeof(ContactInfoType), nameParts[0]); + var category = Convert.ToInt32(nameParts[1]); + + bool categoryIsExists = Enum.GetValues(ContactInfo.GetCategory(contactInfoType)) + .Cast() + .Any(categoryEnum => (int)categoryEnum == category); + if (!categoryIsExists) + throw new ArgumentException(String.Format("Category for {0} not found", nameParts[0])); + + if (contactInfoType == ContactInfoType.Address) + { + var addressPart = (AddressPart)Enum.Parse(typeof(AddressPart), nameParts[2]); + + var findedAddress = + contactInfos.Find( + item => + (category == item.Category) && (item.InfoType == ContactInfoType.Address)); + + if (findedAddress == null) + { + findedAddress = new ContactInfo + { + Category = category, + InfoType = contactInfoType, + Data = addressTemplateStr, + ContactID = contact.ID + }; + + contactInfos.Add(findedAddress); + } + + Dictionary addressParts = JsonSerializer.Deserialize>(findedAddress.Data); + addressParts[addressPart.ToString().ToLower()] = GetValue(key); + string newJson = JsonSerializer.Serialize(addressParts); + + findedAddress.Data = JsonSerializer.Serialize(addressParts); + + continue; + } + + var fieldValue = GetValue(key); + + if (String.IsNullOrEmpty(fieldValue)) continue; + + contactInfos.Add(new ContactInfo + { + Category = category, + InfoType = contactInfoType, + Data = fieldValue, + ContactID = contact.ID, + IsPrimary = true + }); + } + else if (String.Compare(key, "tag", true) == 0) + { + var tags = _context.Request.Form["tag"]; + + daoFactory.GetTagDao().SetTagToEntity(EntityType.Contact, contact.ID, tags); + } + } + + contactInfos.ForEach( + item => + fieldCollector[item.InfoType.ToLocalizedString()] = + PrepareteDataToView(item.InfoType, item.Data)); + + daoFactory.GetContactInfoDao().SaveList(contactInfos, contact); + + var notifyList = GetValue("notify_list"); + + if (!String.IsNullOrEmpty(notifyList)) + NotifyClient.SendAboutCreateNewContact( + notifyList + .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + .Select(item => new Guid(item)).ToList(), contact.ID, contact.GetTitle(), fieldCollector); + + var managersList = GetValue("managers_list"); + SetPermission(contact, managersList); + + if (contact is Person && !String.IsNullOrEmpty(companyName)) + AssignPersonToCompany((Person)contact, companyName, managersList, daoFactory); + + if (contact is Company && !String.IsNullOrEmpty(firstName) && !String.IsNullOrEmpty(lastName)) + AssignCompanyToPerson((Company)contact, firstName, lastName, managersList, daoFactory); + + SecurityContext.Logout(); + + var newURL = new UriBuilder(GetValue("return_url")).Uri.AbsoluteUri; + + + context.Response.Clear(); + context.Response.StatusCode = (int)HttpStatusCode.Found; + context.Response.Headers.Add("Location", newURL); + + await context.Response.WriteAsync(""); + await context.Response.WriteAsync(String.Format("", newURL)); + await context.Response.WriteAsync(String.Format("", newURL)); + await context.Response.WriteAsync(""); + await context.Response.WriteAsync(""); + } + catch (Exception error) + { + Logger.Error(error); + context.Response.StatusCode = (int)HttpStatusCode.BadRequest; + + await context.Response.WriteAsync(HttpUtility.HtmlEncode(error.Message)); + } + } + + private String PrepareteDataToView(ContactInfoType contactInfoType, String data) + { + if (contactInfoType != ContactInfoType.Address) return data; + + var addressParts = JsonDocument.Parse(data).RootElement; + + var address = new StringBuilder(); + + foreach (AddressPart addressPartEnum in Enum.GetValues(typeof(AddressPart))) + address.Append(addressParts.GetProperty(addressPartEnum.ToString().ToLower()).GetString() + " "); + + return address.ToString(); + } + + public bool IsReusable + { + get { return false; } + } + + protected void SetPermission(Contact contact, String privateList) + { + if (String.IsNullOrEmpty(privateList)) return; + + var selectedUsers = privateList + .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + .Select(item => new Guid(item)).ToList(); + + CRMSecurity.SetAccessTo(contact, selectedUsers); + } + + protected void AssignCompanyToPerson(Company company, String firstName, String lastName, String privateList, DaoFactory daoFactory) + { + var person = new Person + { + FirstName = firstName, + LastName = lastName, + CompanyID = company.ID + }; + person.ID = daoFactory.GetContactDao().SaveContact(person); + SetPermission(person, privateList); + } + + + protected void AssignPersonToCompany(Person person, String companyName, String privateList, DaoFactory daoFactory) + { + Company company; + + var findedCompanies = daoFactory.GetContactDao().GetContactsByName(companyName, true).ToList(); + + if (findedCompanies.Count == 0) + { + company = new Company + { + CompanyName = companyName + }; + + company.ID = daoFactory.GetContactDao().SaveContact(company); + + SetPermission(company, privateList); + } + else + { + company = (Company)findedCompanies[0]; + } + + daoFactory.GetContactDao().AddMember(person.ID, company.ID); + } + } + + public static class WebToLeadFromHandlerMiddlewareExtensions + { + public static IApplicationBuilder UseWebToLeadFromHandlerHandler(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Program.cs b/products/ASC.CRM/Server/Program.cs index 40cb0088ae6..8e70bacd1d5 100644 --- a/products/ASC.CRM/Server/Program.cs +++ b/products/ASC.CRM/Server/Program.cs @@ -1,84 +1,79 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading.Tasks; - -using ASC.Common.DependencyInjection; -using ASC.Common.Utils; - -using Autofac; -using Autofac.Extensions.DependencyInjection; - -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; - -namespace ASC.CRM -{ - public class Program - { - public async static Task Main(string[] args) - { - var host = CreateHostBuilder(args).Build(); - - await host.RunAsync(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .UseSystemd() - .UseWindowsService() - .UseServiceProviderFactory(new AutofacServiceProviderFactory()) - .ConfigureWebHostDefaults(webBuilder => - { - var builder = webBuilder.UseStartup(); - - builder.ConfigureKestrel((hostingContext, serverOptions) => - { - var kestrelConfig = hostingContext.Configuration.GetSection("Kestrel"); - - if (!kestrelConfig.Exists()) return; - - var unixSocket = kestrelConfig.GetValue("ListenUnixSocket"); - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - if (!String.IsNullOrWhiteSpace(unixSocket)) - { - unixSocket = String.Format(unixSocket, hostingContext.HostingEnvironment.ApplicationName.Replace("ASC.", "").Replace(".", "")); - - serverOptions.ListenUnixSocket(unixSocket); - } - } - }); - }) - .ConfigureAppConfiguration((hostingContext, config) => - { - var buided = config.Build(); - var path = buided["pathToConf"]; - if (!Path.IsPathRooted(path)) - { - path = Path.GetFullPath(CrossPlatform.PathCombine(hostingContext.HostingEnvironment.ContentRootPath, path)); - } - - config.SetBasePath(path); - config - .AddJsonFile("appsettings.json") - .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true) - .AddJsonFile("storage.json") - .AddJsonFile("kafka.json") - .AddJsonFile($"kafka.{hostingContext.HostingEnvironment.EnvironmentName}.json", true) - .AddEnvironmentVariables() - .AddCommandLine(args) - .AddInMemoryCollection(new Dictionary - { - {"pathToConf", path} - }); - }) - .ConfigureContainer((context, builder) => - { - builder.Register(context.Configuration, true, false); - });//if (!FilesIntegration.IsRegisteredFileSecurityProvider("crm", "crm_common"))//{// FilesIntegration.RegisterFileSecurityProvider("crm", "crm_common", new FileSecurityProvider());//}////Register prodjects' calendar events//CalendarManager.Instance.RegistryCalendarProvider(userid =>//{// if (WebItemSecurity.IsAvailableForUser(WebItemManager.CRMProductID, userid))// {// return new List { new CRMCalendar(userid) };// }// return new List();//}); - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; + +using ASC.Common.DependencyInjection; +using ASC.Common.Utils; + +using Autofac; +using Autofac.Extensions.DependencyInjection; + +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; + +namespace ASC.CRM +{ + public class Program + { + public async static System.Threading.Tasks.Task Main(string[] args) + { + var host = CreateHostBuilder(args).Build(); + + await host.RunAsync(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseSystemd() + .UseWindowsService() + .UseServiceProviderFactory(new AutofacServiceProviderFactory()) + .ConfigureWebHostDefaults(webBuilder => + { + var builder = webBuilder.UseStartup(); + + builder.ConfigureKestrel((hostingContext, serverOptions) => + { + var kestrelConfig = hostingContext.Configuration.GetSection("Kestrel"); + + if (!kestrelConfig.Exists()) return; + + var unixSocket = kestrelConfig.GetValue("ListenUnixSocket"); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + if (!String.IsNullOrWhiteSpace(unixSocket)) + { + unixSocket = String.Format(unixSocket, hostingContext.HostingEnvironment.ApplicationName.Replace("ASC.", "").Replace(".", "")); + + serverOptions.ListenUnixSocket(unixSocket); + } + } + }); + }) + .ConfigureAppConfiguration((hostingContext, config) => + { + var buided = config.Build(); + var path = buided["pathToConf"]; + if (!Path.IsPathRooted(path)) + { + path = Path.GetFullPath(CrossPlatform.PathCombine(hostingContext.HostingEnvironment.ContentRootPath, path)); + } + + config.SetBasePath(path); + config + .AddJsonFile("appsettings.json") + .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true) + .AddJsonFile("storage.json") + .AddJsonFile("kafka.json") + .AddJsonFile($"kafka.{hostingContext.HostingEnvironment.EnvironmentName}.json", true) + .AddEnvironmentVariables() + .AddCommandLine(args) + .AddInMemoryCollection(new Dictionary + { + {"pathToConf", path} + }); + }); + } +} diff --git a/products/ASC.CRM/Server/Properties/launchSettings.json b/products/ASC.CRM/Server/Properties/launchSettings.json index 25ff3e6f31b..724b07dc597 100644 --- a/products/ASC.CRM/Server/Properties/launchSettings.json +++ b/products/ASC.CRM/Server/Properties/launchSettings.json @@ -1,29 +1,29 @@ -{ - "profiles": { - "Kestrel WebServer": { - "commandName": "Project", - "launchBrowser": false, - "launchUrl": "http://localhost:5021/api/2.0/crm/info", - "environmentVariables": { - "$STORAGE_ROOT": "../../../Data", - "log__dir": "../../../Logs", - "log__name": "crm", - "ASPNETCORE_ENVIRONMENT": "Development", - "ASPNETCORE_URLS": "http://localhost:5021" - } - }, - "WSL 2 : Ubuntu 20.04": { - "commandName": "WSL2", - "launchBrowser": false, - "launchUrl": "http://localhost:5021/api/2.0/crm/info", - "environmentVariables": { - "$STORAGE_ROOT": "../../../Data", - "log__dir": "../../../Logs", - "log__name": "crm", - "ASPNETCORE_ENVIRONMENT": "Development", - "ASPNETCORE_URLS": "http://localhost:5021" - }, - "distributionName": "Ubuntu-20.04" - } - } +{ + "profiles": { + "Kestrel WebServer": { + "commandName": "Project", + "launchBrowser": false, + "launchUrl": "http://localhost:5021/api/2.0/crm/info", + "environmentVariables": { + "$STORAGE_ROOT": "../../../Data", + "log__dir": "../../../Logs", + "log__name": "crm", + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_URLS": "http://localhost:5021" + } + }, + "WSL 2 : Ubuntu 20.04": { + "commandName": "WSL2", + "launchBrowser": false, + "launchUrl": "http://localhost:5021/api/2.0/crm/info", + "environmentVariables": { + "$STORAGE_ROOT": "../../../Data", + "log__dir": "../../../Logs", + "log__name": "crm", + "ASPNETCORE_ENVIRONMENT": "Development", + "ASPNETCORE_URLS": "http://localhost:5021" + }, + "distributionName": "Ubuntu-20.04" + } + } } \ No newline at end of file diff --git a/products/ASC.CRM/Server/ReportTemplates/SalesByManagers.docbuilder b/products/ASC.CRM/Server/ReportTemplates/SalesByManagers.docbuilder new file mode 100644 index 00000000000..90713e0a8be --- /dev/null +++ b/products/ASC.CRM/Server/ReportTemplates/SalesByManagers.docbuilder @@ -0,0 +1,201 @@ + +builder.CreateFile("xlsx"); + +var mainHeaderFontSize = 24; +var headerFontSize = 14; +var chartHeaderFontSize = 12; +var smallFontSize = 10; +var legendFontSize = 9; + +var blackFontColor = Api.CreateColorFromRGB(63, 63, 63); +var grayFontColor = Api.CreateColorFromRGB(127, 127, 127); + +var lightGrayBgColor = Api.CreateColorFromRGB(242, 242, 242); +var darkGrayBgColor = Api.CreateColorFromRGB(216, 216, 216); + +var lightGrayBorderColor = Api.CreateColorFromRGB(216, 216, 216); +var darkGrayBorderColor = Api.CreateColorFromRGB(127, 127, 127); + +var reportData = ${reportData}; + +reportData.resource.sheetName = reportData.resource.sheetName.slice(0, 31); + +var oWorksheet = Api.GetActiveSheet(); + +function toColumnName(num) { + for (var res = '', a = 1, b = 26; (num -= a) >= 0; a = b, b *= 26) { + res = String.fromCharCode(parseInt((num % b) / a) + 65) + res; + } + return res; +} + +function customizeWorksheet() { + Api.SetThemeColors("Origin"); + + oWorksheet.SetName(reportData.resource.sheetName); + oWorksheet.SetDisplayGridlines(false); + oWorksheet.SetColumnWidth(0, 6); + oWorksheet.SetColumnWidth(1, 30); + + for (var i = 2; i < 35; i++) { + oWorksheet.SetColumnWidth(i, 15); + } +} + +function writeText() { + var sRange = oWorksheet.GetRange("B2"); + sRange.SetFontSize(mainHeaderFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header); + + sRange = oWorksheet.GetRange("B3"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetBold(true); + sRange.SetValue(reportData.resource.dateRangeLabel); + + sRange = oWorksheet.GetRange("B4"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetAlignHorizontal("left"); + sRange.SetValue(reportData.resource.dateRangeValue); + + sRange = oWorksheet.GetRange("B39"); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header1); +} + +function writeRow(startColIndex, startRowIndex, dataArray, fontSize, fontColor, bold, borderType, borderColor, bgColor, wrap) { + var range = toColumnName(startColIndex) + startRowIndex + ":"; + + for (var i = 0; i < dataArray.length; i++) { + var cell = oWorksheet.GetRange(toColumnName(startColIndex) + startRowIndex); + + if (typeof(dataArray[i]) == "object") { + cell.SetNumberFormat(dataArray[i].format); + cell.SetValue(dataArray[i].value); + } else { + cell.SetValue(dataArray[i].toString()); + } + + if (i == 0) { + cell.SetAlignHorizontal("left"); + } else { + cell.SetAlignHorizontal("right"); + } + + startColIndex++; + } + + range += toColumnName(startColIndex - 1) + startRowIndex; + + var sRange = oWorksheet.GetRange(range); + + if (fontSize) + sRange.SetFontSize(fontSize); + + if (fontColor) + sRange.SetFontColor(fontColor); + + if (bold) + sRange.SetBold(true); + + if (borderType) + sRange.SetBorders(borderType, "Thin", borderColor); + + sRange.SetWrap(wrap || false) +} + +function writeCharts(startColIndex, startRowIndex, rowIndex) { + var chartRange1 = "$" + toColumnName(startColIndex) + "$" + startRowIndex + ":$" + toColumnName(startColIndex + reportData.tbody[0].length - 1) + "$" + (startRowIndex + reportData.tbody.length); + var chartRange2 = "$" + toColumnName(startColIndex) + "$" + (rowIndex - reportData.tbody.length - 1) + ":$" + toColumnName(startColIndex + 1) + "$" + (rowIndex - 1); + + var oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange1, true, "barStacked", 2, 240 * 36000, 70 * 36000, 1, 0, 7, 0); + oChart.SetTitle(reportData.resource.chartName1, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); + + oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange2, false, "bar", 2, 240 * 36000, 70 * 36000, 1, 0, 21, -0.5 * 36000); + oChart.SetTitle(reportData.resource.chartName2, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("none"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); +} + +function writeTable(startColIndex, startRowIndex) { + + var colIndex = startColIndex; + var rowIndex = startRowIndex; + + var headerRow = [reportData.resource.manager].concat(reportData.thead); + writeRow(colIndex, rowIndex, headerRow, smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + for (var j = 0; j < reportData.tbody.length; j++) { + if (j == reportData.tbody.length - 1) { + writeRow(colIndex, rowIndex, reportData.tbody[j], smallFontSize, blackFontColor, false, "Bottom", darkGrayBorderColor, null); + } else { + writeRow(colIndex, rowIndex, reportData.tbody[j], smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + } + rowIndex++; + } + + var startCell; + var endCell; + + var footerRow = [reportData.resource.total]; + for (var k = 1; k < reportData.tbody[0].length; k++) { + startCell = toColumnName(colIndex + k) + (startRowIndex + 1); + endCell = toColumnName(colIndex + k) + (startRowIndex + reportData.tbody.length); + footerRow[k] = "=SUM(" + startCell + ":" + endCell + ")"; + } + writeRow(colIndex, rowIndex, footerRow, smallFontSize, blackFontColor, true, null, null, null); + + rowIndex = rowIndex + 3; + + var sRange = oWorksheet.GetRange(toColumnName(colIndex) + rowIndex); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header2); + + rowIndex = rowIndex + 2; + + headerRow = [reportData.resource.manager, reportData.resource.summary]; + writeRow(colIndex, rowIndex, headerRow, smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + for (var l = 0; l <= reportData.tbody.length; l++) { + startCell = toColumnName(startColIndex + 1) + (startRowIndex + 1 + l); + endCell = toColumnName(startColIndex + reportData.tbody[0].length - 1) + (startRowIndex + 1 + l); + var row = ["=" + toColumnName(startColIndex) + (startRowIndex + 1 + l), "=SUM(" + startCell + ":" + endCell + ")"]; + + if(l == reportData.tbody.length) { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, true, null, null, null); + } else { + if(l == reportData.tbody.length - 1) { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", darkGrayBorderColor, null); + } else { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + } + rowIndex++; + } + } + + writeCharts(startColIndex, startRowIndex, rowIndex); +} + +customizeWorksheet(); +writeText(); + +if (reportData.tbody && reportData.tbody.length && reportData.thead && reportData.thead.length) { + writeTable(2, 41); +} + +builder.SaveFile("xlsx", "${outputFilePath}"); +builder.CloseFile(); \ No newline at end of file diff --git a/products/ASC.CRM/Server/ReportTemplates/SalesForecast.docbuilder b/products/ASC.CRM/Server/ReportTemplates/SalesForecast.docbuilder new file mode 100644 index 00000000000..bc87996e27d --- /dev/null +++ b/products/ASC.CRM/Server/ReportTemplates/SalesForecast.docbuilder @@ -0,0 +1,165 @@ + +builder.CreateFile("xlsx"); + +var mainHeaderFontSize = 24; +var headerFontSize = 14; +var chartHeaderFontSize = 12; +var smallFontSize = 10; +var legendFontSize = 9; + +var blackFontColor = Api.CreateColorFromRGB(63, 63, 63); +var grayFontColor = Api.CreateColorFromRGB(127, 127, 127); + +var lightGrayBgColor = Api.CreateColorFromRGB(242, 242, 242); +var darkGrayBgColor = Api.CreateColorFromRGB(216, 216, 216); + +var lightGrayBorderColor = Api.CreateColorFromRGB(216, 216, 216); +var darkGrayBorderColor = Api.CreateColorFromRGB(127, 127, 127); + +var reportData = ${reportData}; + +reportData.resource.sheetName = reportData.resource.sheetName.slice(0, 31); + +var oWorksheet = Api.GetActiveSheet(); + +function toColumnName(num) { + for (var res = '', a = 1, b = 26; (num -= a) >= 0; a = b, b *= 26) { + res = String.fromCharCode(parseInt((num % b) / a) + 65) + res; + } + return res; +} + +function customizeWorksheet() { + Api.SetThemeColors("Origin"); + + oWorksheet.SetName(reportData.resource.sheetName); + oWorksheet.SetDisplayGridlines(false); + oWorksheet.SetColumnWidth(0, 6); + oWorksheet.SetColumnWidth(1, 30); + + for (var i = 2; i < 35; i++) { + oWorksheet.SetColumnWidth(i, 15); + } +} + +function writeText() { + var sRange = oWorksheet.GetRange("B2"); + sRange.SetFontSize(mainHeaderFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header); + + sRange = oWorksheet.GetRange("B3"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetBold(true); + sRange.SetValue(reportData.resource.dateRangeLabel); + + sRange = oWorksheet.GetRange("B4"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetAlignHorizontal("left"); + sRange.SetValue(reportData.resource.dateRangeValue); + + sRange = oWorksheet.GetRange("B25"); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header1); +} + +function writeRow(startColIndex, startRowIndex, dataArray, fontSize, fontColor, bold, borderType, borderColor, bgColor, wrap) { + var range = toColumnName(startColIndex) + startRowIndex + ":"; + + for (var i = 0; i < dataArray.length; i++) { + var cell = oWorksheet.GetRange(toColumnName(startColIndex) + startRowIndex); + + if (typeof(dataArray[i]) == "object") { + cell.SetNumberFormat(dataArray[i].format); + cell.SetValue(dataArray[i].value); + } else { + cell.SetValue(dataArray[i].toString()); + } + + if (i == 0) { + cell.SetAlignHorizontal("left"); + } else { + cell.SetAlignHorizontal("right"); + } + + startColIndex++; + } + + range += toColumnName(startColIndex - 1) + startRowIndex; + + var sRange = oWorksheet.GetRange(range); + + if (fontSize) + sRange.SetFontSize(fontSize); + + if (fontColor) + sRange.SetFontColor(fontColor); + + if (bold) + sRange.SetBold(true); + + if (borderType) + sRange.SetBorders(borderType, "Thin", borderColor); + + if (bgColor) + sRange.SetFillColor(bgColor); + + sRange.SetWrap(wrap || false); +} + +function writeCharts(startColIndex, startRowIndex, rowIndex) { + var chartRange = "$" + toColumnName(startColIndex) + "$" + startRowIndex + ":$" + toColumnName(startColIndex + reportData.tbody[0].length - 1) + "$" + (startRowIndex + reportData.tbody.length); + + var oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange, false, "lineNormal", 2, 240 * 36000, 70 * 36000, 1, 0, 7, 0); + oChart.SetTitle(reportData.resource.chartName, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); +} + +function writeTable(startColIndex, startRowIndex) { + + var colIndex = startColIndex; + var rowIndex = startRowIndex; + + writeRow(colIndex, rowIndex, reportData.thead, smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + for (var i = 0; i < reportData.tbody.length; i++) { + if (i == reportData.tbody.length - 1) { + writeRow(colIndex, rowIndex, reportData.tbody[i], smallFontSize, blackFontColor, false, "Bottom", darkGrayBorderColor, null); + } else { + writeRow(colIndex, rowIndex, reportData.tbody[i], smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + } + + rowIndex++; + } + + var startCell; + var endCell; + + var footerRow = [reportData.resource.total]; + for (var k = 1; k < reportData.tbody[0].length; k++) { + startCell = toColumnName(colIndex + k) + (startRowIndex + 1); + endCell = toColumnName(colIndex + k) + (startRowIndex + reportData.tbody.length); + footerRow[k] = "=SUM(" + startCell + ":" + endCell + ")"; + } + writeRow(colIndex, rowIndex, footerRow, smallFontSize, blackFontColor, true, null, null, null); + + writeCharts(startColIndex, startRowIndex, rowIndex); +} + +customizeWorksheet(); +writeText(); + +if (reportData.tbody && reportData.tbody.length && reportData.thead && reportData.thead.length) { + writeTable(2, 27); +} + +builder.SaveFile("xlsx", "${outputFilePath}"); +builder.CloseFile(); \ No newline at end of file diff --git a/products/ASC.CRM/Server/ReportTemplates/SalesFunnel.docbuilder b/products/ASC.CRM/Server/ReportTemplates/SalesFunnel.docbuilder new file mode 100644 index 00000000000..10ea1e7f8ce --- /dev/null +++ b/products/ASC.CRM/Server/ReportTemplates/SalesFunnel.docbuilder @@ -0,0 +1,329 @@ + +builder.CreateFile("xlsx"); + +var mainHeaderFontSize = 24; +var headerFontSize = 14; +var chartHeaderFontSize = 12; +var smallFontSize = 10; +var legendFontSize = 9; + +var blackFontColor = Api.CreateColorFromRGB(63, 63, 63); +var grayFontColor = Api.CreateColorFromRGB(127, 127, 127); + +var lightGrayBgColor = Api.CreateColorFromRGB(242, 242, 242); +var darkGrayBgColor = Api.CreateColorFromRGB(216, 216, 216); + +var lightGrayBorderColor = Api.CreateColorFromRGB(216, 216, 216); +var darkGrayBorderColor = Api.CreateColorFromRGB(127, 127, 127); + +var reportData = ${reportData}; + +reportData.resource.sheetName = reportData.resource.sheetName.slice(0, 31); + +var oWorksheet = Api.GetActiveSheet(); + +function toColumnName(num) { + for (var res = '', a = 1, b = 26; (num -= a) >= 0; a = b, b *= 26) { + res = String.fromCharCode(parseInt((num % b) / a) + 65) + res; + } + return res; +} + +function customizeWorksheet() { + Api.SetThemeColors("Origin"); + + oWorksheet.SetName(reportData.resource.sheetName); + oWorksheet.SetDisplayGridlines(false); + oWorksheet.SetColumnWidth(0, 6); + oWorksheet.SetColumnWidth(1, 30); + + for (var i = 2; i < 35; i++) { + oWorksheet.SetColumnWidth(i, 15); + } +} + +function writeText() { + var sRange = oWorksheet.GetRange("B2"); + sRange.SetFontSize(mainHeaderFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header); + + sRange = oWorksheet.GetRange("B3"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetBold(true); + sRange.SetValue(reportData.resource.dateRangeLabel); + + sRange = oWorksheet.GetRange("B4"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetAlignHorizontal("left"); + sRange.SetValue(reportData.resource.dateRangeValue); + + sRange = oWorksheet.GetRange("B39"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetAlignHorizontal("left"); + sRange.SetValue(reportData.resource.totalCountLabel); + + sRange = oWorksheet.GetRange("B40"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetAlignHorizontal("left"); + sRange.SetBold(true); + sRange.SetValue(reportData.resource.totalCountValue.toString()); + + sRange = oWorksheet.GetRange("B41"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetAlignHorizontal("left"); + sRange.SetValue(reportData.resource.totalBudgetLabel); + + sRange = oWorksheet.GetRange("B42"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetAlignHorizontal("left"); + sRange.SetBold(true); + sRange.SetNumberFormat("0.00"); + sRange.SetValue(reportData.resource.totalBudgetValue.toString()); + + sRange = oWorksheet.GetRange("B43"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetAlignHorizontal("left"); + sRange.SetValue(reportData.resource.averageBidLabel); + + sRange = oWorksheet.GetRange("B44"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetAlignHorizontal("left"); + sRange.SetBold(true); + sRange.SetNumberFormat("0.00"); + sRange.SetValue(reportData.resource.averageBidValue.toString()); + + sRange = oWorksheet.GetRange("B45"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetAlignHorizontal("left"); + sRange.SetValue(reportData.resource.averageDurationLabel); + + sRange = oWorksheet.GetRange("B46"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetAlignHorizontal("left"); + sRange.SetBold(true); + sRange.SetValue(reportData.resource.averageDurationValue.toString()); +} + +function writeRow(startColIndex, startRowIndex, dataArray, fontSize, fontColor, bold, borderType, borderColor, bgColor, wrap) { + var range = toColumnName(startColIndex) + startRowIndex + ":"; + + for (var i = 0; i < dataArray.length; i++) { + var cell = oWorksheet.GetRange(toColumnName(startColIndex) + startRowIndex); + + if (typeof(dataArray[i]) == "object") { + cell.SetNumberFormat(dataArray[i].format); + cell.SetValue(dataArray[i].value); + } else { + cell.SetValue(dataArray[i].toString()); + } + + if (i == 0) { + cell.SetAlignHorizontal("left"); + } else { + cell.SetAlignHorizontal("right"); + } + + startColIndex++; + } + + range += toColumnName(startColIndex - 1) + startRowIndex; + + var sRange = oWorksheet.GetRange(range); + + if (fontSize) + sRange.SetFontSize(fontSize); + + if (fontColor) + sRange.SetFontColor(fontColor); + + if (bold) + sRange.SetBold(true); + + if (borderType) + sRange.SetBorders(borderType, "Thin", borderColor); + + if (bgColor) + sRange.SetFillColor(bgColor); + + sRange.SetWrap(wrap || false); +} + +function writeCharts(startColIndex, startRowIndex, length) { + var chartRange = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 8) + ":$" + toColumnName(startColIndex + 1) + "$" + (startRowIndex + 7 + length); + var chartRange1 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 18 + length) + ":$" + toColumnName(startColIndex + 1) + "$" + (startRowIndex + 17 + 2 * length); + var chartRange2 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 3) + ":$" + toColumnName(startColIndex + 1) + "$" + (startRowIndex + 5); + var chartRange3 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 13 + length) + ":$" + toColumnName(startColIndex + 1) + "$" + (startRowIndex + 15 + length); + + var oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange, false, "horizontalBar", 2, 120 * 36000, 70 * 36000, 1, 0, 7, 0); + oChart.SetTitle(reportData.resource.chartName, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("none"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); + oChart.SetHorAxisTickLabelPosition("none"); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); + oChart.SetMajorHorizontalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + oChart.SetMajorVerticalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + oChart.SetVerAxisOrientation(false); + + oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange1, false, "horizontalBar", 2, 120 * 36000, 70 * 36000, 5, -0.5 * 36000, 7, 0); + oChart.SetTitle(reportData.resource.chartName1, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("none"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); + oChart.SetHorAxisTickLabelPosition("none"); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); + oChart.SetMajorHorizontalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + oChart.SetMajorVerticalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + oChart.SetVerAxisOrientation(false); + + oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange2, false, "pie", 2, 120 * 36000, 70 * 36000, 1, 0, 21, -0.5 * 36000); + oChart.SetTitle(reportData.resource.chartName2, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); + + oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange3, false, "pie", 2, 120 * 36000, 70 * 36000, 5, -0.5 * 36000, 21, -0.5 * 36000); + oChart.SetTitle(reportData.resource.chartName3, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); +} + +function getConversion(cell1, cell2){ + return "=IF(" + cell2 + ">0;" + cell1 + "/" + cell2 +";\"0%\")"; +} + +function writeTable(startColIndex, startRowIndex) { + + var colIndex = startColIndex; + var rowIndex = startRowIndex; + + var sRange = oWorksheet.GetRange(toColumnName(colIndex) + rowIndex); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header1); + + rowIndex++; + rowIndex++; + + var row = []; + var typeBody = [ + [reportData.resource.status0, 0, 0], + [reportData.resource.status1, 0, 0], + [reportData.resource.status2, 0, 0], + ]; + var stageBody = []; + + for (var i = 0; i < reportData.data.length; i++) { + typeBody[reportData.data[i][1]][1]+=reportData.data[i][2]; + typeBody[reportData.data[i][1]][2]+=reportData.data[i][3]; + + if(reportData.data[i][1] != 2){ + stageBody.push([reportData.data[i][0], reportData.data[i][2], reportData.data[i][3]]); + } + } + + var head = [reportData.resource.deals, reportData.resource.count, "%"]; + + writeRow(colIndex, rowIndex, head, smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + for (var i = 0; i < typeBody.length; i++) { + row = [typeBody[i][0], typeBody[i][1], {format: "0%", value: getConversion(toColumnName(colIndex + 1) + rowIndex, "B40")}]; + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + rowIndex++; + } + + rowIndex++; + + head = [reportData.resource.stage, reportData.resource.count, reportData.resource.conversion]; + + writeRow(colIndex, rowIndex, head, smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + var tmp = reportData.resource.totalCountValue - typeBody[2][1]; + + for (var i = 0; i < stageBody.length; i++) { + if (i == 0) { + row = [stageBody[i][0], tmp, {format: "0%", value: "1"}]; + } else { + row = [stageBody[i][0], tmp, {format: "0%", value: getConversion(toColumnName(colIndex + 1) + rowIndex, toColumnName(colIndex + 1) + (rowIndex - 1))}]; + } + + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + rowIndex++; + + tmp-=stageBody[i][1]; + } + + rowIndex+=2; + + sRange = oWorksheet.GetRange(toColumnName(colIndex) + rowIndex); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header2); + + rowIndex+=2; + + head = [reportData.resource.deals, reportData.resource.budget, "%"]; + + writeRow(colIndex, rowIndex, head, smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + for (var i = 0; i < typeBody.length; i++) { + row = [typeBody[i][0], {format: "0.00", value: typeBody[i][2].toString()}, {format: "0%", value: getConversion(toColumnName(colIndex + 1) + rowIndex, "B42")}]; + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + rowIndex++; + } + + rowIndex++; + + head = [reportData.resource.stage, reportData.resource.budget, reportData.resource.conversion]; + + writeRow(colIndex, rowIndex, head, smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + tmp = reportData.resource.totalBudgetValue - typeBody[2][2]; + + for (var i = 0; i < stageBody.length; i++) { + if (i == 0) { + row = [stageBody[i][0], {format: "0.00", value: tmp.toString()}, {format: "0%", value: "1"}]; + } else { + row = [stageBody[i][0], {format: "0.00", value: tmp.toString()}, {format: "0%", value: getConversion(toColumnName(colIndex + 1) + rowIndex, toColumnName(colIndex + 1) + (rowIndex - 1))}]; + } + + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + rowIndex++; + + tmp-=stageBody[i][2]; + } + + writeCharts(startColIndex, startRowIndex, stageBody.length); +} + +customizeWorksheet(); +writeText(); + +if (reportData.data && reportData.data.length) { + writeTable(2, 49); +} + +builder.SaveFile("xlsx", "${outputFilePath}"); +builder.CloseFile(); \ No newline at end of file diff --git a/products/ASC.CRM/Server/ReportTemplates/SummaryAtThisMoment.docbuilder b/products/ASC.CRM/Server/ReportTemplates/SummaryAtThisMoment.docbuilder new file mode 100644 index 00000000000..b37b977015a --- /dev/null +++ b/products/ASC.CRM/Server/ReportTemplates/SummaryAtThisMoment.docbuilder @@ -0,0 +1,327 @@ + +builder.CreateFile("xlsx"); + +var mainHeaderFontSize = 24; +var headerFontSize = 14; +var chartHeaderFontSize = 12; +var smallFontSize = 10; +var legendFontSize = 9; + +var blackFontColor = Api.CreateColorFromRGB(63, 63, 63); +var grayFontColor = Api.CreateColorFromRGB(127, 127, 127); + +var lightGrayBgColor = Api.CreateColorFromRGB(242, 242, 242); +var darkGrayBgColor = Api.CreateColorFromRGB(216, 216, 216); + +var lightGrayBorderColor = Api.CreateColorFromRGB(216, 216, 216); +var darkGrayBorderColor = Api.CreateColorFromRGB(127, 127, 127); + +var reportData = ${reportData}; + +reportData.resource.sheetName = reportData.resource.sheetName.slice(0, 31); + +var oWorksheet = Api.GetActiveSheet(); + +function toColumnName(num) { + for (var res = '', a = 1, b = 26; (num -= a) >= 0; a = b, b *= 26) { + res = String.fromCharCode(parseInt((num % b) / a) + 65) + res; + } + return res; +} + +function customizeWorksheet() { + Api.SetThemeColors("Origin"); + + oWorksheet.SetName(reportData.resource.sheetName); + oWorksheet.SetDisplayGridlines(false); + oWorksheet.SetColumnWidth(0, 6); + oWorksheet.SetColumnWidth(1, 30); + + for (var i = 2; i < 35; i++) { + oWorksheet.SetColumnWidth(i, 15); + } +} + +function writeText() { + var sRange = oWorksheet.GetRange("B2"); + sRange.SetFontSize(mainHeaderFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header); + + sRange = oWorksheet.GetRange("B3"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetBold(true); + sRange.SetValue(reportData.resource.dateRangeLabel); + + sRange = oWorksheet.GetRange("B4"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetAlignHorizontal("left"); + sRange.SetValue(reportData.resource.dateRangeValue); + + sRange = oWorksheet.GetRange("B53"); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header1); +} + +function writeRow(startColIndex, startRowIndex, dataArray, fontSize, fontColor, bold, borderType, borderColor, bgColor, wrap) { + var range = toColumnName(startColIndex) + startRowIndex + ":"; + + for (var i = 0; i < dataArray.length; i++) { + var cell = oWorksheet.GetRange(toColumnName(startColIndex) + startRowIndex); + + if (typeof(dataArray[i]) == "object") { + cell.SetNumberFormat(dataArray[i].format); + cell.SetValue(dataArray[i].value); + } else { + cell.SetValue(dataArray[i].toString()); + } + + if (i == 0) { + cell.SetAlignHorizontal("left"); + } else { + cell.SetAlignHorizontal("right"); + } + + startColIndex++; + } + + range += toColumnName(startColIndex - 1) + startRowIndex; + + var sRange = oWorksheet.GetRange(range); + + if (fontSize) + sRange.SetFontSize(fontSize); + + if (fontColor) + sRange.SetFontColor(fontColor); + + if (bold) + sRange.SetBold(true); + + if (borderType) + sRange.SetBorders(borderType, "Thin", borderColor); + + if (bgColor) + sRange.SetFillColor(bgColor); + + sRange.SetWrap(wrap || false); +} + +function writeCharts(startColIndex, startRowIndex, rowIndex) { + var chartRange = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 1) + ":$" + toColumnName(startColIndex + 1) + "$" + (startRowIndex + 3); + var chartRange1 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 6) + ":$" + toColumnName(startColIndex + 1) + "$" + (startRowIndex + 5 + reportData.data.DealsInfo.ByStage.length); + var chartRange2 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 11 + reportData.data.DealsInfo.ByStage.length) + ":$" + toColumnName(startColIndex + 1) + "$" + (startRowIndex + 10 + reportData.data.DealsInfo.ByStage.length + reportData.data.ContactsInfo.ByType.length); + var chartRange3 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 13 + reportData.data.DealsInfo.ByStage.length + reportData.data.ContactsInfo.ByType.length) + ":$" + toColumnName(startColIndex + 1) + "$" + (startRowIndex + 12 + reportData.data.DealsInfo.ByStage.length + reportData.data.ContactsInfo.ByType.length + reportData.data.ContactsInfo.ByStage.length); + var chartRange4 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 17 + reportData.data.DealsInfo.ByStage.length + reportData.data.ContactsInfo.ByType.length + reportData.data.ContactsInfo.ByStage.length) + ":$" + toColumnName(startColIndex + 2) + "$" + (startRowIndex + 17 + reportData.data.DealsInfo.ByStage.length + reportData.data.ContactsInfo.ByType.length + reportData.data.ContactsInfo.ByStage.length + reportData.data.TasksInfo.length); + var chartRange5 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 23 + reportData.data.DealsInfo.ByStage.length + reportData.data.ContactsInfo.ByType.length + reportData.data.ContactsInfo.ByStage.length + reportData.data.TasksInfo.length) + ":$" + toColumnName(startColIndex + 1) + "$" + (startRowIndex + 24 + reportData.data.DealsInfo.ByStage.length + reportData.data.ContactsInfo.ByType.length + reportData.data.ContactsInfo.ByStage.length + reportData.data.TasksInfo.length); + + var oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange, true, "bar", 2, 120 * 36000, 70 * 36000, 1, 0, 7, 0); + oChart.SetTitle(reportData.resource.chartName, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); + oChart.SetVertAxisTickLabelPosition("none"); + oChart.SetHorAxisTickLabelPosition("none"); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); + oChart.SetMajorHorizontalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + oChart.SetMajorVerticalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + + oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange1, false, "horizontalBar", 2, 120 * 36000, 70 * 36000, 5, -0.5 * 36000, 7, 0); + oChart.SetTitle(reportData.resource.chartName1, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("none"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); + oChart.SetHorAxisTickLabelPosition("none"); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); + oChart.SetMajorHorizontalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + oChart.SetMajorVerticalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + oChart.SetVerAxisOrientation(false); + + oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange2, false, "pie", 2, 120 * 36000, 70 * 36000, 1, 0, 21, -0.5 * 36000); + oChart.SetTitle(reportData.resource.chartName2, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); + + oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange3, false, "pie", 2, 120 * 36000, 70 * 36000, 5, -0.5 * 36000, 21, -0.5 * 36000); + oChart.SetTitle(reportData.resource.chartName3, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); + + oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange4, false, "bar", 2, 120 * 36000, 70 * 36000, 1, 0, 35, -1 * 36000); + oChart.SetTitle(reportData.resource.chartName4, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); + + oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange5, true, "bar", 2, 120 * 36000, 70 * 36000, 5, -0.5 * 36000, 35, -1 * 36000); + oChart.SetTitle(reportData.resource.chartName5, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); + oChart.SetVertAxisTickLabelPosition("none"); + oChart.SetHorAxisTickLabelPosition("none"); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); + oChart.SetMajorHorizontalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + oChart.SetMajorVerticalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); +} + +function writeTable(startColIndex, startRowIndex) { + + var colIndex = startColIndex; + var rowIndex = startRowIndex; + + //deals by status + writeRow(colIndex, rowIndex, [reportData.resource.status, reportData.resource.budget, reportData.resource.count], smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.open, {format: "0.00", value: (reportData.data.DealsInfo.Open[0][1] || 0).toString()}, reportData.data.DealsInfo.Open[0][0] || 0], smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.overdue, {format: "0.00", value: (reportData.data.DealsInfo.Overdue[0][1] || 0).toString()}, reportData.data.DealsInfo.Overdue[0][0] || 0], smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.near, {format: "0.00", value: (reportData.data.DealsInfo.Near[0][1] || 0).toString()}, reportData.data.DealsInfo.Near[0][0] || 0], smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + rowIndex++; + + rowIndex++; + + //deals by count + writeRow(colIndex, rowIndex, [reportData.resource.stage, reportData.resource.budget, reportData.resource.count], smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + for (var i = 0; i < reportData.data.DealsInfo.ByStage.length; i++) { + var row = [reportData.data.DealsInfo.ByStage[i][0] || reportData.resource.notSpecified, {format: "0.00", value: (reportData.data.DealsInfo.ByStage[i][2] || 0).toString()}, reportData.data.DealsInfo.ByStage[i][1] || 0]; + + if (i == reportData.data.DealsInfo.ByStage.length - 1) { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, null, null, null); + } else { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + } + + rowIndex++; + } + + rowIndex++; + rowIndex++; + + //contacts by type + var sRange = oWorksheet.GetRange(toColumnName(colIndex) + rowIndex); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header2); + + rowIndex++; + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.type, ""], smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + for (var i = 0; i < reportData.data.ContactsInfo.ByType.length; i++) { + var row = reportData.data.ContactsInfo.ByType[i]; + + if(!row[0]) row[0] = reportData.resource.notSpecified; + + if (i == reportData.data.ContactsInfo.ByType.length - 1) { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, null, null, null); + } else { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + } + + rowIndex++; + } + + rowIndex++; + + //contacts by stage + writeRow(colIndex, rowIndex, [reportData.resource.temperature, ""], smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + for (var i = 0; i < reportData.data.ContactsInfo.ByStage.length; i++) { + var row = reportData.data.ContactsInfo.ByStage[i]; + + if(!row[0]) row[0] = reportData.resource.notSpecified; + + if (i == reportData.data.ContactsInfo.ByStage.length - 1) { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, null, null, null); + } else { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + } + + rowIndex++; + } + + rowIndex++; + rowIndex++; + + //tasks by the period + sRange = oWorksheet.GetRange(toColumnName(colIndex) + rowIndex); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header3); + + rowIndex++; + rowIndex++; + + var headerRow = [reportData.resource.type, reportData.resource.open, reportData.resource.overdue]; + writeRow(colIndex, rowIndex, headerRow, smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + for (var i = 0; i < reportData.data.TasksInfo.length; i++) { + var row = reportData.data.TasksInfo[i]; + + if (i == reportData.data.TasksInfo.length - 1) { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, null, null, null); + } else { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + } + + rowIndex++; + } + + rowIndex++; + rowIndex++; + + //invoices by status + sRange = oWorksheet.GetRange(toColumnName(colIndex) + rowIndex); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header4); + + rowIndex++; + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.status, ""], smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.billed, reportData.data.InvoicesInfo[0][0] || 0], smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.overdue, reportData.data.InvoicesInfo[0][1] || 0], smallFontSize, blackFontColor, false, null, null, null); + + writeCharts(startColIndex, startRowIndex, rowIndex); +} + +customizeWorksheet(); +writeText(); + +if (reportData.data) { + writeTable(2, 55); +} + +builder.SaveFile("xlsx", "${outputFilePath}"); +builder.CloseFile(); \ No newline at end of file diff --git a/products/ASC.CRM/Server/ReportTemplates/SummaryForThePeriod.docbuilder b/products/ASC.CRM/Server/ReportTemplates/SummaryForThePeriod.docbuilder new file mode 100644 index 00000000000..3913de0ae28 --- /dev/null +++ b/products/ASC.CRM/Server/ReportTemplates/SummaryForThePeriod.docbuilder @@ -0,0 +1,375 @@ + +builder.CreateFile("xlsx"); + +var mainHeaderFontSize = 24; +var headerFontSize = 14; +var chartHeaderFontSize = 12; +var smallFontSize = 10; +var legendFontSize = 9; + +var blackFontColor = Api.CreateColorFromRGB(63, 63, 63); +var grayFontColor = Api.CreateColorFromRGB(127, 127, 127); + +var lightGrayBgColor = Api.CreateColorFromRGB(242, 242, 242); +var darkGrayBgColor = Api.CreateColorFromRGB(216, 216, 216); + +var lightGrayBorderColor = Api.CreateColorFromRGB(216, 216, 216); +var darkGrayBorderColor = Api.CreateColorFromRGB(127, 127, 127); + +var reportData = ${reportData}; + +reportData.resource.sheetName = reportData.resource.sheetName.slice(0, 31); + +var oWorksheet = Api.GetActiveSheet(); + +function toColumnName(num) { + for (var res = '', a = 1, b = 26; (num -= a) >= 0; a = b, b *= 26) { + res = String.fromCharCode(parseInt((num % b) / a) + 65) + res; + } + return res; +} + +function customizeWorksheet() { + Api.SetThemeColors("Origin"); + + oWorksheet.SetName(reportData.resource.sheetName); + oWorksheet.SetDisplayGridlines(false); + oWorksheet.SetColumnWidth(0, 6); + oWorksheet.SetColumnWidth(1, 30); + + for (var i = 2; i < 35; i++) { + oWorksheet.SetColumnWidth(i, 15); + } +} + +function writeText() { + var sRange = oWorksheet.GetRange("B2"); + sRange.SetFontSize(mainHeaderFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header); + + sRange = oWorksheet.GetRange("B3"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetBold(true); + sRange.SetValue(reportData.resource.dateRangeLabel); + + sRange = oWorksheet.GetRange("B4"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetAlignHorizontal("left"); + sRange.SetValue(reportData.resource.dateRangeValue); + + sRange = oWorksheet.GetRange("B67"); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header1); +} + +function writeRow(startColIndex, startRowIndex, dataArray, fontSize, fontColor, bold, borderType, borderColor, bgColor, wrap) { + var range = toColumnName(startColIndex) + startRowIndex + ":"; + + for (var i = 0; i < dataArray.length; i++) { + var cell = oWorksheet.GetRange(toColumnName(startColIndex) + startRowIndex); + + if (typeof(dataArray[i]) == "object") { + cell.SetNumberFormat(dataArray[i].format); + cell.SetValue(dataArray[i].value); + } else { + cell.SetValue(dataArray[i].toString()); + } + + if (i == 0) { + cell.SetAlignHorizontal("left"); + } else { + cell.SetAlignHorizontal("right"); + } + + startColIndex++; + } + + range += toColumnName(startColIndex - 1) + startRowIndex; + + var sRange = oWorksheet.GetRange(range); + + if (fontSize) + sRange.SetFontSize(fontSize); + + if (fontColor) + sRange.SetFontColor(fontColor); + + if (bold) + sRange.SetBold(true); + + if (borderType) + sRange.SetBorders(borderType, "Thin", borderColor); + + if (bgColor) + sRange.SetFillColor(bgColor); + + sRange.SetWrap(wrap || false); +} + +function secondsToTimeFormat(seconds) { + var hours = Math.floor(seconds / 3600); + + if(hours < 10) hours="0"+hours; + + var minutes = Math.floor(seconds % 3600 / 60); + + if(minutes < 10) minutes="0"+minutes; + + var seconds = Math.floor(seconds % 3600 % 60); + + if(seconds < 10) seconds="0"+seconds; + + return {format: reportData.resource.timeFormat, value: hours + ":" + minutes + ":" + seconds}; +} + +function writeCharts(startColIndex, startRowIndex, rowIndex) { + var chartRange = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 1) + ":$" + toColumnName(startColIndex + 1) + "$" + (startRowIndex + 4); + var chartRange1 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 7) + ":$" + toColumnName(startColIndex + 1) + "$" + (startRowIndex + 10); + var chartRange2 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 16) + ":$" + toColumnName(startColIndex + 1) + "$" + (startRowIndex + 15 + reportData.data.ContactsInfo.length); + var chartRange3 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 20 + reportData.data.ContactsInfo.length) + ":$" + toColumnName(startColIndex + 3) + "$" + (startRowIndex + 20 + reportData.data.ContactsInfo.length + reportData.data.TasksInfo.length); + var chartRange4 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 26 + reportData.data.ContactsInfo.length + reportData.data.TasksInfo.length) + ":$" + toColumnName(startColIndex + 1) + "$" + (startRowIndex + 29 + reportData.data.ContactsInfo.length + reportData.data.TasksInfo.length); + var chartRange5 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 35 + reportData.data.ContactsInfo.length + reportData.data.TasksInfo.length) + ":$" + toColumnName(startColIndex + 1) + "$" + (startRowIndex + 36 + reportData.data.ContactsInfo.length + reportData.data.TasksInfo.length); + + var oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange, false, "bar", 2, 120 * 36000, 70 * 36000, 1, 0, 7, 0); + oChart.SetTitle(reportData.resource.chartName, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("none"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); + oChart.SetVertAxisTickLabelPosition("none"); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); + oChart.SetMajorHorizontalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + oChart.SetMajorVerticalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + + oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange1, false, "bar", 2, 120 * 36000, 70 * 36000, 5, -0.5 * 36000, 7, 0); + oChart.SetTitle(reportData.resource.chartName1, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("none"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); + oChart.SetVertAxisTickLabelPosition("none"); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); + oChart.SetMajorHorizontalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + oChart.SetMajorVerticalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + + oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange2, false, "pie", 2, 120 * 36000, 70 * 36000, 1, 0, 21, -0.5 * 36000); + oChart.SetTitle(reportData.resource.chartName2, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); + + oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange4, true, "bar", 2, 120 * 36000, 70 * 36000, 5, -0.5 * 36000, 21, -0.5 * 36000); + oChart.SetTitle(reportData.resource.chartName4, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); + oChart.SetVertAxisTickLabelPosition("none"); + oChart.SetHorAxisTickLabelPosition("none"); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); + oChart.SetMajorHorizontalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + oChart.SetMajorVerticalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + + oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange3, false, "bar", 2, 240 * 36000 - 0.5 * 36000, 70 * 36000, 1, 0, 35, -1 * 36000); + oChart.SetTitle(reportData.resource.chartName3, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); + + oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange5, false, "pie", 2, 120 * 36000, 70 * 36000, 1, 0, 49, -1.5 * 36000); + oChart.SetTitle(reportData.resource.chartName5, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); +} + +function writeTable(startColIndex, startRowIndex) { + + var colIndex = startColIndex; + var rowIndex = startRowIndex; + + //deals by budget + writeRow(colIndex, rowIndex, [reportData.resource.byBudget, reportData.resource.currency], smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.created, {format: "0.00", value: (reportData.data.DealsInfo.Created[0][1] || 0).toString()}], smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.won, {format: "0.00", value: (reportData.data.DealsInfo.Won[0][1] || 0).toString()}], smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.lost, {format: "0.00", value: (reportData.data.DealsInfo.Lost[0][1] || 0).toString()}], smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.overdue, {format: "0.00", value: (reportData.data.DealsInfo.Overdue[0][1] || 0).toString()}], smallFontSize, blackFontColor, false, null, null, null); + rowIndex++; + + rowIndex++; + + //deals by count + writeRow(colIndex, rowIndex, [reportData.resource.byCount, reportData.resource.item], smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.created, reportData.data.DealsInfo.Created[0][0] || 0], smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.won, reportData.data.DealsInfo.Won[0][0] || 0], smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.lost, reportData.data.DealsInfo.Lost[0][0] || 0], smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.overdue, reportData.data.DealsInfo.Overdue[0][0] || 0], smallFontSize, blackFontColor, false, null, null, null); + rowIndex++; + + rowIndex++; + rowIndex++; + + //contacts by type + var sRange = oWorksheet.GetRange(toColumnName(colIndex) + rowIndex); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header2); + + rowIndex++; + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.type, reportData.resource.item], smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + for (var i = 0; i < reportData.data.ContactsInfo.length; i++) { + var row = reportData.data.ContactsInfo[i]; + + if(!row[0]) row[0] = reportData.resource.notSpecified; + + if (i == reportData.data.ContactsInfo.length - 1) { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, null, null, null); + } else { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + } + + rowIndex++; + } + + rowIndex++; + rowIndex++; + + //tasks by the period + sRange = oWorksheet.GetRange(toColumnName(colIndex) + rowIndex); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header3); + + rowIndex++; + rowIndex++; + + var headerRow = [reportData.resource.type, reportData.resource.closed, reportData.resource.overdue, reportData.resource.created]; + writeRow(colIndex, rowIndex, headerRow, smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + for (var i = 0; i < reportData.data.TasksInfo.length; i++) { + var row = reportData.data.TasksInfo[i]; + + if (i == reportData.data.TasksInfo.length - 1) { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, null, null, null); + } else { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + } + + rowIndex++; + } + + rowIndex++; + rowIndex++; + + //invoices by status + sRange = oWorksheet.GetRange(toColumnName(colIndex) + rowIndex); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header4); + + rowIndex++; + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.status, reportData.resource.item], smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.billed, reportData.data.InvoicesInfo[0][0] || 0], smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.paid, reportData.data.InvoicesInfo[0][1] || 0], smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.rejected, reportData.data.InvoicesInfo[0][2] || 0], smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.overdue, reportData.data.InvoicesInfo[0][3] || 0], smallFontSize, blackFontColor, false, null, null, null); + rowIndex++; + + rowIndex++; + rowIndex++; + + //calls by the period + sRange = oWorksheet.GetRange(toColumnName(colIndex) + rowIndex); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header5); + + rowIndex++; + rowIndex++; + + writeRow(colIndex, rowIndex, ["", reportData.resource.count, reportData.resource.duration], smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + var incomingCount=0; + var incomingDuration=0; + var outcomingCount=0; + var outcomingDuration=0; + var missingCount=0; + + for (var i = 0; i < reportData.data.VoipInfo.length; i++) { + if (reportData.data.VoipInfo[i][0] == 1) { + outcomingCount+=reportData.data.VoipInfo[i][1]; + outcomingDuration+=reportData.data.VoipInfo[i][2]; + } else { + incomingCount+=reportData.data.VoipInfo[i][1]; + incomingDuration+=reportData.data.VoipInfo[i][2]; + + if(reportData.data.VoipInfo[i][0] == 3){ + missingCount+=reportData.data.VoipInfo[i][1]; + } + } + } + + writeRow(colIndex, rowIndex, [reportData.resource.incoming, incomingCount, secondsToTimeFormat(incomingDuration)], smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + sRange = oWorksheet.GetRange(toColumnName(colIndex+3) + rowIndex); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetValue(reportData.resource.missed.replace("{0}", missingCount)); + rowIndex++; + + writeRow(colIndex, rowIndex, [reportData.resource.outcoming, outcomingCount, secondsToTimeFormat(outcomingDuration)], smallFontSize, blackFontColor, false, null, null, null); + + writeCharts(startColIndex, startRowIndex, rowIndex); +} + +customizeWorksheet(); +writeText(); + +if (reportData.data) { + writeTable(2, 69); +} + +builder.SaveFile("xlsx", "${outputFilePath}"); +builder.CloseFile(); \ No newline at end of file diff --git a/products/ASC.CRM/Server/ReportTemplates/WorkloadByContacts.docbuilder b/products/ASC.CRM/Server/ReportTemplates/WorkloadByContacts.docbuilder new file mode 100644 index 00000000000..1ebd5b93874 --- /dev/null +++ b/products/ASC.CRM/Server/ReportTemplates/WorkloadByContacts.docbuilder @@ -0,0 +1,289 @@ + +builder.CreateFile("xlsx"); + +var mainHeaderFontSize = 24; +var headerFontSize = 14; +var chartHeaderFontSize = 12; +var smallFontSize = 10; +var legendFontSize = 9; + +var blackFontColor = Api.CreateColorFromRGB(63, 63, 63); +var grayFontColor = Api.CreateColorFromRGB(127, 127, 127); + +var lightGrayBgColor = Api.CreateColorFromRGB(242, 242, 242); +var darkGrayBgColor = Api.CreateColorFromRGB(216, 216, 216); + +var lightGrayBorderColor = Api.CreateColorFromRGB(216, 216, 216); +var darkGrayBorderColor = Api.CreateColorFromRGB(127, 127, 127); + +var reportData = ${reportData}; + +reportData.resource.sheetName = reportData.resource.sheetName.slice(0, 31); + +var oWorksheet = Api.GetActiveSheet(); + +function prepareData(){ + reportData.header = [reportData.resource.manager]; + + var obj = {Name: "", Total: 0, WithDeals: 0}; + + for(var i=0; i 0 ? item.CategoryName : reportData.resource.noSet; + + if(!obj.hasOwnProperty(propName)){ + obj[propName] = { + Name: categoryName, + Count: 0 + }; + + reportData.header.push(categoryName); + } + } + + var data = {}; + var count = 0; + + for(var i=0; i= 0; a = b, b *= 26) { + res = String.fromCharCode(parseInt((num % b) / a) + 65) + res; + } + return res; +} + +function customizeWorksheet() { + Api.SetThemeColors("Origin"); + + oWorksheet.SetName(reportData.resource.sheetName); + oWorksheet.SetDisplayGridlines(false); + oWorksheet.SetColumnWidth(0, 6); + oWorksheet.SetColumnWidth(1, 30); + + for (var i = 2; i < 35; i++) { + oWorksheet.SetColumnWidth(i, 15); + } +} + +function writeText() { + var sRange = oWorksheet.GetRange("B2"); + sRange.SetFontSize(mainHeaderFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header); + + sRange = oWorksheet.GetRange("B3"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetBold(true); + sRange.SetValue(reportData.resource.dateRangeLabel); + + sRange = oWorksheet.GetRange("B4"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetAlignHorizontal("left"); + sRange.SetValue(reportData.resource.dateRangeValue); +} + +function writeRow(startColIndex, startRowIndex, dataArray, fontSize, fontColor, bold, borderType, borderColor, bgColor, wrap) { + var range = toColumnName(startColIndex) + startRowIndex + ":"; + + for (var i = 0; i < dataArray.length; i++) { + var cell = oWorksheet.GetRange(toColumnName(startColIndex) + startRowIndex); + + if (typeof(dataArray[i]) == "object") { + cell.SetNumberFormat(dataArray[i].format); + cell.SetValue(dataArray[i].value); + } else { + cell.SetValue(dataArray[i].toString()); + } + + if (i == 0) { + cell.SetAlignHorizontal("left"); + } else { + cell.SetAlignHorizontal("right"); + } + + startColIndex++; + } + + range += toColumnName(startColIndex - 1) + startRowIndex; + + var sRange = oWorksheet.GetRange(range); + + if (fontSize) + sRange.SetFontSize(fontSize); + + if (fontColor) + sRange.SetFontColor(fontColor); + + if (bold) + sRange.SetBold(true); + + if (borderType) + sRange.SetBorders(borderType, "Thin", borderColor); + + if (bgColor) + sRange.SetFillColor(bgColor); + + sRange.SetWrap(wrap || false); +} + +function writeCharts(startColIndex, startRowIndex, count, length) { + var chartRange1 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 2) + ":$" + toColumnName(startColIndex + length - 1) + "$" + (startRowIndex + 2 + count); + var chartRange2 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 8 + count) + ":$" + toColumnName(startColIndex + 2) + "$" + (startRowIndex + 8 + 2 * count); + + var oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange1, false, "barStacked", 2, 240 * 36000, 70 * 36000, 1, 0, 7, 0); + oChart.SetTitle(reportData.resource.header1, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); + oChart.SetVertAxisTickLabelPosition("none"); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); + oChart.SetMajorHorizontalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + oChart.SetMajorVerticalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + + oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange2, false, "barStacked", 2, 240 * 36000, 70 * 36000, 1, 0, 21, -0.5 * 36000); + oChart.SetTitle(reportData.resource.header2, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); + oChart.SetVertAxisTickLabelPosition("none"); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); + oChart.SetMajorHorizontalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + oChart.SetMajorVerticalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); +} + +function writeTable(startColIndex, startRowIndex) { + + var colIndex = startColIndex; + var rowIndex = startRowIndex; + + var sRange = oWorksheet.GetRange(toColumnName(colIndex) + rowIndex); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header1); + + rowIndex = rowIndex + 2; + + writeRow(colIndex, rowIndex, reportData.header, smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + var index = 0; + + for(var propertyName in reportData.data) { + if(propertyName.indexOf("user") != 0) continue; + + var row = [reportData.data[propertyName].Name]; + + for(var propName in reportData.data[propertyName]) { + if(propName.indexOf("category") != 0) continue; + + row.push(reportData.data[propertyName][propName].Count) + } + + if (index == reportData.count - 1) { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", darkGrayBorderColor, null); + } else { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + } + + rowIndex++; + index++; + } + + var startCell; + var endCell; + + var footerRow = [reportData.resource.total]; + for (var i = 1; i < reportData.header.length; i++) { + startCell = toColumnName(colIndex + i) + (startRowIndex + 3); + endCell = toColumnName(colIndex + i) + (startRowIndex + 2 + reportData.count); + footerRow[i] = "=SUM(" + startCell + ":" + endCell + ")"; + } + writeRow(colIndex, rowIndex, footerRow, smallFontSize, blackFontColor, true, null, null, null); + + rowIndex = rowIndex + 3; + + + sRange = oWorksheet.GetRange(toColumnName(colIndex) + rowIndex); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header2); + + rowIndex = rowIndex + 2; + + var headerRow = [reportData.resource.manager, reportData.resource.withouthDeals, reportData.resource.withDeals]; + writeRow(colIndex, rowIndex, headerRow, smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + index = 0; + + for(var propertyName in reportData.data) { + if(propertyName.indexOf("user") != 0) continue; + + var row = [reportData.data[propertyName].Name]; + row.push(reportData.data[propertyName].Total - reportData.data[propertyName].WithDeals); + row.push(reportData.data[propertyName].WithDeals); + + if (index == reportData.count - 1) { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", darkGrayBorderColor, null); + } else { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + } + + rowIndex++; + index++; + } + + startCell; + endCell; + + footerRow = [reportData.resource.total]; + for (var i = 1; i < headerRow.length; i++) { + startCell = toColumnName(colIndex + i) + (startRowIndex + 2 + reportData.count + 7); + endCell = toColumnName(colIndex + i) + (startRowIndex + 2 + reportData.count + 6 + reportData.count); + footerRow[i] = "=SUM(" + startCell + ":" + endCell + ")"; + } + writeRow(colIndex, rowIndex, footerRow, smallFontSize, blackFontColor, true, null, null, null); + + writeCharts(startColIndex, startRowIndex, reportData.count, reportData.header.length); +} + +customizeWorksheet(); +writeText(); + +if (reportData.data && reportData.data.length) { + prepareData(); + writeTable(2, 39); +} + +builder.SaveFile("xlsx", "${outputFilePath}"); +builder.CloseFile(); \ No newline at end of file diff --git a/products/ASC.CRM/Server/ReportTemplates/WorkloadByDeals.docbuilder b/products/ASC.CRM/Server/ReportTemplates/WorkloadByDeals.docbuilder new file mode 100644 index 00000000000..3f85e3e8c34 --- /dev/null +++ b/products/ASC.CRM/Server/ReportTemplates/WorkloadByDeals.docbuilder @@ -0,0 +1,269 @@ + +builder.CreateFile("xlsx"); + +var mainHeaderFontSize = 24; +var headerFontSize = 14; +var chartHeaderFontSize = 12; +var smallFontSize = 10; +var legendFontSize = 9; + +var blackFontColor = Api.CreateColorFromRGB(63, 63, 63); +var grayFontColor = Api.CreateColorFromRGB(127, 127, 127); + +var lightGrayBgColor = Api.CreateColorFromRGB(242, 242, 242); +var darkGrayBgColor = Api.CreateColorFromRGB(216, 216, 216); + +var lightGrayBorderColor = Api.CreateColorFromRGB(216, 216, 216); +var darkGrayBorderColor = Api.CreateColorFromRGB(127, 127, 127); + +var reportData = ${reportData}; + +reportData.resource.sheetName = reportData.resource.sheetName.slice(0, 31); + +var oWorksheet = Api.GetActiveSheet(); + +function toColumnName(num) { + for (var res = '', a = 1, b = 26; (num -= a) >= 0; a = b, b *= 26) { + res = String.fromCharCode(parseInt((num % b) / a) + 65) + res; + } + return res; +} + +function customizeWorksheet() { + Api.SetThemeColors("Origin"); + + oWorksheet.SetName(reportData.resource.sheetName); + oWorksheet.SetDisplayGridlines(false); + oWorksheet.SetColumnWidth(0, 6); + oWorksheet.SetColumnWidth(1, 30); + + for (var i = 2; i < 35; i++) { + oWorksheet.SetColumnWidth(i, 15); + } +} + +function writeText() { + var sRange = oWorksheet.GetRange("B2"); + sRange.SetFontSize(mainHeaderFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header); + + sRange = oWorksheet.GetRange("B3"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetBold(true); + sRange.SetValue(reportData.resource.dateRangeLabel); + + sRange = oWorksheet.GetRange("B4"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetAlignHorizontal("left"); + sRange.SetValue(reportData.resource.dateRangeValue); +} + +function writeRow(startColIndex, startRowIndex, dataArray, fontSize, fontColor, bold, borderType, borderColor, bgColor, wrap) { + var range = toColumnName(startColIndex) + startRowIndex + ":"; + + for (var i = 0; i < dataArray.length; i++) { + var cell = oWorksheet.GetRange(toColumnName(startColIndex) + startRowIndex); + + if (typeof(dataArray[i]) == "object") { + cell.SetNumberFormat(dataArray[i].format); + cell.SetValue(dataArray[i].value); + } else { + cell.SetValue(dataArray[i].toString()); + } + + if (i == 0) { + cell.SetAlignHorizontal("left"); + } else { + cell.SetAlignHorizontal("right"); + } + + startColIndex++; + } + + range += toColumnName(startColIndex - 1) + startRowIndex; + + var sRange = oWorksheet.GetRange(range); + + if (fontSize) + sRange.SetFontSize(fontSize); + + if (fontColor) + sRange.SetFontColor(fontColor); + + if (bold) + sRange.SetBold(true); + + if (borderType) + sRange.SetBorders(borderType, "Thin", borderColor); + + if (bgColor) + sRange.SetFillColor(bgColor); + + sRange.SetWrap(wrap || false); +} + +function writeCharts(startColIndex, startRowIndex, count) { + var chartRange1 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 2) + ":$" + toColumnName(startColIndex + 3) + "$" + (startRowIndex + 2 + count); + var chartRange2 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 8 + count) + ":$" + toColumnName(startColIndex + 3) + "$" + (startRowIndex + 8 + 2 * count); + + var oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange1, false, "bar", 2, 240 * 36000, 70 * 36000, 1, 0, 7, 0); + oChart.SetTitle(reportData.resource.chartName, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); + oChart.SetVertAxisTickLabelPosition("none"); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); + oChart.SetMajorHorizontalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + oChart.SetMajorVerticalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + + oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange2, false, "bar", 2, 240 * 36000, 70 * 36000, 1, 0, 21, -0.5 * 36000); + oChart.SetTitle(reportData.resource.chartName1, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); + oChart.SetVertAxisTickLabelPosition("none"); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); + oChart.SetMajorHorizontalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + oChart.SetMajorVerticalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); +} + +function writeTable(startColIndex, startRowIndex) { + + var colIndex = startColIndex; + var rowIndex = startRowIndex; + + var sRange = oWorksheet.GetRange(toColumnName(colIndex) + rowIndex); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header1); + + rowIndex = rowIndex + 2; + + var headerRow = [reportData.resource.manager, reportData.resource.status0, reportData.resource.status1, reportData.resource.status2]; + writeRow(colIndex, rowIndex, headerRow, smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + var data = {}; + var count = 0; + + for (var i = 0; i < reportData.data.length; i++) { + var propertyName = "usr_" + reportData.data[i][0]; + var status = reportData.data[i][2]; + + if(data[propertyName]){ + switch(status){ + case 0: + data[propertyName].count.open = reportData.data[i][3]; + data[propertyName].budget.open = reportData.data[i][4]; + break; + case 1: + data[propertyName].count.won = reportData.data[i][3]; + data[propertyName].budget.won = reportData.data[i][4]; + break; + case 2: + data[propertyName].count.lost = reportData.data[i][3]; + data[propertyName].budget.lost = reportData.data[i][4]; + break; + } + } else { + data[propertyName] = { + name: reportData.data[i][1], + count: { + open: status == 0 ? reportData.data[i][3] : 0, + won: status == 1 ? reportData.data[i][3] : 0, + lost: status == 2 ? reportData.data[i][3] : 0 + }, + budget: { + open: status == 0 ? reportData.data[i][4] : 0, + won: status == 1 ? reportData.data[i][4] : 0, + lost: status == 2 ? reportData.data[i][4] : 0 + } + }; + count++; + } + } + + var index = 0; + + for(var propertyName in data) { + if(propertyName.indexOf("usr_") != 0) continue; + + var row = [data[propertyName].name, data[propertyName].count.open, data[propertyName].count.won, data[propertyName].count.lost]; + + if (index == count - 1) { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", darkGrayBorderColor, null); + } else { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + } + + rowIndex++; + index++; + } + + var startCell; + var endCell; + + var footerRow = [reportData.resource.total]; + for (var i = 1; i < 4; i++) { + startCell = toColumnName(colIndex + i) + (startRowIndex + 3); + endCell = toColumnName(colIndex + i) + (startRowIndex + 2 + count); + footerRow[i] = "=SUM(" + startCell + ":" + endCell + ")"; + } + writeRow(colIndex, rowIndex, footerRow, smallFontSize, blackFontColor, true, null, null, null); + + rowIndex = rowIndex + 3; + + sRange = oWorksheet.GetRange(toColumnName(colIndex) + rowIndex); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header2); + + rowIndex = rowIndex + 2; + + writeRow(colIndex, rowIndex, headerRow, smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + var index = 0; + + for(var propertyName in data) { + if(propertyName.indexOf("usr_") != 0) continue; + + var row = [data[propertyName].name, {format: "0.00", value: data[propertyName].budget.open.toString()}, {format: "0.00", value: data[propertyName].budget.won.toString()}, {format: "0.00", value: data[propertyName].budget.lost.toString()}]; + + if (index == count - 1) { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", darkGrayBorderColor, null); + } else { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + } + + rowIndex++; + index++; + } + + footerRow = [reportData.resource.total]; + for (var i = 1; i < 4; i++) { + startCell = toColumnName(colIndex + i) + (startRowIndex + 9 + count); + endCell = toColumnName(colIndex + i) + (startRowIndex + 8 + 2 * count); + footerRow[i] = "=SUM(" + startCell + ":" + endCell + ")"; + } + writeRow(colIndex, rowIndex, footerRow, smallFontSize, blackFontColor, true, null, null, null); + + writeCharts(startColIndex, startRowIndex, count); +} + +customizeWorksheet(); +writeText(); + +if (reportData.data && reportData.data.length) { + writeTable(2, 39); +} + +builder.SaveFile("xlsx", "${outputFilePath}"); +builder.CloseFile(); \ No newline at end of file diff --git a/products/ASC.CRM/Server/ReportTemplates/WorkloadByInvoices.docbuilder b/products/ASC.CRM/Server/ReportTemplates/WorkloadByInvoices.docbuilder new file mode 100644 index 00000000000..92816385493 --- /dev/null +++ b/products/ASC.CRM/Server/ReportTemplates/WorkloadByInvoices.docbuilder @@ -0,0 +1,211 @@ + +builder.CreateFile("xlsx"); + +var mainHeaderFontSize = 24; +var headerFontSize = 14; +var chartHeaderFontSize = 12; +var smallFontSize = 10; +var legendFontSize = 9; + +var blackFontColor = Api.CreateColorFromRGB(63, 63, 63); +var grayFontColor = Api.CreateColorFromRGB(127, 127, 127); + +var lightGrayBgColor = Api.CreateColorFromRGB(242, 242, 242); +var darkGrayBgColor = Api.CreateColorFromRGB(216, 216, 216); + +var lightGrayBorderColor = Api.CreateColorFromRGB(216, 216, 216); +var darkGrayBorderColor = Api.CreateColorFromRGB(127, 127, 127); + +var reportData = ${reportData}; + +reportData.resource.sheetName = reportData.resource.sheetName.slice(0, 31); + +var oWorksheet = Api.GetActiveSheet(); + +function toColumnName(num) { + for (var res = '', a = 1, b = 26; (num -= a) >= 0; a = b, b *= 26) { + res = String.fromCharCode(parseInt((num % b) / a) + 65) + res; + } + return res; +} + +function customizeWorksheet() { + Api.SetThemeColors("Origin"); + + oWorksheet.SetName(reportData.resource.sheetName); + oWorksheet.SetDisplayGridlines(false); + oWorksheet.SetColumnWidth(0, 6); + oWorksheet.SetColumnWidth(1, 30); + + for (var i = 2; i < 35; i++) { + oWorksheet.SetColumnWidth(i, 15); + } +} + +function writeText() { + var sRange = oWorksheet.GetRange("B2"); + sRange.SetFontSize(mainHeaderFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header); + + sRange = oWorksheet.GetRange("B3"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetBold(true); + sRange.SetValue(reportData.resource.dateRangeLabel); + + sRange = oWorksheet.GetRange("B4"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetAlignHorizontal("left"); + sRange.SetValue(reportData.resource.dateRangeValue); +} + +function writeRow(startColIndex, startRowIndex, dataArray, fontSize, fontColor, bold, borderType, borderColor, bgColor, wrap) { + var range = toColumnName(startColIndex) + startRowIndex + ":"; + + for (var i = 0; i < dataArray.length; i++) { + var cell = oWorksheet.GetRange(toColumnName(startColIndex) + startRowIndex); + + if (typeof(dataArray[i]) == "object") { + cell.SetNumberFormat(dataArray[i].format); + cell.SetValue(dataArray[i].value); + } else { + cell.SetValue(dataArray[i].toString()); + } + + if (i == 0) { + cell.SetAlignHorizontal("left"); + } else { + cell.SetAlignHorizontal("right"); + } + + startColIndex++; + } + + range += toColumnName(startColIndex - 1) + startRowIndex; + + var sRange = oWorksheet.GetRange(range); + + if (fontSize) + sRange.SetFontSize(fontSize); + + if (fontColor) + sRange.SetFontColor(fontColor); + + if (bold) + sRange.SetBold(true); + + if (borderType) + sRange.SetBorders(borderType, "Thin", borderColor); + + if (bgColor) + sRange.SetFillColor(bgColor); + + sRange.SetWrap(wrap || false); +} + +function writeCharts(startColIndex, startRowIndex, count) { + var chartRange1 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 2) + ":$" + toColumnName(startColIndex + 1) + "$" + (startRowIndex + 2 + count); + var chartRange2 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 6 + count) + ":$" + toColumnName(startColIndex + 3) + "$" + (startRowIndex + 6 + 2 * count); + + var oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange1, false, "bar", 2, 240 * 36000, 70 * 36000, 1, 0, 7, 0); + oChart.SetTitle(reportData.resource.chartName, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("none"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); + oChart.SetVertAxisTickLabelPosition("none"); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); + oChart.SetMajorHorizontalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + oChart.SetMajorVerticalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + + oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange2, false, "bar", 2, 240 * 36000, 70 * 36000, 1, 0, 21, -0.5 * 36000); + oChart.SetTitle(reportData.resource.chartName1, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); + oChart.SetVertAxisTickLabelPosition("none"); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); + oChart.SetMajorHorizontalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); + oChart.SetMajorVerticalGridlines(Api.CreateStroke(0, Api.CreateNoFill())); +} + +function writeTable(startColIndex, startRowIndex) { + + var colIndex = startColIndex; + var rowIndex = startRowIndex; + + var sRange = oWorksheet.GetRange(toColumnName(colIndex) + rowIndex); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header1); + + rowIndex = rowIndex + 2; + + var headerRow = [reportData.resource.manager, reportData.resource.billed]; + writeRow(colIndex, rowIndex, headerRow, smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + for (var i = 0; i < reportData.data.length; i++) { + var item = reportData.data[i]; + var row = [item.UserName, item.SentCount]; + + if (i == reportData.data.length - 1) { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", darkGrayBorderColor, null); + } else { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + } + + rowIndex++; + } + + var startCell = toColumnName(colIndex + 1) + (startRowIndex + 3); + var endCell = toColumnName(colIndex + 1) + (startRowIndex + 2 + reportData.data.length); + var footerRow = [reportData.resource.total, "=SUM(" + startCell + ":" + endCell + ")"]; + writeRow(colIndex, rowIndex, footerRow, smallFontSize, blackFontColor, true, null, null, null); + + rowIndex = rowIndex + 3; + + + headerRow = [reportData.resource.manager, reportData.resource.paid, reportData.resource.rejected, reportData.resource.overdue]; + + writeRow(colIndex, rowIndex, headerRow, smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + for (var i = 0; i < reportData.data.length; i++) { + var item = reportData.data[i]; + var row = [item.UserName, item.PaidCount, item.RejectedCount, item.OverdueCount]; + + if (i == reportData.data.length - 1) { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", darkGrayBorderColor, null); + } else { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + } + + rowIndex++; + } + + footerRow = [reportData.resource.total]; + for (var i = 1; i < 4; i++) { + startCell = toColumnName(colIndex + i) + (startRowIndex + 2 + reportData.data.length + 5); + endCell = toColumnName(colIndex + i) + (startRowIndex + 2 + reportData.data.length + 4 + reportData.data.length); + footerRow[i] = "=SUM(" + startCell + ":" + endCell + ")"; + } + writeRow(colIndex, rowIndex, footerRow, smallFontSize, blackFontColor, true, null, null, null); + + writeCharts(startColIndex, startRowIndex, reportData.data.length); +} + +customizeWorksheet(); +writeText(); + +if (reportData.data && reportData.data.length) { + writeTable(2, 39); +} + +builder.SaveFile("xlsx", "${outputFilePath}"); +builder.CloseFile(); \ No newline at end of file diff --git a/products/ASC.CRM/Server/ReportTemplates/WorkloadByTasks.docbuilder b/products/ASC.CRM/Server/ReportTemplates/WorkloadByTasks.docbuilder new file mode 100644 index 00000000000..613ee39e2e2 --- /dev/null +++ b/products/ASC.CRM/Server/ReportTemplates/WorkloadByTasks.docbuilder @@ -0,0 +1,348 @@ + +builder.CreateFile("xlsx"); + +var mainHeaderFontSize = 24; +var headerFontSize = 14; +var chartHeaderFontSize = 12; +var smallFontSize = 10; +var legendFontSize = 9; + +var blackFontColor = Api.CreateColorFromRGB(63, 63, 63); +var grayFontColor = Api.CreateColorFromRGB(127, 127, 127); + +var lightGrayBgColor = Api.CreateColorFromRGB(242, 242, 242); +var darkGrayBgColor = Api.CreateColorFromRGB(216, 216, 216); + +var lightGrayBorderColor = Api.CreateColorFromRGB(216, 216, 216); +var darkGrayBorderColor = Api.CreateColorFromRGB(127, 127, 127); + +var reportData = ${reportData}; + +reportData.resource.sheetName = reportData.resource.sheetName.slice(0, 31); + +var oWorksheet = Api.GetActiveSheet(); + +function prepareData(){ + reportData.header = [reportData.resource.manager]; + + var obj = {Name: ""}; + var groups = ["Closed", "Created", "Overdue"]; + + for(var i=0; i= 0; a = b, b *= 26) { + res = String.fromCharCode(parseInt((num % b) / a) + 65) + res; + } + return res; +} + +function customizeWorksheet() { + Api.SetThemeColors("Origin"); + + oWorksheet.SetName(reportData.resource.sheetName); + oWorksheet.SetDisplayGridlines(false); + oWorksheet.SetColumnWidth(0, 6); + oWorksheet.SetColumnWidth(1, 30); + + for (var i = 2; i < 35; i++) { + oWorksheet.SetColumnWidth(i, 15); + } +} + +function writeText() { + var sRange = oWorksheet.GetRange("B2"); + sRange.SetFontSize(mainHeaderFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header); + + sRange = oWorksheet.GetRange("B3"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetBold(true); + sRange.SetValue(reportData.resource.dateRangeLabel); + + sRange = oWorksheet.GetRange("B4"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetAlignHorizontal("left"); + sRange.SetValue(reportData.resource.dateRangeValue); +} + +function writeRow(startColIndex, startRowIndex, dataArray, fontSize, fontColor, bold, borderType, borderColor, bgColor, wrap) { + var range = toColumnName(startColIndex) + startRowIndex + ":"; + + for (var i = 0; i < dataArray.length; i++) { + var cell = oWorksheet.GetRange(toColumnName(startColIndex) + startRowIndex); + + if (typeof(dataArray[i]) == "object") { + cell.SetNumberFormat(dataArray[i].format); + cell.SetValue(dataArray[i].value); + } else { + cell.SetValue(dataArray[i].toString()); + } + + if (i == 0) { + cell.SetAlignHorizontal("left"); + } else { + cell.SetAlignHorizontal("right"); + } + + startColIndex++; + } + + range += toColumnName(startColIndex - 1) + startRowIndex; + + var sRange = oWorksheet.GetRange(range); + + if (fontSize) + sRange.SetFontSize(fontSize); + + if (fontColor) + sRange.SetFontColor(fontColor); + + if (bold) + sRange.SetBold(true); + + if (borderType) + sRange.SetBorders(borderType, "Thin", borderColor); + + if (bgColor) + sRange.SetFillColor(bgColor); + + sRange.SetWrap(wrap || false); +} + +function writeCharts(startColIndex, startRowIndex, count, length) { + var chartRange1 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 2) + ":$" + toColumnName(startColIndex + length - 1) + "$" + (startRowIndex + 2 + count); + var chartRange2 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 8 + count) + ":$" + toColumnName(startColIndex + length - 1) + "$" + (startRowIndex + 8 + 2 * count); + var chartRange3 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 14 + 2 * count) + ":$" + toColumnName(startColIndex + length - 1) + "$" + (startRowIndex + 14 + 3 * count); + + var oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange1, false, "bar", 2, 240 * 36000, 70 * 36000, 1, 0, 7, 0); + oChart.SetTitle(reportData.resource.header1, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); + + oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange2, false, "bar", 2, 240 * 36000, 70 * 36000, 1, 0, 21, -0.5 * 36000); + oChart.SetTitle(reportData.resource.header2, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); + + oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange3, false, "bar", 2, 240 * 36000, 70 * 36000, 1, 0, 35, -1 * 36000); + oChart.SetTitle(reportData.resource.header3, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); +} + +function writeTable(startColIndex, startRowIndex) { + + var colIndex = startColIndex; + var rowIndex = startRowIndex; + + var sRange = oWorksheet.GetRange(toColumnName(colIndex) + rowIndex); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header1); + + rowIndex = rowIndex + 2; + + writeRow(colIndex, rowIndex, reportData.header, smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + var index = 0; + + for(var propertyName in reportData.data) { + if(propertyName.indexOf("user") != 0) continue; + + var row = [reportData.data[propertyName].Name]; + + for(var propName in reportData.data[propertyName]) { + if(propName.indexOf("category") != 0) continue; + + row.push(reportData.data[propertyName][propName].Closed) + } + + if (index == reportData.count - 1) { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", darkGrayBorderColor, null); + } else { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + } + + rowIndex++; + index++; + } + + var startCell; + var endCell; + + var footerRow = [reportData.resource.total]; + for (var i = 1; i < reportData.header.length; i++) { + startCell = toColumnName(colIndex + i) + (startRowIndex + 3); + endCell = toColumnName(colIndex + i) + (startRowIndex + 2 + reportData.count); + footerRow[i] = "=SUM(" + startCell + ":" + endCell + ")"; + } + writeRow(colIndex, rowIndex, footerRow, smallFontSize, blackFontColor, true, null, null, null); + + rowIndex = rowIndex + 3; + + + sRange = oWorksheet.GetRange(toColumnName(colIndex) + rowIndex); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header2); + + rowIndex = rowIndex + 2; + + writeRow(colIndex, rowIndex, reportData.header, smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + index = 0; + + for(var propertyName in reportData.data) { + if(propertyName.indexOf("user") != 0) continue; + + var row = [reportData.data[propertyName].Name]; + + for(var propName in reportData.data[propertyName]) { + if(propName.indexOf("category") != 0) continue; + + row.push(reportData.data[propertyName][propName].Created) + } + + if (index == reportData.count - 1) { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", darkGrayBorderColor, null); + } else { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + } + + rowIndex++; + index++; + } + + startCell; + endCell; + + footerRow = [reportData.resource.total]; + for (var i = 1; i < reportData.header.length; i++) { + startCell = toColumnName(colIndex + i) + (startRowIndex + 2 + reportData.count + 7); + endCell = toColumnName(colIndex + i) + (startRowIndex + 2 + reportData.count + 6 + reportData.count); + footerRow[i] = "=SUM(" + startCell + ":" + endCell + ")"; + } + writeRow(colIndex, rowIndex, footerRow, smallFontSize, blackFontColor, true, null, null, null); + + rowIndex = rowIndex + 3; + + + sRange = oWorksheet.GetRange(toColumnName(colIndex) + rowIndex); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header3); + + rowIndex = rowIndex + 2; + + writeRow(colIndex, rowIndex, reportData.header, smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + index = 0; + + for(var propertyName in reportData.data) { + if(propertyName.indexOf("user") != 0) continue; + + var row = [reportData.data[propertyName].Name]; + + for(var propName in reportData.data[propertyName]) { + if(propName.indexOf("category") != 0) continue; + + row.push(reportData.data[propertyName][propName].Overdue) + } + + if (index == reportData.count - 1) { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", darkGrayBorderColor, null); + } else { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + } + + rowIndex++; + index++; + } + + startCell; + endCell; + + footerRow = [reportData.resource.total]; + for (var i = 1; i < reportData.header.length; i++) { + startCell = toColumnName(colIndex + i) + (startRowIndex + 2 + reportData.count + 6 + reportData.count + 7); + endCell = toColumnName(colIndex + i) + (startRowIndex + 2 + reportData.count + 6 + reportData.count + 6 + reportData.count); + footerRow[i] = "=SUM(" + startCell + ":" + endCell + ")"; + } + writeRow(colIndex, rowIndex, footerRow, smallFontSize, blackFontColor, true, null, null, null); + + + writeCharts(startColIndex, startRowIndex, reportData.count, reportData.header.length); +} + +customizeWorksheet(); +writeText(); + +if (reportData.data) { + prepareData(); + writeTable(2, 53); +} + +builder.SaveFile("xlsx", "${outputFilePath}"); +builder.CloseFile(); \ No newline at end of file diff --git a/products/ASC.CRM/Server/ReportTemplates/WorkloadByVoip.docbuilder b/products/ASC.CRM/Server/ReportTemplates/WorkloadByVoip.docbuilder new file mode 100644 index 00000000000..137b68c6dc0 --- /dev/null +++ b/products/ASC.CRM/Server/ReportTemplates/WorkloadByVoip.docbuilder @@ -0,0 +1,256 @@ + +builder.CreateFile("xlsx"); + +var mainHeaderFontSize = 24; +var headerFontSize = 14; +var chartHeaderFontSize = 12; +var smallFontSize = 10; +var legendFontSize = 9; + +var blackFontColor = Api.CreateColorFromRGB(63, 63, 63); +var grayFontColor = Api.CreateColorFromRGB(127, 127, 127); + +var lightGrayBgColor = Api.CreateColorFromRGB(242, 242, 242); +var darkGrayBgColor = Api.CreateColorFromRGB(216, 216, 216); + +var lightGrayBorderColor = Api.CreateColorFromRGB(216, 216, 216); +var darkGrayBorderColor = Api.CreateColorFromRGB(127, 127, 127); + +var reportData = ${reportData}; + +reportData.resource.sheetName = reportData.resource.sheetName.slice(0, 31); + +var oWorksheet = Api.GetActiveSheet(); + +function toColumnName(num) { + for (var res = '', a = 1, b = 26; (num -= a) >= 0; a = b, b *= 26) { + res = String.fromCharCode(parseInt((num % b) / a) + 65) + res; + } + return res; +} + +function customizeWorksheet() { + Api.SetThemeColors("Origin"); + + oWorksheet.SetName(reportData.resource.sheetName); + oWorksheet.SetDisplayGridlines(false); + oWorksheet.SetColumnWidth(0, 6); + oWorksheet.SetColumnWidth(1, 30); + + for (var i = 2; i < 35; i++) { + oWorksheet.SetColumnWidth(i, 15); + } +} + +function writeText() { + var sRange = oWorksheet.GetRange("B2"); + sRange.SetFontSize(mainHeaderFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header); + + sRange = oWorksheet.GetRange("B3"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetBold(true); + sRange.SetValue(reportData.resource.dateRangeLabel); + + sRange = oWorksheet.GetRange("B4"); + sRange.SetFontSize(smallFontSize); + sRange.SetFontColor(grayFontColor); + sRange.SetAlignHorizontal("left"); + sRange.SetValue(reportData.resource.dateRangeValue); +} + +function writeRow(startColIndex, startRowIndex, dataArray, fontSize, fontColor, bold, borderType, borderColor, bgColor, wrap) { + var range = toColumnName(startColIndex) + startRowIndex + ":"; + + for (var i = 0; i < dataArray.length; i++) { + var cell = oWorksheet.GetRange(toColumnName(startColIndex) + startRowIndex); + + if (typeof(dataArray[i]) == "object") { + cell.SetNumberFormat(dataArray[i].format); + cell.SetValue(dataArray[i].value); + } else { + cell.SetValue(dataArray[i].toString()); + } + + if (i == 0) { + cell.SetAlignHorizontal("left"); + } else { + cell.SetAlignHorizontal("right"); + } + + startColIndex++; + } + + range += toColumnName(startColIndex - 1) + startRowIndex; + + var sRange = oWorksheet.GetRange(range); + + if (fontSize) + sRange.SetFontSize(fontSize); + + if (fontColor) + sRange.SetFontColor(fontColor); + + if (bold) + sRange.SetBold(true); + + if (borderType) + sRange.SetBorders(borderType, "Thin", borderColor); + + if (bgColor) + sRange.SetFillColor(bgColor); + + sRange.SetWrap(wrap || false); +} + +function writeCharts(startColIndex, startRowIndex, count) { + var chartRange1 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 2) + ":$" + toColumnName(startColIndex + 2) + "$" + (startRowIndex + 2 + count); + var chartRange2 = "$" + toColumnName(startColIndex) + "$" + (startRowIndex + 8 + count) + ":$" + toColumnName(startColIndex + 2) + "$" + (startRowIndex + 8 + 2 * count); + + var oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange1, false, "barStacked", 2, 240 * 36000, 70 * 36000, 1, 0, 7, 0); + oChart.SetTitle(reportData.resource.chartName, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); + + oChart = oWorksheet.AddChart("'" + reportData.resource.sheetName + "'!" + chartRange2, false, "barStacked", 2, 240 * 36000, 70 * 36000, 1, 0, 21, -0.5 * 36000); + oChart.SetTitle(reportData.resource.chartName1, chartHeaderFontSize, false); + oChart.ApplyChartStyle(0); + oChart.SetLegendPos("right"); + oChart.SetLegendFontSize(legendFontSize); + oChart.SetShowDataLabels(false, false, true, false); + oChart.SetHorAxisLablesFontSize(legendFontSize); + oChart.SetVertAxisLablesFontSize(legendFontSize); +} + +function writeTable(startColIndex, startRowIndex) { + + var colIndex = startColIndex; + var rowIndex = startRowIndex; + + var sRange = oWorksheet.GetRange(toColumnName(colIndex) + rowIndex); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header1); + + rowIndex = rowIndex + 2; + + var headerRow = [reportData.resource.manager, reportData.resource.outcoming, reportData.resource.incoming, reportData.resource.total]; + writeRow(colIndex, rowIndex, headerRow, smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + var data = {}; + var count = 0; + + for (var i = 0; i < reportData.data.length; i++) { + var propertyName = "usr_" + reportData.data[i][0]; + var status = reportData.data[i][2]; + + if(data[propertyName]) { + if(status == 1) { + data[propertyName].count.outcoming = reportData.data[i][3]; + data[propertyName].duration.outcoming = reportData.data[i][4]; + } else { + data[propertyName].count.incoming = reportData.data[i][3]; + data[propertyName].duration.incoming = reportData.data[i][4]; + } + } else { + data[propertyName] = { + name: reportData.data[i][1], + count: { + outcoming: status == 1 ? reportData.data[i][3] : 0, + incoming: status != 1 ? reportData.data[i][3] : 0 + }, + duration: { + outcoming: status == 1 ? reportData.data[i][4] : 0, + incoming: status != 1 ? reportData.data[i][4] : 0 + } + }; + count++; + } + } + + var index = 0; + + for(var propertyName in data) { + if(propertyName.indexOf("usr_") != 0) continue; + + var sum = "=SUM(" + toColumnName(colIndex + 1) + rowIndex + ":" + toColumnName(colIndex + 2) + rowIndex + ")"; + var row = [data[propertyName].name, data[propertyName].count.outcoming, data[propertyName].count.incoming, sum]; + + if (index == count - 1) { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", darkGrayBorderColor, null); + } else { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + } + + rowIndex++; + index++; + } + + var startCell; + var endCell; + + var footerRow = [reportData.resource.total]; + for (var i = 1; i < 4; i++) { + startCell = toColumnName(colIndex + i) + (startRowIndex + 3); + endCell = toColumnName(colIndex + i) + (startRowIndex + 2 + count); + footerRow[i] = "=SUM(" + startCell + ":" + endCell + ")"; + } + writeRow(colIndex, rowIndex, footerRow, smallFontSize, blackFontColor, true, null, null, null); + + rowIndex = rowIndex + 3; + + sRange = oWorksheet.GetRange(toColumnName(colIndex) + rowIndex); + sRange.SetFontSize(headerFontSize); + sRange.SetFontColor(blackFontColor); + sRange.SetValue(reportData.resource.header2); + + rowIndex = rowIndex + 2; + + writeRow(colIndex, rowIndex, headerRow, smallFontSize, grayFontColor, false, "Bottom", darkGrayBorderColor, null, true); + rowIndex++; + + var index = 0; + + for(var propertyName in data) { + if(propertyName.indexOf("usr_") != 0) continue; + + var sum = {format: reportData.resource.timeFormat, value: "=SUM(" + toColumnName(colIndex + 1) + rowIndex + ":" + toColumnName(colIndex + 2) + rowIndex + ")"}; + var row = [data[propertyName].name, data[propertyName].duration.outcoming, data[propertyName].duration.incoming, sum]; + + if (index == count - 1) { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", darkGrayBorderColor, null); + } else { + writeRow(colIndex, rowIndex, row, smallFontSize, blackFontColor, false, "Bottom", lightGrayBorderColor, null); + } + + rowIndex++; + index++; + } + + footerRow = [reportData.resource.total]; + for (var i = 1; i < 4; i++) { + startCell = toColumnName(colIndex + i) + (startRowIndex + 9 + count); + endCell = toColumnName(colIndex + i) + (startRowIndex + 8 + 2 * count); + footerRow[i] = {format: reportData.resource.timeFormat, value: "=SUM(" + startCell + ":" + endCell + ")"}; + } + writeRow(colIndex, rowIndex, footerRow, smallFontSize, blackFontColor, true, null, null, null); + + writeCharts(startColIndex, startRowIndex, count); +} + +customizeWorksheet(); +writeText(); + +if (reportData.data && reportData.data.length) { + writeTable(2, 39); +} + +builder.SaveFile("xlsx", "${outputFilePath}"); +builder.CloseFile(); \ No newline at end of file diff --git a/products/ASC.CRM/Server/Resources/CRMCasesResource.Designer.cs b/products/ASC.CRM/Server/Resources/CRMCasesResource.Designer.cs new file mode 100644 index 00000000000..89a46dd4bda --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMCasesResource.Designer.cs @@ -0,0 +1,468 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.CRM.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class CRMCasesResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal CRMCasesResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.CRM.Resources.CRMCasesResource", typeof(CRMCasesResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Save and Create New Case. + /// + public static string AddThisAndCreateCaseButton { + get { + return ResourceManager.GetString("AddThisAndCreateCaseButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All Cases. + /// + public static string AllCases { + get { + return ResourceManager.GetString("AllCases", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Case. + /// + public static string Case { + get { + return ResourceManager.GetString("Case", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The permissions will be set to the cases where you are the author. + /// + public static string CasesAccessRightsLimit { + get { + return ResourceManager.GetString("CasesAccessRightsLimit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Status. + /// + public static string CasesByStatus { + get { + return ResourceManager.GetString("CasesByStatus", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Case Participant. + /// + public static string CasesParticipant { + get { + return ResourceManager.GetString("CasesParticipant", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Case Participants. + /// + public static string CasesParticipants { + get { + return ResourceManager.GetString("CasesParticipants", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Case Tag. + /// + public static string CasesTag { + get { + return ResourceManager.GetString("CasesTag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Case Tag List. + /// + public static string CasesTagList { + get { + return ResourceManager.GetString("CasesTagList", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Status. + /// + public static string CaseStatus { + get { + return ResourceManager.GetString("CaseStatus", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Closed. + /// + public static string CaseStatusClosed { + get { + return ResourceManager.GetString("CaseStatusClosed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open. + /// + public static string CaseStatusOpened { + get { + return ResourceManager.GetString("CaseStatusOpened", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Case title. + /// + public static string CaseTitle { + get { + return ResourceManager.GetString("CaseTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Close case. + /// + public static string CloseCase { + get { + return ResourceManager.GetString("CloseCase", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Creating case.... + /// + public static string CreateCaseProggress { + get { + return ResourceManager.GetString("CreateCaseProggress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create the first case. + /// + public static string CreateFirstCase { + get { + return ResourceManager.GetString("CreateFirstCase", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create new case. + /// + public static string CreateNewCase { + get { + return ResourceManager.GetString("CreateNewCase", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save. + /// + public static string CreateThisCaseButton { + get { + return ResourceManager.GetString("CreateThisCaseButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete case. + /// + public static string DeleteThisCase { + get { + return ResourceManager.GetString("DeleteThisCase", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deleting cases. + /// + public static string DeletingCases { + get { + return ResourceManager.GetString("DeletingCases", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit case. + /// + public static string EditCase { + get { + return ResourceManager.GetString("EditCase", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit the «{0}» case. + /// + public static string EditCaseHeader { + get { + return ResourceManager.GetString("EditCaseHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cases allow you to keep in one and the same place all the correspondence history, assign tasks, upload documents for the contacts who take part in a common event. For instance, it can be a team training or exhibition organization.. + /// + public static string EmptyContentCasesDescribe { + get { + return ResourceManager.GetString("EmptyContentCasesDescribe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No cases matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the cases in this section. Or you can create the case if it is absent from the list.. + /// + public static string EmptyContentCasesFilterDescribe { + get { + return ResourceManager.GetString("EmptyContentCasesFilterDescribe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No cases to be displayed for this filter here. + /// + public static string EmptyContentCasesFilterHeader { + get { + return ResourceManager.GetString("EmptyContentCasesFilterHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The case list is empty. + /// + public static string EmptyContentCasesHeader { + get { + return ResourceManager.GetString("EmptyContentCasesHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The participant list is empty. + /// + public static string EmptyPeopleInCaseContent { + get { + return ResourceManager.GetString("EmptyPeopleInCaseContent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add participants to your case from the list of companies or persons. After this is done you will know who is going to take part in the upcoming events associated with this case.. + /// + public static string EmptyPeopleInCaseDescript { + get { + return ResourceManager.GetString("EmptyPeopleInCaseDescript", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Import cases. + /// + public static string ImportCases { + get { + return ResourceManager.GetString("ImportCases", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A properly formed CSV file should be selected on your computer HDD. It must contain the fields or columns separated by the selected delimiter which will be used to import cases. The files containing more than {0} rows should be divided into smaller parts for proper importing. Make sure you selected the portal users who need to be granted access to the information to be imported, otherwise the data from the CSV file will be available to all portal users.. + /// + public static string ImportFromCSVStepOneDescription { + get { + return ResourceManager.GetString("ImportFromCSVStepOneDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select a CSV file and set access rights before you import cases. + /// + public static string ImportFromCSVStepOneHeader { + get { + return ResourceManager.GetString("ImportFromCSVStepOneHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Match your file columns with the corresponding ONLYOFFICE™ CRM fields. Please pay your attention to the fact that to import cases correctly you need to have at least one column from your file matched with the 'Case Title' field of the ONLYOFFICE™ CRM. The fields containing the dates must have the following format: {0}.. + /// + public static string ImportFromCSVStepTwoDescription { + get { + return ResourceManager.GetString("ImportFromCSVStepTwoDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please, verify the fields. + /// + public static string ImportFromCSVStepTwoHeader { + get { + return ResourceManager.GetString("ImportFromCSVStepTwoHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Go to cases list. + /// + public static string ImportStartingPanelButton { + get { + return ResourceManager.GetString("ImportStartingPanelButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Import of cases can take several minutes depending on the amount of your data.. + /// + public static string ImportStartingPanelDescription { + get { + return ResourceManager.GetString("ImportStartingPanelDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Importing started. + /// + public static string ImportStartingPanelHeader { + get { + return ResourceManager.GetString("ImportStartingPanelHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Case participants. + /// + public static string MembersCase { + get { + return ResourceManager.GetString("MembersCase", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open case. + /// + public static string OpenCase { + get { + return ResourceManager.GetString("OpenCase", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Participants. + /// + public static string PeopleInCase { + get { + return ResourceManager.GetString("PeopleInCase", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Restrict access to the case. + /// + public static string PrivatePanelCheckBoxLabel { + get { + return ResourceManager.GetString("PrivatePanelCheckBoxLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save Changes. + /// + public static string SaveChanges { + get { + return ResourceManager.GetString("SaveChanges", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Go to profile. + /// + public static string ShowCaseProfile { + get { + return ResourceManager.GetString("ShowCaseProfile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open in a new tab. + /// + public static string ShowCaseProfileNewTab { + get { + return ResourceManager.GetString("ShowCaseProfileNewTab", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Start Importing. + /// + public static string StartImport { + get { + return ResourceManager.GetString("StartImport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All cases. + /// + public static string TotalCases { + get { + return ResourceManager.GetString("TotalCases", resourceCulture); + } + } + } +} diff --git a/products/ASC.CRM/Server/Resources/CRMCasesResource.resx b/products/ASC.CRM/Server/Resources/CRMCasesResource.resx new file mode 100644 index 00000000000..16fa8a52375 --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMCasesResource.resx @@ -0,0 +1,255 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Save and Create New Case + + + All Cases + + + Case + + + The permissions will be set to the cases where you are the author + + + Status + + + Case Participant + + + Case Participants + + + Case Tag + + + Case Tag List + + + Status + + + Closed + + + Open + + + Case title + + + Close case + + + Creating case... + + + Create the first case + + + Create new case + + + Save + + + Delete case + + + Deleting cases + + + Edit case + + + Edit the «{0}» case + + + Cases allow you to keep in one and the same place all the correspondence history, assign tasks, upload documents for the contacts who take part in a common event. For instance, it can be a team training or exhibition organization. + + + No cases matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the cases in this section. Or you can create the case if it is absent from the list. + + + No cases to be displayed for this filter here + + + The case list is empty + + + The participant list is empty + + + Add participants to your case from the list of companies or persons. After this is done you will know who is going to take part in the upcoming events associated with this case. + + + Import cases + + + A properly formed CSV file should be selected on your computer HDD. It must contain the fields or columns separated by the selected delimiter which will be used to import cases. The files containing more than {0} rows should be divided into smaller parts for proper importing. Make sure you selected the portal users who need to be granted access to the information to be imported, otherwise the data from the CSV file will be available to all portal users. + + + Select a CSV file and set access rights before you import cases + + + Match your file columns with the corresponding ONLYOFFICE™ CRM fields. Please pay your attention to the fact that to import cases correctly you need to have at least one column from your file matched with the 'Case Title' field of the ONLYOFFICE™ CRM. The fields containing the dates must have the following format: {0}. + + + Please, verify the fields + + + Go to cases list + + + Import of cases can take several minutes depending on the amount of your data. + + + Importing started + + + Case participants + + + Open case + + + Participants + + + Restrict access to the case + + + Save Changes + + + Go to profile + + + Open in a new tab + + + Start Importing + + + All cases + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Resources/CRMCommonResource.Designer.cs b/products/ASC.CRM/Server/Resources/CRMCommonResource.Designer.cs index dfeb8e934fe..072470aff78 100644 --- a/products/ASC.CRM/Server/Resources/CRMCommonResource.Designer.cs +++ b/products/ASC.CRM/Server/Resources/CRMCommonResource.Designer.cs @@ -1,108 +1,3079 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace ASC.CRM.Resources { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class CRMCommonResource { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal CRMCommonResource() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.CRM.Resources.CRMCommonResource", typeof(CRMCommonResource).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to Do the same as CRM user|Import, export data|Send emails to CRM contacts|Manage settings|Manage any contact, task, case, opportunity. - /// - internal static string ProductAdminOpportunities { - get { - return ResourceManager.GetString("ProductAdminOpportunities", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Manage your customer relations, identify priority contacts, track communication history and sales performance.. - /// - internal static string ProductDescription { - get { - return ResourceManager.GetString("ProductDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Manage your customer relations, identify priority contacts, track communication history and sales performance.. - /// - internal static string ProductDescriptionEx { - get { - return ResourceManager.GetString("ProductDescriptionEx", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to CRM. - /// - internal static string ProductName { - get { - return ResourceManager.GetString("ProductName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Create a contact, task, case, opportunity with a possibility to limit access to them|Edit a contact, task, case, opportunity with the access rights set up. - /// - internal static string ProductUserOpportunities { - get { - return ResourceManager.GetString("ProductUserOpportunities", resourceCulture); - } - } - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.CRM.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class CRMCommonResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal CRMCommonResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.CRM.Resources.CRMCommonResource", typeof(CRMCommonResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to for reading. + /// + public static string AccessRightsForReading { + get { + return ResourceManager.GetString("AccessRightsForReading", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to for reading/writing. + /// + public static string AccessRightsForReadWriting { + get { + return ResourceManager.GetString("AccessRightsForReadWriting", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The access rights will be set only for the contacts which were created by you.. + /// + public static string AccessRightsLimit { + get { + return ResourceManager.GetString("AccessRightsLimit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Actions. + /// + public static string Actions { + get { + return ResourceManager.GetString("Actions", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add. + /// + public static string Add { + get { + return ResourceManager.GetString("Add", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add {!group}. + /// + public static string AddGroup { + get { + return ResourceManager.GetString("AddGroup", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Additional information. + /// + public static string AdditionalInformation { + get { + return ResourceManager.GetString("AdditionalInformation", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add mail account. + /// + public static string AddMailAccount { + get { + return ResourceManager.GetString("AddMailAccount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add Tag. + /// + public static string AddNewTag { + get { + return ResourceManager.GetString("AddNewTag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add participant. + /// + public static string AddParticipant { + get { + return ResourceManager.GetString("AddParticipant", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add This Event. + /// + public static string AddThisNote { + get { + return ResourceManager.GetString("AddThisNote", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Adding the note.... + /// + public static string AddThisNoteProggress { + get { + return ResourceManager.GetString("AddThisNoteProggress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add {!user}. + /// + public static string AddUser { + get { + return ResourceManager.GetString("AddUser", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use the {0}?{1} and {0}*{1} symbols to perform a single/multiple-character search. For example, to search for all the words beginning with {0}O{1}, use the {0}O*{1} query. It's not recommended to use these symbols at the beginning of your query. To search for the exact phrase use the quotation marks.{2}Learn more...{3}. + /// + public static string AdvansedFilterInfoText { + get { + return ResourceManager.GetString("AdvansedFilterInfoText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Alert. + /// + public static string Alert { + get { + return ResourceManager.GetString("Alert", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Corresponding ONLYOFFICE™ CRM field. + /// + public static string AssignedField { + get { + return ResourceManager.GetString("AssignedField", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Attachments. + /// + public static string Attachments { + get { + return ResourceManager.GetString("Attachments", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Link with contact. + /// + public static string AttachThisNoteToContact { + get { + return ResourceManager.GetString("AttachThisNoteToContact", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Author. + /// + public static string Author { + get { + return ResourceManager.GetString("Author", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Back. + /// + public static string Back { + get { + return ResourceManager.GetString("Back", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Learn more.... + /// + public static string ButtonLearnMore { + get { + return ResourceManager.GetString("ButtonLearnMore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Category. + /// + public static string ByCategory { + get { + return ResourceManager.GetString("ByCategory", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to By title. + /// + public static string ByTitle { + get { + return ResourceManager.GetString("ByTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cancel. + /// + public static string Cancel { + get { + return ResourceManager.GetString("Cancel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Case. + /// + public static string Case { + get { + return ResourceManager.GetString("Case", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cases. + /// + public static string CasesModuleName { + get { + return ResourceManager.GetString("CasesModuleName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Category. + /// + public static string Category { + get { + return ResourceManager.GetString("Category", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change. + /// + public static string Change { + get { + return ResourceManager.GetString("Change", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Choose. + /// + public static string Choose { + get { + return ResourceManager.GetString("Choose", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Reset filter. + /// + public static string ClearFilter { + get { + return ResourceManager.GetString("ClearFilter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Close. + /// + public static string Close { + get { + return ResourceManager.GetString("Close", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Close this window. + /// + public static string CloseThisWindow { + get { + return ResourceManager.GetString("CloseThisWindow", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Close window. + /// + public static string CloseWindow { + get { + return ResourceManager.GetString("CloseWindow", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Colon. + /// + public static string Colon { + get { + return ResourceManager.GetString("Colon", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File column. + /// + public static string Column { + get { + return ResourceManager.GetString("Column", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Comma. + /// + public static string Comma { + get { + return ResourceManager.GetString("Comma", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Compose Mail. + /// + public static string ComposeMail { + get { + return ResourceManager.GetString("ComposeMail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Compose a letter to be sent to all the clients selected in the contact list. You can check the number of selected clients in the 'To' field, enter a common 'Subject' and use the 'Personal tags' to include all the necessary information to your letter. This is a beta version for those who want to try out new features of ONLYOFFICE™ CRM.. + /// + public static string ComposeMailDescription { + get { + return ResourceManager.GetString("ComposeMailDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Confirmation. + /// + public static string Confirmation { + get { + return ResourceManager.GetString("Confirmation", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Confirmation of changes. + /// + public static string ConfirmationAccessRightsPanelHeader { + get { + return ResourceManager.GetString("ConfirmationAccessRightsPanelHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You deleted yourself from contact managers.. + /// + public static string ConfirmationAccessRightsPanelText1 { + get { + return ResourceManager.GetString("ConfirmationAccessRightsPanelText1", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You will lose access to the contact for reading and writing, if the appropriate access is not allowed for all CRM users.. + /// + public static string ConfirmationAccessRightsPanelText2 { + get { + return ResourceManager.GetString("ConfirmationAccessRightsPanelText2", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to delete these elements?. + /// + public static string ConfirmationDeleteText { + get { + return ResourceManager.GetString("ConfirmationDeleteText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to To change the list of the contact temperature levels, refer to the CRM {0}Settings{1} section. + /// + public static string ContactCategoriesHelpInfo { + get { + return ResourceManager.GetString("ContactCategoriesHelpInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact settings. + /// + public static string ContactSettings { + get { + return ResourceManager.GetString("ContactSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Content. + /// + public static string Content { + get { + return ResourceManager.GetString("Content", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Date. + /// + public static string ConversionDate { + get { + return ResourceManager.GetString("ConversionDate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Rate. + /// + public static string ConversionRate { + get { + return ResourceManager.GetString("ConversionRate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Currency convertor. + /// + public static string ConversionRates { + get { + return ResourceManager.GetString("ConversionRates", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Convert. + /// + public static string Convert { + get { + return ResourceManager.GetString("Convert", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Quantity. + /// + public static string Count { + get { + return ResourceManager.GetString("Count", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create. + /// + public static string Create { + get { + return ResourceManager.GetString("Create", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Creation date. + /// + public static string CreateDate { + get { + return ResourceManager.GetString("CreateDate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create Invoices. + /// + public static string CreateInvoices { + get { + return ResourceManager.GetString("CreateInvoices", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create New. + /// + public static string CreateNew { + get { + return ResourceManager.GetString("CreateNew", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create New Contact. + /// + public static string CreateNewContact { + get { + return ResourceManager.GetString("CreateNewContact", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create project. + /// + public static string CreateNewProject { + get { + return ResourceManager.GetString("CreateNewProject", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add new tag. + /// + public static string CreateNewTag { + get { + return ResourceManager.GetString("CreateNewTag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Creation date. + /// + public static string CreationDate { + get { + return ResourceManager.GetString("CreationDate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The CRM module is not enabled. + /// + public static string CRMProductIsDisabled { + get { + return ResourceManager.GetString("CRMProductIsDisabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Currency. + /// + public static string Currency { + get { + return ResourceManager.GetString("Currency", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Algerian Dinar. + /// + public static string Currency_AlgerianDinar { + get { + return ResourceManager.GetString("Currency_AlgerianDinar", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Argentinian Peso. + /// + public static string Currency_ArgentinianPeso { + get { + return ResourceManager.GetString("Currency_ArgentinianPeso", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Armenian Dram. + /// + public static string Currency_ArmenianDram { + get { + return ResourceManager.GetString("Currency_ArmenianDram", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Australian Dollar. + /// + public static string Currency_AustralianDollar { + get { + return ResourceManager.GetString("Currency_AustralianDollar", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Azerbaijani Manat. + /// + public static string Currency_AzerbaijaniManat { + get { + return ResourceManager.GetString("Currency_AzerbaijaniManat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bangladeshi Taka. + /// + public static string Currency_BangladeshiTaka { + get { + return ResourceManager.GetString("Currency_BangladeshiTaka", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Basic currencies. + /// + public static string Currency_Basic { + get { + return ResourceManager.GetString("Currency_Basic", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Belarusian Ruble. + /// + public static string Currency_BelarusianRuble { + get { + return ResourceManager.GetString("Currency_BelarusianRuble", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Brazilian Real. + /// + public static string Currency_BrazilianReal { + get { + return ResourceManager.GetString("Currency_BrazilianReal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bulgarian Lev. + /// + public static string Currency_BulgarianLev { + get { + return ResourceManager.GetString("Currency_BulgarianLev", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Canadian Dollar. + /// + public static string Currency_CanadianDollar { + get { + return ResourceManager.GetString("Currency_CanadianDollar", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Central African Franc. + /// + public static string Currency_CentralAfricanFranc { + get { + return ResourceManager.GetString("Currency_CentralAfricanFranc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Chilean Peso. + /// + public static string Currency_ChileanPeso { + get { + return ResourceManager.GetString("Currency_ChileanPeso", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Chinese Yuan. + /// + public static string Currency_ChineseYuan { + get { + return ResourceManager.GetString("Currency_ChineseYuan", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Colombian Peso. + /// + public static string Currency_ColombianPeso { + get { + return ResourceManager.GetString("Currency_ColombianPeso", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Costa Rican Colón. + /// + public static string Currency_CostaRicanColon { + get { + return ResourceManager.GetString("Currency_CostaRicanColon", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Croatian Kuna. + /// + public static string Currency_CroatianKuna { + get { + return ResourceManager.GetString("Currency_CroatianKuna", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cuban Peso. + /// + public static string Currency_CubanPeso { + get { + return ResourceManager.GetString("Currency_CubanPeso", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Czech Koruna. + /// + public static string Currency_CzechKoruna { + get { + return ResourceManager.GetString("Currency_CzechKoruna", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Danish Krone. + /// + public static string Currency_DanishKrone { + get { + return ResourceManager.GetString("Currency_DanishKrone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Dominican Peso. + /// + public static string Currency_DominicanPeso { + get { + return ResourceManager.GetString("Currency_DominicanPeso", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Euro. + /// + public static string Currency_Euro { + get { + return ResourceManager.GetString("Currency_Euro", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Georgian Lari. + /// + public static string Currency_GeorgianLari { + get { + return ResourceManager.GetString("Currency_GeorgianLari", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hong Kong Dollar. + /// + public static string Currency_HongKongDollar { + get { + return ResourceManager.GetString("Currency_HongKongDollar", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hungarian Forint. + /// + public static string Currency_HungarianForint { + get { + return ResourceManager.GetString("Currency_HungarianForint", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Indian Rupee. + /// + public static string Currency_IndianRupee { + get { + return ResourceManager.GetString("Currency_IndianRupee", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Indonesian Rupiah. + /// + public static string Currency_IndonesianRupiah { + get { + return ResourceManager.GetString("Currency_IndonesianRupiah", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Israeli Shekel. + /// + public static string Currency_IsraeliSheqel { + get { + return ResourceManager.GetString("Currency_IsraeliSheqel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Japanese Yen. + /// + public static string Currency_JapaneseYen { + get { + return ResourceManager.GetString("Currency_JapaneseYen", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Kazakhstani Tenge. + /// + public static string Currency_KazakhstaniTenge { + get { + return ResourceManager.GetString("Currency_KazakhstaniTenge", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Kenyan Shilling. + /// + public static string Currency_KenyanShilling { + get { + return ResourceManager.GetString("Currency_KenyanShilling", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Kuwaiti Dinar. + /// + public static string Currency_KuwaitiDinar { + get { + return ResourceManager.GetString("Currency_KuwaitiDinar", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Latvian Lat. + /// + public static string Currency_LatvianLats { + get { + return ResourceManager.GetString("Currency_LatvianLats", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Lithuanian Litas. + /// + public static string Currency_LithuanianLitas { + get { + return ResourceManager.GetString("Currency_LithuanianLitas", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Malagasy Ariary. + /// + public static string Currency_MalagasyAriary { + get { + return ResourceManager.GetString("Currency_MalagasyAriary", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Malaysian Ringgit. + /// + public static string Currency_MalaysianRinggit { + get { + return ResourceManager.GetString("Currency_MalaysianRinggit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mauritian Rupee. + /// + public static string Currency_MauritianRupee { + get { + return ResourceManager.GetString("Currency_MauritianRupee", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mexican Peso. + /// + public static string Currency_MexicanPeso { + get { + return ResourceManager.GetString("Currency_MexicanPeso", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Moldovan Leu. + /// + public static string Currency_MoldovanLeu { + get { + return ResourceManager.GetString("Currency_MoldovanLeu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Moroccan Dirham. + /// + public static string Currency_MoroccanDirham { + get { + return ResourceManager.GetString("Currency_MoroccanDirham", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New Belarusian Ruble. + /// + public static string Currency_NewBelarusianRuble { + get { + return ResourceManager.GetString("Currency_NewBelarusianRuble", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New Zealand Dollar. + /// + public static string Currency_NewZealandDollar { + get { + return ResourceManager.GetString("Currency_NewZealandDollar", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Nigerian Naira. + /// + public static string Currency_NigerianNaira { + get { + return ResourceManager.GetString("Currency_NigerianNaira", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Norwegian Krone. + /// + public static string Currency_NorwegianKrone { + get { + return ResourceManager.GetString("Currency_NorwegianKrone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Omani Rial. + /// + public static string Currency_OmaniRial { + get { + return ResourceManager.GetString("Currency_OmaniRial", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Other currencies. + /// + public static string Currency_Other { + get { + return ResourceManager.GetString("Currency_Other", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Pacific franc. + /// + public static string Currency_PacificFranc { + get { + return ResourceManager.GetString("Currency_PacificFranc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Pakistani Rupee. + /// + public static string Currency_PakistaniRupee { + get { + return ResourceManager.GetString("Currency_PakistaniRupee", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Philippine Peso. + /// + public static string Currency_PhilippinePeso { + get { + return ResourceManager.GetString("Currency_PhilippinePeso", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Polish Zloty. + /// + public static string Currency_PolishZloty { + get { + return ResourceManager.GetString("Currency_PolishZloty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Pound Sterling. + /// + public static string Currency_PoundSterling { + get { + return ResourceManager.GetString("Currency_PoundSterling", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Qatari Riyal. + /// + public static string Currency_QatariRiyal { + get { + return ResourceManager.GetString("Currency_QatariRiyal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Romanian Leu. + /// + public static string Currency_RomanianLeu { + get { + return ResourceManager.GetString("Currency_RomanianLeu", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Rouble. + /// + public static string Currency_Rouble { + get { + return ResourceManager.GetString("Currency_Rouble", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Saudi Riyal. + /// + public static string Currency_SaudiRiyal { + get { + return ResourceManager.GetString("Currency_SaudiRiyal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Serbian Dinar. + /// + public static string Currency_SerbianDinar { + get { + return ResourceManager.GetString("Currency_SerbianDinar", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Singapore Dollar. + /// + public static string Currency_SingaporeDollar { + get { + return ResourceManager.GetString("Currency_SingaporeDollar", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to South African Rand. + /// + public static string Currency_SouthAfricanRand { + get { + return ResourceManager.GetString("Currency_SouthAfricanRand", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to South Korean Won. + /// + public static string Currency_SouthKoreanWon { + get { + return ResourceManager.GetString("Currency_SouthKoreanWon", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Swedish Krona. + /// + public static string Currency_SwedishKrona { + get { + return ResourceManager.GetString("Currency_SwedishKrona", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Swiss Franc. + /// + public static string Currency_SwissFranc { + get { + return ResourceManager.GetString("Currency_SwissFranc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Thai Baht. + /// + public static string Currency_ThaiBaht { + get { + return ResourceManager.GetString("Currency_ThaiBaht", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Trinidad and Tobago Dollar. + /// + public static string Currency_TrinidadAndTobagoDollar { + get { + return ResourceManager.GetString("Currency_TrinidadAndTobagoDollar", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tunisian Dinar. + /// + public static string Currency_TunisianDinar { + get { + return ResourceManager.GetString("Currency_TunisianDinar", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Turkish New Lira. + /// + public static string Currency_TurkishNewLira { + get { + return ResourceManager.GetString("Currency_TurkishNewLira", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ukraine Hryvnia. + /// + public static string Currency_UkraineHryvnia { + get { + return ResourceManager.GetString("Currency_UkraineHryvnia", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to United Arab Emirates Dirham. + /// + public static string Currency_UnitedArabEmiratesDirham { + get { + return ResourceManager.GetString("Currency_UnitedArabEmiratesDirham", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to United States Dollar. + /// + public static string Currency_UnitedStatesDollar { + get { + return ResourceManager.GetString("Currency_UnitedStatesDollar", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Uzbekistani Som. + /// + public static string Currency_UzbekistaniSom { + get { + return ResourceManager.GetString("Currency_UzbekistaniSom", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Venezuelan Bolivar. + /// + public static string Currency_VenezuelanBolivar { + get { + return ResourceManager.GetString("Currency_VenezuelanBolivar", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Vietnamese Dong. + /// + public static string Currency_VietnameseDong { + get { + return ResourceManager.GetString("Currency_VietnameseDong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to West African Franc. + /// + public static string Currency_WestAfricanFranc { + get { + return ResourceManager.GetString("Currency_WestAfricanFranc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Current {!user}. + /// + public static string CurrentUser { + get { + return ResourceManager.GetString("CurrentUser", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Custom. + /// + public static string Custom { + get { + return ResourceManager.GetString("Custom", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Custom period. + /// + public static string CustomDateFilter { + get { + return ResourceManager.GetString("CustomDateFilter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Other users. + /// + public static string CustomResponsibleFilter { + get { + return ResourceManager.GetString("CustomResponsibleFilter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Manage Relations with Customers. + /// + public static string DashboardTitle { + get { + return ResourceManager.GetString("DashboardTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Fill in various contact details, link persons with companies.. + /// + public static string DashContactsDetails { + get { + return ResourceManager.GetString("DashContactsDetails", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add contacts manually or import the list from file.. + /// + public static string DashContactsImport { + get { + return ResourceManager.GetString("DashContactsImport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use the mass mailing feature to address multiple contacts.. + /// + public static string DashContactsMail { + get { + return ResourceManager.GetString("DashContactsMail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change CRM module settings, adapting it maximally to your needs.. + /// + public static string DashCustomizeCRM { + get { + return ResourceManager.GetString("DashCustomizeCRM", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Customize user fields, task categories or opportunity stages.. + /// + public static string DashCustomizeFields { + get { + return ResourceManager.GetString("DashCustomizeFields", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use website contact form to add new contacts right from your site.. + /// + public static string DashCustomizeWebsite { + get { + return ResourceManager.GetString("DashCustomizeWebsite", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Simplify invoice creation adding products and services.. + /// + public static string DashInvoiceProductsAndServices { + get { + return ResourceManager.GetString("DashInvoiceProductsAndServices", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add taxes specific to certain countries and use them in invoices.. + /// + public static string DashInvoiceTaxes { + get { + return ResourceManager.GetString("DashInvoiceTaxes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Issue invoices to your clients right from the portal interface.. + /// + public static string DashIssueInvoices { + get { + return ResourceManager.GetString("DashIssueInvoices", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add opportunities, set budget and estimate success probability.. + /// + public static string DashSalesOpportunities { + get { + return ResourceManager.GetString("DashSalesOpportunities", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Organize cases and specify the list of their participants.. + /// + public static string DashSalesParticipants { + get { + return ResourceManager.GetString("DashSalesParticipants", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Assign tasks to CRM managers linking them with the contacts.. + /// + public static string DashSalesTasks { + get { + return ResourceManager.GetString("DashSalesTasks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Date. + /// + public static string Date { + get { + return ResourceManager.GetString("Date", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Due in {0} day(s). + /// + public static string DateDueInDays { + get { + return ResourceManager.GetString("DateDueInDays", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} day(s) overdue. + /// + public static string DateOverueByDays { + get { + return ResourceManager.GetString("DateOverueByDays", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunities. + /// + public static string DealModuleName { + get { + return ResourceManager.GetString("DealModuleName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete. + /// + public static string Delete { + get { + return ResourceManager.GetString("Delete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete contact. + /// + public static string DeleteContact { + get { + return ResourceManager.GetString("DeleteContact", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete event. + /// + public static string DeleteHistoryEvent { + get { + return ResourceManager.GetString("DeleteHistoryEvent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete participant. + /// + public static string DeleteParticipant { + get { + return ResourceManager.GetString("DeleteParticipant", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete tag. + /// + public static string DeleteTag { + get { + return ResourceManager.GetString("DeleteTag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete user. + /// + public static string DeleteUser { + get { + return ResourceManager.GetString("DeleteUser", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Description. + /// + public static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deselect all. + /// + public static string DeselectAll { + get { + return ResourceManager.GetString("DeselectAll", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Documents. + /// + public static string Documents { + get { + return ResourceManager.GetString("Documents", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Do the same for all future occurrences. + /// + public static string DontAskAnymore { + get { + return ResourceManager.GetString("DontAskAnymore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Double quote. + /// + public static string DoubleQuote { + get { + return ResourceManager.GetString("DoubleQuote", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Duplicate Records. + /// + public static string DuplicateRecords { + get { + return ResourceManager.GetString("DuplicateRecords", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {!Users}. + /// + public static string Employees { + get { + return ResourceManager.GetString("Employees", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Be aware of what your clients think about your produce and brand in the social network.. + /// + public static string EmptyContentTwitterAccountsDescribe { + get { + return ResourceManager.GetString("EmptyContentTwitterAccountsDescribe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No linked twitter accounts. + /// + public static string EmptyContentTwitterAccountsHeader { + get { + return ResourceManager.GetString("EmptyContentTwitterAccountsHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Empty file. + /// + public static string EmptyFileErrorMsg { + get { + return ResourceManager.GetString("EmptyFileErrorMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No records matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the records in this section. You can also look for the records you need in other contact history.. + /// + public static string EmptyHistoryDescription { + get { + return ResourceManager.GetString("EmptyHistoryDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No records for this filter can be found here. + /// + public static string EmptyHistoryHeader { + get { + return ResourceManager.GetString("EmptyHistoryHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The opportunity participant list is empty. + /// + public static string EmptyPeopleInDealContent { + get { + return ResourceManager.GetString("EmptyPeopleInDealContent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enter amount. + /// + public static string EnterAmount { + get { + return ResourceManager.GetString("EnterAmount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error. + /// + public static string Error { + get { + return ResourceManager.GetString("Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File size exceeds {1}{0}{2}. + /// + public static string ErrorMessage_UploadFileSize { + get { + return ResourceManager.GetString("ErrorMessage_UploadFileSize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Export current list to CSV-file. + /// + public static string ExportCurrentListToCsvFile { + get { + return ResourceManager.GetString("ExportCurrentListToCsvFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Export data. + /// + public static string ExportData { + get { + return ResourceManager.GetString("ExportData", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All exported files you can always see, download, edit, or delete them in {0}Documents/My Documents{1}. + /// + public static string ExportDataInfo { + get { + return ResourceManager.GetString("ExportDataInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Export data ({0}% complete). + /// + public static string ExportDataProgress { + get { + return ResourceManager.GetString("ExportDataProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File. + /// + public static string File { + get { + return ResourceManager.GetString("File", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Files. + /// + public static string Files { + get { + return ResourceManager.GetString("Files", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The maximum file size is exceeded. + /// + public static string FileSizeErrorMsg { + get { + return ResourceManager.GetString("FileSizeErrorMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Creation date. + /// + public static string FilterByCreationDate { + get { + return ResourceManager.GetString("FilterByCreationDate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Due date. + /// + public static string FilterByDate { + get { + return ResourceManager.GetString("FilterByDate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Responsible. + /// + public static string FilterByResponsible { + get { + return ResourceManager.GetString("FilterByResponsible", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to With tag. + /// + public static string FilterWithTag { + get { + return ResourceManager.GetString("FilterWithTag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Find a company or a person by name.... + /// + public static string FindContactByName { + get { + return ResourceManager.GetString("FindContactByName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to From. + /// + public static string From { + get { + return ResourceManager.GetString("From", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hide. + /// + public static string Hide { + get { + return ResourceManager.GetString("Hide", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hide file attachment panel. + /// + public static string HideAttachPanel { + get { + return ResourceManager.GetString("HideAttachPanel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to History. + /// + public static string History { + get { + return ResourceManager.GetString("History", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can change the list for categories of history events {0}here{1}. + /// + public static string HistoryCategoriesHelpInfo { + get { + return ResourceManager.GetString("HistoryCategoriesHelpInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Phone call. + /// + public static string HistoryCategory_Call { + get { + return ResourceManager.GetString("HistoryCategory_Call", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Email. + /// + public static string HistoryCategory_Email { + get { + return ResourceManager.GetString("HistoryCategory_Email", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Appointment. + /// + public static string HistoryCategory_Meeting { + get { + return ResourceManager.GetString("HistoryCategory_Meeting", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Note. + /// + public static string HistoryCategory_Note { + get { + return ResourceManager.GetString("HistoryCategory_Note", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to has been received. + /// + public static string HistoryEventInboxMail { + get { + return ResourceManager.GetString("HistoryEventInboxMail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to has been sent. + /// + public static string HistoryEventOutboxMail { + get { + return ResourceManager.GetString("HistoryEventOutboxMail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Max. number of characters exceeded: {0}. + /// + public static string HistoryLongDataMsg { + get { + return ResourceManager.GetString("HistoryLongDataMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ignore the first row when importing. + /// + public static string IgnoreFirstRow { + get { + return ResourceManager.GetString("IgnoreFirstRow", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Duplicate. + /// + public static string ImportFromCSV_DublicateBehavior_Clone { + get { + return ResourceManager.GetString("ImportFromCSV_DublicateBehavior_Clone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use this option to import contacts duplicating the coinciding contacts. E.g. there are two records - 'A' and 'B' - in your ONLYOFFICE™ CRM. You want to import 'B' and 'C' records. In case you select the 'Duplicate' option, a duplicate record to 'B' will be created while importing.. + /// + public static string ImportFromCSV_DublicateBehavior_CloneDescription { + get { + return ResourceManager.GetString("ImportFromCSV_DublicateBehavior_CloneDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Check for duplicates will be performed based on the Email field.. + /// + public static string ImportFromCSV_DublicateBehavior_Description { + get { + return ResourceManager.GetString("ImportFromCSV_DublicateBehavior_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Overwrite. + /// + public static string ImportFromCSV_DublicateBehavior_Overwrite { + get { + return ResourceManager.GetString("ImportFromCSV_DublicateBehavior_Overwrite", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use this option to import contacts overwriting the existing contacts. E.g. there are two records - 'A' and 'B' - in your ONLYOFFICE™ CRM. You want to import 'B' and 'C' records. In case you select the 'Overwrite' option, the existing 'B' will be overwritten by the imported one while importing.. + /// + public static string ImportFromCSV_DublicateBehavior_OverwriteDescription { + get { + return ResourceManager.GetString("ImportFromCSV_DublicateBehavior_OverwriteDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Skip. + /// + public static string ImportFromCSV_DublicateBehavior_Skip { + get { + return ResourceManager.GetString("ImportFromCSV_DublicateBehavior_Skip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use this option to import contacts keeping the existing contacts. E.g. there are two records - 'A' and 'B' - in your ONLYOFFICE™ CRM. You want to import 'B' and 'C' records. In case you select the 'Skip' option, the existing 'B' will be preserved while importing.. + /// + public static string ImportFromCSV_DublicateBehavior_SkipDescription { + get { + return ResourceManager.GetString("ImportFromCSV_DublicateBehavior_SkipDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delimiter. + /// + public static string ImportFromCSV_ReadFileSettings_DelimiterCharacter { + get { + return ResourceManager.GetString("ImportFromCSV_ReadFileSettings_DelimiterCharacter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Encoding. + /// + public static string ImportFromCSV_ReadFileSettings_Encoding { + get { + return ResourceManager.GetString("ImportFromCSV_ReadFileSettings_Encoding", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CSV File Settings. + /// + public static string ImportFromCSV_ReadFileSettings_Header { + get { + return ResourceManager.GetString("ImportFromCSV_ReadFileSettings_Header", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Text Delimiter. + /// + public static string ImportFromCSV_ReadFileSettings_QuoteCharacter { + get { + return ResourceManager.GetString("ImportFromCSV_ReadFileSettings_QuoteCharacter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Scanning the file.... + /// + public static string ImportFromCSVStepOneProgressLable { + get { + return ResourceManager.GetString("ImportFromCSVStepOneProgressLable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Start importing data. + /// + public static string ImportFromCSVStepTwoProgressLable { + get { + return ResourceManager.GetString("ImportFromCSVStepTwoProgressLable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Go to Contact List. + /// + public static string ImportStartingPanelButton { + get { + return ResourceManager.GetString("ImportStartingPanelButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Import of contacts can take several minutes depending on the amount of your data. You will be informed when it is done.. + /// + public static string ImportStartingPanelDescription { + get { + return ResourceManager.GetString("ImportStartingPanelDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Importing started. + /// + public static string ImportStartingPanelHeader { + get { + return ResourceManager.GetString("ImportStartingPanelHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Information is submitted. + /// + public static string InformationProvidedBy { + get { + return ResourceManager.GetString("InformationProvidedBy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invoices. + /// + public static string InvoiceModuleName { + get { + return ResourceManager.GetString("InvoiceModuleName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invoice settings. + /// + public static string InvoiceSettings { + get { + return ResourceManager.GetString("InvoiceSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Taxes. + /// + public static string InvoiceTaxes { + get { + return ResourceManager.GetString("InvoiceTaxes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Last month. + /// + public static string LastMonth { + get { + return ResourceManager.GetString("LastMonth", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Learn more.... + /// + public static string LearnMore { + get { + return ResourceManager.GetString("LearnMore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Letter Body. + /// + public static string LetterBody { + get { + return ResourceManager.GetString("LetterBody", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Link with. + /// + public static string LinkTo { + get { + return ResourceManager.GetString("LinkTo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to . + /// + public static string LoadingProcessing { + get { + return ResourceManager.GetString("LoadingProcessing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please wait .... + /// + public static string LoadingWait { + get { + return ResourceManager.GetString("LoadingWait", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Email body. + /// + public static string MailBody { + get { + return ResourceManager.GetString("MailBody", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copy. + /// + public static string MailCopyTo { + get { + return ResourceManager.GetString("MailCopyTo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to From. + /// + public static string MailFrom { + get { + return ResourceManager.GetString("MailFrom", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ONLYOFFICE™ Mass Send Feature: "{0}". + /// + public static string MailHistoryEventTemplate { + get { + return ResourceManager.GetString("MailHistoryEventTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sending Mail. + /// + public static string MailSend { + get { + return ResourceManager.GetString("MailSend", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to deliver message to {0}. + /// + public static string MailSender_FailedDeliverException { + get { + return ResourceManager.GetString("MailSender_FailedDeliverException", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid email address: {0}. + /// + public static string MailSender_InvalidEmail { + get { + return ResourceManager.GetString("MailSender_InvalidEmail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delivery failed - will retry in {0} seconds.. + /// + public static string MailSender_MailboxBusyException { + get { + return ResourceManager.GetString("MailSender_MailboxBusyException", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Email subject. + /// + public static string MailSubject { + get { + return ResourceManager.GetString("MailSubject", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to To. + /// + public static string MailTo { + get { + return ResourceManager.GetString("MailTo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Manage Contacts. + /// + public static string ManageContacts { + get { + return ResourceManager.GetString("ManageContacts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Message. + /// + public static string Message { + get { + return ResourceManager.GetString("Message", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Calculator. + /// + public static string MoneyCalculator { + get { + return ResourceManager.GetString("MoneyCalculator", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to My. + /// + public static string My { + get { + return ResourceManager.GetString("My", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to My opportunities. + /// + public static string MyDeals { + get { + return ResourceManager.GetString("MyDeals", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to My events. + /// + public static string MyEventsFilter { + get { + return ResourceManager.GetString("MyEventsFilter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to My tasks. + /// + public static string MyTasks { + get { + return ResourceManager.GetString("MyTasks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Next record. + /// + public static string NextSample { + get { + return ResourceManager.GetString("NextSample", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No. + /// + public static string No { + get { + return ResourceManager.GetString("No", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No records. + /// + public static string NoLoadedMessages { + get { + return ResourceManager.GetString("NoLoadedMessages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No mail accounts available. + /// + public static string NoMailAccountsAvailableError { + get { + return ResourceManager.GetString("NoMailAccountsAvailableError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No matches. + /// + public static string NoMatches { + get { + return ResourceManager.GetString("NoMatches", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Not specified. + /// + public static string NoSet { + get { + return ResourceManager.GetString("NoSet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Notify. + /// + public static string Notify { + get { + return ResourceManager.GetString("Notify", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Not specified. + /// + public static string NotSpecified { + get { + return ResourceManager.GetString("NotSpecified", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to OK. + /// + public static string OK { + get { + return ResourceManager.GetString("OK", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to On top. + /// + public static string OnTop { + get { + return ResourceManager.GetString("OnTop", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Organization Profile. + /// + public static string OrganisationProfile { + get { + return ResourceManager.GetString("OrganisationProfile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Other. + /// + public static string Other { + get { + return ResourceManager.GetString("Other", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Other settings. + /// + public static string OtherSettings { + get { + return ResourceManager.GetString("OtherSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Overview. + /// + public static string Overview { + get { + return ResourceManager.GetString("Overview", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Share. + /// + public static string Part { + get { + return ResourceManager.GetString("Part", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please wait. + /// + public static string PleaseWait { + get { + return ResourceManager.GetString("PleaseWait", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Portal name. + /// + public static string PortalName { + get { + return ResourceManager.GetString("PortalName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Preview Mail. + /// + public static string PreviewMail { + get { + return ResourceManager.GetString("PreviewMail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Here you can see the letter you are composing. Use the 'Back' button to make some changes or use the 'Send' button, if you like the result.. + /// + public static string PreviewMailDescription { + get { + return ResourceManager.GetString("PreviewMailDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Previous record. + /// + public static string PrevSample { + get { + return ResourceManager.GetString("PrevSample", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Previous Step. + /// + public static string PrevStep { + get { + return ResourceManager.GetString("PrevStep", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to private. + /// + public static string Private { + get { + return ResourceManager.GetString("Private", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Available to {!users}. + /// + public static string PrivatePanelAccessListLable { + get { + return ResourceManager.GetString("PrivatePanelAccessListLable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Restrict access. + /// + public static string PrivatePanelCheckBoxLabel { + get { + return ResourceManager.GetString("PrivatePanelCheckBoxLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The access is granted to all the portal {!users} by default. To restrict the access check the box below. + /// . + /// + public static string PrivatePanelDescription { + get { + return ResourceManager.GetString("PrivatePanelDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Access rights. + /// + public static string PrivatePanelHeader { + get { + return ResourceManager.GetString("PrivatePanelHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Do the same as CRM user|Import, export data|Send emails to CRM contacts|Manage settings|Manage any contact, task, case, opportunity. + /// + public static string ProductAdminOpportunities { + get { + return ResourceManager.GetString("ProductAdminOpportunities", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Manage your customer relations, identify priority contacts, track communication history and sales performance.. + /// + public static string ProductDescription { + get { + return ResourceManager.GetString("ProductDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Manage your customer relations, identify priority contacts, track communication history and sales performance.. + /// + public static string ProductDescriptionEx { + get { + return ResourceManager.GetString("ProductDescriptionEx", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CRM. + /// + public static string ProductName { + get { + return ResourceManager.GetString("ProductName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Products & Services. + /// + public static string ProductsAndServices { + get { + return ResourceManager.GetString("ProductsAndServices", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create contact, task, case, opportunity with a possibility to limit access to them|Edit contact, task, case, opportunity with the access rights set up. + /// + public static string ProductUserOpportunities { + get { + return ResourceManager.GetString("ProductUserOpportunities", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Profile. + /// + public static string Profile { + get { + return ResourceManager.GetString("Profile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Projects. + /// + public static string Projects { + get { + return ResourceManager.GetString("Projects", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Linked with. + /// + public static string RelativeTo { + get { + return ResourceManager.GetString("RelativeTo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Reports. + /// + public static string ReportsModuleName { + get { + return ResourceManager.GetString("ReportsModuleName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Responsible. + /// + public static string Responsible { + get { + return ResourceManager.GetString("Responsible", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Value. + /// + public static string SampleValues { + get { + return ResourceManager.GetString("SampleValues", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save. + /// + public static string Save { + get { + return ResourceManager.GetString("Save", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save changes. + /// + public static string SaveChanges { + get { + return ResourceManager.GetString("SaveChanges", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Saving changes.... + /// + public static string SaveChangesProggress { + get { + return ResourceManager.GetString("SaveChangesProggress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select. + /// + public static string Select { + get { + return ResourceManager.GetString("Select", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select All. + /// + public static string SelectAll { + get { + return ResourceManager.GetString("SelectAll", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Notify users. + /// + public static string SelectUsersToNotify { + get { + return ResourceManager.GetString("SelectUsersToNotify", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Semicolon. + /// + public static string Semicolon { + get { + return ResourceManager.GetString("Semicolon", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Send. + /// + public static string Send { + get { + return ResourceManager.GetString("Send", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Send Email. + /// + public static string SendEmail { + get { + return ResourceManager.GetString("SendEmail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Set Permissions. + /// + public static string SetPermissions { + get { + return ResourceManager.GetString("SetPermissions", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Settings. + /// + public static string SettingModuleName { + get { + return ResourceManager.GetString("SettingModuleName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Set up & Customize. + /// + public static string SetUpCustomize { + get { + return ResourceManager.GetString("SetUpCustomize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show. + /// + public static string Show { + get { + return ResourceManager.GetString("Show", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show file attachment panel. + /// + public static string ShowAttachPanel { + get { + return ResourceManager.GetString("ShowAttachPanel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show information. + /// + public static string ShowInformation { + get { + return ResourceManager.GetString("ShowInformation", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show more. + /// + public static string ShowMore { + get { + return ResourceManager.GetString("ShowMore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show on page. + /// + public static string ShowOnPage { + get { + return ResourceManager.GetString("ShowOnPage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Single quote . + /// + public static string SingleQuote { + get { + return ResourceManager.GetString("SingleQuote", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Space. + /// + public static string Space { + get { + return ResourceManager.GetString("Space", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Store this message in History. + /// + public static string StoreThisLetterInHistory { + get { + return ResourceManager.GetString("StoreThisLetterInHistory", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Subject. + /// + public static string Subject { + get { + return ResourceManager.GetString("Subject", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New events. + /// + public static string SubscriptionType_AddRelationshipEvent { + get { + return ResourceManager.GetString("SubscriptionType_AddRelationshipEvent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New contacts. + /// + public static string SubscriptionType_CreateNewContact { + get { + return ResourceManager.GetString("SubscriptionType_CreateNewContact", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Export completed. + /// + public static string SubscriptionType_ExportCompleted { + get { + return ResourceManager.GetString("SubscriptionType_ExportCompleted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Import completed. + /// + public static string SubscriptionType_ImportCompleted { + get { + return ResourceManager.GetString("SubscriptionType_ImportCompleted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Responsible for opportunity. + /// + public static string SubscriptionType_ResponsibleForOpportunity { + get { + return ResourceManager.GetString("SubscriptionType_ResponsibleForOpportunity", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tasks assigned to you. + /// + public static string SubscriptionType_ResponsibleForTask { + get { + return ResourceManager.GetString("SubscriptionType_ResponsibleForTask", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Access granted. + /// + public static string SubscriptionType_SetAccess { + get { + return ResourceManager.GetString("SubscriptionType_SetAccess", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Summary table. + /// + public static string SummaryTable { + get { + return ResourceManager.GetString("SummaryTable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tabulation. + /// + public static string Tabulation { + get { + return ResourceManager.GetString("Tabulation", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tags. + /// + public static string Tags { + get { + return ResourceManager.GetString("Tags", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tasks. + /// + public static string TaskModuleName { + get { + return ResourceManager.GetString("TaskModuleName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The email. + /// + public static string TheEmail { + get { + return ResourceManager.GetString("TheEmail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This month. + /// + public static string ThisMonth { + get { + return ResourceManager.GetString("ThisMonth", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Title. + /// + public static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to To. + /// + public static string To { + get { + return ResourceManager.GetString("To", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Today. + /// + public static string Today { + get { + return ResourceManager.GetString("Today", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Total. + /// + public static string Total { + get { + return ResourceManager.GetString("Total", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Track Sales. + /// + public static string TrackSales { + get { + return ResourceManager.GetString("TrackSales", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type. + /// + public static string Type { + get { + return ResourceManager.GetString("Type", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Company/Person. + /// + public static string TypeCompanyOrPerson { + get { + return ResourceManager.GetString("TypeCompanyOrPerson", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unlink contact. + /// + public static string UnlinkContact { + get { + return ResourceManager.GetString("UnlinkContact", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Up. + /// + public static string Up { + get { + return ResourceManager.GetString("Up", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Attach file. + /// + public static string UploadFile { + get { + return ResourceManager.GetString("UploadFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show files. + /// + public static string ViewFiles { + get { + return ResourceManager.GetString("ViewFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to View mailing history with this participant. + /// + public static string ViewMailingHistoryWithParticipant { + get { + return ResourceManager.GetString("ViewMailingHistoryWithParticipant", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Calls. + /// + public static string VoIPCallsSettings { + get { + return ResourceManager.GetString("VoIPCallsSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Common. + /// + public static string VoIPCommonSettings { + get { + return ResourceManager.GetString("VoIPCommonSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Numbers. + /// + public static string VoIPNumbersSettings { + get { + return ResourceManager.GetString("VoIPNumbersSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to VoIP Settings. + /// + public static string VoIPSettings { + get { + return ResourceManager.GetString("VoIPSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Whole CRM module. + /// + public static string WholeCRMModule { + get { + return ResourceManager.GetString("WholeCRMModule", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Yes. + /// + public static string Yes { + get { + return ResourceManager.GetString("Yes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Yesterday. + /// + public static string Yesterday { + get { + return ResourceManager.GetString("Yesterday", resourceCulture); + } + } + } +} diff --git a/products/ASC.CRM/Server/Resources/CRMCommonResource.resx b/products/ASC.CRM/Server/Resources/CRMCommonResource.resx index 2855edb2f5a..f750289c13b 100644 --- a/products/ASC.CRM/Server/Resources/CRMCommonResource.resx +++ b/products/ASC.CRM/Server/Resources/CRMCommonResource.resx @@ -1,76 +1,1126 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Do the same as CRM user|Import, export data|Send emails to CRM contacts|Manage settings|Manage any contact, task, case, opportunity - - - Manage your customer relations, identify priority contacts, track communication history and sales performance. - - - Manage your customer relations, identify priority contacts, track communication history and sales performance. - - - CRM - - - Create a contact, task, case, opportunity with a possibility to limit access to them|Edit a contact, task, case, opportunity with the access rights set up - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + for reading + + + for reading/writing + + + The access rights will be set only for the contacts which were created by you. + + + Actions + + + Add + + + Add {!group} + + + Additional information + + + Add mail account + + + Add Tag + + + Add participant + + + Add This Event + + + Adding the note... + + + Add {!user} + + + Use the {0}?{1} and {0}*{1} symbols to perform a single/multiple-character search. For example, to search for all the words beginning with {0}O{1}, use the {0}O*{1} query. It's not recommended to use these symbols at the beginning of your query. To search for the exact phrase use the quotation marks.{2}Learn more...{3} + + + Alert + + + Corresponding ONLYOFFICE™ CRM field + + + Attachments + + + Link with contact + + + Author + + + Back + + + Learn more... + + + Category + + + By title + + + Cancel + + + Case + + + Cases + + + Category + + + Change + + + Choose + + + Reset filter + + + Close + + + Close this window + + + Close window + + + Colon + + + File column + + + Comma + + + Compose Mail + + + Compose a letter to be sent to all the clients selected in the contact list. You can check the number of selected clients in the 'To' field, enter a common 'Subject' and use the 'Personal tags' to include all the necessary information to your letter. This is a beta version for those who want to try out new features of ONLYOFFICE™ CRM. + + + Confirmation + + + Are you sure you want to delete these elements? + + + To change the list of the contact temperature levels, refer to the CRM {0}Settings{1} section + + + Contact settings + + + Content + + + Date + + + Rate + + + Currency convertor + + + Convert + + + Quantity + + + Create + + + Creation date + + + Create Invoices + + + Create New + + + Create New Contact + + + Create project + + + Add new tag + + + The CRM module is not enabled + + + Currency + + + Algerian Dinar + + + Argentinian Peso + + + Armenian Dram + + + Australian Dollar + + + Azerbaijani Manat + + + Bangladeshi Taka + + + Basic currencies + + + Belarusian Ruble + + + Brazilian Real + + + Bulgarian Lev + + + Canadian Dollar + + + Central African Franc + + + Chilean Peso + + + Chinese Yuan + + + Colombian Peso + + + Costa Rican Colón + + + Croatian Kuna + + + Cuban Peso + + + Czech Koruna + + + Danish Krone + + + Dominican Peso + + + Euro + + + Georgian Lari + + + Hong Kong Dollar + + + Hungarian Forint + + + Indian Rupee + + + Indonesian Rupiah + + + Israeli Shekel + + + Japanese Yen + + + Kazakhstani Tenge + + + Kenyan Shilling + + + Kuwaiti Dinar + + + Latvian Lat + + + Lithuanian Litas + + + Malagasy Ariary + + + Malaysian Ringgit + + + Mauritian Rupee + + + Mexican Peso + + + Moldovan Leu + + + Moroccan Dirham + + + New Zealand Dollar + + + Nigerian Naira + + + Norwegian Krone + + + Omani Rial + + + Other currencies + + + Pakistani Rupee + + + Philippine Peso + + + Polish Zloty + + + Pound Sterling + + + Rouble + + + Saudi Riyal + + + Singapore Dollar + + + South African Rand + + + South Korean Won + + + Swedish Krona + + + Swiss Franc + + + Thai Baht + + + Trinidad and Tobago Dollar + + + Turkish New Lira + + + Ukraine Hryvnia + + + United Arab Emirates Dirham + + + United States Dollar + + + Uzbekistani Som + + + Venezuelan Bolivar + + + Vietnamese Dong + + + West African Franc + + + Current {!user} + + + Custom + + + Custom period + + + Other users + + + Manage Relations with Customers + + + Fill in various contact details, link persons with companies. + + + Add contacts manually or import the list from file. + + + Use the mass mailing feature to address multiple contacts. + + + Change CRM module settings, adapting it maximally to your needs. + + + Customize user fields, task categories or opportunity stages. + + + Use website contact form to add new contacts right from your site. + + + Simplify invoice creation adding products and services. + + + Add taxes specific to certain countries and use them in invoices. + + + Issue invoices to your clients right from the portal interface. + + + Add opportunities, set budget and estimate success probability. + + + Organize cases and specify the list of their participants. + + + Assign tasks to CRM managers linking them with the contacts. + + + Date + + + Due in {0} day(s) + + + {0} day(s) overdue + + + Opportunities + + + Delete + + + Delete contact + + + Delete event + + + Delete participant + + + Delete tag + + + Delete user + + + Description + + + Deselect all + + + Documents + + + Do the same for all future occurrences + + + Double quote + + + Duplicate Records + + + {!Users} + + + Be aware of what your clients think about your produce and brand in the social network. + + + No linked twitter accounts + + + Empty file + + + No records matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the records in this section. You can also look for the records you need in other contact history. + + + No records for this filter can be found here + + + The opportunity participant list is empty + + + Enter amount + + + Error + + + File size exceeds {1}{0}{2} + + + Export current list to CSV-file + + + File + + + Files + + + The maximum file size is exceeded + + + Creation date + + + Due date + + + Responsible + + + With tag + + + Find a company or a person by name... + + + From + + + Hide + + + Hide file attachment panel + + + History + + + You can change the list for categories of history events {0}here{1} + + + Phone call + + + Email + + + Appointment + + + Note + + + has been received + + + has been sent + + + Max. number of characters exceeded: {0} + + + Ignore the first row when importing + + + Duplicate + + + Use this option to import contacts duplicating the coinciding contacts. E.g. there are two records - 'A' and 'B' - in your ONLYOFFICE™ CRM. You want to import 'B' and 'C' records. In case you select the 'Duplicate' option, a duplicate record to 'B' will be created while importing. + + + Check for duplicates will be performed based on the Email field. + + + Overwrite + + + Use this option to import contacts overwriting the existing contacts. E.g. there are two records - 'A' and 'B' - in your ONLYOFFICE™ CRM. You want to import 'B' and 'C' records. In case you select the 'Overwrite' option, the existing 'B' will be overwritten by the imported one while importing. + + + Skip + + + Use this option to import contacts keeping the existing contacts. E.g. there are two records - 'A' and 'B' - in your ONLYOFFICE™ CRM. You want to import 'B' and 'C' records. In case you select the 'Skip' option, the existing 'B' will be preserved while importing. + + + Delimiter + + + Encoding + + + CSV File Settings + + + Text Delimiter + + + Scanning the file... + + + Start importing data + + + Go to Contact List + + + Import of contacts can take several minutes depending on the amount of your data. You will be informed when it is done. + + + Importing started + + + Information is submitted + + + Invoices + + + Invoice settings + + + Taxes + + + Last month + + + Learn more... + + + Letter Body + + + Link with + + + + + + Please wait ... + + + Email body + + + Copy + + + From + + + ONLYOFFICE™ Mass Send Feature: "{0}" + + + Sending Mail + + + Failed to deliver message to {0} + + + Invalid email address: {0} + + + Email subject + + + To + + + Manage Contacts + + + Message + + + Calculator + + + My + + + My opportunities + + + My events + + + My tasks + + + Next record + + + No + + + No records + + + No mail accounts available + + + No matches + + + Not specified + + + Notify + + + Not specified + + + OK + + + On top + + + Organization Profile + + + Other + + + Other settings + + + Overview + + + Share + + + Please wait + + + Portal name + + + Preview Mail + + + Here you can see the letter you are composing. Use the 'Back' button to make some changes or use the 'Send' button, if you like the result. + + + Previous record + + + Previous Step + + + private + + + Available to {!users} + + + Restrict access + + + The access is granted to all the portal {!users} by default. To restrict the access check the box below. + + + + Access rights + + + Do the same as CRM user|Import, export data|Send emails to CRM contacts|Manage settings|Manage any contact, task, case, opportunity + + + Manage your customer relations, identify priority contacts, track communication history and sales performance. + + + Manage your customer relations, identify priority contacts, track communication history and sales performance. + + + CRM + + + Products & Services + + + Create contact, task, case, opportunity with a possibility to limit access to them|Edit contact, task, case, opportunity with the access rights set up + + + Profile + + + Projects + + + Linked with + + + Responsible + + + Value + + + Save + + + Save changes + + + Saving changes... + + + Select + + + Select All + + + Notify users + + + Semicolon + + + Send + + + Send Email + + + Set Permissions + + + Settings + + + Set up & Customize + + + Show + + + Show file attachment panel + + + Show information + + + Show more + + + Show on page + + + Single quote + + + Space + + + Store this message in History + + + Subject + + + New events + + + New contacts + + + Export completed + + + Import completed + + + Responsible for opportunity + + + Tasks assigned to you + + + Access granted + + + Summary table + + + Tabulation + + + Tags + + + Tasks + + + The email + + + This month + + + Title + + + To + + + Today + + + Total + + + Track Sales + + + Type + + + Company/Person + + + Unlink contact + + + Up + + + Attach file + + + Show files + + + View mailing history with this participant + + + Calls + + + Common + + + Numbers + + + VoIP Settings + + + Yes + + + Yesterday + + + Romanian Leu + + + New Belarusian Ruble + + + Creation date + + + Reports + + + Confirmation of changes + + + You deleted yourself from contact managers. + + + You will lose access to the contact for reading and writing, if the appropriate access is not allowed for all CRM users. + + + Pacific franc + + + Export data + + + Export data ({0}% complete) + + + Delivery failed - will retry in {0} seconds. + + + All exported files you can always see, download, edit, or delete them in {0}Documents/My Documents{1} + + + Whole CRM module + + + Serbian Dinar + + + Tunisian Dinar + + + Qatari Riyal + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Resources/CRMContactResource.Designer.cs b/products/ASC.CRM/Server/Resources/CRMContactResource.Designer.cs new file mode 100644 index 00000000000..288307b658c --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMContactResource.Designer.cs @@ -0,0 +1,1867 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.CRM.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class CRMContactResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal CRMContactResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.CRM.Resources.CRMContactResource", typeof(CRMContactResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Abort Blanket Mailing. + /// + public static string AbortMassSend { + get { + return ResourceManager.GetString("AbortMassSend", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Note. + /// + public static string About { + get { + return ResourceManager.GetString("About", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Adding company.... + /// + public static string AddingCompany { + get { + return ResourceManager.GetString("AddingCompany", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Adding contact.... + /// + public static string AddingPersonProgress { + get { + return ResourceManager.GetString("AddingPersonProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Additional information. + /// + public static string AdditionalContactInformation { + get { + return ResourceManager.GetString("AdditionalContactInformation", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add new address. + /// + public static string AddNewAddress { + get { + return ResourceManager.GetString("AddNewAddress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add new company. + /// + public static string AddNewCompany { + get { + return ResourceManager.GetString("AddNewCompany", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add new person. + /// + public static string AddNewContact { + get { + return ResourceManager.GetString("AddNewContact", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add email. + /// + public static string AddNewEmail { + get { + return ResourceManager.GetString("AddNewEmail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add phone. + /// + public static string AddNewPhone { + get { + return ResourceManager.GetString("AddNewPhone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This default action can be changed {0}here{1}. + /// + public static string AddTagToContactGroupHelpForAdmin { + get { + return ResourceManager.GetString("AddTagToContactGroupHelpForAdmin", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Only CRM administrator can reset this parameter afterwards. + /// + public static string AddTagToContactGroupHelpForUser { + get { + return ResourceManager.GetString("AddTagToContactGroupHelpForUser", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save and Create New Company. + /// + public static string AddThisAndCreateCompanyButton { + get { + return ResourceManager.GetString("AddThisAndCreateCompanyButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save and Create New Person. + /// + public static string AddThisAndCreatePeopleButton { + get { + return ResourceManager.GetString("AddThisAndCreatePeopleButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save. + /// + public static string AddThisCompanyButton { + get { + return ResourceManager.GetString("AddThisCompanyButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save. + /// + public static string AddThisPersonButton { + get { + return ResourceManager.GetString("AddThisPersonButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add to Letter Body. + /// + public static string AddToLetterBody { + get { + return ResourceManager.GetString("AddToLetterBody", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Temperature level. + /// + public static string AfterStage { + get { + return ResourceManager.GetString("AfterStage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All Contacts. + /// + public static string AllContacts { + get { + return ResourceManager.GetString("AllContacts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All persons. + /// + public static string AllPersons { + get { + return ResourceManager.GetString("AllPersons", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Link or add person. + /// + public static string AssignContact { + get { + return ResourceManager.GetString("AssignContact", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select from existing contacts. + /// + public static string AssignContactFromExisting { + get { + return ResourceManager.GetString("AssignContactFromExisting", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Assign manager to this contact. + /// + public static string AssignContactManager { + get { + return ResourceManager.GetString("AssignContactManager", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select from existing persons or add a new one. + /// + public static string AssignPersonFromExisting { + get { + return ResourceManager.GetString("AssignPersonFromExisting", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Link or add project. + /// + public static string AssignProject { + get { + return ResourceManager.GetString("AssignProject", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Batch size. + /// + public static string BatchSize { + get { + return ResourceManager.GetString("BatchSize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to We suggest that you send your emails in batch quantities for the mailing software to work efficiently. You can change the number of recipients per batch if you wish.. + /// + public static string BatchSizeInfoText { + get { + return ResourceManager.GetString("BatchSizeInfoText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hide email addresses from recipients?. + /// + public static string BlindLinkInfoText { + get { + return ResourceManager.GetString("BlindLinkInfoText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create new company. + /// + public static string BreadCrumbsAddCompany { + get { + return ResourceManager.GetString("BreadCrumbsAddCompany", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create new person. + /// + public static string BreadCrumbsAddPerson { + get { + return ResourceManager.GetString("BreadCrumbsAddPerson", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Manager. + /// + public static string ByContactManager { + get { + return ResourceManager.GetString("ByContactManager", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add to company only. + /// + public static string CancelAddContactTagToGroupForCompany { + get { + return ResourceManager.GetString("CancelAddContactTagToGroupForCompany", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add to person only. + /// + public static string CancelAddContactTagToGroupForPerson { + get { + return ResourceManager.GetString("CancelAddContactTagToGroupForPerson", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No, for current contact only. + /// + public static string CancelContactStatusGroupChange { + get { + return ResourceManager.GetString("CancelContactStatusGroupChange", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete from company only. + /// + public static string CancelDeleteContactTagFromGroupForCompany { + get { + return ResourceManager.GetString("CancelDeleteContactTagFromGroupForCompany", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete from person only. + /// + public static string CancelDeleteContactTagFromGroupForPerson { + get { + return ResourceManager.GetString("CancelDeleteContactTagFromGroupForPerson", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No category is specified. + /// + public static string CategoryNotSpecified { + get { + return ResourceManager.GetString("CategoryNotSpecified", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change photo. + /// + public static string ChangePhoto { + get { + return ResourceManager.GetString("ChangePhoto", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change photo to default. + /// + public static string ChangePhotoToDefault { + get { + return ResourceManager.GetString("ChangePhotoToDefault", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Choose a profile photo. + /// + public static string ChooseProfilePhoto { + get { + return ResourceManager.GetString("ChooseProfilePhoto", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Companies. + /// + public static string Companies { + get { + return ResourceManager.GetString("Companies", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Company. + /// + public static string Company { + get { + return ResourceManager.GetString("Company", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Company Name. + /// + public static string CompanyName { + get { + return ResourceManager.GetString("CompanyName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Some persons are linked with this company. + ///Do you want to add this tag to all these persons as well?. + /// + public static string ConfirmationAddTagToCompanyGroup { + get { + return ResourceManager.GetString("ConfirmationAddTagToCompanyGroup", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This person belongs to a company. + ///Do you want to add this tag also to the company and to all its persons?. + /// + public static string ConfirmationAddTagToPersonGroup { + get { + return ResourceManager.GetString("ConfirmationAddTagToPersonGroup", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Do you want to change the contact status of the company this person is linked with?. + /// + public static string ConfirmationChangeCompanyStatus { + get { + return ResourceManager.GetString("ConfirmationChangeCompanyStatus", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Do you want to change the contact status of all the persons linked with this company?. + /// + public static string ConfirmationChangePersonsStatus { + get { + return ResourceManager.GetString("ConfirmationChangePersonsStatus", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This company has linked persons. + ///Do you want to delete this tag also from all persons linked with this company?. + /// + public static string ConfirmationDeleteTagFromCompanyGroup { + get { + return ResourceManager.GetString("ConfirmationDeleteTagFromCompanyGroup", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This person belongs to a company. + ///Do you want to delete this tag also from the company and from all its persons?. + /// + public static string ConfirmationDeleteTagFromPersonGroup { + get { + return ResourceManager.GetString("ConfirmationDeleteTagFromPersonGroup", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact. + /// + public static string Contact { + get { + return ResourceManager.GetString("Contact", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact manager. + /// + public static string ContactManager { + get { + return ResourceManager.GetString("ContactManager", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can upload an image file in PNG or JPG format not larger than {1}200x300 pixels{2}, otherwise resizing will take place. The maximum image size cannot exceed {1}{0}{2}.. + /// + public static string ContactPhotoInfo { + get { + return ResourceManager.GetString("ContactPhotoInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contacts. + /// + public static string Contacts { + get { + return ResourceManager.GetString("Contacts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact Temperature Level. + /// + public static string ContactStage { + get { + return ResourceManager.GetString("ContactStage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact Temperature Levels. + /// + public static string ContactStages { + get { + return ResourceManager.GetString("ContactStages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cold. + /// + public static string ContactStatus_Cold { + get { + return ResourceManager.GetString("ContactStatus_Cold", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hot. + /// + public static string ContactStatus_Hot { + get { + return ResourceManager.GetString("ContactStatus_Hot", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Warm. + /// + public static string ContactStatus_Warm { + get { + return ResourceManager.GetString("ContactStatus_Warm", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This default action can be changed {0}here{1}. + /// + public static string ContactStatusGroupChangeHelpForAdmin { + get { + return ResourceManager.GetString("ContactStatusGroupChangeHelpForAdmin", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Only CRM administrator can reset this parameter afterwards. + /// + public static string ContactStatusGroupChangeHelpForUser { + get { + return ResourceManager.GetString("ContactStatusGroupChangeHelpForUser", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact Tag. + /// + public static string ContactTag { + get { + return ResourceManager.GetString("ContactTag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact Tag List. + /// + public static string ContactTagList { + get { + return ResourceManager.GetString("ContactTagList", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact Tags. + /// + public static string ContactTags { + get { + return ResourceManager.GetString("ContactTags", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact Name. + /// + public static string ContactTitle { + get { + return ResourceManager.GetString("ContactTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enter your Twitter account name. For example, teamlabdotcom. + /// + public static string ContactTwitterDescription { + get { + return ResourceManager.GetString("ContactTwitterDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact Type. + /// + public static string ContactType { + get { + return ResourceManager.GetString("ContactType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Client. + /// + public static string ContactType_Client { + get { + return ResourceManager.GetString("ContactType_Client", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Competitor. + /// + public static string ContactType_Competitor { + get { + return ResourceManager.GetString("ContactType_Competitor", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Partner. + /// + public static string ContactType_Partner { + get { + return ResourceManager.GetString("ContactType_Partner", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Supplier. + /// + public static string ContactType_Supplier { + get { + return ResourceManager.GetString("ContactType_Supplier", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Web Site/Social Networks. + /// + public static string ContactWebSiteAndSocialProfiles { + get { + return ResourceManager.GetString("ContactWebSiteAndSocialProfiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Content. + /// + public static string Content { + get { + return ResourceManager.GetString("Content", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Continue. + /// + public static string Continue { + get { + return ResourceManager.GetString("Continue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create the first company. + /// + public static string CreateFirstCompany { + get { + return ResourceManager.GetString("CreateFirstCompany", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create the first person. + /// + public static string CreateFirstPerson { + get { + return ResourceManager.GetString("CreateFirstPerson", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create project. + /// + public static string CreateProject { + get { + return ResourceManager.GetString("CreateProject", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create task. + /// + public static string CreateTask { + get { + return ResourceManager.GetString("CreateTask", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Customer. + /// + public static string Customer { + get { + return ResourceManager.GetString("Customer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Companies. + /// + public static string Dashboard { + get { + return ResourceManager.GetString("Dashboard", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete contact. + /// + public static string DeleteContact { + get { + return ResourceManager.GetString("DeleteContact", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This default action can be changed {0}here{1}. + /// + public static string DeleteTagFromContactGroupHelpForAdmin { + get { + return ResourceManager.GetString("DeleteTagFromContactGroupHelpForAdmin", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Only CRM administrator will be able to reset this parameter in future. + /// + public static string DeleteTagFromContactGroupHelpForUser { + get { + return ResourceManager.GetString("DeleteTagFromContactGroupHelpForUser", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete company. + /// + public static string DeleteThisCompany { + get { + return ResourceManager.GetString("DeleteThisCompany", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete person. + /// + public static string DeleteThisPerson { + get { + return ResourceManager.GetString("DeleteThisPerson", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deleting contacts. + /// + public static string DeletingContacts { + get { + return ResourceManager.GetString("DeletingContacts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deselect all. + /// + public static string DeselectAll { + get { + return ResourceManager.GetString("DeselectAll", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Do not import this field. + /// + public static string DoNotImportThisField { + get { + return ResourceManager.GetString("DoNotImportThisField", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit. + /// + public static string Edit { + get { + return ResourceManager.GetString("Edit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit company «{0}». + /// + public static string EditCompany { + get { + return ResourceManager.GetString("EditCompany", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit contact. + /// + public static string EditContact { + get { + return ResourceManager.GetString("EditContact", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit person «{0}». + /// + public static string EditPerson { + get { + return ResourceManager.GetString("EditPerson", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit company. + /// + public static string EditProfileCompany { + get { + return ResourceManager.GetString("EditProfileCompany", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit person. + /// + public static string EditProfilePerson { + get { + return ResourceManager.GetString("EditProfilePerson", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Email. + /// + public static string Email { + get { + return ResourceManager.GetString("Email", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Keep all the contacts your company works with in one place. Add new companies and persons, divide them into {0}types{1}, link with each other, add personal information. Import multiple contacts that you already have from a {2}CSV file{3} at one go.. + /// + public static string EmptyContactListDescription { + get { + return ResourceManager.GetString("EmptyContactListDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No contacts matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the contacts in this section. Or you can create the contact in case it is absent from the list.. + /// + public static string EmptyContactListFilterDescribe { + get { + return ResourceManager.GetString("EmptyContactListFilterDescribe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No contacts to be displayed for this filter here. + /// + public static string EmptyContactListFilterHeader { + get { + return ResourceManager.GetString("EmptyContactListFilterHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The contact list is empty. + /// + public static string EmptyContactListHeader { + get { + return ResourceManager.GetString("EmptyContactListHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select the existing projects from the list to link them with the CRM contacts, or go to the Projects module to create new projects. This will help you organize your workflow and make plans. A deeper integration with the projects will help you manage your work more efficiently.. + /// + public static string EmptyContactProjectListDescription { + get { + return ResourceManager.GetString("EmptyContactProjectListDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You haven't got the permission to link contacts with projects. You need to be project manager or portal administrator to be able to do that.. + /// + public static string EmptyContactProjectListDescriptionWithoutButton { + get { + return ResourceManager.GetString("EmptyContactProjectListDescriptionWithoutButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The project list is empty. + /// + public static string EmptyContactProjectListHeader { + get { + return ResourceManager.GetString("EmptyContactProjectListHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add persons here creating new people or linking the existing ones to the companies they work for. When a person is created, you can fill all the contact information for him/her and set the access rights level for other portal users.. + /// + public static string EmptyContentPeopleDescribe { + get { + return ResourceManager.GetString("EmptyContentPeopleDescribe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The person list is empty. + /// + public static string EmptyContentPeopleHeader { + get { + return ResourceManager.GetString("EmptyContentPeopleHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enter a company name. + /// + public static string ErrorEmptyCompanyName { + get { + return ResourceManager.GetString("ErrorEmptyCompanyName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enter a first name. + /// + public static string ErrorEmptyContactFirstName { + get { + return ResourceManager.GetString("ErrorEmptyContactFirstName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The uploaded file could not be found. + /// + public static string ErrorEmptyUploadFileSelected { + get { + return ResourceManager.GetString("ErrorEmptyUploadFileSelected", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid email. + /// + public static string ErrorInvalidEmail { + get { + return ResourceManager.GetString("ErrorInvalidEmail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid phone number. + /// + public static string ErrorInvalidPhone { + get { + return ResourceManager.GetString("ErrorInvalidPhone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to all users (including the deleted users). + /// + public static string Everyone { + get { + return ResourceManager.GetString("Everyone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Files. + /// + public static string Files { + get { + return ResourceManager.GetString("Files", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Accessibility. + /// + public static string FilterByAccessibility { + get { + return ResourceManager.GetString("FilterByAccessibility", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Manager. + /// + public static string FilterByContactManager { + get { + return ResourceManager.GetString("FilterByContactManager", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Find a company by name.... + /// + public static string FindCompanyByName { + get { + return ResourceManager.GetString("FindCompanyByName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Find a person by name.... + /// + public static string FindContactByName { + get { + return ResourceManager.GetString("FindContactByName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Find an employee by name.... + /// + public static string FindEmployeeByName { + get { + return ResourceManager.GetString("FindEmployeeByName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to First Name. + /// + public static string FirstName { + get { + return ResourceManager.GetString("FirstName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to General information. + /// + public static string GeneralInformation { + get { + return ResourceManager.GetString("GeneralInformation", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Generate. + /// + public static string Generate { + get { + return ResourceManager.GetString("Generate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Generate links. + /// + public static string GenerateLinks { + get { + return ResourceManager.GetString("GenerateLinks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Generation. + /// + public static string Generation { + get { + return ResourceManager.GetString("Generation", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Incoming call. Duration: {0}. + /// + public static string HistoryVoipIncomingNote { + get { + return ResourceManager.GetString("HistoryVoipIncomingNote", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Outgoing call. Duration: {0}. + /// + public static string HistoryVoipOutcomingNote { + get { + return ResourceManager.GetString("HistoryVoipOutcomingNote", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Import contacts. + /// + public static string ImportContacts { + get { + return ResourceManager.GetString("ImportContacts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A properly formed CSV file should be selected on your computer HDD. It must contain the fields or columns separated by a delimiter which will be used to import persons or companies - 'First Name' and 'Last Name' for persons, 'Company Name' for companies. The files containing more than {0} rows should be divided into smaller parts for proper importing. Make sure you selected the portal users who need to be granted access to the contact details, otherwise the data from the CSV file will be available to you on [rest of string was truncated]";. + /// + public static string ImportFromCSVStepOneDescription { + get { + return ResourceManager.GetString("ImportFromCSVStepOneDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select a CSV file and set access rights before you import contacts. + /// + public static string ImportFromCSVStepOneHeader { + get { + return ResourceManager.GetString("ImportFromCSVStepOneHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Match your file columns with the corresponding ONLYOFFICE™ CRM fields. Please pay your attention to the fact that to import persons correctly you need to have at least one column from your file matched with the 'First Name' field of the ONLYOFFICE™ CRM. The same is for the company import, you will need to match the 'Company Name' field to be able to import a company. The fields containing the dates must have the following format: {0}.. + /// + public static string ImportFromCSVStepTwoDescription { + get { + return ResourceManager.GetString("ImportFromCSVStepTwoDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please, verify the fields. + /// + public static string ImportFromCSVStepTwoHeader { + get { + return ResourceManager.GetString("ImportFromCSVStepTwoHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Go to Contact List. + /// + public static string ImportStartingPanelButton { + get { + return ResourceManager.GetString("ImportStartingPanelButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Import of contacts can take several minutes depending on the amount of your data.. + /// + public static string ImportStartingPanelDescription { + get { + return ResourceManager.GetString("ImportStartingPanelDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Importing started. + /// + public static string ImportStartingPanelHeader { + get { + return ResourceManager.GetString("ImportStartingPanelHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Industry. + /// + public static string Industry { + get { + return ResourceManager.GetString("Industry", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Position. + /// + public static string JobTitle { + get { + return ResourceManager.GetString("JobTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Last Name. + /// + public static string LastName { + get { + return ResourceManager.GetString("LastName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Lead. + /// + public static string Lead { + get { + return ResourceManager.GetString("Lead", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Upload photo. + /// + public static string LoadPhoto { + get { + return ResourceManager.GetString("LoadPhoto", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Upload photo from PC. + /// + public static string LoadPhotoFromPC { + get { + return ResourceManager.GetString("LoadPhotoFromPC", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select from social networks. + /// + public static string LoadPhotoFromSocialMedia { + get { + return ResourceManager.GetString("LoadPhotoFromSocialMedia", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Allow access to the company for all CRM users. + /// + public static string MakePublicPanelCheckBoxLabelForCompany { + get { + return ResourceManager.GetString("MakePublicPanelCheckBoxLabelForCompany", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Allow all CRM users access this contact. + /// + public static string MakePublicPanelCheckBoxLabelForImportContacts { + get { + return ResourceManager.GetString("MakePublicPanelCheckBoxLabelForImportContacts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Allow access to the person for all CRM users. + /// + public static string MakePublicPanelCheckBoxLabelForPerson { + get { + return ResourceManager.GetString("MakePublicPanelCheckBoxLabelForPerson", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The access is granted to contact managers only. To allow access to this contact for all users check the box below. + /// + public static string MakePublicPanelDescrForContact { + get { + return ResourceManager.GetString("MakePublicPanelDescrForContact", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Make this company public. + /// + public static string MakePublicPanelTitleForCompany { + get { + return ResourceManager.GetString("MakePublicPanelTitleForCompany", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Make contact public. + /// + public static string MakePublicPanelTitleForImportContacts { + get { + return ResourceManager.GetString("MakePublicPanelTitleForImportContacts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Make this person public. + /// + public static string MakePublicPanelTitleForPerson { + get { + return ResourceManager.GetString("MakePublicPanelTitleForPerson", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Already sent. + /// + public static string MassSendAlreadySent { + get { + return ResourceManager.GetString("MassSendAlreadySent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Total emails. + /// + public static string MassSendEmailsTotal { + get { + return ResourceManager.GetString("MassSendEmailsTotal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Errors. + /// + public static string MassSendErrors { + get { + return ResourceManager.GetString("MassSendErrors", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Estimated end time. + /// + public static string MassSendEstimatedTime { + get { + return ResourceManager.GetString("MassSendEstimatedTime", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your last mailing is in progress. Thank you for using our blanket mailing feature!. + /// + public static string MassSendInfo { + get { + return ResourceManager.GetString("MassSendInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This is a duplicate. + /// + public static string MergeButtonText { + get { + return ResourceManager.GetString("MergeButtonText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Start Merging Contacts. + /// + public static string MergePanelButtonStartText { + get { + return ResourceManager.GetString("MergePanelButtonStartText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please select the contact which is duplicated by the current contact. The two contact data will be combined and the current contact will be deleted.. + /// + public static string MergePanelDescriptionText { + get { + return ResourceManager.GetString("MergePanelDescriptionText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Merge contacts. + /// + public static string MergePanelHeaderText { + get { + return ResourceManager.GetString("MergePanelHeaderText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Merging contacts.... + /// + public static string MergePanelProgress { + get { + return ResourceManager.GetString("MergePanelProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No matches in ONLYOFFICE™ CRM, select from list. + /// + public static string NoMatchSelect { + get { + return ResourceManager.GetString("NoMatchSelect", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There are no available photos corresponding to the profile. + /// + public static string NoPhotoFromSocialMedia { + get { + return ResourceManager.GetString("NoPhotoFromSocialMedia", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Notify contact manager by email. + /// + public static string NotifyContactManager { + get { + return ResourceManager.GetString("NotifyContactManager", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add to linked contacts. + /// + public static string OKAddContactTagToGroup { + get { + return ResourceManager.GetString("OKAddContactTagToGroup", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Yes, change for the persons. + /// + public static string OKCompanyStatusGroupChange { + get { + return ResourceManager.GetString("OKCompanyStatusGroupChange", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete from linked contact. + /// + public static string OKDeleteContactTagFromGroup { + get { + return ResourceManager.GetString("OKDeleteContactTagFromGroup", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Yes, change for the company. + /// + public static string OKPersonStatusGroupChange { + get { + return ResourceManager.GetString("OKPersonStatusGroupChange", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Description. + /// + public static string Overview { + get { + return ResourceManager.GetString("Overview", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to People. + /// + public static string People { + get { + return ResourceManager.GetString("People", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Person. + /// + public static string Person { + get { + return ResourceManager.GetString("Person", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Personal tags. + /// + public static string PersonalTags { + get { + return ResourceManager.GetString("PersonalTags", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Position. + /// + public static string PersonPosition { + get { + return ResourceManager.GetString("PersonPosition", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Persons. + /// + public static string Persons { + get { + return ResourceManager.GetString("Persons", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Public. + /// + public static string PublicContacts { + get { + return ResourceManager.GetString("PublicContacts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Associated identity. + /// + public static string RelativeEntity { + get { + return ResourceManager.GetString("RelativeEntity", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Restricted. + /// + public static string RestrictedContacts { + get { + return ResourceManager.GetString("RestrictedContacts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save Changes. + /// + public static string SaveChanges { + get { + return ResourceManager.GetString("SaveChanges", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save and Create company. + /// + public static string SaveThisAndCreateCompanyButton { + get { + return ResourceManager.GetString("SaveThisAndCreateCompanyButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save and Create person. + /// + public static string SaveThisAndCreatePeopleButton { + get { + return ResourceManager.GetString("SaveThisAndCreatePeopleButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Saving changes.... + /// + public static string SavingChangesProgress { + get { + return ResourceManager.GetString("SavingChangesProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Search. + /// + public static string Search { + get { + return ResourceManager.GetString("Search", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enter a text to search. + /// + public static string SearchText { + get { + return ResourceManager.GetString("SearchText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select a CSV file. + /// + public static string SelectCSVFileButton { + get { + return ResourceManager.GetString("SelectCSVFileButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show. + /// + public static string Show { + get { + return ResourceManager.GetString("Show", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show all the results. + /// + public static string ShowAllSearchResult { + get { + return ResourceManager.GetString("ShowAllSearchResult", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Go to profile. + /// + public static string ShowContactProfile { + get { + return ResourceManager.GetString("ShowContactProfile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open in a new tab. + /// + public static string ShowContactProfileNewTab { + get { + return ResourceManager.GetString("ShowContactProfileNewTab", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show on the map. + /// + public static string ShowOnMap { + get { + return ResourceManager.GetString("ShowOnMap", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show on page. + /// + public static string ShowOnPage { + get { + return ResourceManager.GetString("ShowOnPage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Staff. + /// + public static string Staff { + get { + return ResourceManager.GetString("Staff", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact type. + /// + public static string Stage { + get { + return ResourceManager.GetString("Stage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Start importing contacts. + /// + public static string StartImport { + get { + return ResourceManager.GetString("StartImport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Supplier. + /// + public static string Supplier { + get { + return ResourceManager.GetString("Supplier", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tag. + /// + public static string Tag { + get { + return ResourceManager.GetString("Tag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tags. + /// + public static string Tags { + get { + return ResourceManager.GetString("Tags", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to We will add the text watermark to your letter like "Powered by ONLYOFFICE.com". You can see it in the next (message preview) step.. + /// + public static string TeamlabWatermarkInfo { + get { + return ResourceManager.GetString("TeamlabWatermarkInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Position. + /// + public static string Title { + get { + return ResourceManager.GetString("Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CSV (comma-separated values) files store tabular data in the plain-text form. You can create such files from any spreadsheets using ONLYOFFICE™ Documents.. + /// + public static string TooltipCsv { + get { + return ResourceManager.GetString("TooltipCsv", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact types let you characterize your potential customers in a certain way. The default contact types are cold, warm and hot which mean the contact's willingness to purchase your product. To be able to edit them, the administrator rights are needed.. + /// + public static string TooltipTypes { + get { + return ResourceManager.GetString("TooltipTypes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All contacts. + /// + public static string TotalContacts { + get { + return ResourceManager.GetString("TotalContacts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Total found. + /// + public static string TotalFinded { + get { + return ResourceManager.GetString("TotalFinded", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show info. + /// + public static string ViewInfo { + get { + return ResourceManager.GetString("ViewInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to View mailing history. + /// + public static string ViewMailingHistory { + get { + return ResourceManager.GetString("ViewMailingHistory", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No contact manager. + /// + public static string WithoutContactManager { + get { + return ResourceManager.GetString("WithoutContactManager", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Write message. + /// + public static string WriteEmail { + get { + return ResourceManager.GetString("WriteEmail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Starting with this message its conversation thread will be displayed in the contact history.. + /// + public static string WriteEmailByMailProductDscr { + get { + return ResourceManager.GetString("WriteEmailByMailProductDscr", resourceCulture); + } + } + } +} diff --git a/products/ASC.CRM/Server/Resources/CRMContactResource.resx b/products/ASC.CRM/Server/Resources/CRMContactResource.resx new file mode 100644 index 00000000000..e670a07bb8c --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMContactResource.resx @@ -0,0 +1,724 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Abort Blanket Mailing + + + Note + + + Adding company... + + + Adding contact... + + + Additional information + + + Add new address + + + Add new company + + + Add new person + + + Add email + + + Add phone + + + This default action can be changed {0}here{1} + + + Only CRM administrator can reset this parameter afterwards + + + Save and Create New Company + + + Save and Create New Person + + + Save + + + Save + + + Add to Letter Body + + + Temperature level + + + All Contacts + + + All persons + + + Link or add person + + + Select from existing contacts + + + Assign manager to this contact + + + Select from existing persons or add a new one + + + Link or add project + + + Batch size + + + We suggest that you send your emails in batch quantities for the mailing software to work efficiently. You can change the number of recipients per batch if you wish. + + + Hide email addresses from recipients? + + + Create new company + + + Create new person + + + Manager + + + Add to company only + + + Add to person only + + + No, for current contact only + + + Delete from company only + + + Delete from person only + + + No category is specified + + + Change photo + + + Change photo to default + + + Choose a profile photo + + + Companies + + + Company + + + Company Name + + + Some persons are linked with this company. +Do you want to add this tag to all these persons as well? + + + This person belongs to a company. +Do you want to add this tag also to the company and to all its persons? + + + Do you want to change the contact status of the company this person is linked with? + + + Do you want to change the contact status of all the persons linked with this company? + + + This company has linked persons. +Do you want to delete this tag also from all persons linked with this company? + + + This person belongs to a company. +Do you want to delete this tag also from the company and from all its persons? + + + Contact + + + Contact manager + + + You can upload an image file in PNG or JPG format not larger than {1}200x300 pixels{2}, otherwise resizing will take place. The maximum image size cannot exceed {1}{0}{2}. + + + Contacts + + + Contact Temperature Level + + + Contact Temperature Levels + + + Cold + + + Hot + + + Warm + + + This default action can be changed {0}here{1} + + + Only CRM administrator can reset this parameter afterwards + + + Contact Tag + + + Contact Tag List + + + Contact Tags + + + Contact Name + + + Enter your Twitter account name. For example, teamlabdotcom + + + Contact Type + + + Client + + + Competitor + + + Partner + + + Supplier + + + Web Site/Social Networks + + + Content + + + Continue + + + Create the first company + + + Create the first person + + + Create project + + + Create task + + + Customer + + + Companies + + + Delete contact + + + This default action can be changed {0}here{1} + + + Only CRM administrator will be able to reset this parameter in future + + + Delete company + + + Delete person + + + Deleting contacts + + + Deselect all + + + Do not import this field + + + Edit + + + Edit company «{0}» + + + Edit contact + + + Edit person «{0}» + + + Edit company + + + Edit person + + + Email + + + Keep all the contacts your company works with in one place. Add new companies and persons, divide them into {0}types{1}, link with each other, add personal information. Import multiple contacts that you already have from a {2}CSV file{3} at one go. + + + No contacts matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the contacts in this section. Or you can create the contact in case it is absent from the list. + + + No contacts to be displayed for this filter here + + + The contact list is empty + + + Select the existing projects from the list to link them with the CRM contacts, or go to the Projects module to create new projects. This will help you organize your workflow and make plans. A deeper integration with the projects will help you manage your work more efficiently. + + + You haven't got the permission to link contacts with projects. You need to be project manager or portal administrator to be able to do that. + + + The project list is empty + + + Add persons here creating new people or linking the existing ones to the companies they work for. When a person is created, you can fill all the contact information for him/her and set the access rights level for other portal users. + + + The person list is empty + + + Enter a company name + + + Enter a first name + + + The uploaded file could not be found + + + Invalid email + + + Invalid phone number + + + all users (including the deleted users) + + + Files + + + Accessibility + + + Manager + + + Find a company by name... + + + Find a person by name... + + + Find an employee by name... + + + First Name + + + General information + + + Generate + + + Generate links + + + Generation + + + Incoming call. Duration: {0} + + + Outgoing call. Duration: {0} + + + Import contacts + + + A properly formed CSV file should be selected on your computer HDD. It must contain the fields or columns separated by a delimiter which will be used to import persons or companies - 'First Name' and 'Last Name' for persons, 'Company Name' for companies. The files containing more than {0} rows should be divided into smaller parts for proper importing. Make sure you selected the portal users who need to be granted access to the contact details, otherwise the data from the CSV file will be available to you only. + + + Select a CSV file and set access rights before you import contacts + + + Match your file columns with the corresponding ONLYOFFICE™ CRM fields. Please pay your attention to the fact that to import persons correctly you need to have at least one column from your file matched with the 'First Name' field of the ONLYOFFICE™ CRM. The same is for the company import, you will need to match the 'Company Name' field to be able to import a company. The fields containing the dates must have the following format: {0}. + + + Please, verify the fields + + + Go to Contact List + + + Import of contacts can take several minutes depending on the amount of your data. + + + Importing started + + + Industry + + + Position + + + Last Name + + + Lead + + + Upload photo + + + Upload photo from PC + + + Select from social networks + + + Allow access to the company for all CRM users + + + Allow all CRM users access this contact + + + Allow access to the person for all CRM users + + + The access is granted to contact managers only. To allow access to this contact for all users check the box below + + + Make this company public + + + Make contact public + + + Make this person public + + + Already sent + + + Total emails + + + Errors + + + Estimated end time + + + Your last mailing is in progress. Thank you for using our blanket mailing feature! + + + This is a duplicate + + + Start Merging Contacts + + + Please select the contact which is duplicated by the current contact. The two contact data will be combined and the current contact will be deleted. + + + Merge contacts + + + Merging contacts... + + + No matches in ONLYOFFICE™ CRM, select from list + + + There are no available photos corresponding to the profile + + + Notify contact manager by email + + + Add to linked contacts + + + Yes, change for the persons + + + Delete from linked contact + + + Yes, change for the company + + + Description + + + People + + + Person + + + Personal tags + + + Position + + + Persons + + + Public + + + Associated identity + + + Restricted + + + Save Changes + + + Save and Create company + + + Save and Create person + + + Saving changes... + + + Search + + + Enter a text to search + + + Select a CSV file + + + Show + + + Show all the results + + + Go to profile + + + Open in a new tab + + + Show on the map + + + Show on page + + + Staff + + + Contact type + + + Start importing contacts + + + Supplier + + + Tag + + + Tags + + + We will add the text watermark to your letter like "Powered by ONLYOFFICE.com". You can see it in the next (message preview) step. + + + Position + + + CSV (comma-separated values) files store tabular data in the plain-text form. You can create such files from any spreadsheets using ONLYOFFICE™ Documents. + + + Contact types let you characterize your potential customers in a certain way. The default contact types are cold, warm and hot which mean the contact's willingness to purchase your product. To be able to edit them, the administrator rights are needed. + + + All contacts + + + Total found + + + Show info + + + View mailing history + + + No contact manager + + + Write message + + + Starting with this message its conversation thread will be displayed in the contact history. + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Resources/CRMDealResource.Designer.cs b/products/ASC.CRM/Server/Resources/CRMDealResource.Designer.cs new file mode 100644 index 00000000000..205863726a0 --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMDealResource.Designer.cs @@ -0,0 +1,954 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.CRM.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class CRMDealResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal CRMDealResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.CRM.Resources.CRMDealResource", typeof(CRMDealResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Add new opportunity. + /// + public static string AddNewDeal { + get { + return ResourceManager.GetString("AddNewDeal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save and Create New Opportunity. + /// + public static string AddThisAndCreateDealButton { + get { + return ResourceManager.GetString("AddThisAndCreateDealButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save. + /// + public static string AddThisDealButton { + get { + return ResourceManager.GetString("AddThisDealButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All Opportunities. + /// + public static string AllDeals { + get { + return ResourceManager.GetString("AllDeals", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stake. + /// + public static string BidType { + get { + return ResourceManager.GetString("BidType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to fix. + /// + public static string BidType_FixedBid { + get { + return ResourceManager.GetString("BidType_FixedBid", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to per day. + /// + public static string BidType_PerDay { + get { + return ResourceManager.GetString("BidType_PerDay", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to per hour. + /// + public static string BidType_PerHour { + get { + return ResourceManager.GetString("BidType_PerHour", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to per month. + /// + public static string BidType_PerMonth { + get { + return ResourceManager.GetString("BidType_PerMonth", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to per week. + /// + public static string BidType_PerWeek { + get { + return ResourceManager.GetString("BidType_PerWeek", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to per year. + /// + public static string BidType_PerYear { + get { + return ResourceManager.GetString("BidType_PerYear", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Payment recurrence. + /// + public static string BidTypePeriod { + get { + return ResourceManager.GetString("BidTypePeriod", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stage. + /// + public static string ByStage { + get { + return ResourceManager.GetString("ByStage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stage type. + /// + public static string ByStageType { + get { + return ResourceManager.GetString("ByStageType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunity contact. + /// + public static string ClientDeal { + get { + return ResourceManager.GetString("ClientDeal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add the first opportunity. + /// + public static string CreateFirstDeal { + get { + return ResourceManager.GetString("CreateFirstDeal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create new opportunity. + /// + public static string CreateNewDeal { + get { + return ResourceManager.GetString("CreateNewDeal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Current opportunity. + /// + public static string CurrentDeal { + get { + return ResourceManager.GetString("CurrentDeal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunity at stage. + /// + public static string CurrentDealMilestone { + get { + return ResourceManager.GetString("CurrentDealMilestone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Custom period. + /// + public static string CustomDateFilter { + get { + return ResourceManager.GetString("CustomDateFilter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Other users. + /// + public static string CustomResponsibleFilter { + get { + return ResourceManager.GetString("CustomResponsibleFilter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunity. + /// + public static string Deal { + get { + return ResourceManager.GetString("Deal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The permissions will be set to the opportunities where you are the author. + /// + public static string DealAccessRightsLimit { + get { + return ResourceManager.GetString("DealAccessRightsLimit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sum. + /// + public static string DealAmount { + get { + return ResourceManager.GetString("DealAmount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Qty. + /// + public static string DealCount { + get { + return ResourceManager.GetString("DealCount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunity Stages. + /// + public static string DealMilestone { + get { + return ResourceManager.GetString("DealMilestone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The salesperson takes a series of question and answer sessions in order to identify the requirements of the prospect. + /// + public static string DealMilestone_Champion_Description { + get { + return ResourceManager.GetString("DealMilestone_Champion_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Need assessment. + /// + public static string DealMilestone_Champion_Title { + get { + return ResourceManager.GetString("DealMilestone_Champion_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A contact with the potential buyer is needed to understand the future sales prospective. + /// + public static string DealMilestone_InitialContact_Description { + get { + return ResourceManager.GetString("DealMilestone_InitialContact_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Initial contact. + /// + public static string DealMilestone_InitialContact_Title { + get { + return ResourceManager.GetString("DealMilestone_InitialContact_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A failure. The opportunity is closed due to the lack of perspective. + /// + public static string DealMilestone_Lost_Description { + get { + return ResourceManager.GetString("DealMilestone_Lost_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unsuccessfully closed. + /// + public static string DealMilestone_Lost_Title { + get { + return ResourceManager.GetString("DealMilestone_Lost_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The potential buyer has expressed his interest in the salesperson's goods or services. + /// + public static string DealMilestone_Opportunity_Description { + get { + return ResourceManager.GetString("DealMilestone_Opportunity_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Qualified prospect. + /// + public static string DealMilestone_Opportunity_Title { + get { + return ResourceManager.GetString("DealMilestone_Opportunity_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The salesperson tries to identify whether the customer can be his/her potential buyer. + /// + public static string DealMilestone_Preapproach_Description { + get { + return ResourceManager.GetString("DealMilestone_Preapproach_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Application of initial fit criteria. + /// + public static string DealMilestone_Preapproach_Title { + get { + return ResourceManager.GetString("DealMilestone_Preapproach_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The potential buyer has received a written offering from the salesperson. + /// + public static string DealMilestone_Prospect_Description { + get { + return ResourceManager.GetString("DealMilestone_Prospect_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Proposal. + /// + public static string DealMilestone_Prospect_Title { + get { + return ResourceManager.GetString("DealMilestone_Prospect_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The potential buyer is identified as a person or entity that has the interest and authority to purchase a product or service. + /// + public static string DealMilestone_Suspect_Description { + get { + return ResourceManager.GetString("DealMilestone_Suspect_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Suspect. + /// + public static string DealMilestone_Suspect_Title { + get { + return ResourceManager.GetString("DealMilestone_Suspect_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The potential buyer has already discussed your offering in details. + /// + public static string DealMilestone_Verbal_Description { + get { + return ResourceManager.GetString("DealMilestone_Verbal_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Negotiations. + /// + public static string DealMilestone_Verbal_Title { + get { + return ResourceManager.GetString("DealMilestone_Verbal_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A deal. The opportunity is closed successfully. + /// + public static string DealMilestone_Won_Description { + get { + return ResourceManager.GetString("DealMilestone_Won_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Closure/Transaction. + /// + public static string DealMilestone_Won_Title { + get { + return ResourceManager.GetString("DealMilestone_Won_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stage type. + /// + public static string DealMilestoneType { + get { + return ResourceManager.GetString("DealMilestoneType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunity Participant. + /// + public static string DealParticipant { + get { + return ResourceManager.GetString("DealParticipant", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunity Participants. + /// + public static string DealParticipants { + get { + return ResourceManager.GetString("DealParticipants", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunities. + /// + public static string Deals { + get { + return ResourceManager.GetString("Deals", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunity Tag. + /// + public static string DealTag { + get { + return ResourceManager.GetString("DealTag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunity Tag List. + /// + public static string DealTagList { + get { + return ResourceManager.GetString("DealTagList", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunity Tags. + /// + public static string DealTags { + get { + return ResourceManager.GetString("DealTags", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete opportunity. + /// + public static string DeleteDeal { + get { + return ResourceManager.GetString("DeleteDeal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deleting opportunities. + /// + public static string DeletingDeals { + get { + return ResourceManager.GetString("DeletingDeals", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Description. + /// + public static string DescriptionDeal { + get { + return ResourceManager.GetString("DescriptionDeal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit «{0}» opportunity. + /// + public static string EditDealLabel { + get { + return ResourceManager.GetString("EditDealLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit opportunity. + /// + public static string EditThisDealButton { + get { + return ResourceManager.GetString("EditThisDealButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunities let you track potential sales to your clients. Determine the {0}opportunity stages{1} in accordance with your business process, keep history, assign tasks, upload documents, calculate profit with every client.. + /// + public static string EmptyContentDealsDescribe { + get { + return ResourceManager.GetString("EmptyContentDealsDescribe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No opportunities matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the opportunities in this section. Or you can create the opportunity in case it is absent from the list.. + /// + public static string EmptyContentDealsFilterDescribe { + get { + return ResourceManager.GetString("EmptyContentDealsFilterDescribe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No opportunities to be displayed for this filter here. + /// + public static string EmptyContentDealsFilterHeader { + get { + return ResourceManager.GetString("EmptyContentDealsFilterHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The opportunity list is empty. + /// + public static string EmptyContentDealsHeader { + get { + return ResourceManager.GetString("EmptyContentDealsHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enter the opportunity title. + /// + public static string EmptyDealName { + get { + return ResourceManager.GetString("EmptyDealName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Every opportunity must have a responsible one. + /// + public static string EmptyDealResponsible { + get { + return ResourceManager.GetString("EmptyDealResponsible", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The participant list is empty. + /// + public static string EmptyPeopleInDealContent { + get { + return ResourceManager.GetString("EmptyPeopleInDealContent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add participants to your opportunity from the list of companies or persons. After this is done you will know what contacts are associated with this opportunity and who you can deal with.. + /// + public static string EmptyPeopleInDealDescript { + get { + return ResourceManager.GetString("EmptyPeopleInDealDescript", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Estimated/Actual due date. + /// + public static string Estimated { + get { + return ResourceManager.GetString("Estimated", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Budget. + /// + public static string ExpectedValue { + get { + return ResourceManager.GetString("ExpectedValue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact. + /// + public static string FilterByContact { + get { + return ResourceManager.GetString("FilterByContact", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Participant. + /// + public static string FilterByParticipant { + get { + return ResourceManager.GetString("FilterByParticipant", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stage/stage type. + /// + public static string FilterByStageOrStageType { + get { + return ResourceManager.GetString("FilterByStageOrStageType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Find opportunity by title. + /// + public static string FindDealByName { + get { + return ResourceManager.GetString("FindDealByName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Import opportunities. + /// + public static string ImportDeals { + get { + return ResourceManager.GetString("ImportDeals", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A properly formed CSV file should be selected on your computer HDD. It must contain the fields or columns separated by the selected delimiter which will be used to import opportunities. The files containing more than {0} rows should be divided into smaller parts for proper importing. Make sure you selected the portal users who need to be granted access to the information to be imported, otherwise the data from the CSV file will be available to all portal users.. + /// + public static string ImportFromCSVStepOneDescription { + get { + return ResourceManager.GetString("ImportFromCSVStepOneDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select a CSV file and set access rights before you import opportunities. + /// + public static string ImportFromCSVStepOneHeader { + get { + return ResourceManager.GetString("ImportFromCSVStepOneHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Match your file columns with the corresponding ONLYOFFICE™ CRM fields. Please pay your attention to the fact that to import opportunities correctly you need to have at least the following columns from your file matched with the fields of the ONLYOFFICE™ CRM: 'Opportunity Title', 'Opportunity Responsible'. The fields containing the dates must have the following format: {0}. The currency field must be set in accordance with the international three letter code (e.g. USD, JPY, AUD).. + /// + public static string ImportFromCSVStepTwoDescription { + get { + return ResourceManager.GetString("ImportFromCSVStepTwoDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please, verify the fields. + /// + public static string ImportFromCSVStepTwoHeader { + get { + return ResourceManager.GetString("ImportFromCSVStepTwoHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Go to oppotunity list. + /// + public static string ImportStartingPanelButton { + get { + return ResourceManager.GetString("ImportStartingPanelButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Import of opportunities can take several minutes depending on the amount of your data.. + /// + public static string ImportStartingPanelDescription { + get { + return ResourceManager.GetString("ImportStartingPanelDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Importing started. + /// + public static string ImportStartingPanelHeader { + get { + return ResourceManager.GetString("ImportStartingPanelHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select from existing opportunities or add a new one. + /// + public static string LinkDealsFromExisting { + get { + return ResourceManager.GetString("LinkDealsFromExisting", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Link or create opportunity. + /// + public static string LinkOrCreateDeal { + get { + return ResourceManager.GetString("LinkOrCreateDeal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to My opportunities. + /// + public static string MyDeals { + get { + return ResourceManager.GetString("MyDeals", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunity title. + /// + public static string NameDeal { + get { + return ResourceManager.GetString("NameDeal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Budget is not set. + /// + public static string NoExpectedValue { + get { + return ResourceManager.GetString("NoExpectedValue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Other opportunity participants. + /// + public static string OtherMembersDeal { + get { + return ResourceManager.GetString("OtherMembersDeal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Overdue. + /// + public static string Overdue { + get { + return ResourceManager.GetString("Overdue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Participants. + /// + public static string PeopleInDeal { + get { + return ResourceManager.GetString("PeopleInDeal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Restrict access to the opportunity. + /// + public static string PrivatePanelCheckBoxLabel { + get { + return ResourceManager.GetString("PrivatePanelCheckBoxLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Success probability. + /// + public static string ProbabilityOfWinning { + get { + return ResourceManager.GetString("ProbabilityOfWinning", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Responsible for the opportunity. + /// + public static string ResponsibleDeal { + get { + return ResourceManager.GetString("ResponsibleDeal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Customize user fields. + /// + public static string SettingCustomFields { + get { + return ResourceManager.GetString("SettingCustomFields", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Go to profile. + /// + public static string ShowDealProfile { + get { + return ResourceManager.GetString("ShowDealProfile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open in a new tab. + /// + public static string ShowDealProfileNewTab { + get { + return ResourceManager.GetString("ShowDealProfileNewTab", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show total amount. + /// + public static string ShowTotalAmount { + get { + return ResourceManager.GetString("ShowTotalAmount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Start Importing. + /// + public static string StartImport { + get { + return ResourceManager.GetString("StartImport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunity stages let you determine steps necessary to complete the deal. The default opportunity stages are approach, discussion, evaluation, negotiation, commitment, success, zero-point. To be able to edit them the portal administrator rights are needed.. + /// + public static string TooltipStages { + get { + return ResourceManager.GetString("TooltipStages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Total. + /// + public static string Total { + get { + return ResourceManager.GetString("Total", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Total amount. + /// + public static string TotalAmount { + get { + return ResourceManager.GetString("TotalAmount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All opportunities. + /// + public static string TotalDeals { + get { + return ResourceManager.GetString("TotalDeals", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Page total. + /// + public static string TotalOnPage { + get { + return ResourceManager.GetString("TotalOnPage", resourceCulture); + } + } + } +} diff --git a/products/ASC.CRM/Server/Resources/CRMDealResource.resx b/products/ASC.CRM/Server/Resources/CRMDealResource.resx new file mode 100644 index 00000000000..4b66f9ebc74 --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMDealResource.resx @@ -0,0 +1,417 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Save and Create New Opportunity + + + Save + + + All Opportunities + + + Stake + + + fix + + + per day + + + per hour + + + per month + + + per week + + + per year + + + Payment recurrence + + + Stage + + + Stage type + + + Opportunity contact + + + Add the first opportunity + + + Create new opportunity + + + Current opportunity + + + Opportunity at stage + + + Custom period + + + Other users + + + Opportunity + + + The permissions will be set to the opportunities where you are the author + + + Sum + + + Qty + + + Opportunity Stages + + + The salesperson takes a series of question and answer sessions in order to identify the requirements of the prospect + + + Need assessment + + + A contact with the potential buyer is needed to understand the future sales prospective + + + Initial contact + + + A failure. The opportunity is closed due to the lack of perspective + + + Unsuccessfully closed + + + The potential buyer has expressed his interest in the salesperson's goods or services + + + Qualified prospect + + + The salesperson tries to identify whether the customer can be his/her potential buyer + + + Application of initial fit criteria + + + The potential buyer has received a written offering from the salesperson + + + Proposal + + + The potential buyer is identified as a person or entity that has the interest and authority to purchase a product or service + + + Suspect + + + The potential buyer has already discussed your offering in details + + + Negotiations + + + A deal. The opportunity is closed successfully + + + Closure/Transaction + + + Stage type + + + Opportunity Participant + + + Opportunity Participants + + + Opportunities + + + Opportunity Tag + + + Opportunity Tag List + + + Opportunity Tags + + + Delete opportunity + + + Deleting opportunities + + + Description + + + Edit «{0}» opportunity + + + Edit opportunity + + + Opportunities let you track potential sales to your clients. Determine the {0}opportunity stages{1} in accordance with your business process, keep history, assign tasks, upload documents, calculate profit with every client. + + + No opportunities matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the opportunities in this section. Or you can create the opportunity in case it is absent from the list. + + + No opportunities to be displayed for this filter here + + + The opportunity list is empty + + + Enter the opportunity title + + + Every opportunity must have a responsible one + + + The participant list is empty + + + Add participants to your opportunity from the list of companies or persons. After this is done you will know what contacts are associated with this opportunity and who you can deal with. + + + Estimated/Actual due date + + + Budget + + + Contact + + + Participant + + + Stage/stage type + + + Import opportunities + + + A properly formed CSV file should be selected on your computer HDD. It must contain the fields or columns separated by the selected delimiter which will be used to import opportunities. The files containing more than {0} rows should be divided into smaller parts for proper importing. Make sure you selected the portal users who need to be granted access to the information to be imported, otherwise the data from the CSV file will be available to all portal users. + + + Select a CSV file and set access rights before you import opportunities + + + Match your file columns with the corresponding ONLYOFFICE™ CRM fields. Please pay your attention to the fact that to import opportunities correctly you need to have at least the following columns from your file matched with the fields of the ONLYOFFICE™ CRM: 'Opportunity Title', 'Opportunity Responsible'. The fields containing the dates must have the following format: {0}. The currency field must be set in accordance with the international three letter code (e.g. USD, JPY, AUD). + + + Please, verify the fields + + + Go to oppotunity list + + + Import of opportunities can take several minutes depending on the amount of your data. + + + Importing started + + + Link or create opportunity + + + My opportunities + + + Opportunity title + + + Budget is not set + + + Other opportunity participants + + + Overdue + + + Participants + + + Restrict access to the opportunity + + + Success probability + + + Responsible for the opportunity + + + Customize user fields + + + Go to profile + + + Open in a new tab + + + Show total amount + + + Start Importing + + + Opportunity stages let you determine steps necessary to complete the deal. The default opportunity stages are approach, discussion, evaluation, negotiation, commitment, success, zero-point. To be able to edit them the portal administrator rights are needed. + + + Total + + + Total amount + + + All opportunities + + + Page total + + + Add new opportunity + + + Select from existing opportunities or add a new one + + + Find opportunity by title + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Resources/CRMEnumResource.Designer.cs b/products/ASC.CRM/Server/Resources/CRMEnumResource.Designer.cs new file mode 100644 index 00000000000..7510ddd31a2 --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMEnumResource.Designer.cs @@ -0,0 +1,684 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.CRM.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class CRMEnumResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal CRMEnumResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.CRM.Resources.CRMEnumResource", typeof(CRMEnumResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Billing. + /// + public static string AddressCategory_Billing { + get { + return ResourceManager.GetString("AddressCategory_Billing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Home. + /// + public static string AddressCategory_Home { + get { + return ResourceManager.GetString("AddressCategory_Home", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Office. + /// + public static string AddressCategory_Office { + get { + return ResourceManager.GetString("AddressCategory_Office", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Other. + /// + public static string AddressCategory_Other { + get { + return ResourceManager.GetString("AddressCategory_Other", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delivery. + /// + public static string AddressCategory_Postal { + get { + return ResourceManager.GetString("AddressCategory_Postal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Work. + /// + public static string AddressCategory_Work { + get { + return ResourceManager.GetString("AddressCategory_Work", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to City. + /// + public static string AddressPart_City { + get { + return ResourceManager.GetString("AddressPart_City", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Country. + /// + public static string AddressPart_Country { + get { + return ResourceManager.GetString("AddressPart_Country", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to State. + /// + public static string AddressPart_State { + get { + return ResourceManager.GetString("AddressPart_State", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Street. + /// + public static string AddressPart_Street { + get { + return ResourceManager.GetString("AddressPart_Street", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Zip. + /// + public static string AddressPart_Zip { + get { + return ResourceManager.GetString("AddressPart_Zip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Home. + /// + public static string ContactInfoBaseCategory_Home { + get { + return ResourceManager.GetString("ContactInfoBaseCategory_Home", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Other. + /// + public static string ContactInfoBaseCategory_Other { + get { + return ResourceManager.GetString("ContactInfoBaseCategory_Other", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Work. + /// + public static string ContactInfoBaseCategory_Work { + get { + return ResourceManager.GetString("ContactInfoBaseCategory_Work", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Address. + /// + public static string ContactInfoType_Address { + get { + return ResourceManager.GetString("ContactInfoType_Address", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to AIM. + /// + public static string ContactInfoType_AIM { + get { + return ResourceManager.GetString("ContactInfoType_AIM", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Blogger. + /// + public static string ContactInfoType_Blogger { + get { + return ResourceManager.GetString("ContactInfoType_Blogger", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Email. + /// + public static string ContactInfoType_Email { + get { + return ResourceManager.GetString("ContactInfoType_Email", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Facebook. + /// + public static string ContactInfoType_Facebook { + get { + return ResourceManager.GetString("ContactInfoType_Facebook", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to GMail. + /// + public static string ContactInfoType_GMail { + get { + return ResourceManager.GetString("ContactInfoType_GMail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ICQ. + /// + public static string ContactInfoType_ICQ { + get { + return ResourceManager.GetString("ContactInfoType_ICQ", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Jabber. + /// + public static string ContactInfoType_Jabber { + get { + return ResourceManager.GetString("ContactInfoType_Jabber", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to LinkedIn. + /// + public static string ContactInfoType_LinkedIn { + get { + return ResourceManager.GetString("ContactInfoType_LinkedIn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to LiveJournal. + /// + public static string ContactInfoType_LiveJournal { + get { + return ResourceManager.GetString("ContactInfoType_LiveJournal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to MSN. + /// + public static string ContactInfoType_MSN { + get { + return ResourceManager.GetString("ContactInfoType_MSN", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to MySpace. + /// + public static string ContactInfoType_MySpace { + get { + return ResourceManager.GetString("ContactInfoType_MySpace", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Phone. + /// + public static string ContactInfoType_Phone { + get { + return ResourceManager.GetString("ContactInfoType_Phone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Skype. + /// + public static string ContactInfoType_Skype { + get { + return ResourceManager.GetString("ContactInfoType_Skype", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Twitter. + /// + public static string ContactInfoType_Twitter { + get { + return ResourceManager.GetString("ContactInfoType_Twitter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to VK. + /// + public static string ContactInfoType_VK { + get { + return ResourceManager.GetString("ContactInfoType_VK", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Web site. + /// + public static string ContactInfoType_Website { + get { + return ResourceManager.GetString("ContactInfoType_Website", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Yahoo. + /// + public static string ContactInfoType_Yahoo { + get { + return ResourceManager.GetString("ContactInfoType_Yahoo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All companies and persons. + /// + public static string ContactListViewType_All { + get { + return ResourceManager.GetString("ContactListViewType_All", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Companies. + /// + public static string ContactListViewType_Company { + get { + return ResourceManager.GetString("ContactListViewType_Company", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Persons. + /// + public static string ContactListViewType_Person { + get { + return ResourceManager.GetString("ContactListViewType_Person", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to With opportunities. + /// + public static string ContactListViewType_WithOpportunity { + get { + return ResourceManager.GetString("ContactListViewType_WithOpportunity", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Flag. + /// + public static string CustomFieldType_CheckBox { + get { + return ResourceManager.GetString("CustomFieldType_CheckBox", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Date. + /// + public static string CustomFieldType_Date { + get { + return ResourceManager.GetString("CustomFieldType_Date", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Title. + /// + public static string CustomFieldType_Heading { + get { + return ResourceManager.GetString("CustomFieldType_Heading", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Drop-down list. + /// + public static string CustomFieldType_SelectBox { + get { + return ResourceManager.GetString("CustomFieldType_SelectBox", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Text area. + /// + public static string CustomFieldType_TextArea { + get { + return ResourceManager.GetString("CustomFieldType_TextArea", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Text area. + /// + public static string CustomFieldType_TextField { + get { + return ResourceManager.GetString("CustomFieldType_TextField", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unsuccessfully closed. + /// + public static string DealMilestoneStatus_ClosedAndLost { + get { + return ResourceManager.GetString("DealMilestoneStatus_ClosedAndLost", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Successfully closed. + /// + public static string DealMilestoneStatus_ClosedAndWon { + get { + return ResourceManager.GetString("DealMilestoneStatus_ClosedAndWon", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open. + /// + public static string DealMilestoneStatus_Open { + get { + return ResourceManager.GetString("DealMilestoneStatus_Open", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Case. + /// + public static string EntityType_Case { + get { + return ResourceManager.GetString("EntityType_Case", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Company. + /// + public static string EntityType_Company { + get { + return ResourceManager.GetString("EntityType_Company", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact. + /// + public static string EntityType_Contact { + get { + return ResourceManager.GetString("EntityType_Contact", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File. + /// + public static string EntityType_File { + get { + return ResourceManager.GetString("EntityType_File", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invoice. + /// + public static string EntityType_Invoice { + get { + return ResourceManager.GetString("EntityType_Invoice", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunity. + /// + public static string EntityType_Opportunity { + get { + return ResourceManager.GetString("EntityType_Opportunity", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Person. + /// + public static string EntityType_Person { + get { + return ResourceManager.GetString("EntityType_Person", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to History. + /// + public static string EntityType_RelationshipEvent { + get { + return ResourceManager.GetString("EntityType_RelationshipEvent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Task. + /// + public static string EntityType_Task { + get { + return ResourceManager.GetString("EntityType_Task", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Files are uploaded. + /// + public static string HistoryCategorySystem_FilesUpload { + get { + return ResourceManager.GetString("HistoryCategorySystem_FilesUpload", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mail Message. + /// + public static string HistoryCategorySystem_MailMessage { + get { + return ResourceManager.GetString("HistoryCategorySystem_MailMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Task is closed. + /// + public static string HistoryCategorySystem_TaskClosed { + get { + return ResourceManager.GetString("HistoryCategorySystem_TaskClosed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Archive. + /// + public static string InvoiceStatus_Archived { + get { + return ResourceManager.GetString("InvoiceStatus_Archived", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Draft. + /// + public static string InvoiceStatus_Draft { + get { + return ResourceManager.GetString("InvoiceStatus_Draft", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Overdue. + /// + public static string InvoiceStatus_Overdue { + get { + return ResourceManager.GetString("InvoiceStatus_Overdue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Paid. + /// + public static string InvoiceStatus_Paid { + get { + return ResourceManager.GetString("InvoiceStatus_Paid", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Rejected. + /// + public static string InvoiceStatus_Rejected { + get { + return ResourceManager.GetString("InvoiceStatus_Rejected", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Billed. + /// + public static string InvoiceStatus_Sent { + get { + return ResourceManager.GetString("InvoiceStatus_Sent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Direct. + /// + public static string PhoneCategory_Direct { + get { + return ResourceManager.GetString("PhoneCategory_Direct", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Fax. + /// + public static string PhoneCategory_Fax { + get { + return ResourceManager.GetString("PhoneCategory_Fax", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Home. + /// + public static string PhoneCategory_Home { + get { + return ResourceManager.GetString("PhoneCategory_Home", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mobile. + /// + public static string PhoneCategory_Mobile { + get { + return ResourceManager.GetString("PhoneCategory_Mobile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Other. + /// + public static string PhoneCategory_Other { + get { + return ResourceManager.GetString("PhoneCategory_Other", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Work. + /// + public static string PhoneCategory_Work { + get { + return ResourceManager.GetString("PhoneCategory_Work", resourceCulture); + } + } + } +} diff --git a/products/ASC.CRM/Server/Resources/CRMEnumResource.resx b/products/ASC.CRM/Server/Resources/CRMEnumResource.resx new file mode 100644 index 00000000000..13a28c8f0be --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMEnumResource.resx @@ -0,0 +1,327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Billing + + + Home + + + Office + + + Other + + + Delivery + + + Work + + + City + + + Country + + + State + + + Street + + + Zip + + + Home + + + Other + + + Work + + + Address + + + AIM + + + Blogger + + + Email + + + Facebook + + + GMail + + + ICQ + + + Jabber + + + LinkedIn + + + LiveJournal + + + MSN + + + MySpace + + + Phone + + + Skype + + + Twitter + + + Web site + + + Yahoo + + + All companies and persons + + + Companies + + + Persons + + + With opportunities + + + Flag + + + Date + + + Title + + + Drop-down list + + + Text area + + + Text area + + + Unsuccessfully closed + + + Successfully closed + + + Open + + + Case + + + Company + + + Contact + + + File + + + Invoice + + + Opportunity + + + Person + + + History + + + Task + + + Files are uploaded + + + Mail Message + + + Task is closed + + + Archive + + + Draft + + + Overdue + + + Paid + + + Rejected + + + Billed + + + Direct + + + Fax + + + Home + + + Mobile + + + Other + + + Work + + + VK + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Resources/CRMErrorsResource.Designer.cs b/products/ASC.CRM/Server/Resources/CRMErrorsResource.Designer.cs new file mode 100644 index 00000000000..681c736a9f6 --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMErrorsResource.Designer.cs @@ -0,0 +1,423 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.CRM.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class CRMErrorsResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal CRMErrorsResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.CRM.Resources.CRMErrorsResource", typeof(CRMErrorsResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Access Denied. + /// + public static string AccessDenied { + get { + return ResourceManager.GetString("AccessDenied", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Can't be deleted. + /// + public static string BasicCannotBeDeleted { + get { + return ResourceManager.GetString("BasicCannotBeDeleted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Can't be edited. + /// + public static string BasicCannotBeEdited { + get { + return ResourceManager.GetString("BasicCannotBeEdited", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact is not a company. + /// + public static string ContactIsNotCompany { + get { + return ResourceManager.GetString("ContactIsNotCompany", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact is not a person. + /// + public static string ContactIsNotPerson { + get { + return ResourceManager.GetString("ContactIsNotPerson", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact is not found. + /// + public static string ContactNotFound { + get { + return ResourceManager.GetString("ContactNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unknown photo size. + /// + public static string ContactPhotoSizeUnknown { + get { + return ResourceManager.GetString("ContactPhotoSizeUnknown", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact type is not found. + /// + public static string ContactTypeNotFound { + get { + return ResourceManager.GetString("ContactTypeNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Currency is not found. + /// + public static string CurrencyNotFound { + get { + return ResourceManager.GetString("CurrencyNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mask is not valid. + /// + public static string CustomFieldMaskNotValid { + get { + return ResourceManager.GetString("CustomFieldMaskNotValid", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to invalid date time format. + /// + public static string DateTimeFormatInvalid { + get { + return ResourceManager.GetString("DateTimeFormatInvalid", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There are some opportunities at this stage. + /// + public static string DealMilestoneHasRelatedDeals { + get { + return ResourceManager.GetString("DealMilestoneHasRelatedDeals", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deal milestone is not found. + /// + public static string DealMilestoneNotFound { + get { + return ResourceManager.GetString("DealMilestoneNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to opportunity is not found. + /// + public static string DealNotFound { + get { + return ResourceManager.GetString("DealNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Entity type is unknown. + /// + public static string EntityTypeUnknown { + get { + return ResourceManager.GetString("EntityTypeUnknown", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to empty exchange rate. + /// + public static string ExchangeRateNotSet { + get { + return ResourceManager.GetString("ExchangeRateNotSet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Export data is null. + /// + public static string ExportToCSVDataEmpty { + get { + return ResourceManager.GetString("ExportToCSVDataEmpty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File was not created. + /// + public static string FileCreateError { + get { + return ResourceManager.GetString("FileCreateError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There are some related contacts. + /// + public static string HasRelatedContacts { + get { + return ResourceManager.GetString("HasRelatedContacts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There are some history events with this category. + /// + public static string HistoryCategoryHasRelatedEvents { + get { + return ResourceManager.GetString("HistoryCategoryHasRelatedEvents", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Data too long for column 'content'. + /// + public static string HistoryEventDataTooLong { + get { + return ResourceManager.GetString("HistoryEventDataTooLong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid currency rate: {0}. + /// + public static string InvalidCurrencyRate { + get { + return ResourceManager.GetString("InvalidCurrencyRate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid file. + /// + public static string InvalidFile { + get { + return ResourceManager.GetString("InvalidFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to consignee is null. + /// + public static string InvoiceConsigneeNotFound { + get { + return ResourceManager.GetString("InvoiceConsigneeNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to conctact is null. + /// + public static string InvoiceContactNotFound { + get { + return ResourceManager.GetString("InvoiceContactNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to invalid dueDate. + /// + public static string InvoiceDueDateInvalid { + get { + return ResourceManager.GetString("InvoiceDueDateInvalid", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Issue date more than due date. + /// + public static string InvoiceIssueMoreThanDue { + get { + return ResourceManager.GetString("InvoiceIssueMoreThanDue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to invoiceItem is not found. + /// + public static string InvoiceItemNotFound { + get { + return ResourceManager.GetString("InvoiceItemNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to invoiceItems list is empty. + /// + public static string InvoiceItemsListEmpty { + get { + return ResourceManager.GetString("InvoiceItemsListEmpty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invoice is not found. + /// + public static string InvoiceNotFound { + get { + return ResourceManager.GetString("InvoiceNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to invoice with same number is already exist. + /// + public static string InvoiceNumberBusy { + get { + return ResourceManager.GetString("InvoiceNumberBusy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to invoiceTax is not found. + /// + public static string InvoiceTaxNotFound { + get { + return ResourceManager.GetString("InvoiceTaxNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to terms is null. + /// + public static string InvoiceTermsNotFound { + get { + return ResourceManager.GetString("InvoiceTermsNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to language is null. + /// + public static string LanguageNotFound { + get { + return ResourceManager.GetString("LanguageNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to responsible user cannot be visitor. + /// + public static string ResponsibleCannotBeVisitor { + get { + return ResourceManager.GetString("ResponsibleCannotBeVisitor", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tag with this name already exists. + /// + public static string TagNameBusy { + get { + return ResourceManager.GetString("TagNameBusy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tag name is empty. + /// + public static string TagNameNotSet { + get { + return ResourceManager.GetString("TagNameNotSet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There are some tasks with this category. + /// + public static string TaskCategoryHasRelatedTasks { + get { + return ResourceManager.GetString("TaskCategoryHasRelatedTasks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Category is not found. + /// + public static string TaskCategoryNotFound { + get { + return ResourceManager.GetString("TaskCategoryNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unknown currency: {0}. + /// + public static string UnknownCurrency { + get { + return ResourceManager.GetString("UnknownCurrency", resourceCulture); + } + } + } +} diff --git a/products/ASC.CRM/Server/Resources/CRMErrorsResource.resx b/products/ASC.CRM/Server/Resources/CRMErrorsResource.resx new file mode 100644 index 00000000000..313b82fd78f --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMErrorsResource.resx @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Access Denied + + + Can't be deleted + + + Can't be edited + + + Contact is not a company + + + Contact is not a person + + + Contact is not found + + + Unknown photo size + + + Contact type is not found + + + Currency is not found + + + Mask is not valid + + + invalid date time format + + + There are some opportunities at this stage + + + Deal milestone is not found + + + opportunity is not found + + + Entity type is unknown + + + empty exchange rate + + + Export data is null + + + File was not created + + + There are some related contacts + + + There are some history events with this category + + + Data too long for column 'content' + + + Invalid currency rate: {0} + + + Invalid file + + + consignee is null + + + conctact is null + + + invalid dueDate + + + Issue date more than due date + + + invoiceItem is not found + + + invoiceItems list is empty + + + Invoice is not found + + + invoice with same number is already exist + + + invoiceTax is not found + + + terms is null + + + language is null + + + responsible user cannot be visitor + + + Tag with this name already exists + + + Tag name is empty + + + There are some tasks with this category + + + Category is not found + + + Unknown currency: {0} + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Resources/CRMInvoiceResource.Designer.cs b/products/ASC.CRM/Server/Resources/CRMInvoiceResource.Designer.cs new file mode 100644 index 00000000000..759a34846d6 --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMInvoiceResource.Designer.cs @@ -0,0 +1,1575 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.CRM.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class CRMInvoiceResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal CRMInvoiceResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.CRM.Resources.CRMInvoiceResource", typeof(CRMInvoiceResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Add and Create New Invoice. + /// + public static string AddAndCreateNewInvoiceButton { + get { + return ResourceManager.GetString("AddAndCreateNewInvoiceButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add billing address. + /// + public static string AddBillingAddress { + get { + return ResourceManager.GetString("AddBillingAddress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add delivery address. + /// + public static string AddDeliveryAddress { + get { + return ResourceManager.GetString("AddDeliveryAddress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add Line. + /// + public static string AddLineButton { + get { + return ResourceManager.GetString("AddLineButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Address has been successfully updated. + /// + public static string AddressesUpdated { + get { + return ResourceManager.GetString("AddressesUpdated", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create new tax. + /// + public static string AddTaxHeaderText { + get { + return ResourceManager.GetString("AddTaxHeaderText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Creating a tax. + /// + public static string AddTaxProcessText { + get { + return ResourceManager.GetString("AddTaxProcessText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save and Create New Item. + /// + public static string AddThisAndCreateInvoiceItemButton { + get { + return ResourceManager.GetString("AddThisAndCreateInvoiceItemButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add This Invoice. + /// + public static string AddThisInvoiceButton { + get { + return ResourceManager.GetString("AddThisInvoiceButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All invoices. + /// + public static string AllInvoices { + get { + return ResourceManager.GetString("AllInvoices", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Amount. + /// + public static string AmountCol { + get { + return ResourceManager.GetString("AmountCol", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Attach document. + /// + public static string AttachDocumentButton { + get { + return ResourceManager.GetString("AttachDocumentButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to automatically generated. + /// + public static string AutomaticallyGenerated { + get { + return ResourceManager.GetString("AutomaticallyGenerated", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bill To. + /// + public static string BillTo { + get { + return ResourceManager.GetString("BillTo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Currency. + /// + public static string ByCurrency { + get { + return ResourceManager.GetString("ByCurrency", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Due date. + /// + public static string ByDueDate { + get { + return ResourceManager.GetString("ByDueDate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change format. + /// + public static string ChangeFormat { + get { + return ResourceManager.GetString("ChangeFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All the future invoices will have the prefix/number format set using the fields below. Check the appropriate box to generate numbers automatically.. + /// + public static string ChangeFormatInfo { + get { + return ResourceManager.GetString("ChangeFormatInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change status. + /// + public static string ChangeInvoiceStatus { + get { + return ResourceManager.GetString("ChangeInvoiceStatus", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Client notes. + /// + public static string ClientNotes { + get { + return ResourceManager.GetString("ClientNotes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Will be displayed on the invoice and in the letter body if you send the invoice per ONLYOFFICE™ Mail.. + /// + public static string ClientNotessHelpInfoText { + get { + return ResourceManager.GetString("ClientNotessHelpInfoText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Consignee. + /// + public static string Consignee { + get { + return ResourceManager.GetString("Consignee", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to coincides with the client. + /// + public static string ConsigneeEqualClient { + get { + return ResourceManager.GetString("ConsigneeEqualClient", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create first invoice. + /// + public static string CreateFirstInvoice { + get { + return ResourceManager.GetString("CreateFirstInvoice", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create first item. + /// + public static string CreateFirstInvoiceItem { + get { + return ResourceManager.GetString("CreateFirstInvoiceItem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create first tax. + /// + public static string CreateFirstInvoiceTax { + get { + return ResourceManager.GetString("CreateFirstInvoiceTax", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create item. + /// + public static string CreateInvoiceItem { + get { + return ResourceManager.GetString("CreateInvoiceItem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Creating invoice item. + /// + public static string CreateInvoiceItemProggress { + get { + return ResourceManager.GetString("CreateInvoiceItemProggress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create tax. + /// + public static string CreateInvoiceTax { + get { + return ResourceManager.GetString("CreateInvoiceTax", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create new invoice. + /// + public static string CreateNewInvoice { + get { + return ResourceManager.GetString("CreateNewInvoice", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create new item. + /// + public static string CreateNewInvoiceItem { + get { + return ResourceManager.GetString("CreateNewInvoiceItem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create new tax. + /// + public static string CreateNewInvoiceTax { + get { + return ResourceManager.GetString("CreateNewInvoiceTax", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Currency. + /// + public static string Currency { + get { + return ResourceManager.GetString("Currency", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Every invoice must have a currency. + /// + public static string CurrencyRequiredErrorMsg { + get { + return ResourceManager.GetString("CurrencyRequiredErrorMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} days. + /// + public static string DaysCount { + get { + return ResourceManager.GetString("DaysCount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to These default terms will be used on future invoices.. + /// + public static string DefaultTermsInfo { + get { + return ResourceManager.GetString("DefaultTermsInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to delete the '{0}' invoice?. + /// + public static string DeleteInvoiceConfirmMessage { + get { + return ResourceManager.GetString("DeleteInvoiceConfirmMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deleting invoice.... + /// + public static string DeleteInvoiceInProgress { + get { + return ResourceManager.GetString("DeleteInvoiceInProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deleting item.... + /// + public static string DeleteInvoiceItemInProgress { + get { + return ResourceManager.GetString("DeleteInvoiceItemInProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to delete the «{0}» item?. + /// + public static string DeleteItemConfirmMessage { + get { + return ResourceManager.GetString("DeleteItemConfirmMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to delete the «{0}» tax?. + /// + public static string DeleteTaxConfirmMessage { + get { + return ResourceManager.GetString("DeleteTaxConfirmMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deleting tax.... + /// + public static string DeleteTaxInProgress { + get { + return ResourceManager.GetString("DeleteTaxInProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete this invoice. + /// + public static string DeleteThisInvoice { + get { + return ResourceManager.GetString("DeleteThisInvoice", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deleting invoices. + /// + public static string DeletingInvoices { + get { + return ResourceManager.GetString("DeletingInvoices", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Description. + /// + public static string DescriptionCol { + get { + return ResourceManager.GetString("DescriptionCol", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Discount. + /// + public static string DiscountCol { + get { + return ResourceManager.GetString("DiscountCol", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Download. + /// + public static string Download { + get { + return ResourceManager.GetString("Download", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Due date. + /// + public static string DueDate { + get { + return ResourceManager.GetString("DueDate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please, set custom Due date or choose one of the presets on the right to link this date with the Date of issue.. + /// + public static string DueDateHelpInfoText { + get { + return ResourceManager.GetString("DueDateHelpInfoText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Issue date more than due date. + /// + public static string DueDateInvalidErrorMsg { + get { + return ResourceManager.GetString("DueDateInvalidErrorMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Due on receipt. + /// + public static string DueDatePresetInfoText { + get { + return ResourceManager.GetString("DueDatePresetInfoText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Every invoice must have a due date. + /// + public static string DueDateRequiredErrorMsg { + get { + return ResourceManager.GetString("DueDateRequiredErrorMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Duplicate and Create New Invoice. + /// + public static string DuplicateAndCreateNewInvoiceButton { + get { + return ResourceManager.GetString("DuplicateAndCreateNewInvoiceButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Duplicate invoice. + /// + public static string DuplicateInvoice { + get { + return ResourceManager.GetString("DuplicateInvoice", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Duplicate Invoice. + /// + public static string DuplicateInvoiceButton { + get { + return ResourceManager.GetString("DuplicateInvoiceButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Duplicate invoice «{0}». + /// + public static string DuplicateInvoiceHeader { + get { + return ResourceManager.GetString("DuplicateInvoiceHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit billing address. + /// + public static string EditBillingAddress { + get { + return ResourceManager.GetString("EditBillingAddress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit delivery address. + /// + public static string EditDeliveryAddress { + get { + return ResourceManager.GetString("EditDeliveryAddress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit invoice. + /// + public static string EditInvoice { + get { + return ResourceManager.GetString("EditInvoice", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit invoice «{0}». + /// + public static string EditInvoiceHeader { + get { + return ResourceManager.GetString("EditInvoiceHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit. + /// + public static string EditInvoiceItem { + get { + return ResourceManager.GetString("EditInvoiceItem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit tax. + /// + public static string EditInvoiceTax { + get { + return ResourceManager.GetString("EditInvoiceTax", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This section allows you to set the list of items which can be included into invoices. After the list is created you can select the necessary item from it to add to your payment document. You can also add new items whenever needed.. + /// + public static string EmptyContentInvoiceItemsDescribe { + get { + return ResourceManager.GetString("EmptyContentInvoiceItemsDescribe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No invoice items matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the invoice items in this section. Or you can create the invoice item if it is absent from the list.. + /// + public static string EmptyContentInvoiceItemsFilterDescribe { + get { + return ResourceManager.GetString("EmptyContentInvoiceItemsFilterDescribe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No invoice items to be displayed for this filter here. + /// + public static string EmptyContentInvoiceItemsFilterHeader { + get { + return ResourceManager.GetString("EmptyContentInvoiceItemsFilterHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Products & Services list is empty. + /// + public static string EmptyContentInvoiceItemsHeader { + get { + return ResourceManager.GetString("EmptyContentInvoiceItemsHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The 'Invoices' section allows you to create payment documents that you can provide your clients with. After the invoices are added the list of the available ones will be displayed here.. + /// + public static string EmptyContentInvoicesDescribe { + get { + return ResourceManager.GetString("EmptyContentInvoicesDescribe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No invoices matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the invoices in this section. Or you can create the invoice if it is absent from the list.. + /// + public static string EmptyContentInvoicesFilterDescribe { + get { + return ResourceManager.GetString("EmptyContentInvoicesFilterDescribe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No invoices to be displayed for this filter here. + /// + public static string EmptyContentInvoicesFilterHeader { + get { + return ResourceManager.GetString("EmptyContentInvoicesFilterHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The invoice list is empty. + /// + public static string EmptyContentInvoicesHeader { + get { + return ResourceManager.GetString("EmptyContentInvoicesHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invoice taxes allow you to create additional taxes specific for the countries or areas you or your clients live in. You can add the tax name, interest rate and description. You can also add new taxes whenever needed.. + /// + public static string EmptyContentInvoiceTaxesDescribe { + get { + return ResourceManager.GetString("EmptyContentInvoiceTaxesDescribe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The invoice taxes list is empty. + /// + public static string EmptyContentInvoiceTaxesHeader { + get { + return ResourceManager.GetString("EmptyContentInvoiceTaxesHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No opportunities found. + /// + public static string EmptyOpportunitiesText { + get { + return ResourceManager.GetString("EmptyOpportunitiesText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select Item. + /// + public static string EmptyProductText { + get { + return ResourceManager.GetString("EmptyProductText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The tax name field cannot be empty. + /// + public static string EmptyTaxNameError { + get { + return ResourceManager.GetString("EmptyTaxNameError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The rate field cannot be empty. + /// + public static string EmptyTaxRateError { + get { + return ResourceManager.GetString("EmptyTaxRateError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to None. + /// + public static string EmptyTaxText { + get { + return ResourceManager.GetString("EmptyTaxText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Price is empty. + /// + public static string ErrorEmptyPrice { + get { + return ResourceManager.GetString("ErrorEmptyPrice", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Price is incorect. + /// + public static string ErrorIncorrectPrice { + get { + return ResourceManager.GetString("ErrorIncorrectPrice", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Quantity is incorrect. + /// + public static string ErrorIncorrectQuantity { + get { + return ResourceManager.GetString("ErrorIncorrectQuantity", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to the rate value is incorect. + /// + public static string ErrorIncorrectRate { + get { + return ResourceManager.GetString("ErrorIncorrectRate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Exchange rate. + /// + public static string ExchangeRate { + get { + return ResourceManager.GetString("ExchangeRate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You must fill this field. + /// + public static string ExchangeRateRequiredErrorMsg { + get { + return ResourceManager.GetString("ExchangeRateRequiredErrorMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The tax name already exist. + /// + public static string ExistTaxNameError { + get { + return ResourceManager.GetString("ExistTaxNameError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Inventory stock. + /// + public static string FormInvoiceItemInventoryStock { + get { + return ResourceManager.GetString("FormInvoiceItemInventoryStock", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Item name. + /// + public static string FormInvoiceItemName { + get { + return ResourceManager.GetString("FormInvoiceItemName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Price. + /// + public static string FormInvoiceItemPrice { + get { + return ResourceManager.GetString("FormInvoiceItemPrice", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stock quantity. + /// + public static string FormInvoiceItemStockQuantity { + get { + return ResourceManager.GetString("FormInvoiceItemStockQuantity", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Taxes. + /// + public static string FormInvoiceItemTaxes { + get { + return ResourceManager.GetString("FormInvoiceItemTaxes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Find by title, SKU or description. + /// + public static string InfoiceItemsFilterWatermarkText { + get { + return ResourceManager.GetString("InfoiceItemsFilterWatermarkText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invoice. + /// + public static string Invoice { + get { + return ResourceManager.GetString("Invoice", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Client. + /// + public static string InvoiceClient { + get { + return ResourceManager.GetString("InvoiceClient", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Every invoice must have a client. + /// + public static string InvoiceClientReqiuredErrorMsg { + get { + return ResourceManager.GetString("InvoiceClientReqiuredErrorMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Consignee. + /// + public static string InvoiceConsignee { + get { + return ResourceManager.GetString("InvoiceConsignee", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your default CRM currency is used to set prices in Products & Services Catalog.<br/>To change it, please go to <a href="settings.aspx?type=common" target="_blank">Common Settings</a>. + /// + public static string InvoiceCurrencyHelpInfo { + get { + return ResourceManager.GetString("InvoiceCurrencyHelpInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Active. + /// + public static string InvoiceItemActiveStatus { + get { + return ResourceManager.GetString("InvoiceItemActiveStatus", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Archived. + /// + public static string InvoiceItemArchivedStatus { + get { + return ResourceManager.GetString("InvoiceItemArchivedStatus", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Name. + /// + public static string InvoiceItemName { + get { + return ResourceManager.GetString("InvoiceItemName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to One or more invoice items have negative stock quantities. + /// + public static string InvoiceItemNegativeQuantityErrorText { + get { + return ResourceManager.GetString("InvoiceItemNegativeQuantityErrorText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Price. + /// + public static string InvoiceItemPrice { + get { + return ResourceManager.GetString("InvoiceItemPrice", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to With inventory stock. + /// + public static string InvoiceItemWithIventoryStock { + get { + return ResourceManager.GetString("InvoiceItemWithIventoryStock", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Without inventory stock. + /// + public static string InvoiceItemWithoutIventoryStock { + get { + return ResourceManager.GetString("InvoiceItemWithoutIventoryStock", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invoice language. + /// + public static string InvoiceLanguage { + get { + return ResourceManager.GetString("InvoiceLanguage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Every invoice must have a language. + /// + public static string InvoiceLanguageRequiredErrorMsgs { + get { + return ResourceManager.GetString("InvoiceLanguageRequiredErrorMsgs", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invoice number. + /// + public static string InvoiceNumber { + get { + return ResourceManager.GetString("InvoiceNumber", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The invoice number is already taken. + /// + public static string InvoiceNumberBusyError { + get { + return ResourceManager.GetString("InvoiceNumberBusyError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invoices. + /// + public static string Invoices { + get { + return ResourceManager.GetString("Invoices", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Status. + /// + public static string InvoicesByStatus { + get { + return ResourceManager.GetString("InvoicesByStatus", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tax 1 name. + /// + public static string InvoiceTax1Name { + get { + return ResourceManager.GetString("InvoiceTax1Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tax 1 rate. + /// + public static string InvoiceTax1Rate { + get { + return ResourceManager.GetString("InvoiceTax1Rate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tax 2 name. + /// + public static string InvoiceTax2Name { + get { + return ResourceManager.GetString("InvoiceTax2Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tax 2 rate. + /// + public static string InvoiceTax2Rate { + get { + return ResourceManager.GetString("InvoiceTax2Rate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tax with such a name has already been registered. + /// + public static string InvoiceTaxAlreadyRegisteredError { + get { + return ResourceManager.GetString("InvoiceTaxAlreadyRegisteredError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tax categories let you create additional taxes specific for the countries or areas you or your clients live in. You can add the tax name, interest rate and description.. + /// + public static string InvoiceTaxesDescriptionText { + get { + return ResourceManager.GetString("InvoiceTaxesDescriptionText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can delete the tax category only in case it is not used in the Products&Services section.. + /// + public static string InvoiceTaxesDescriptionTextEditDelete { + get { + return ResourceManager.GetString("InvoiceTaxesDescriptionTextEditDelete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tax name. + /// + public static string InvoiceTaxName { + get { + return ResourceManager.GetString("InvoiceTaxName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Rate. + /// + public static string InvoiceTaxRate { + get { + return ResourceManager.GetString("InvoiceTaxRate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invoice Date. + /// + public static string IssueDate { + get { + return ResourceManager.GetString("IssueDate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Every invoice must have a date. + /// + public static string IssueDateRequiredErrorMsg { + get { + return ResourceManager.GetString("IssueDateRequiredErrorMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Item. + /// + public static string ItemCol { + get { + return ResourceManager.GetString("ItemCol", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your default CRM currency.<br/>To change it, please go to <a href="?type=common" target="_blank">Common Settings</a>. + /// + public static string ItemCurrencyHelpInfo { + get { + return ResourceManager.GetString("ItemCurrencyHelpInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add the quantity of items you currently have in stock. This number will decrease every time the item is added to an invoice. When you receive more inventory, simply add it to your current inventory here.. + /// + public static string IventoryStockHelpInfo { + get { + return ResourceManager.GetString("IventoryStockHelpInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Link with opportunity. + /// + public static string LinkOpportunityButton { + get { + return ResourceManager.GetString("LinkOpportunityButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Make out an invoice. + /// + public static string MakeOutAnInvoice { + get { + return ResourceManager.GetString("MakeOutAnInvoice", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mark as draft. + /// + public static string MarkAsDraft { + get { + return ResourceManager.GetString("MarkAsDraft", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mark as paid. + /// + public static string MarkAsPaid { + get { + return ResourceManager.GetString("MarkAsPaid", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mark as rejected. + /// + public static string MarkAsRejected { + get { + return ResourceManager.GetString("MarkAsRejected", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mark as billed. + /// + public static string MarkAsSend { + get { + return ResourceManager.GetString("MarkAsSend", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Number. + /// + public static string Number { + get { + return ResourceManager.GetString("Number", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Format is incorrect or invoice with the same number already exists. + /// + public static string NumberFormatErrorMsg { + get { + return ResourceManager.GetString("NumberFormatErrorMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Start number is empty. + /// + public static string NumberFormatRequiredErrorMsg { + get { + return ResourceManager.GetString("NumberFormatRequiredErrorMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Every invoice must have a number. + /// + public static string NumberRequiredErrorMsg { + get { + return ResourceManager.GetString("NumberRequiredErrorMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Overdue. + /// + public static string OverdueInvoicesFilter { + get { + return ResourceManager.GetString("OverdueInvoicesFilter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to PO Number. + /// + public static string PONumber { + get { + return ResourceManager.GetString("PONumber", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Prefix. + /// + public static string Prefix { + get { + return ResourceManager.GetString("Prefix", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Price. + /// + public static string PriceCol { + get { + return ResourceManager.GetString("PriceCol", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Print. + /// + public static string Print { + get { + return ResourceManager.GetString("Print", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Products & Services. + /// + public static string ProductsAndServices { + get { + return ResourceManager.GetString("ProductsAndServices", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select product or service from list or add new. + /// + public static string ProductsAndServicesEmptyItemErrorMsgs { + get { + return ResourceManager.GetString("ProductsAndServicesEmptyItemErrorMsgs", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Every invoice must have items. + /// + public static string ProductsAndServicesRequiredErrorMsg { + get { + return ResourceManager.GetString("ProductsAndServicesRequiredErrorMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Quantity. + /// + public static string QuantityCol { + get { + return ResourceManager.GetString("QuantityCol", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Quantity. + /// + public static string QuantityItem { + get { + return ResourceManager.GetString("QuantityItem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Saving number settings.... + /// + public static string SaveNumberSettingsProgress { + get { + return ResourceManager.GetString("SaveNumberSettingsProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Saving terms settings.... + /// + public static string SaveTermsSettingsProgress { + get { + return ResourceManager.GetString("SaveTermsSettingsProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to An error "{0}" occurred while saving the invoice. Please try again later.. + /// + public static string SavingInvoiceServerError { + get { + return ResourceManager.GetString("SavingInvoiceServerError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Seller. + /// + public static string Seller { + get { + return ResourceManager.GetString("Seller", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Send by email. + /// + public static string SendByEmail { + get { + return ResourceManager.GetString("SendByEmail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Set default. + /// + public static string SetDefault { + get { + return ResourceManager.GetString("SetDefault", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Set default terms. + /// + public static string SetDefaultTerms { + get { + return ResourceManager.GetString("SetDefaultTerms", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Set empty. + /// + public static string SetEmpty { + get { + return ResourceManager.GetString("SetEmpty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ship To. + /// + public static string ShipTo { + get { + return ResourceManager.GetString("ShipTo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show invoice profile. + /// + public static string ShowInvoiceProfile { + get { + return ResourceManager.GetString("ShowInvoiceProfile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open in a new tab. + /// + public static string ShowInvoiceProfileNewTab { + get { + return ResourceManager.GetString("ShowInvoiceProfileNewTab", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Start number. + /// + public static string StartNumber { + get { + return ResourceManager.GetString("StartNumber", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Status. + /// + public static string Status { + get { + return ResourceManager.GetString("Status", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stock keeping unit (SKU). + /// + public static string StockKeepingUnit { + get { + return ResourceManager.GetString("StockKeepingUnit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Subtotal. + /// + public static string Subtotal { + get { + return ResourceManager.GetString("Subtotal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tax. + /// + public static string Tax { + get { + return ResourceManager.GetString("Tax", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tax. + /// + public static string TaxCol { + get { + return ResourceManager.GetString("TaxCol", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Terms. + /// + public static string Terms { + get { + return ResourceManager.GetString("Terms", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Every invoice must have terms. + /// + public static string TermsRequiredErrorMsg { + get { + return ResourceManager.GetString("TermsRequiredErrorMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invoice Total. + /// + public static string Total { + get { + return ResourceManager.GetString("Total", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Total invoices. + /// + public static string TotalInvoices { + get { + return ResourceManager.GetString("TotalInvoices", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Track inventory. + /// + public static string TrackInventory { + get { + return ResourceManager.GetString("TrackInventory", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Update item «{0}». + /// + public static string UpdateInvoiceItem { + get { + return ResourceManager.GetString("UpdateInvoiceItem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Update «{0}» tax. + /// + public static string UpdateTaxHeaderText { + get { + return ResourceManager.GetString("UpdateTaxHeaderText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Saving changes.... + /// + public static string UpdateTaxProcessText { + get { + return ResourceManager.GetString("UpdateTaxProcessText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Warning. + /// + public static string Warning { + get { + return ResourceManager.GetString("Warning", resourceCulture); + } + } + } +} diff --git a/products/ASC.CRM/Server/Resources/CRMInvoiceResource.resx b/products/ASC.CRM/Server/Resources/CRMInvoiceResource.resx new file mode 100644 index 00000000000..2a53624d952 --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMInvoiceResource.resx @@ -0,0 +1,624 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Add and Create New Invoice + + + Add billing address + + + Add delivery address + + + Add Line + + + Address has been successfully updated + + + Create new tax + + + Creating a tax + + + Save and Create New Item + + + Add This Invoice + + + All invoices + + + Amount + + + Attach document + + + automatically generated + + + Bill To + + + Currency + + + Due date + + + Change format + + + All the future invoices will have the prefix/number format set using the fields below. Check the appropriate box to generate numbers automatically. + + + Change status + + + Client notes + + + Will be displayed on the invoice and in the letter body if you send the invoice per ONLYOFFICE™ Mail. + + + Consignee + + + coincides with the client + + + Create first invoice + + + Create first item + + + Create first tax + + + Create item + + + Creating invoice item + + + Create tax + + + Create new invoice + + + Create new item + + + Create new tax + + + Currency + + + Every invoice must have a currency + + + {0} days + + + These default terms will be used on future invoices. + + + Are you sure you want to delete the '{0}' invoice? + + + Deleting invoice... + + + Deleting item... + + + Are you sure you want to delete the «{0}» item? + + + Are you sure you want to delete the «{0}» tax? + + + Deleting tax... + + + Delete this invoice + + + Deleting invoices + + + Description + + + Discount + + + Download + + + Due date + + + Please, set custom Due date or choose one of the presets on the right to link this date with the Date of issue. + + + Issue date more than due date + + + Due on receipt + + + Every invoice must have a due date + + + Duplicate and Create New Invoice + + + Duplicate invoice + + + Duplicate Invoice + + + Duplicate invoice «{0}» + + + Edit billing address + + + Edit delivery address + + + Edit invoice + + + Edit invoice «{0}» + + + Edit + + + Edit tax + + + This section allows you to set the list of items which can be included into invoices. After the list is created you can select the necessary item from it to add to your payment document. You can also add new items whenever needed. + + + No invoice items matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the invoice items in this section. Or you can create the invoice item if it is absent from the list. + + + No invoice items to be displayed for this filter here + + + Products & Services list is empty + + + The 'Invoices' section allows you to create payment documents that you can provide your clients with. After the invoices are added the list of the available ones will be displayed here. + + + No invoices matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the invoices in this section. Or you can create the invoice if it is absent from the list. + + + No invoices to be displayed for this filter here + + + The invoice list is empty + + + Invoice taxes allow you to create additional taxes specific for the countries or areas you or your clients live in. You can add the tax name, interest rate and description. You can also add new taxes whenever needed. + + + The invoice taxes list is empty + + + No opportunities found + + + Select Item + + + The tax name field cannot be empty + + + The rate field cannot be empty + + + None + + + Price is empty + + + Price is incorect + + + Quantity is incorrect + + + the rate value is incorect + + + Exchange rate + + + You must fill this field + + + Inventory stock + + + Item name + + + Price + + + Stock quantity + + + Taxes + + + Find by title, SKU or description + + + Invoice + + + Client + + + Every invoice must have a client + + + Consignee + + + Your default CRM currency is used to set prices in Products & Services Catalog.<br/>To change it, please go to <a href="settings.aspx?type=common" target="_blank">Common Settings</a> + + + Active + + + Archived + + + Name + + + One or more invoice items have negative stock quantities + + + Price + + + With inventory stock + + + Without inventory stock + + + Invoice language + + + Every invoice must have a language + + + Invoice number + + + The invoice number is already taken + + + Invoices + + + Status + + + Tax 1 name + + + Tax 1 rate + + + Tax 2 name + + + Tax 2 rate + + + Tax with such a name has already been registered + + + Tax categories let you create additional taxes specific for the countries or areas you or your clients live in. You can add the tax name, interest rate and description. + + + You can delete the tax category only in case it is not used in the Products&Services section. + + + Tax name + + + Rate + + + Invoice Date + + + Every invoice must have a date + + + Item + + + Your default CRM currency.<br/>To change it, please go to <a href="?type=common" target="_blank">Common Settings</a> + + + Add the quantity of items you currently have in stock. This number will decrease every time the item is added to an invoice. When you receive more inventory, simply add it to your current inventory here. + + + Link with opportunity + + + Make out an invoice + + + Mark as draft + + + Mark as paid + + + Mark as rejected + + + Mark as billed + + + Number + + + Format is incorrect or invoice with the same number already exists + + + Start number is empty + + + Every invoice must have a number + + + Overdue + + + PO Number + + + Prefix + + + Price + + + Print + + + Products & Services + + + Select product or service from list or add new + + + Every invoice must have items + + + Quantity + + + Quantity + + + Saving number settings... + + + Saving terms settings... + + + An error "{0}" occurred while saving the invoice. Please try again later. + + + Seller + + + Send by email + + + Set default + + + Set default terms + + + Set empty + + + Ship To + + + Show invoice profile + + + Open in a new tab + + + Start number + + + Status + + + Stock keeping unit (SKU) + + + Subtotal + + + Tax + + + Tax + + + Terms + + + Every invoice must have terms + + + Invoice Total + + + Total invoices + + + Track inventory + + + Update item «{0}» + + + Update «{0}» tax + + + Saving changes... + + + Warning + + + The tax name already exist + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Resources/CRMJSResource.Designer.cs b/products/ASC.CRM/Server/Resources/CRMJSResource.Designer.cs new file mode 100644 index 00000000000..83d3931eaf6 --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMJSResource.Designer.cs @@ -0,0 +1,1558 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.CRM.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class CRMJSResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal CRMJSResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.CRM.Resources.CRMJSResource", typeof(CRMJSResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Actual date of the deal. + /// + public static string ActualCloseDate { + get { + return ResourceManager.GetString("ActualCloseDate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Adding an opportunity.... + /// + public static string AddNewDealProgress { + get { + return ResourceManager.GetString("AddNewDealProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add email. + /// + public static string AddNewEmail { + get { + return ResourceManager.GetString("AddNewEmail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Adding an invoice.... + /// + public static string AddNewInvoiceProgress { + get { + return ResourceManager.GetString("AddNewInvoiceProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add phone. + /// + public static string AddNewPhone { + get { + return ResourceManager.GetString("AddNewPhone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create New Task. + /// + public static string AddNewTask { + get { + return ResourceManager.GetString("AddNewTask", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to addresses. + /// + public static string AddressGenitivePlural { + get { + return ResourceManager.GetString("AddressGenitivePlural", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to addresses. + /// + public static string AddressGenitiveSingular { + get { + return ResourceManager.GetString("AddressGenitiveSingular", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Address. + /// + public static string AddressWatermark { + get { + return ResourceManager.GetString("AddressWatermark", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save. + /// + public static string AddThisTask { + get { + return ResourceManager.GetString("AddThisTask", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to batch. + /// + public static string Batch { + get { + return ResourceManager.GetString("Batch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to per day. + /// + public static string BidType_PerDay { + get { + return ResourceManager.GetString("BidType_PerDay", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to per hour. + /// + public static string BidType_PerHour { + get { + return ResourceManager.GetString("BidType_PerHour", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to per month. + /// + public static string BidType_PerMonth { + get { + return ResourceManager.GetString("BidType_PerMonth", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to per week. + /// + public static string BidType_PerWeek { + get { + return ResourceManager.GetString("BidType_PerWeek", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to per year. + /// + public static string BidType_PerYear { + get { + return ResourceManager.GetString("BidType_PerYear", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Billing address. + /// + public static string BillingAddress { + get { + return ResourceManager.GetString("BillingAddress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Browse. + /// + public static string Browse { + get { + return ResourceManager.GetString("Browse", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Case. + /// + public static string Case { + get { + return ResourceManager.GetString("Case", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Closed. + /// + public static string CaseStatusClosed { + get { + return ResourceManager.GetString("CaseStatusClosed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open. + /// + public static string CaseStatusOpened { + get { + return ResourceManager.GetString("CaseStatusOpened", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit. + /// + public static string Change { + get { + return ResourceManager.GetString("Change", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mark as primary. + /// + public static string CheckAsPrimary { + get { + return ResourceManager.GetString("CheckAsPrimary", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Choose. + /// + public static string Choose { + get { + return ResourceManager.GetString("Choose", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to City. + /// + public static string CityWatermark { + get { + return ResourceManager.GetString("CityWatermark", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Close case. + /// + public static string CloseCase { + get { + return ResourceManager.GetString("CloseCase", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Company. + /// + public static string Company { + get { + return ResourceManager.GetString("Company", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to change this key?. + /// + public static string ConfirmChangeKey { + get { + return ResourceManager.GetString("ConfirmChangeKey", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Note: all previously created forms will no longer work. + /// + public static string ConfirmChangeKeyNote { + get { + return ResourceManager.GetString("ConfirmChangeKeyNote", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to delete this task?. + /// + public static string ConfirmDeleteTask { + get { + return ResourceManager.GetString("ConfirmDeleteTask", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All the entered information will be lost. + ///Are you sure you want to continue anyway?. + /// + public static string ConfirmGoToCustomFieldPage { + get { + return ResourceManager.GetString("ConfirmGoToCustomFieldPage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please enter the name of your Twitter account. For example, teamlabdotcom. + /// + public static string ContactTwitterDescription { + get { + return ResourceManager.GetString("ContactTwitterDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Country. + /// + public static string CountryWatermark { + get { + return ResourceManager.GetString("CountryWatermark", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Current month. + /// + public static string CurrentMonth { + get { + return ResourceManager.GetString("CurrentMonth", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Current user. + /// + public static string CurrentUser { + get { + return ResourceManager.GetString("CurrentUser", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Current week. + /// + public static string CurrentWeek { + get { + return ResourceManager.GetString("CurrentWeek", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Custom period. + /// + public static string CustomPeriod { + get { + return ResourceManager.GetString("CustomPeriod", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunity. + /// + public static string Deal { + get { + return ResourceManager.GetString("Deal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to delete the '{0}' case?. + /// + public static string DeleteCaseConfirmMessage { + get { + return ResourceManager.GetString("DeleteCaseConfirmMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deleting case.... + /// + public static string DeleteCaseInProgress { + get { + return ResourceManager.GetString("DeleteCaseInProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete company. + /// + public static string DeleteCompany { + get { + return ResourceManager.GetString("DeleteCompany", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to delete the '{0}' company?. + /// + public static string DeleteCompanyConfirmMessage { + get { + return ResourceManager.GetString("DeleteCompanyConfirmMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Note: this action cannot be undone. + /// + public static string DeleteConfirmNote { + get { + return ResourceManager.GetString("DeleteConfirmNote", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deleting contact.... + /// + public static string DeleteContactInProgress { + get { + return ResourceManager.GetString("DeleteContactInProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to delete the '{0}' opportunity?. + /// + public static string DeleteDealConfirmMessage { + get { + return ResourceManager.GetString("DeleteDealConfirmMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deleting opportunity.... + /// + public static string DeleteDealInProgress { + get { + return ResourceManager.GetString("DeleteDealInProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to delete the '{0}' person?. + /// + public static string DeletePersonConfirmMessage { + get { + return ResourceManager.GetString("DeletePersonConfirmMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delivery address. + /// + public static string DeliveryAddress { + get { + return ResourceManager.GetString("DeliveryAddress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit email. + /// + public static string EditEmail { + get { + return ResourceManager.GetString("EditEmail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Saving changes.... + /// + public static string EditingDealProgress { + get { + return ResourceManager.GetString("EditingDealProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Saving changes.... + /// + public static string EditingInvoiceProgress { + get { + return ResourceManager.GetString("EditingInvoiceProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit phone. + /// + public static string EditPhone { + get { + return ResourceManager.GetString("EditPhone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit «{0}» field. + /// + public static string EditSelectedCustomField { + get { + return ResourceManager.GetString("EditSelectedCustomField", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit task. + /// + public static string EditTask { + get { + return ResourceManager.GetString("EditTask", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} selected. + /// + public static string ElementsSelectedCount { + get { + return ResourceManager.GetString("ElementsSelectedCount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Empty. + /// + public static string Empty { + get { + return ResourceManager.GetString("Empty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The settings fields cannot be empty. + /// + public static string EmptyFieldsOfSettings { + get { + return ResourceManager.GetString("EmptyFieldsOfSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The element list cannot be empty. + /// + public static string EmptyItemList { + get { + return ResourceManager.GetString("EmptyItemList", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Every field must have a title. + /// + public static string EmptyLabelError { + get { + return ResourceManager.GetString("EmptyLabelError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Every letter must have a body. + /// + public static string EmptyLetterBodyContent { + get { + return ResourceManager.GetString("EmptyLetterBodyContent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Every task must have a due date. + /// + public static string EmptyTaskDeadline { + get { + return ResourceManager.GetString("EmptyTaskDeadline", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Every task must have a responsible one. + /// + public static string EmptyTaskResponsible { + get { + return ResourceManager.GetString("EmptyTaskResponsible", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Every task must have a title. + /// + public static string EmptyTaskTitle { + get { + return ResourceManager.GetString("EmptyTaskTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The title field cannot be empty. + /// + public static string EmptyTitleError { + get { + return ResourceManager.GetString("EmptyTitleError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No fields corresponding to the 'Case Title' could be found. + /// + public static string ErrorCasesNotMappingBasicColumn { + get { + return ResourceManager.GetString("ErrorCasesNotMappingBasicColumn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No person is selected. + /// + public static string ErrorContactIsNotSelected { + get { + return ResourceManager.GetString("ErrorContactIsNotSelected", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No fields corresponding to the 'First Name' or 'Company Name' could be found. + /// + public static string ErrorContactNotMappingBasicColumn { + get { + return ResourceManager.GetString("ErrorContactNotMappingBasicColumn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} email recipients maximum. + /// + public static string ErrorEmailRecipientsCount { + get { + return ResourceManager.GetString("ErrorEmailRecipientsCount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enter the case title. + /// + public static string ErrorEmptyCaseTitle { + get { + return ResourceManager.GetString("ErrorEmptyCaseTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Obligatory fields must not be duplicated. + /// + public static string ErrorMappingMoreBasicColumn { + get { + return ResourceManager.GetString("ErrorMappingMoreBasicColumn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Image format is not supported. + /// + public static string ErrorMessage_NotImageSupportFormat { + get { + return ResourceManager.GetString("ErrorMessage_NotImageSupportFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File format is not supported. + /// + public static string ErrorMessage_NotSupportedFileFormat { + get { + return ResourceManager.GetString("ErrorMessage_NotSupportedFileFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to An error occurred while uploading the image to the server. + /// + public static string ErrorMessage_SaveImageError { + get { + return ResourceManager.GetString("ErrorMessage_SaveImageError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Incorrect email. + /// + public static string ErrorNotCorrectEmail { + get { + return ResourceManager.GetString("ErrorNotCorrectEmail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No fields corresponding to the 'Opportunity Title', 'Opportunity Responsible' could be found. + /// + public static string ErrorOpportunityNotMappingBasicColumn { + get { + return ResourceManager.GetString("ErrorOpportunityNotMappingBasicColumn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No fields corresponding to the 'Task Title', 'Responsible', 'Due Date' could be found. + /// + public static string ErrorTaskNotMappingBasicColumn { + get { + return ResourceManager.GetString("ErrorTaskNotMappingBasicColumn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The "{0}" type is the last one. It cannot be deleted.. + /// + public static string ErrorTheLastContactStage { + get { + return ResourceManager.GetString("ErrorTheLastContactStage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The "{0}" type is the last one. It cannot be deleted.. + /// + public static string ErrorTheLastContactType { + get { + return ResourceManager.GetString("ErrorTheLastContactType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This is the only opportunity stage. It cannot be deleted.. + /// + public static string ErrorTheLastDealMilestone { + get { + return ResourceManager.GetString("ErrorTheLastDealMilestone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The "{0}" category is the last one. It cannot be deleted.. + /// + public static string ErrorTheLastHistoryCategory { + get { + return ResourceManager.GetString("ErrorTheLastHistoryCategory", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The "{0}" category is the last one. It cannot be deleted.. + /// + public static string ErrorTheLastTaskCategory { + get { + return ResourceManager.GetString("ErrorTheLastTaskCategory", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Estimated deal due date. + /// + public static string ExpectedCloseDate { + get { + return ResourceManager.GetString("ExpectedCloseDate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Find account in Twitter. + /// + public static string FindTwitter { + get { + return ResourceManager.GetString("FindTwitter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your recipient list has been generated. {0} Click the links below to pass your email addresses over to the email program.. + /// + public static string GenerateLinkInfo { + get { + return ResourceManager.GetString("GenerateLinkInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Go to Settings. + /// + public static string GoToSettings { + get { + return ResourceManager.GetString("GoToSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open the portal Settings page if you need to restrict the access to the CRM module and give some users administrator privileges.. + /// + public static string HelpContentAccessRights { + get { + return ResourceManager.GetString("HelpContentAccessRights", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Click this button and select the Import contacts option if you need to transfer your contacts from a CSV file, all at once.. + /// + public static string HelpContentAddContacts { + get { + return ResourceManager.GetString("HelpContentAddContacts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use this button to start creating your customer database adding persons and companies and linking them.. + /// + public static string HelpContentCreateNew { + get { + return ResourceManager.GetString("HelpContentCreateNew", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use this link to build a form that you can publish on your site and capture new contacts automatically.. + /// + public static string HelpContentCreateWebsite { + get { + return ResourceManager.GetString("HelpContentCreateWebsite", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Manage Access Rights. + /// + public static string HelpTitleAccessRights { + get { + return ResourceManager.GetString("HelpTitleAccessRights", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add Contacts in Bulk. + /// + public static string HelpTitleAddContacts { + get { + return ResourceManager.GetString("HelpTitleAddContacts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create New Contact. + /// + public static string HelpTitleCreateNew { + get { + return ResourceManager.GetString("HelpTitleCreateNew", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create a Website Contact Form. + /// + public static string HelpTitleCreateWebsite { + get { + return ResourceManager.GetString("HelpTitleCreateWebsite", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Link with project. + /// + public static string LinkWithProject { + get { + return ResourceManager.GetString("LinkWithProject", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Uploading. + /// + public static string Loading { + get { + return ResourceManager.GetString("Loading", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Loading.... + /// + public static string LoadingProcessing { + get { + return ResourceManager.GetString("LoadingProcessing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to cases. + /// + public static string ManyCases { + get { + return ResourceManager.GetString("ManyCases", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to companies. + /// + public static string ManyCompanies { + get { + return ResourceManager.GetString("ManyCompanies", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to contacts. + /// + public static string ManyContacts { + get { + return ResourceManager.GetString("ManyContacts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to opportunities. + /// + public static string ManyDeals { + get { + return ResourceManager.GetString("ManyDeals", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to events. + /// + public static string ManyEvents { + get { + return ResourceManager.GetString("ManyEvents", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to persons. + /// + public static string ManyPersons { + get { + return ResourceManager.GetString("ManyPersons", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to tasks. + /// + public static string ManyTasks { + get { + return ResourceManager.GetString("ManyTasks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New document. + /// + public static string NewDocument { + get { + return ResourceManager.GetString("NewDocument", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Next. + /// + public static string Next { + get { + return ResourceManager.GetString("Next", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Next (to preview). + /// + public static string NextPreview { + get { + return ResourceManager.GetString("NextPreview", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There are no cases. + /// + public static string NoCases { + get { + return ResourceManager.GetString("NoCases", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No close date. + /// + public static string NoCloseDate { + get { + return ResourceManager.GetString("NoCloseDate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There are no companies. + /// + public static string NoCompanies { + get { + return ResourceManager.GetString("NoCompanies", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There are no contacts. + /// + public static string NoContacts { + get { + return ResourceManager.GetString("NoContacts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There are no opportunities. + /// + public static string NoDeals { + get { + return ResourceManager.GetString("NoDeals", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There are no events. + /// + public static string NoEvents { + get { + return ResourceManager.GetString("NoEvents", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No file has been selected. + /// + public static string NoFileSelected { + get { + return ResourceManager.GetString("NoFileSelected", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There are no persons. + /// + public static string NoPersons { + get { + return ResourceManager.GetString("NoPersons", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to no subject. + /// + public static string NoSubject { + get { + return ResourceManager.GetString("NoSubject", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There are no tasks. + /// + public static string NoTasks { + get { + return ResourceManager.GetString("NoTasks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to case. + /// + public static string OneCase { + get { + return ResourceManager.GetString("OneCase", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to company. + /// + public static string OneCompany { + get { + return ResourceManager.GetString("OneCompany", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to contact. + /// + public static string OneContact { + get { + return ResourceManager.GetString("OneContact", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to opportunity. + /// + public static string OneDeal { + get { + return ResourceManager.GetString("OneDeal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to event. + /// + public static string OneEvent { + get { + return ResourceManager.GetString("OneEvent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to person. + /// + public static string OnePerson { + get { + return ResourceManager.GetString("OnePerson", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to task. + /// + public static string OneTask { + get { + return ResourceManager.GetString("OneTask", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open a case. + /// + public static string OpenCase { + get { + return ResourceManager.GetString("OpenCase", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Overall progress. + /// + public static string OverallProgress { + get { + return ResourceManager.GetString("OverallProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to during {0} days. + /// + public static string PerPeriodDays { + get { + return ResourceManager.GetString("PerPeriodDays", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to during {0} hours. + /// + public static string PerPeriodHours { + get { + return ResourceManager.GetString("PerPeriodHours", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to during {0} months. + /// + public static string PerPeriodMonths { + get { + return ResourceManager.GetString("PerPeriodMonths", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to during {0} weeks. + /// + public static string PerPeriodWeeks { + get { + return ResourceManager.GetString("PerPeriodWeeks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to during {0} years. + /// + public static string PerPeriodYears { + get { + return ResourceManager.GetString("PerPeriodYears", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please reload the page. + /// + public static string PleaseRefreshThePage { + get { + return ResourceManager.GetString("PleaseRefreshThePage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please wait.... + /// + public static string PleaseWait { + get { + return ResourceManager.GetString("PleaseWait", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Possible profiles for {0}. + /// + public static string PossibleSocialMediaAccounts { + get { + return ResourceManager.GetString("PossibleSocialMediaAccounts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Previous. + /// + public static string Previous { + get { + return ResourceManager.GetString("Previous", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Primary. + /// + public static string Primary { + get { + return ResourceManager.GetString("Primary", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} recipients didn't have email addresses.. + /// + public static string RecipientsWithoutEmail { + get { + return ResourceManager.GetString("RecipientsWithoutEmail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save changes. + /// + public static string SaveChanges { + get { + return ResourceManager.GetString("SaveChanges", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select a CSV file. + /// + public static string SelectCSVFileButton { + get { + return ResourceManager.GetString("SelectCSVFileButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Selected CSV file: {0}. + /// + public static string SelectedCSVFileLabel { + get { + return ResourceManager.GetString("SelectedCSVFileLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Choices. + /// + public static string SelectOptions { + get { + return ResourceManager.GetString("SelectOptions", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Send. + /// + public static string Send { + get { + return ResourceManager.GetString("Send", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Settings have been successfully updated. + /// + public static string SettingsUpdated { + get { + return ResourceManager.GetString("SettingsUpdated", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show {0} more from {1}. + /// + public static string ShowMore { + get { + return ResourceManager.GetString("ShowMore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show more. + /// + public static string ShowMoreButtonText { + get { + return ResourceManager.GetString("ShowMoreButtonText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Temperature level. + /// + public static string Stage { + get { + return ResourceManager.GetString("Stage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to State. + /// + public static string StateWatermark { + get { + return ResourceManager.GetString("StateWatermark", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Successfully closed. + /// + public static string SuccessfullyClosed { + get { + return ResourceManager.GetString("SuccessfullyClosed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The «{0}» task is closed. + /// + public static string TaskIsOver { + get { + return ResourceManager.GetString("TaskIsOver", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Powered by {0}. + /// + public static string TeamlabWatermark { + get { + return ResourceManager.GetString("TeamlabWatermark", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} days {1} hours {2} minutes from the container start. + /// + public static string TemplateFixedDeadline { + get { + return ResourceManager.GetString("TemplateFixedDeadline", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to From the previous task {0} days {1} hours {2} minutes. + /// + public static string TemplateNotFixedDeadline { + get { + return ResourceManager.GetString("TemplateNotFixedDeadline", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Today. + /// + public static string Today { + get { + return ResourceManager.GetString("Today", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unsuccessfully closed. + /// + public static string UnsuccessfullyClosed { + get { + return ResourceManager.GetString("UnsuccessfullyClosed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Attach file. + /// + public static string UploadFile { + get { + return ResourceManager.GetString("UploadFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The file «{0}» is being uploaded.... + /// + public static string UploadingProgress { + get { + return ResourceManager.GetString("UploadingProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Agent. + /// + public static string VoipCallAgent { + get { + return ResourceManager.GetString("VoipCallAgent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Answered. + /// + public static string VoipCallAnsweredType { + get { + return ResourceManager.GetString("VoipCallAnsweredType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Caller. + /// + public static string VoipCallCaller { + get { + return ResourceManager.GetString("VoipCallCaller", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to contact removed. + /// + public static string VoipCallContactRemoved { + get { + return ResourceManager.GetString("VoipCallContactRemoved", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cost. + /// + public static string VoipCallCost { + get { + return ResourceManager.GetString("VoipCallCost", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Date. + /// + public static string VoipCallDate { + get { + return ResourceManager.GetString("VoipCallDate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Duration. + /// + public static string VoipCallDuration { + get { + return ResourceManager.GetString("VoipCallDuration", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Missed. + /// + public static string VoipCallMissedType { + get { + return ResourceManager.GetString("VoipCallMissedType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Outgoing. + /// + public static string VoipCallOutgoingType { + get { + return ResourceManager.GetString("VoipCallOutgoingType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type. + /// + public static string VoipCallType { + get { + return ResourceManager.GetString("VoipCallType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Zip code. + /// + public static string ZipCodeWatermark { + get { + return ResourceManager.GetString("ZipCodeWatermark", resourceCulture); + } + } + } +} diff --git a/products/ASC.CRM/Server/Resources/CRMJSResource.resx b/products/ASC.CRM/Server/Resources/CRMJSResource.resx new file mode 100644 index 00000000000..158d14700db --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMJSResource.resx @@ -0,0 +1,619 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Actual date of the deal + + + Adding an opportunity... + + + Add email + + + Adding an invoice... + + + Add phone + + + Create New Task + + + addresses + + + addresses + + + Address + + + Save + + + batch + + + per day + + + per hour + + + per month + + + per week + + + per year + + + Billing address + + + Browse + + + Case + + + Closed + + + Open + + + Edit + + + Mark as primary + + + Choose + + + City + + + Close case + + + Company + + + Are you sure you want to change this key? + + + Note: all previously created forms will no longer work + + + Are you sure you want to delete this task? + + + All the entered information will be lost. +Are you sure you want to continue anyway? + + + Please enter the name of your Twitter account. For example, teamlabdotcom + + + Current month + + + Current user + + + Current week + + + Custom period + + + Opportunity + + + Are you sure you want to delete the '{0}' case? + + + Deleting case... + + + Delete company + + + Are you sure you want to delete the '{0}' company? + + + Note: this action cannot be undone + + + Deleting contact... + + + Are you sure you want to delete the '{0}' opportunity? + + + Deleting opportunity... + + + Are you sure you want to delete the '{0}' person? + + + Delivery address + + + Edit email + + + Saving changes... + + + Saving changes... + + + Edit phone + + + Edit «{0}» field + + + Edit task + + + {0} selected + + + Empty + + + The settings fields cannot be empty + + + The element list cannot be empty + + + Every field must have a title + + + Every letter must have a body + + + Every task must have a due date + + + Every task must have a responsible one + + + Every task must have a title + + + The title field cannot be empty + + + No fields corresponding to the 'Case Title' could be found + + + No person is selected + + + No fields corresponding to the 'First Name' or 'Company Name' could be found + + + {0} email recipients maximum + + + Enter the case title + + + Obligatory fields must not be duplicated + + + Image format is not supported + + + File format is not supported + + + An error occurred while uploading the image to the server + + + Incorrect email + + + No fields corresponding to the 'Opportunity Title', 'Opportunity Responsible' could be found + + + No fields corresponding to the 'Task Title', 'Responsible', 'Due Date' could be found + + + The "{0}" type is the last one. It cannot be deleted. + + + The "{0}" type is the last one. It cannot be deleted. + + + This is the only opportunity stage. It cannot be deleted. + + + The "{0}" category is the last one. It cannot be deleted. + + + The "{0}" category is the last one. It cannot be deleted. + + + Estimated deal due date + + + Find account in Twitter + + + Your recipient list has been generated. {0} Click the links below to pass your email addresses over to the email program. + + + Go to Settings + + + Open the portal Settings page if you need to restrict the access to the CRM module and give some users administrator privileges. + + + Click this button and select the Import contacts option if you need to transfer your contacts from a CSV file, all at once. + + + Use this button to start creating your customer database adding persons and companies and linking them. + + + Use this link to build a form that you can publish on your site and capture new contacts automatically. + + + Manage Access Rights + + + Add Contacts in Bulk + + + Create New Contact + + + Create a Website Contact Form + + + Link with project + + + Uploading + + + Loading... + + + cases + + + companies + + + contacts + + + opportunities + + + events + + + persons + + + tasks + + + New document + + + Next + + + Next (to preview) + + + There are no cases + + + No close date + + + There are no companies + + + There are no contacts + + + There are no opportunities + + + There are no events + + + No file has been selected + + + There are no persons + + + no subject + + + There are no tasks + + + case + + + company + + + contact + + + opportunity + + + event + + + person + + + task + + + Open a case + + + Overall progress + + + during {0} days + + + during {0} hours + + + during {0} months + + + during {0} weeks + + + during {0} years + + + Please reload the page + + + Please wait... + + + Possible profiles for {0} + + + Previous + + + Primary + + + {0} recipients didn't have email addresses. + + + Save changes + + + Select a CSV file + + + Selected CSV file: {0} + + + Choices + + + Send + + + Settings have been successfully updated + + + Show {0} more from {1} + + + Show more + + + Temperature level + + + State + + + Successfully closed + + + The «{0}» task is closed + + + Powered by {0} + + + {0} days {1} hours {2} minutes from the container start + + + From the previous task {0} days {1} hours {2} minutes + + + Today + + + Unsuccessfully closed + + + Attach file + + + The file «{0}» is being uploaded... + + + Agent + + + Answered + + + Caller + + + contact removed + + + Cost + + + Date + + + Duration + + + Missed + + + Outgoing + + + Type + + + Zip code + + + Country + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Resources/CRMReportResource.Designer.cs b/products/ASC.CRM/Server/Resources/CRMReportResource.Designer.cs new file mode 100644 index 00000000000..17ad6e6df75 --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMReportResource.Designer.cs @@ -0,0 +1,1181 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.CRM.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class CRMReportResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal CRMReportResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.CRM.Resources.CRMReportResource", typeof(CRMReportResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to The average budget of opportunities. + /// + public static string AverageDealsBudget { + get { + return ResourceManager.GetString("AverageDealsBudget", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The average duration of a successful opportunity, days. + /// + public static string AverageDealsDuration { + get { + return ResourceManager.GetString("AverageDealsDuration", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Billed. + /// + public static string Billed { + get { + return ResourceManager.GetString("Billed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Billed invoices . + /// + public static string BilledInvoices { + get { + return ResourceManager.GetString("BilledInvoices", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Budget. + /// + public static string Budget { + get { + return ResourceManager.GetString("Budget", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Empty docbuilder path. + /// + public static string BuildErrorEmptyDocbuilderPath { + get { + return ResourceManager.GetString("BuildErrorEmptyDocbuilderPath", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Empty docbuilder template. + /// + public static string BuildErrorEmptyDocbuilderTemplate { + get { + return ResourceManager.GetString("BuildErrorEmptyDocbuilderTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to An error has occurred. Empty output file. + /// + public static string BuildErrorEmptyOutputFile { + get { + return ResourceManager.GetString("BuildErrorEmptyOutputFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Maximum number of process is exceeded. Try later. + /// + public static string BuildErrorMaxProcessExceeded { + get { + return ResourceManager.GetString("BuildErrorMaxProcessExceeded", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to By the budget. + /// + public static string ByBudget { + get { + return ResourceManager.GetString("ByBudget", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to By number. + /// + public static string ByCount { + get { + return ResourceManager.GetString("ByCount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Calls. + /// + public static string Calls { + get { + return ResourceManager.GetString("Calls", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Calls count. + /// + public static string CallsCount { + get { + return ResourceManager.GetString("CallsCount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Calls duration. + /// + public static string CallsDuration { + get { + return ResourceManager.GetString("CallsDuration", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Calls for the period. + /// + public static string CallsForThePeriod { + get { + return ResourceManager.GetString("CallsForThePeriod", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Closed. + /// + public static string Closed { + get { + return ResourceManager.GetString("Closed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Closed tasks. + /// + public static string ClosedTasks { + get { + return ResourceManager.GetString("ClosedTasks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Complete. + /// + public static string Complete { + get { + return ResourceManager.GetString("Complete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contacts by temperature level. + /// + public static string ContactsByStage { + get { + return ResourceManager.GetString("ContactsByStage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contacts by type. + /// + public static string ContactsByType { + get { + return ResourceManager.GetString("ContactsByType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to With opportunities. + /// + public static string ContactsWithDeals { + get { + return ResourceManager.GetString("ContactsWithDeals", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Without opportunities. + /// + public static string ContactsWithoutDeals { + get { + return ResourceManager.GetString("ContactsWithoutDeals", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Conversion. + /// + public static string Conversion { + get { + return ResourceManager.GetString("Conversion", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Count. + /// + public static string Count { + get { + return ResourceManager.GetString("Count", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Created. + /// + public static string Created { + get { + return ResourceManager.GetString("Created", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to To generate the report the opportunity budget must be brought to the base currency. Setup the used currency exchange rates to do that.. + /// + public static string CurrencySettingsDialogBody { + get { + return ResourceManager.GetString("CurrencySettingsDialogBody", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Currency Settings. + /// + public static string CurrencySettingsDialogHdr { + get { + return ResourceManager.GetString("CurrencySettingsDialogHdr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Current month. + /// + public static string CurrentMonth { + get { + return ResourceManager.GetString("CurrentMonth", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Current quarter. + /// + public static string CurrentQuarter { + get { + return ResourceManager.GetString("CurrentQuarter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Current week. + /// + public static string CurrentWeek { + get { + return ResourceManager.GetString("CurrentWeek", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Current year. + /// + public static string CurrentYear { + get { + return ResourceManager.GetString("CurrentYear", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Day. + /// + public static string Day { + get { + return ResourceManager.GetString("Day", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Budget of opportunities. + /// + public static string DealsBudget { + get { + return ResourceManager.GetString("DealsBudget", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunities by budget. + /// + public static string DealsByBudget { + get { + return ResourceManager.GetString("DealsByBudget", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunities by number. + /// + public static string DealsByCount { + get { + return ResourceManager.GetString("DealsByCount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunities by stage. + /// + public static string DealsByStage { + get { + return ResourceManager.GetString("DealsByStage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunities by status. + /// + public static string DealsByStatus { + get { + return ResourceManager.GetString("DealsByStatus", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Number of opportunities. + /// + public static string DealsCount { + get { + return ResourceManager.GetString("DealsCount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Duration. + /// + public static string Duration { + get { + return ResourceManager.GetString("Duration", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to During all time. + /// + public static string DuringAllTime { + get { + return ResourceManager.GetString("DuringAllTime", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error. + /// + public static string Error { + get { + return ResourceManager.GetString("Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not get docbuilder response. + /// + public static string ErrorNullDocbuilderResponse { + get { + return ResourceManager.GetString("ErrorNullDocbuilderResponse", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not get report data. + /// + public static string ErrorNullReportData { + get { + return ResourceManager.GetString("ErrorNullReportData", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not get report script. + /// + public static string ErrorNullReportScript { + get { + return ResourceManager.GetString("ErrorNullReportScript", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Generated reports. + /// + public static string GeneratedReports { + get { + return ResourceManager.GetString("GeneratedReports", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All your generated reports are displayed here. You can edit, delete or download the report files. To create a report, select a template from the list to the left, set the filters and click the 'Generate' button.. + /// + public static string GeneratedReportsDescription { + get { + return ResourceManager.GetString("GeneratedReportsDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Generate report. + /// + public static string GenerateReport { + get { + return ResourceManager.GetString("GenerateReport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If all opportunities won. + /// + public static string IfAllOpportunitiesWon { + get { + return ResourceManager.GetString("IfAllOpportunitiesWon", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Incoming. + /// + public static string Incoming { + get { + return ResourceManager.GetString("Incoming", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invoices by status. + /// + public static string InvoicesByStatus { + get { + return ResourceManager.GetString("InvoicesByStatus", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invoices for the period. + /// + public static string InvoicesForThePeriod { + get { + return ResourceManager.GetString("InvoicesForThePeriod", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Item. + /// + public static string Item { + get { + return ResourceManager.GetString("Item", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Lost. + /// + public static string Lost { + get { + return ResourceManager.GetString("Lost", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Manager. + /// + public static string Manager { + get { + return ResourceManager.GetString("Manager", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Managers. + /// + public static string Managers { + get { + return ResourceManager.GetString("Managers", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to of which {0} are unanswered. + /// + public static string MissedCount { + get { + return ResourceManager.GetString("MissedCount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Month. + /// + public static string Month { + get { + return ResourceManager.GetString("Month", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Will close in the next 30 days. + /// + public static string Near { + get { + return ResourceManager.GetString("Near", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New. + /// + public static string New { + get { + return ResourceManager.GetString("New", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New contacts. + /// + public static string NewContacts { + get { + return ResourceManager.GetString("NewContacts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New contacts with and without opportunities. + /// + public static string NewContactsWithAndWithoutDeals { + get { + return ResourceManager.GetString("NewContactsWithAndWithoutDeals", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New tasks. + /// + public static string NewTasks { + get { + return ResourceManager.GetString("NewTasks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Next month. + /// + public static string NextMonth { + get { + return ResourceManager.GetString("NextMonth", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Next quarter. + /// + public static string NextQuarter { + get { + return ResourceManager.GetString("NextQuarter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Next week. + /// + public static string NextWeek { + get { + return ResourceManager.GetString("NextWeek", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Next year. + /// + public static string NextYear { + get { + return ResourceManager.GetString("NextYear", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No data for the report. Select another filter.. + /// + public static string NoData { + get { + return ResourceManager.GetString("NoData", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open. + /// + public static string Open { + get { + return ResourceManager.GetString("Open", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opened. + /// + public static string Opened { + get { + return ResourceManager.GetString("Opened", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Outcoming. + /// + public static string Outcoming { + get { + return ResourceManager.GetString("Outcoming", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Overdue. + /// + public static string Overdue { + get { + return ResourceManager.GetString("Overdue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Overdue tasks. + /// + public static string OverdueTasks { + get { + return ResourceManager.GetString("OverdueTasks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Paid. + /// + public static string Paid { + get { + return ResourceManager.GetString("Paid", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Previous month. + /// + public static string PreviousMonth { + get { + return ResourceManager.GetString("PreviousMonth", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Previous quarter. + /// + public static string PreviousQuarter { + get { + return ResourceManager.GetString("PreviousQuarter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Previous week. + /// + public static string PreviousWeek { + get { + return ResourceManager.GetString("PreviousWeek", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Previous year. + /// + public static string PreviousYear { + get { + return ResourceManager.GetString("PreviousYear", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Rejected. + /// + public static string Rejected { + get { + return ResourceManager.GetString("Rejected", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Report building. + /// + public static string ReportBuilding { + get { + return ResourceManager.GetString("ReportBuilding", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All generated reports you can always see, download, edit, or delete them in {0}CRM/Reports/Generated reports{1}. + /// + public static string ReportBuildingInfo { + get { + return ResourceManager.GetString("ReportBuildingInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Report building ({0}% complete). + /// + public static string ReportBuildingProgress { + get { + return ResourceManager.GetString("ReportBuildingProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Reports. + /// + public static string Reports { + get { + return ResourceManager.GetString("Reports", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sales by the day, by the managers. + /// + public static string SalesByDay { + get { + return ResourceManager.GetString("SalesByDay", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sales by the hour, by the managers. + /// + public static string SalesByHour { + get { + return ResourceManager.GetString("SalesByHour", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sales By Managers. + /// + public static string SalesByManagersReport { + get { + return ResourceManager.GetString("SalesByManagersReport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This report is intended to show the successfully closed opportunities for the selected {1}Time period{2} for all the managers or only the selected ones.{0} When the week or month period is selected, the report shows the results grouped by days, the quarter and year periods will group the data by months.. + /// + public static string SalesByManagersReportDescription { + get { + return ResourceManager.GetString("SalesByManagersReportDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sales by the month, by the managers. + /// + public static string SalesByMonth { + get { + return ResourceManager.GetString("SalesByMonth", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sales Forecast. + /// + public static string SalesForecastReport { + get { + return ResourceManager.GetString("SalesForecastReport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This report is intended to show the sales forecast for a particular period of time, taking into account the opportunity probability. It shows the number of opportunities for the selected {1}Time period{2} for all the managers or only the selected ones.{0} The data includes the total budget for the opportunities, which estimated closing date falls into the selected period of time.. + /// + public static string SalesForecastReportDescription { + get { + return ResourceManager.GetString("SalesForecastReportDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sales funnel by budget of opportunities. + /// + public static string SalesFunnelByBudget { + get { + return ResourceManager.GetString("SalesFunnelByBudget", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sales funnel by number of opportunities. + /// + public static string SalesFunnelByCount { + get { + return ResourceManager.GetString("SalesFunnelByCount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sales Funnel. + /// + public static string SalesFunnelReport { + get { + return ResourceManager.GetString("SalesFunnelReport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This report is intended for building sales funnel for a particular period of time. It shows the number of opportunities for the selected {1}Time period{2} for all the managers or only the selected ones. + ///{0} The main data includes the number of opportunities in the funnel, average opportunity budget, average duration of successfully closed opportunities, the ratio of open to closed opportunities. + ///{0} The data is sorted by the opportunity number and budget.. + /// + public static string SalesFunnelReportDescription { + get { + return ResourceManager.GetString("SalesFunnelReportDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stage. + /// + public static string Stage { + get { + return ResourceManager.GetString("Stage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Status. + /// + public static string Status { + get { + return ResourceManager.GetString("Status", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sum. + /// + public static string Sum { + get { + return ResourceManager.GetString("Sum", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This report is intended to show the summary data for the opportunities, contacts, tasks and invoices by the selected {1}Time period{2} for all the managers or only the selected ones. The report will show the opportunities specified by the status and by their stage, contacts by type and stage, tasks and invoices by their status.. + /// + public static string SummaryAtThisMomentDescription { + get { + return ResourceManager.GetString("SummaryAtThisMomentDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Summary for the Moment. + /// + public static string SummaryAtThisMomentReport { + get { + return ResourceManager.GetString("SummaryAtThisMomentReport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Summary for the Period. + /// + public static string SummaryForThePeriodReport { + get { + return ResourceManager.GetString("SummaryForThePeriodReport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This report is intended to show the summary data for the opportunities, contacts, tasks and invoices for the selected {1}Time period{2} for all the managers or only the selected ones. The report will show the opportunities specified by the budget and by their number, contacts by type, tasks and invoices by the time period.. + /// + public static string SummaryForThePeriodReportDescription { + get { + return ResourceManager.GetString("SummaryForThePeriodReportDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tasks by status. + /// + public static string TasksByStatus { + get { + return ResourceManager.GetString("TasksByStatus", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tasks for the period. + /// + public static string TasksForThePeriod { + get { + return ResourceManager.GetString("TasksForThePeriod", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Time period. + /// + public static string TimePeriod { + get { + return ResourceManager.GetString("TimePeriod", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Today. + /// + public static string Today { + get { + return ResourceManager.GetString("Today", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tomorrow. + /// + public static string Tomorrow { + get { + return ResourceManager.GetString("Tomorrow", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Total. + /// + public static string Total { + get { + return ResourceManager.GetString("Total", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The total budget of opportunities. + /// + public static string TotalDealsBudget { + get { + return ResourceManager.GetString("TotalDealsBudget", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The total number of opportunities. + /// + public static string TotalDealsCount { + get { + return ResourceManager.GetString("TotalDealsCount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Total sales for the period by managers. + /// + public static string TotalSalesByManagers { + get { + return ResourceManager.GetString("TotalSalesByManagers", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type. + /// + public static string Type { + get { + return ResourceManager.GetString("Type", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to With respect to probability. + /// + public static string WithRespectToProbability { + get { + return ResourceManager.GetString("WithRespectToProbability", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Won. + /// + public static string Won { + get { + return ResourceManager.GetString("Won", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This report is intended to show the total number of contacts for the selected {1}Time period{2} for all the managers or only the selected ones. The report will show the new contacts specified by type and by the presence and absence of opportunities. The data is grouped by managers only.. + /// + public static string WorkloadByContactsDescription { + get { + return ResourceManager.GetString("WorkloadByContactsDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contacts by Managers. + /// + public static string WorkloadByContactsReport { + get { + return ResourceManager.GetString("WorkloadByContactsReport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This report is intended to show the total workload of won and lost opportunities for the selected {1}Time period{2} for all the managers or only the selected ones. The data is grouped by managers only.. + /// + public static string WorkloadByDealsDescription { + get { + return ResourceManager.GetString("WorkloadByDealsDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunities by Managers. + /// + public static string WorkloadByDealsReport { + get { + return ResourceManager.GetString("WorkloadByDealsReport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This report is intended to show the total number of invoices for the selected {1}Time period{2} for all the managers or only the selected ones. The report will show the total number of sent invoices as well as number of paid, rejected and overdue invoices. The data is grouped by managers only.. + /// + public static string WorkloadByInvoicesDescription { + get { + return ResourceManager.GetString("WorkloadByInvoicesDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invoices by Managers. + /// + public static string WorkloadByInvoicesReport { + get { + return ResourceManager.GetString("WorkloadByInvoicesReport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This report is intended to show the total number of tasks for the selected {1}Time period{2} for all the managers or only the selected ones. The report will show the closed, new and overdue tasks specified by type. The data is grouped by managers only.. + /// + public static string WorkloadByTasksDescription { + get { + return ResourceManager.GetString("WorkloadByTasksDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tasks by Managers. + /// + public static string WorkloadByTasksReport { + get { + return ResourceManager.GetString("WorkloadByTasksReport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This report is intended to show the total number of calls for the selected {1}Time period{2} for all the managers or only the selected ones. The report will show the total number of incoming, outcoming calls and theirs duration. The data is grouped by managers only.. + /// + public static string WorkloadByVoipDescription { + get { + return ResourceManager.GetString("WorkloadByVoipDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please note that this report will only be available if you connect Twillio. To connect, follow the {0}link{1}. + /// + public static string WorkloadByVoipNotAllowed { + get { + return ResourceManager.GetString("WorkloadByVoipNotAllowed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Voip by Managers. + /// + public static string WorkloadByVoipReport { + get { + return ResourceManager.GetString("WorkloadByVoipReport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Yesterday. + /// + public static string Yesterday { + get { + return ResourceManager.GetString("Yesterday", resourceCulture); + } + } + } +} diff --git a/products/ASC.CRM/Server/Resources/CRMReportResource.resx b/products/ASC.CRM/Server/Resources/CRMReportResource.resx new file mode 100644 index 00000000000..f65f4a2a4a1 --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMReportResource.resx @@ -0,0 +1,494 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Generate report + + + Managers + + + Reports + + + Sales By Managers + + + To generate the report the opportunity budget must be brought to the base currency. Setup the used currency exchange rates to do that. + + + Currency Settings + + + Manager + + + Sales by the day, by the managers + + + Sales by the hour, by the managers + + + Sales by the month, by the managers + + + Sum + + + Time period + + + Total + + + Total sales for the period by managers + + + Current month + + + Current quarter + + + Current week + + + Current year + + + Next month + + + Next quarter + + + Next week + + + Next year + + + No data for the report. Select another filter. + + + Previous month + + + Previous quarter + + + Previous week + + + Previous year + + + This report is intended to show the successfully closed opportunities for the selected {1}Time period{2} for all the managers or only the selected ones.{0} When the week or month period is selected, the report shows the results grouped by days, the quarter and year periods will group the data by months. + + + Sales Forecast + + + This report is intended to show the sales forecast for a particular period of time, taking into account the opportunity probability. It shows the number of opportunities for the selected {1}Time period{2} for all the managers or only the selected ones.{0} The data includes the total budget for the opportunities, which estimated closing date falls into the selected period of time. + + + Today + + + Tomorrow + + + Yesterday + + + Day + + + Month + + + Sales Funnel + + + This report is intended for building sales funnel for a particular period of time. It shows the number of opportunities for the selected {1}Time period{2} for all the managers or only the selected ones. +{0} The main data includes the number of opportunities in the funnel, average opportunity budget, average duration of successfully closed opportunities, the ratio of open to closed opportunities. +{0} The data is sorted by the opportunity number and budget. + + + The average budget of opportunities + + + The average duration of a successful opportunity, days + + + Budget + + + By the budget + + + By number + + + Conversion + + + Count + + + Budget of opportunities + + + Number of opportunities + + + Sales funnel by budget of opportunities + + + Sales funnel by number of opportunities + + + The total budget of opportunities + + + The total number of opportunities + + + Contacts by Managers + + + Opportunities by Managers + + + Invoices by Managers + + + Tasks by Managers + + + This report is intended to show the total number of contacts for the selected {1}Time period{2} for all the managers or only the selected ones. The report will show the new contacts specified by type and by the presence and absence of opportunities. The data is grouped by managers only. + + + Lost + + + New + + + Won + + + This report is intended to show the total workload of won and lost opportunities for the selected {1}Time period{2} for all the managers or only the selected ones. The data is grouped by managers only. + + + This report is intended to show the total number of invoices for the selected {1}Time period{2} for all the managers or only the selected ones. The report will show the total number of sent invoices as well as number of paid, rejected and overdue invoices. The data is grouped by managers only. + + + This report is intended to show the total number of tasks for the selected {1}Time period{2} for all the managers or only the selected ones. The report will show the closed, new and overdue tasks specified by type. The data is grouped by managers only. + + + Empty docbuilder path + + + Empty docbuilder template + + + An error has occurred. Empty output file + + + Maximum number of process is exceeded. Try later + + + Report building + + + Generated reports + + + All your generated reports are displayed here. You can edit, delete or download the report files. To create a report, select a template from the list to the left, set the filters and click the 'Generate' button. + + + Complete + + + Error + + + Open + + + All generated reports you can always see, download, edit, or delete them in {0}CRM/Reports/Generated reports{1} + + + Report building ({0}% complete) + + + Closed tasks + + + New tasks + + + Overdue tasks + + + Billed invoices + + + Paid + + + Rejected + + + With opportunities + + + Without opportunities + + + New contacts + + + New contacts with and without opportunities + + + During all time + + + Summary for the Period + + + This report is intended to show the summary data for the opportunities, contacts, tasks and invoices for the selected {1}Time period{2} for all the managers or only the selected ones. The report will show the opportunities specified by the budget and by their number, contacts by type, tasks and invoices by the time period. + + + Closed + + + Contacts by type + + + Created + + + Opportunities by budget + + + Opportunities by number + + + Invoices for the period + + + Item + + + Overdue + + + Status + + + Tasks for the period + + + Type + + + Contacts by temperature level + + + Opportunities by stage + + + Opportunities by status + + + Invoices by status + + + Will close in the next 30 days + + + Stage + + + This report is intended to show the summary data for the opportunities, contacts, tasks and invoices by the selected {1}Time period{2} for all the managers or only the selected ones. The report will show the opportunities specified by the status and by their stage, contacts by type and stage, tasks and invoices by their status. + + + Summary for the Moment + + + Tasks by status + + + Billed + + + Opened + + + Calls count + + + Calls duration + + + Incoming + + + Outcoming + + + This report is intended to show the total number of calls for the selected {1}Time period{2} for all the managers or only the selected ones. The report will show the total number of incoming, outcoming calls and theirs duration. The data is grouped by managers only. + + + Voip by Managers + + + Calls + + + Calls for the period + + + Duration + + + of which {0} are unanswered + + + Please note that this report will only be available if you connect Twillio. To connect, follow the {0}link{1} + + + Could not get docbuilder response + + + Could not get report data + + + Could not get report script + + + If all opportunities won + + + With respect to probability + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Resources/CRMSettingResource.Designer.cs b/products/ASC.CRM/Server/Resources/CRMSettingResource.Designer.cs new file mode 100644 index 00000000000..3aaa2361f2f --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMSettingResource.Designer.cs @@ -0,0 +1,1580 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.CRM.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class CRMSettingResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal CRMSettingResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.CRM.Resources.CRMSettingResource", typeof(CRMSettingResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Cancel. + /// + public static string AbortExport { + get { + return ResourceManager.GetString("AbortExport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Access rights & Tags. + /// + public static string AccessRightsAndTags { + get { + return ResourceManager.GetString("AccessRightsAndTags", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Set up the access rights for the users added via the "Website Contact Form", otherwise all CRM users will see new contacts. Create tags the new clients in the list will be marked with.. + /// + public static string AccessRightsAndTagsDescription { + get { + return ResourceManager.GetString("AccessRightsAndTagsDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Portal Access Rights. + /// + public static string AccessRightsSettings { + get { + return ResourceManager.GetString("AccessRightsSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add rate. + /// + public static string AddCurrencyRate { + get { + return ResourceManager.GetString("AddCurrencyRate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add new tag. + /// + public static string AddNewTag { + get { + return ResourceManager.GetString("AddNewTag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add new task template. + /// + public static string AddNewTaskTemplate { + get { + return ResourceManager.GetString("AddNewTaskTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add new task template container. + /// + public static string AddNewTaskTemplateContainer { + get { + return ResourceManager.GetString("AddNewTaskTemplateContainer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add an Option. + /// + public static string AddOption { + get { + return ResourceManager.GetString("AddOption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Address. + /// + public static string Address { + get { + return ResourceManager.GetString("Address", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add tag. + /// + public static string AddTag { + get { + return ResourceManager.GetString("AddTag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Adding tag. + /// + public static string AddTagInProgressing { + get { + return ResourceManager.GetString("AddTagInProgressing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Do not show the Linking Contacts dialog again. + /// + public static string AddTagWithoutAskingSettingsLabel { + get { + return ResourceManager.GetString("AddTagWithoutAskingSettingsLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add task template. + /// + public static string AddTaskTemplate { + get { + return ResourceManager.GetString("AddTaskTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add task template container. + /// + public static string AddTaskTemplateContainer { + get { + return ResourceManager.GetString("AddTaskTemplateContainer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Adding task template container. + /// + public static string AddTaskTemplateContainerInProgressing { + get { + return ResourceManager.GetString("AddTaskTemplateContainerInProgressing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Adding task template. + /// + public static string AddTaskTemplateInProgressing { + get { + return ResourceManager.GetString("AddTaskTemplateInProgressing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save. + /// + public static string AddThisCategory { + get { + return ResourceManager.GetString("AddThisCategory", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save. + /// + public static string AddThisContactType { + get { + return ResourceManager.GetString("AddThisContactType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save. + /// + public static string AddThisDealMilestone { + get { + return ResourceManager.GetString("AddThisDealMilestone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save. + /// + public static string AddThisField { + get { + return ResourceManager.GetString("AddThisField", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save. + /// + public static string AddThisStage { + get { + return ResourceManager.GetString("AddThisStage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save. + /// + public static string AddThisTag { + get { + return ResourceManager.GetString("AddThisTag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add this task template. + /// + public static string AddThisTaskTemplate { + get { + return ResourceManager.GetString("AddThisTaskTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add this task template container. + /// + public static string AddThisTaskTemplateContainer { + get { + return ResourceManager.GetString("AddThisTaskTemplateContainer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Basic Information. + /// + public static string BasicInformation { + get { + return ResourceManager.GetString("BasicInformation", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Both Person & Company. + /// + public static string BothPersonAndCompany { + get { + return ResourceManager.GetString("BothPersonAndCompany", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change color. + /// + public static string ChangeColor { + get { + return ResourceManager.GetString("ChangeColor", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Do not show the Linking Contacts dialog again. + /// + public static string ChangeContactStatusWithoutAskingSettingsLabel { + get { + return ResourceManager.GetString("ChangeContactStatusWithoutAskingSettingsLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to change the default currency?. + /// + public static string ChangeDefaultCurrencyConfText { + get { + return ResourceManager.GetString("ChangeDefaultCurrencyConfText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change icon. + /// + public static string ChangeIcon { + get { + return ResourceManager.GetString("ChangeIcon", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change Logo. + /// + public static string ChangeLogo { + get { + return ResourceManager.GetString("ChangeLogo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Checkbox. + /// + public static string CheckBox { + get { + return ResourceManager.GetString("CheckBox", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Columns. + /// + public static string Cols { + get { + return ResourceManager.GetString("Cols", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Common Settings. + /// + public static string CommonSettings { + get { + return ResourceManager.GetString("CommonSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Configure the SMTP. + /// + public static string ConfigureTheSMTP { + get { + return ResourceManager.GetString("ConfigureTheSMTP", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to SMTP server is not configured. Fill in the fields and click Save. You can set up the necessary parameters in the 'Settings' section.. + /// + public static string ConfigureTheSMTPInfo { + get { + return ResourceManager.GetString("ConfigureTheSMTPInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact Types. + /// + public static string ContactTypes { + get { + return ResourceManager.GetString("ContactTypes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Creating category. + /// + public static string CreateCategoryInProgressing { + get { + return ResourceManager.GetString("CreateCategoryInProgressing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Creating temperature level. + /// + public static string CreateContactStageInProgressing { + get { + return ResourceManager.GetString("CreateContactStageInProgressing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Creating contact type. + /// + public static string CreateContactTypeInProgressing { + get { + return ResourceManager.GetString("CreateContactTypeInProgressing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create user field. + /// + public static string CreateCustomField { + get { + return ResourceManager.GetString("CreateCustomField", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Creating stage. + /// + public static string CreateDealMilestoneInProgressing { + get { + return ResourceManager.GetString("CreateDealMilestoneInProgressing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create field. + /// + public static string CreateField { + get { + return ResourceManager.GetString("CreateField", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Creating field. + /// + public static string CreateFieldInProgressing { + get { + return ResourceManager.GetString("CreateFieldInProgressing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create new category. + /// + public static string CreateNewCategory { + get { + return ResourceManager.GetString("CreateNewCategory", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New Category. + /// + public static string CreateNewCategoryListButton { + get { + return ResourceManager.GetString("CreateNewCategoryListButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create new contact type. + /// + public static string CreateNewContactType { + get { + return ResourceManager.GetString("CreateNewContactType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New Contact Type. + /// + public static string CreateNewContactTypeListButton { + get { + return ResourceManager.GetString("CreateNewContactTypeListButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create new stage. + /// + public static string CreateNewDealMilestone { + get { + return ResourceManager.GetString("CreateNewDealMilestone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New Stage. + /// + public static string CreateNewDealMilestoneListButton { + get { + return ResourceManager.GetString("CreateNewDealMilestoneListButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create new field. + /// + public static string CreateNewField { + get { + return ResourceManager.GetString("CreateNewField", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New Field. + /// + public static string CreateNewFieldListButton { + get { + return ResourceManager.GetString("CreateNewFieldListButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create new temperature level. + /// + public static string CreateNewStage { + get { + return ResourceManager.GetString("CreateNewStage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New Temperature Level. + /// + public static string CreateNewStageListButton { + get { + return ResourceManager.GetString("CreateNewStageListButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create new tag. + /// + public static string CreateNewTag { + get { + return ResourceManager.GetString("CreateNewTag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New Tag. + /// + public static string CreateNewTagListButton { + get { + return ResourceManager.GetString("CreateNewTagListButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Currency rate. + /// + public static string CurrencyRate { + get { + return ResourceManager.GetString("CurrencyRate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Currency Settings. + /// + public static string CurrencySettings { + get { + return ResourceManager.GetString("CurrencySettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Set up the default currency which will be used to calculate the 'Sum total' for all the opportunities present in the CRM module of your portal. The conversion rates are available for currencies from the list only and are taken from the 'The Money Converter.com' web site.{0}This currency will be the currency for your Products & Services catalog which is used to invoice.. + /// + public static string CurrencySettingsHelp { + get { + return ResourceManager.GetString("CurrencySettingsHelp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User Fields. + /// + public static string CustomFields { + get { + return ResourceManager.GetString("CustomFields", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Date. + /// + public static string Date { + get { + return ResourceManager.GetString("Date", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Closes opportunity unsuccessfully.. + /// + public static string DealMilestoneStatusDescription_ClosedAndLost { + get { + return ResourceManager.GetString("DealMilestoneStatusDescription_ClosedAndLost", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Closes opportunity successfully.. + /// + public static string DealMilestoneStatusDescription_ClosedAndWon { + get { + return ResourceManager.GetString("DealMilestoneStatusDescription_ClosedAndWon", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Default currency. + /// + public static string DefaultCurrency { + get { + return ResourceManager.GetString("DefaultCurrency", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete category. + /// + public static string DeleteCategory { + get { + return ResourceManager.GetString("DeleteCategory", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete temperature level. + /// + public static string DeleteContactStage { + get { + return ResourceManager.GetString("DeleteContactStage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete contact type. + /// + public static string DeleteContactType { + get { + return ResourceManager.GetString("DeleteContactType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete field. + /// + public static string DeleteCustomField { + get { + return ResourceManager.GetString("DeleteCustomField", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The selected field with all the data will be deleted from all contacts where it is present. Are you sure you want to continue?. + /// + public static string DeleteCustomFieldConfirmationText { + get { + return ResourceManager.GetString("DeleteCustomFieldConfirmationText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete stage. + /// + public static string DeleteDealMilestone { + get { + return ResourceManager.GetString("DeleteDealMilestone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete tag. + /// + public static string DeleteTag { + get { + return ResourceManager.GetString("DeleteTag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Category deletion confirmation. + /// + public static string DeleteTaskCategoryConfirmation { + get { + return ResourceManager.GetString("DeleteTaskCategoryConfirmation", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete task template. + /// + public static string DeleteTaskTemplate { + get { + return ResourceManager.GetString("DeleteTaskTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete task template container. + /// + public static string DeleteTaskTemplateContainer { + get { + return ResourceManager.GetString("DeleteTaskTemplateContainer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete unused tags. + /// + public static string DeleteUnusedTags { + get { + return ResourceManager.GetString("DeleteUnusedTags", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Description. + /// + public static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact temperature levels let you characterize your potential buyers in a certain way. For instance, the temperature levels can be "Cold", "Hot", "Warm", which will mean the contact's willingness to buy your product.. + /// + public static string DescriptionTextContactStage { + get { + return ResourceManager.GetString("DescriptionTextContactStage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can edit or delete the contact temperature levels only in case there are no contacts of this level.. + /// + public static string DescriptionTextContactStageEditDelete { + get { + return ResourceManager.GetString("DescriptionTextContactStageEditDelete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Contact types let you characterize your contacts in a certain way. The default contact types are "Client", "Competitor", "Partner", "Supplier" which classify the contacts based on their role in relation to the organization.. + /// + public static string DescriptionTextContactType { + get { + return ResourceManager.GetString("DescriptionTextContactType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can edit or delete the contact type only in case there are no contacts of this type.. + /// + public static string DescriptionTextContactTypeEditDelete { + get { + return ResourceManager.GetString("DescriptionTextContactTypeEditDelete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunity stages let you determine steps necessary to complete the deal. For instance, you can add opportunity stages "Commercial offer", "Prepayment", "Execution". Set + /// + ///the necessary order for the opportunity stages dragging them.. + /// + public static string DescriptionTextDealMilestone { + get { + return ResourceManager.GetString("DescriptionTextDealMilestone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can edit or delete the opportunity stage only in case there are no opportunities at this stage.. + /// + public static string DescriptionTextDealMilestoneEditDelete { + get { + return ResourceManager.GetString("DescriptionTextDealMilestoneEditDelete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to History event categories allow you to split your history events into groups. Standard history event categories are mail, note, phone call, meeting. You need to have administrator rights to be able to edit them.. + /// + public static string DescriptionTextHistoryCategory { + get { + return ResourceManager.GetString("DescriptionTextHistoryCategory", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can edit or delete the history event category only in case there are no history events in this category.. + /// + public static string DescriptionTextHistoryCategoryEditDelete { + get { + return ResourceManager.GetString("DescriptionTextHistoryCategoryEditDelete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Task categories let you divide your tasks into groups depending on the goals you intend to achieve. For instance, a task category can be "Appointment", "Phone + /// + ///call" or "Expression of gratitude".. + /// + public static string DescriptionTextTaskCategory { + get { + return ResourceManager.GetString("DescriptionTextTaskCategory", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can edit or delete the task category stage only in case there are no tasks with this category.. + /// + public static string DescriptionTextTaskCategoryEditDelete { + get { + return ResourceManager.GetString("DescriptionTextTaskCategoryEditDelete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Offset in days. + /// + public static string DisplacementInDays { + get { + return ResourceManager.GetString("DisplacementInDays", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Download all data in one ZIP-archive. + /// + public static string DownloadAllDataInZipArchive { + get { + return ResourceManager.GetString("DownloadAllDataInZipArchive", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ZIP archive generation started. + /// + public static string DownloadingAllDataInZipArchive { + get { + return ResourceManager.GetString("DownloadingAllDataInZipArchive", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Link to download the ZIP archive. + /// + public static string DownloadLinkText { + get { + return ResourceManager.GetString("DownloadLinkText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit category. + /// + public static string EditCategory { + get { + return ResourceManager.GetString("EditCategory", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit temperature level. + /// + public static string EditContactStage { + get { + return ResourceManager.GetString("EditContactStage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit contact type. + /// + public static string EditContactType { + get { + return ResourceManager.GetString("EditContactType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit field. + /// + public static string EditCustomField { + get { + return ResourceManager.GetString("EditCustomField", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit stage. + /// + public static string EditDealMilestone { + get { + return ResourceManager.GetString("EditDealMilestone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit «{0}» task category. + /// + public static string EditSelectedCategory { + get { + return ResourceManager.GetString("EditSelectedCategory", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit the «{0}» temperature level. + /// + public static string EditSelectedContactStage { + get { + return ResourceManager.GetString("EditSelectedContactStage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit the "{0}" contact type. + /// + public static string EditSelectedContactType { + get { + return ResourceManager.GetString("EditSelectedContactType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit «{0}» opportunity stage. + /// + public static string EditSelectedDealMilestone { + get { + return ResourceManager.GetString("EditSelectedDealMilestone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit the «{0}» task template. + /// + public static string EditSelectedTaskTemplate { + get { + return ResourceManager.GetString("EditSelectedTaskTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit the «{0}» task template container. + /// + public static string EditSelectedTaskTemplateContainer { + get { + return ResourceManager.GetString("EditSelectedTaskTemplateContainer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit task template. + /// + public static string EditTaskTemplate { + get { + return ResourceManager.GetString("EditTaskTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit task template container. + /// + public static string EditTaskTemplateContainer { + get { + return ResourceManager.GetString("EditTaskTemplateContainer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The user field list is empty. + /// + public static string EmptyContentCustomFields { + get { + return ResourceManager.GetString("EmptyContentCustomFields", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User fields let you determine additional information about the client, opportunity or case. For instance, you can add the 'Source' field to specify where the user came from: advertisement, direct sale, etc. You can edit or delete the user field only in case there are no contacts or opportunities with this field value.. + /// + public static string EmptyContentCustomFieldsDescript { + get { + return ResourceManager.GetString("EmptyContentCustomFieldsDescript", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The tag list is empty. + /// + public static string EmptyContentTags { + get { + return ResourceManager.GetString("EmptyContentTags", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The tags can be used to describe and group contacts (both companies and persons), opportunities and cases which have something similar in them. For instance, you can tag all the persons from different companies but from the same sphere of business with a common tag for a better sorting. You can edit or delete the tag only in case there are no contacts, opportunities or cases with this tag.. + /// + public static string EmptyContentTagsDescript { + get { + return ResourceManager.GetString("EmptyContentTagsDescript", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The list of task templates is empty. + /// + public static string EmptyContentTaskTemplates { + get { + return ResourceManager.GetString("EmptyContentTaskTemplates", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Task templates allow you to create similar tasks inside the CRM module. You can add the tasks to contacts, opportunities or cases with a single mouse click. Select the container to create a task for every container task template.. + /// + public static string EmptyContentTaskTemplatesDescript { + get { + return ResourceManager.GetString("EmptyContentTaskTemplatesDescript", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This field must be filled correctly. + /// + public static string EmptyField { + get { + return ResourceManager.GetString("EmptyField", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Every field must have a title. + /// + public static string EmptyLabelError { + get { + return ResourceManager.GetString("EmptyLabelError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The field title cannot be empty. + /// + public static string EmptyTitleError { + get { + return ResourceManager.GetString("EmptyTitleError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Export. + /// + public static string Export { + get { + return ResourceManager.GetString("Export", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Export of All Data. + /// + public static string ExportData { + get { + return ResourceManager.GetString("ExportData", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The export function generates a ZIP-archive containing CSV files with all your CRM data (contacts, opportunities, cases, history, etc.) The email with the link to the created archive will be sent to your email address.. + /// + public static string ExportDataSettingsInfo { + get { + return ResourceManager.GetString("ExportDataSettingsInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Generate list for external program. + /// + public static string ExternalClient { + get { + return ResourceManager.GetString("ExternalClient", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Field list. + /// + public static string FieldList { + get { + return ResourceManager.GetString("FieldList", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Fields selection. + /// + public static string FieldsSelection { + get { + return ResourceManager.GetString("FieldsSelection", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select the fields which will be displayed on the form. + /// + public static string FieldsSelectionDescription { + get { + return ResourceManager.GetString("FieldsSelectionDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Form source code. + /// + public static string FormCode { + get { + return ResourceManager.GetString("FormCode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use this form on your site. + /// + public static string FormCodeDescription { + get { + return ResourceManager.GetString("FormCodeDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Preview. + /// + public static string FormPreview { + get { + return ResourceManager.GetString("FormPreview", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This is how the form on your site will look like. + /// + public static string FormPreviewDescription { + get { + return ResourceManager.GetString("FormPreviewDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Form properties. + /// + public static string FormProperties { + get { + return ResourceManager.GetString("FormProperties", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Website Contact Form main settings. + /// + public static string FormPropertiesDescription { + get { + return ResourceManager.GetString("FormPropertiesDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Generate Form. + /// + public static string GenerateForm { + get { + return ResourceManager.GetString("GenerateForm", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Title. + /// + public static string Heading { + get { + return ResourceManager.GetString("Heading", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to History Event Categories. + /// + public static string HistoryCategories { + get { + return ResourceManager.GetString("HistoryCategories", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Internal SMTP ({0} email recipients maximum). + /// + public static string InternalSMTP { + get { + return ResourceManager.GetString("InternalSMTP", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Just for company. + /// + public static string JustForCompany { + get { + return ResourceManager.GetString("JustForCompany", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Just for person. + /// + public static string JustForPerson { + get { + return ResourceManager.GetString("JustForPerson", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Title. + /// + public static string Label { + get { + return ResourceManager.GetString("Label", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Learn more.... + /// + public static string LearnMore { + get { + return ResourceManager.GetString("LearnMore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Success probability. + /// + public static string Likelihood { + get { + return ResourceManager.GetString("Likelihood", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Notify {!users}. + /// + public static string NotifyUsers { + get { + return ResourceManager.GetString("NotifyUsers", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select the {!users} who will receive notification when a new contact is added via the "Website Contact Form". + /// + public static string NotifyUsersDescription { + get { + return ResourceManager.GetString("NotifyUsersDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Count from the previous task. + /// + public static string OffsetFromThePreviousTask { + get { + return ResourceManager.GetString("OffsetFromThePreviousTask", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Count from the container start. + /// + public static string OffsetFromTheStartOfContainer { + get { + return ResourceManager.GetString("OffsetFromTheStartOfContainer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invoice logo. + /// + public static string OrganisationLogo { + get { + return ResourceManager.GetString("OrganisationLogo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Other fields. + /// + public static string OtherFields { + get { + return ResourceManager.GetString("OtherFields", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This is a test mail message created by the ONLYOFFICE™ portal automatically to check the mass mailing feature.\nPlease do not reply to this message.\n\nWe are sorry for any inconveniences caused.\nBest regards,\nPortal {0} administration. + /// + public static string pattern_TestMailSMTPMainBody { + get { + return ResourceManager.GetString("pattern_TestMailSMTPMainBody", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete option. + /// + public static string RemoveOption { + get { + return ResourceManager.GetString("RemoveOption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Restore to Default. + /// + public static string RestoreLogoToDeafult { + get { + return ResourceManager.GetString("RestoreLogoToDeafult", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Redirection URL. + /// + public static string ReturnURL { + get { + return ResourceManager.GetString("ReturnURL", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The address of your web site where the user will be redirected after the form is filled.. + /// + public static string ReturnURLDescription { + get { + return ResourceManager.GetString("ReturnURLDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Lines. + /// + public static string Rows { + get { + return ResourceManager.GetString("Rows", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Saved. + /// + public static string SaveCompleted { + get { + return ResourceManager.GetString("SaveCompleted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select box. + /// + public static string SelectBox { + get { + return ResourceManager.GetString("SelectBox", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Choices. + /// + public static string SelectOptions { + get { + return ResourceManager.GetString("SelectOptions", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please select a category stage to be replaced with the current category in tasks. + /// + public static string SelectTaskCategoryForReplace { + get { + return ResourceManager.GetString("SelectTaskCategoryForReplace", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Customize user fields. + /// + public static string SettingCustomFields { + get { + return ResourceManager.GetString("SettingCustomFields", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Settings. + /// + public static string Settings { + get { + return ResourceManager.GetString("Settings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Set your organization billing address which will be placed to all your invoices. Fill the fields with the {0}Address{1}, {0}City{1}, {0}State{1}, {0}Country{1} and {0}Zip Code{1}.. + /// + public static string SettingsCompanyAddressHelp { + get { + return ResourceManager.GetString("SettingsCompanyAddressHelp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The default {0}logo{1} of your organization will be displayed on your {0}Invoices{1}.{3} + ///To replace the logo click the 'Change Logo' link and select the image stored on your computer to load it to your portal. Please, keep in mind that you can upload an image file in PNG or JPG format not larger than {0}{4}x{5} pixels{1}, otherwise resizing will take place. The maximum image size cannot exceed {0}{2}{1}.. + /// + public static string SettingsCompanyLogoHelp { + get { + return ResourceManager.GetString("SettingsCompanyLogoHelp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Size. + /// + public static string Size { + get { + return ResourceManager.GetString("Size", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Send data. + /// + public static string SubmitFormData { + get { + return ResourceManager.GetString("SubmitFormData", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tag with this title already exists. + /// + public static string TagAlreadyExistsError { + get { + return ResourceManager.GetString("TagAlreadyExistsError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Task templates. + /// + public static string TaskTemplates { + get { + return ResourceManager.GetString("TaskTemplates", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Text area. + /// + public static string TextArea { + get { + return ResourceManager.GetString("TextArea", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Text. + /// + public static string TextField { + get { + return ResourceManager.GetString("TextField", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Title. + /// + public static string TitleItem { + get { + return ResourceManager.GetString("TitleItem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type. + /// + public static string Type { + get { + return ResourceManager.GetString("Type", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Web Form Key. + /// + public static string WebFormKey { + get { + return ResourceManager.GetString("WebFormKey", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A unique web form key which serves for the authentication of the form used to send the data and necessary for the form correct work. This is done for the security reasons to prevent spam, fraud and protect your web form from unauthorized use. When you generate a new key the existing forms with the old key will stop working.. + /// + public static string WebFormKeyDescription { + get { + return ResourceManager.GetString("WebFormKeyDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Website Contact Form. + /// + public static string WebToLeadsForm { + get { + return ResourceManager.GetString("WebToLeadsForm", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid form key. + /// + public static string WebToLeadsForm_InvalidKeyException { + get { + return ResourceManager.GetString("WebToLeadsForm_InvalidKeyException", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use the Website Contact Form functionality to build a form that you can publish on your site. It will allow you to capture new contacts automatically. Define the form properties, select the necessary fields and generate the code to create your own web form.. + /// + public static string WebToLeadsFormHeader { + get { + return ResourceManager.GetString("WebToLeadsFormHeader", resourceCulture); + } + } + } +} diff --git a/products/ASC.CRM/Server/Resources/CRMSettingResource.resx b/products/ASC.CRM/Server/Resources/CRMSettingResource.resx new file mode 100644 index 00000000000..f6163503e30 --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMSettingResource.resx @@ -0,0 +1,629 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Cancel + + + Access rights & Tags + + + Set up the access rights for the users added via the "Website Contact Form", otherwise all CRM users will see new contacts. Create tags the new clients in the list will be marked with. + + + Portal Access Rights + + + Add new tag + + + Add new task template + + + Add new task template container + + + Add an Option + + + Address + + + Add tag + + + Adding tag + + + Do not show the Linking Contacts dialog again + + + Add task template + + + Add task template container + + + Adding task template container + + + Adding task template + + + Save + + + Save + + + Save + + + Save + + + Save + + + Save + + + Add this task template + + + Add this task template container + + + Basic Information + + + Both Person & Company + + + Change color + + + Do not show the Linking Contacts dialog again + + + Are you sure you want to change the default currency? + + + Change icon + + + Change Logo + + + Checkbox + + + Columns + + + Common Settings + + + Configure the SMTP + + + SMTP server is not configured. Fill in the fields and click Save. You can set up the necessary parameters in the 'Settings' section. + + + Contact Types + + + Creating category + + + Creating temperature level + + + Creating contact type + + + Create user field + + + Creating stage + + + Create field + + + Creating field + + + Create new category + + + New Category + + + Create new contact type + + + New Contact Type + + + Create new stage + + + New Stage + + + Create new field + + + New Field + + + Create new temperature level + + + New Temperature Level + + + Create new tag + + + New Tag + + + Currency Settings + + + User Fields + + + Date + + + Closes opportunity unsuccessfully. + + + Closes opportunity successfully. + + + Default currency + + + Delete category + + + Delete temperature level + + + Delete contact type + + + Delete field + + + The selected field with all the data will be deleted from all contacts where it is present. Are you sure you want to continue? + + + Delete stage + + + Delete tag + + + Category deletion confirmation + + + Delete task template + + + Delete task template container + + + Delete unused tags + + + Description + + + Contact temperature levels let you characterize your potential buyers in a certain way. For instance, the temperature levels can be "Cold", "Hot", "Warm", which will mean the contact's willingness to buy your product. + + + You can edit or delete the contact temperature levels only in case there are no contacts of this level. + + + Contact types let you characterize your contacts in a certain way. The default contact types are "Client", "Competitor", "Partner", "Supplier" which classify the contacts based on their role in relation to the organization. + + + You can edit or delete the contact type only in case there are no contacts of this type. + + + Opportunity stages let you determine steps necessary to complete the deal. For instance, you can add opportunity stages "Commercial offer", "Prepayment", "Execution". Set + +the necessary order for the opportunity stages dragging them. + + + You can edit or delete the opportunity stage only in case there are no opportunities at this stage. + + + History event categories allow you to split your history events into groups. Standard history event categories are mail, note, phone call, meeting. You need to have administrator rights to be able to edit them. + + + You can edit or delete the history event category only in case there are no history events in this category. + + + Task categories let you divide your tasks into groups depending on the goals you intend to achieve. For instance, a task category can be "Appointment", "Phone + +call" or "Expression of gratitude". + + + You can edit or delete the task category stage only in case there are no tasks with this category. + + + Offset in days + + + Download all data in one ZIP-archive + + + ZIP archive generation started + + + Link to download the ZIP archive + + + Edit category + + + Edit temperature level + + + Edit contact type + + + Edit field + + + Edit stage + + + Edit «{0}» task category + + + Edit the «{0}» temperature level + + + Edit the "{0}" contact type + + + Edit «{0}» opportunity stage + + + Edit the «{0}» task template + + + Edit the «{0}» task template container + + + Edit task template + + + Edit task template container + + + The user field list is empty + + + User fields let you determine additional information about the client, opportunity or case. For instance, you can add the 'Source' field to specify where the user came from: advertisement, direct sale, etc. You can edit or delete the user field only in case there are no contacts or opportunities with this field value. + + + The tag list is empty + + + The tags can be used to describe and group contacts (both companies and persons), opportunities and cases which have something similar in them. For instance, you can tag all the persons from different companies but from the same sphere of business with a common tag for a better sorting. You can edit or delete the tag only in case there are no contacts, opportunities or cases with this tag. + + + The list of task templates is empty + + + Task templates allow you to create similar tasks inside the CRM module. You can add the tasks to contacts, opportunities or cases with a single mouse click. Select the container to create a task for every container task template. + + + This field must be filled correctly + + + Every field must have a title + + + The field title cannot be empty + + + Export + + + Export of All Data + + + The export function generates a ZIP-archive containing CSV files with all your CRM data (contacts, opportunities, cases, history, etc.) The email with the link to the created archive will be sent to your email address. + + + Generate list for external program + + + Field list + + + Fields selection + + + Select the fields which will be displayed on the form + + + Form source code + + + Use this form on your site + + + Preview + + + This is how the form on your site will look like + + + Form properties + + + Website Contact Form main settings + + + Generate Form + + + Title + + + History Event Categories + + + Internal SMTP ({0} email recipients maximum) + + + Just for company + + + Just for person + + + Title + + + Learn more... + + + Success probability + + + Notify {!users} + + + Select the {!users} who will receive notification when a new contact is added via the "Website Contact Form" + + + Count from the previous task + + + Count from the container start + + + Invoice logo + + + Other fields + + + This is a test mail message created by the ONLYOFFICE™ portal automatically to check the mass mailing feature.\nPlease do not reply to this message.\n\nWe are sorry for any inconveniences caused.\nBest regards,\nPortal {0} administration + + + Delete option + + + Restore to Default + + + Redirection URL + + + The address of your web site where the user will be redirected after the form is filled. + + + Lines + + + Saved + + + Select box + + + Choices + + + Please select a category stage to be replaced with the current category in tasks + + + Customize user fields + + + Settings + + + Set your organization billing address which will be placed to all your invoices. Fill the fields with the {0}Address{1}, {0}City{1}, {0}State{1}, {0}Country{1} and {0}Zip Code{1}. + + + The default {0}logo{1} of your organization will be displayed on your {0}Invoices{1}.{3} +To replace the logo click the 'Change Logo' link and select the image stored on your computer to load it to your portal. Please, keep in mind that you can upload an image file in PNG or JPG format not larger than {0}{4}x{5} pixels{1}, otherwise resizing will take place. The maximum image size cannot exceed {0}{2}{1}. + + + Size + + + Send data + + + Tag with this title already exists + + + Task templates + + + Text area + + + Text + + + Title + + + Type + + + Web Form Key + + + A unique web form key which serves for the authentication of the form used to send the data and necessary for the form correct work. This is done for the security reasons to prevent spam, fraud and protect your web form from unauthorized use. When you generate a new key the existing forms with the old key will stop working. + + + Website Contact Form + + + Invalid form key + + + Use the Website Contact Form functionality to build a form that you can publish on your site. It will allow you to capture new contacts automatically. Define the form properties, select the necessary fields and generate the code to create your own web form. + + + Add rate + + + Currency rate + + + Set up the default currency which will be used to calculate the 'Sum total' for all the opportunities present in the CRM module of your portal. The conversion rates are available for currencies from the list only and are taken from the 'The Money Converter.com' web site.{0}This currency will be the currency for your Products & Services catalog which is used to invoice. + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Resources/CRMSocialMediaResource.Designer.cs b/products/ASC.CRM/Server/Resources/CRMSocialMediaResource.Designer.cs new file mode 100644 index 00000000000..e75ceb4aa6a --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMSocialMediaResource.Designer.cs @@ -0,0 +1,216 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.CRM.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class CRMSocialMediaResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal CRMSocialMediaResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.CRM.Resources.CRMSocialMediaResource", typeof(CRMSocialMediaResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Return. + /// + public static string Back { + get { + return ResourceManager.GetString("Back", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Be aware of what your clients think about your produce and brand in the social network. Simply link the twitter account with the contact and follow the tweet news feed right on your portal.. + /// + public static string EmptyContentTwitterAccountsDescribe { + get { + return ResourceManager.GetString("EmptyContentTwitterAccountsDescribe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No linked Twitter accounts. + /// + public static string EmptyContentTwitterAccountsHeader { + get { + return ResourceManager.GetString("EmptyContentTwitterAccountsHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Internal error. + /// + public static string ErrorInternalServer { + get { + return ResourceManager.GetString("ErrorInternalServer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Twitter account could not be found. + /// + public static string ErrorTwitterAccountNotFound { + get { + return ResourceManager.GetString("ErrorTwitterAccountNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not connect to Twitter. + /// + public static string ErrorTwitterConnectionFailure { + get { + return ResourceManager.GetString("ErrorTwitterConnectionFailure", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Twitter is overloaded. + /// + public static string ErrorTwitterIsDown { + get { + return ResourceManager.GetString("ErrorTwitterIsDown", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Maximal allowed number of requests to Twitter is exceeded. Try again later.. + /// + public static string ErrorTwitterRateLimit { + get { + return ResourceManager.GetString("ErrorTwitterRateLimit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The application has no Twitter access. Please reconnect the Twitter account.. + /// + public static string ErrorTwitterUnauthorized { + get { + return ResourceManager.GetString("ErrorTwitterUnauthorized", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unknown Twitter account. + /// + public static string ErrorUnknownTwitterAccount { + get { + return ResourceManager.GetString("ErrorUnknownTwitterAccount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The information is taken from the following source. + /// + public static string InformationProvidedBy { + get { + return ResourceManager.GetString("InformationProvidedBy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Link Twitter account. + /// + public static string LinkTwitterAccount { + get { + return ResourceManager.GetString("LinkTwitterAccount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No accounts could be found. + /// + public static string NoAccountsHasBeenFound { + get { + return ResourceManager.GetString("NoAccountsHasBeenFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No companies could be found. + /// + public static string NoCompaniesHasBeenFound { + get { + return ResourceManager.GetString("NoCompaniesHasBeenFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please wait.... + /// + public static string PleaseWait { + get { + return ResourceManager.GetString("PleaseWait", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enter a text to search. + /// + public static string SearchText { + get { + return ResourceManager.GetString("SearchText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Twitter account is not connected. + /// + public static string SocialMediaAccountNotFoundTwitter { + get { + return ResourceManager.GetString("SocialMediaAccountNotFoundTwitter", resourceCulture); + } + } + } +} diff --git a/products/ASC.CRM/Server/Resources/CRMSocialMediaResource.resx b/products/ASC.CRM/Server/Resources/CRMSocialMediaResource.resx new file mode 100644 index 00000000000..dbeda7f9f5c --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMSocialMediaResource.resx @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Return + + + Be aware of what your clients think about your produce and brand in the social network. Simply link the twitter account with the contact and follow the tweet news feed right on your portal. + + + No linked Twitter accounts + + + The information is taken from the following source + + + Link Twitter account + + + No accounts could be found + + + No companies could be found + + + Please wait... + + + Enter a text to search + + + Internal error + + + Twitter account could not be found + + + Could not connect to Twitter + + + Twitter is overloaded + + + Maximal allowed number of requests to Twitter is exceeded. Try again later. + + + The application has no Twitter access. Please reconnect the Twitter account. + + + Unknown Twitter account + + + Twitter account is not connected + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Resources/CRMTaskResource.Designer.cs b/products/ASC.CRM/Server/Resources/CRMTaskResource.Designer.cs new file mode 100644 index 00000000000..1dd0713c0f4 --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMTaskResource.Designer.cs @@ -0,0 +1,765 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.CRM.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class CRMTaskResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal CRMTaskResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.CRM.Resources.CRMTaskResource", typeof(CRMTaskResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Create new task. + /// + public static string AddNewTask { + get { + return ResourceManager.GetString("AddNewTask", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create New Task. + /// + public static string AddNewTaskButtonText { + get { + return ResourceManager.GetString("AddNewTaskButtonText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Due date. + /// + public static string ByDueDate { + get { + return ResourceManager.GetString("ByDueDate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cancel. + /// + public static string Cancel { + get { + return ResourceManager.GetString("Cancel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Closed task. + /// + public static string ClosedTask { + get { + return ResourceManager.GetString("ClosedTask", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Closed tasks. + /// + public static string ClosedTasks { + get { + return ResourceManager.GetString("ClosedTasks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Link with contact. + /// + public static string ConnectWithAContact { + get { + return ResourceManager.GetString("ConnectWithAContact", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create the first task. + /// + public static string CreateFirstTask { + get { + return ResourceManager.GetString("CreateFirstTask", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create task. + /// + public static string CreateTask { + get { + return ResourceManager.GetString("CreateTask", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Custom period. + /// + public static string CustomDateFilter { + get { + return ResourceManager.GetString("CustomDateFilter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Other users. + /// + public static string CustomResponsibleFilter { + get { + return ResourceManager.GetString("CustomResponsibleFilter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deadline. + /// + public static string DeadLine { + get { + return ResourceManager.GetString("DeadLine", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete task. + /// + public static string DeleteTask { + get { + return ResourceManager.GetString("DeleteTask", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Description. + /// + public static string Description { + get { + return ResourceManager.GetString("Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Due date. + /// + public static string DueDate { + get { + return ResourceManager.GetString("DueDate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit task. + /// + public static string EditTask { + get { + return ResourceManager.GetString("EditTask", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tasks are easy way to create a reminder of what is to be done. For example, a task can be used to remind of an appointment, or a phone call. You can create tasks of {0}different categories{1}, link them with a contact, opportunity or case, or create a simple reminder for yourself.. + /// + public static string EmptyContentTasksDescribe { + get { + return ResourceManager.GetString("EmptyContentTasksDescribe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No tasks matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the tasks in this section. You can also look for the tasks you need in contacts, opportunities or cases tabs.. + /// + public static string EmptyContentTasksFilterDescribe { + get { + return ResourceManager.GetString("EmptyContentTasksFilterDescribe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No tasks to be displayed for this filter here. + /// + public static string EmptyContentTasksFilterHeader { + get { + return ResourceManager.GetString("EmptyContentTasksFilterHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The task list is empty. + /// + public static string EmptyContentTasksHeader { + get { + return ResourceManager.GetString("EmptyContentTasksHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Find a company or a person by name. + /// + public static string FindContactByName { + get { + return ResourceManager.GetString("FindContactByName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A properly formed CSV file should be selected on your computer HDD. It must contain the fields or columns separated by the selected delimiter which will be used to import tasks. The files containing more than {0} rows should be divided into smaller parts for proper importing.. + /// + public static string ImportFromCSVStepOneDescription { + get { + return ResourceManager.GetString("ImportFromCSVStepOneDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select a CSV file. + /// + public static string ImportFromCSVStepOneHeader { + get { + return ResourceManager.GetString("ImportFromCSVStepOneHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Match your file columns with the corresponding ONLYOFFICE™ CRM fields. Please pay your attention to the fact that to import tasks correctly you need to have at least the following columns from your file matched with the fields of the ONLYOFFICE™ CRM: 'Task Title', 'Responsible', 'Due Date'. The fields containing the dates must have the following format: {0}.. + /// + public static string ImportFromCSVStepTwoDescription { + get { + return ResourceManager.GetString("ImportFromCSVStepTwoDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please, verify the fields. + /// + public static string ImportFromCSVStepTwoHeader { + get { + return ResourceManager.GetString("ImportFromCSVStepTwoHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Go to task list. + /// + public static string ImportStartingPanelButton { + get { + return ResourceManager.GetString("ImportStartingPanelButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Import of tasks can take several minutes depending on the amount of your data.. + /// + public static string ImportStartingPanelDescription { + get { + return ResourceManager.GetString("ImportStartingPanelDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Importing started. + /// + public static string ImportStartingPanelHeader { + get { + return ResourceManager.GetString("ImportStartingPanelHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Import tasks. + /// + public static string ImportTasks { + get { + return ResourceManager.GetString("ImportTasks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to My Tasks. + /// + public static string MyTasks { + get { + return ResourceManager.GetString("MyTasks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to My tasks. + /// + public static string MyTasksFilter { + get { + return ResourceManager.GetString("MyTasksFilter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to never. + /// + public static string Never { + get { + return ResourceManager.GetString("Never", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Closed. + /// + public static string OnlyClosedTasks { + get { + return ResourceManager.GetString("OnlyClosedTasks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open. + /// + public static string OnlyOpenTasks { + get { + return ResourceManager.GetString("OnlyOpenTasks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Overdue. + /// + public static string OverdueTasksFilter { + get { + return ResourceManager.GetString("OverdueTasksFilter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 15 minutes. + /// + public static string PerFifteenMinutes { + get { + return ResourceManager.GetString("PerFifteenMinutes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 5 minutes. + /// + public static string PerFiveMinutes { + get { + return ResourceManager.GetString("PerFiveMinutes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to half an hour. + /// + public static string PerHalfHour { + get { + return ResourceManager.GetString("PerHalfHour", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to an hour. + /// + public static string PerHour { + get { + return ResourceManager.GetString("PerHour", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to a day. + /// + public static string PerNight { + get { + return ResourceManager.GetString("PerNight", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 2 hours. + /// + public static string PerTwoHours { + get { + return ResourceManager.GetString("PerTwoHours", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Responsible. + /// + public static string Responsible { + get { + return ResourceManager.GetString("Responsible", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Saving task. + /// + public static string SavingTask { + get { + return ResourceManager.GetString("SavingTask", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show. + /// + public static string Show { + get { + return ResourceManager.GetString("Show", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Start Importing. + /// + public static string StartImport { + get { + return ResourceManager.GetString("StartImport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Task. + /// + public static string Task { + get { + return ResourceManager.GetString("Task", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tasks Categories. + /// + public static string TaskCategories { + get { + return ResourceManager.GetString("TaskCategories", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Task category. + /// + public static string TaskCategory { + get { + return ResourceManager.GetString("TaskCategory", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Phone call. + /// + public static string TaskCategory_Call { + get { + return ResourceManager.GetString("TaskCategory_Call", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opportunity. + /// + public static string TaskCategory_Deal { + get { + return ResourceManager.GetString("TaskCategory_Deal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Demo. + /// + public static string TaskCategory_Demo { + get { + return ResourceManager.GetString("TaskCategory_Demo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Email. + /// + public static string TaskCategory_Email { + get { + return ResourceManager.GetString("TaskCategory_Email", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Fax. + /// + public static string TaskCategory_Fax { + get { + return ResourceManager.GetString("TaskCategory_Fax", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Execution control. + /// + public static string TaskCategory_FollowUP { + get { + return ResourceManager.GetString("TaskCategory_FollowUP", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Lunch. + /// + public static string TaskCategory_Lunch { + get { + return ResourceManager.GetString("TaskCategory_Lunch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Appointment. + /// + public static string TaskCategory_Meeting { + get { + return ResourceManager.GetString("TaskCategory_Meeting", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Note. + /// + public static string TaskCategory_Note { + get { + return ResourceManager.GetString("TaskCategory_Note", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delivery. + /// + public static string TaskCategory_Ship { + get { + return ResourceManager.GetString("TaskCategory_Ship", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Social networks. + /// + public static string TaskCategory_SocialNetworks { + get { + return ResourceManager.GetString("TaskCategory_SocialNetworks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Expression of gratitude. + /// + public static string TaskCategory_ThankYou { + get { + return ResourceManager.GetString("TaskCategory_ThankYou", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Task description. + /// + public static string TaskDescription { + get { + return ResourceManager.GetString("TaskDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Task «{0}» is closed. + /// + public static string TaskIsOver { + get { + return ResourceManager.GetString("TaskIsOver", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Assign to. + /// + public static string TaskResponsible { + get { + return ResourceManager.GetString("TaskResponsible", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tasks. + /// + public static string Tasks { + get { + return ResourceManager.GetString("Tasks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Status. + /// + public static string TasksByStatus { + get { + return ResourceManager.GetString("TasksByStatus", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Task status. + /// + public static string TaskStatus { + get { + return ResourceManager.GetString("TaskStatus", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Closed. + /// + public static string TaskStatus_Closed { + get { + return ResourceManager.GetString("TaskStatus_Closed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open. + /// + public static string TaskStatus_Open { + get { + return ResourceManager.GetString("TaskStatus_Open", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Task title. + /// + public static string TaskTitle { + get { + return ResourceManager.GetString("TaskTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Upcoming. + /// + public static string TheNextTasksFilter { + get { + return ResourceManager.GetString("TheNextTasksFilter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 3 days. + /// + public static string ThreeDays { + get { + return ResourceManager.GetString("ThreeDays", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Time. + /// + public static string Time { + get { + return ResourceManager.GetString("Time", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Today. + /// + public static string Today { + get { + return ResourceManager.GetString("Today", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Today. + /// + public static string TodayTasksFilter { + get { + return ResourceManager.GetString("TodayTasksFilter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Task categories let you divide your tasks into groups depending on the goals you intend to achieve. The default task categories are phone call, opportunity, demo, email, fax, execution control, lunch, appointment, note, delivery, social networks, expression of gratitude. To be able to edit them, the administrator rights are needed.. + /// + public static string TooltipCategories { + get { + return ResourceManager.GetString("TooltipCategories", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All tasks. + /// + public static string TotalTasks { + get { + return ResourceManager.GetString("TotalTasks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Week. + /// + public static string Week { + get { + return ResourceManager.GetString("Week", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Nobody. + /// + public static string WithoutResponsible { + get { + return ResourceManager.GetString("WithoutResponsible", resourceCulture); + } + } + } +} diff --git a/products/ASC.CRM/Server/Resources/CRMTaskResource.resx b/products/ASC.CRM/Server/Resources/CRMTaskResource.resx new file mode 100644 index 00000000000..b8d30270543 --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMTaskResource.resx @@ -0,0 +1,354 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Create new task + + + Create New Task + + + Due date + + + Cancel + + + Closed task + + + Closed tasks + + + Link with contact + + + Create the first task + + + Create task + + + Custom period + + + Other users + + + Deadline + + + Delete task + + + Description + + + Due date + + + Edit task + + + Tasks are easy way to create a reminder of what is to be done. For example, a task can be used to remind of an appointment, or a phone call. You can create tasks of {0}different categories{1}, link them with a contact, opportunity or case, or create a simple reminder for yourself. + + + No tasks matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the tasks in this section. You can also look for the tasks you need in contacts, opportunities or cases tabs. + + + No tasks to be displayed for this filter here + + + The task list is empty + + + Find a company or a person by name + + + A properly formed CSV file should be selected on your computer HDD. It must contain the fields or columns separated by the selected delimiter which will be used to import tasks. The files containing more than {0} rows should be divided into smaller parts for proper importing. + + + Select a CSV file + + + Match your file columns with the corresponding ONLYOFFICE™ CRM fields. Please pay your attention to the fact that to import tasks correctly you need to have at least the following columns from your file matched with the fields of the ONLYOFFICE™ CRM: 'Task Title', 'Responsible', 'Due Date'. The fields containing the dates must have the following format: {0}. + + + Please, verify the fields + + + Go to task list + + + Import of tasks can take several minutes depending on the amount of your data. + + + Importing started + + + Import tasks + + + My Tasks + + + My tasks + + + never + + + Closed + + + Open + + + Overdue + + + 15 minutes + + + 5 minutes + + + half an hour + + + an hour + + + a day + + + 2 hours + + + Responsible + + + Saving task + + + Show + + + Start Importing + + + Task + + + Tasks Categories + + + Task category + + + Phone call + + + Opportunity + + + Demo + + + Email + + + Fax + + + Execution control + + + Lunch + + + Appointment + + + Note + + + Delivery + + + Social networks + + + Expression of gratitude + + + Task description + + + Task «{0}» is closed + + + Assign to + + + Tasks + + + Status + + + Task status + + + Closed + + + Open + + + Task title + + + Upcoming + + + 3 days + + + Time + + + Today + + + Today + + + Task categories let you divide your tasks into groups depending on the goals you intend to achieve. The default task categories are phone call, opportunity, demo, email, fax, execution control, lunch, appointment, note, delivery, social networks, expression of gratitude. To be able to edit them, the administrator rights are needed. + + + All tasks + + + Week + + + Nobody + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Resources/CRMVoipResource.Designer.cs b/products/ASC.CRM/Server/Resources/CRMVoipResource.Designer.cs new file mode 100644 index 00000000000..e65a65f20a3 --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMVoipResource.Designer.cs @@ -0,0 +1,847 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.CRM.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class CRMVoipResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal CRMVoipResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.CRM.Resources.CRMVoipResource", typeof(CRMVoipResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Добавить оператора. + /// + public static string AddOperatorBtn { + get { + return ResourceManager.GetString("AddOperatorBtn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Добавить операторов. + /// + public static string AddOperatorsBtn { + get { + return ResourceManager.GetString("AddOperatorsBtn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Добавить мелодию. + /// + public static string AddRingtoneBtn { + get { + return ResourceManager.GetString("AddRingtoneBtn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Псевдоним. + /// + public static string Alias { + get { + return ResourceManager.GetString("Alias", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}Псевдоним{1} "дальше идет объяснительный текст". + /// + public static string AliasDscrMsg { + get { + return ResourceManager.GetString("AliasDscrMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Введите название для номера. + /// + public static string AliasPlaceholder { + get { + return ResourceManager.GetString("AliasPlaceholder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Buy phone number. + /// + public static string BuyNumberBtn { + get { + return ResourceManager.GetString("BuyNumberBtn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Покупка номера. + /// + public static string BuyNumberHeader { + get { + return ResourceManager.GetString("BuyNumberHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Call. + /// + public static string Call { + get { + return ResourceManager.GetString("Call", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Agent. + /// + public static string CallAgent { + get { + return ResourceManager.GetString("CallAgent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Client. + /// + public static string CallClient { + get { + return ResourceManager.GetString("CallClient", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cost. + /// + public static string CallCost { + get { + return ResourceManager.GetString("CallCost", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Date/Time. + /// + public static string CallDatetime { + get { + return ResourceManager.GetString("CallDatetime", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Duration. + /// + public static string CallDuration { + get { + return ResourceManager.GetString("CallDuration", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type. + /// + public static string CallType { + get { + return ResourceManager.GetString("CallType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Waiting Time. + /// + public static string CallWaitingTime { + get { + return ResourceManager.GetString("CallWaitingTime", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Отмена. + /// + public static string CancelBtn { + get { + return ResourceManager.GetString("CancelBtn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Удалить. + /// + public static string DeleteBtn { + get { + return ResourceManager.GetString("DeleteBtn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Номер сохранится в twilio, но будет недоступен в рамках текущего портала. + /// + public static string DeleteNumberBody { + get { + return ResourceManager.GetString("DeleteNumberBody", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Удалить номер. + /// + public static string DeleteNumberBtn { + get { + return ResourceManager.GetString("DeleteNumberBtn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Удалить номер. + /// + public static string DeleteNumberHeader { + get { + return ResourceManager.GetString("DeleteNumberHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Удалить мелодию. + /// + public static string DeleteRingtoneBtn { + get { + return ResourceManager.GetString("DeleteRingtoneBtn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Редактировать. + /// + public static string EditBtn { + get { + return ResourceManager.GetString("EditBtn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enter Credentials. + /// + public static string EmptyScreenEnter { + get { + return ResourceManager.GetString("EmptyScreenEnter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to IP Telephony Setup. + /// + public static string EmptyScreenHeader { + get { + return ResourceManager.GetString("EmptyScreenHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Buy a new IP phone number to add it to the ONLYOFFICE portal. Once added you can use this phone number with portal CRM. + ///. + /// + public static string EmptyScreenNumberDescription1 { + get { + return ResourceManager.GetString("EmptyScreenNumberDescription1", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Note: the phone numbers you already have at Twilio website cannot be transferred to the portal.. + /// + public static string EmptyScreenNumberDescription2 { + get { + return ResourceManager.GetString("EmptyScreenNumberDescription2", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No IP telephony numbers have been added yet. + /// + public static string EmptyScreenNumberHeader { + get { + return ResourceManager.GetString("EmptyScreenNumberHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Register Account. + /// + public static string EmptyScreenRegister { + get { + return ResourceManager.GetString("EmptyScreenRegister", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Click the 'Register Account' button to go to Twilio website for registration to be able to use the IP telephony feature.. + /// + public static string EmptyScreenText1 { + get { + return ResourceManager.GetString("EmptyScreenText1", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If you already have a Twilio account, enter the API credentials: Account SID and Auth token, which can be found at the {0}settings{1} page.. + /// + public static string EmptyScreenText2 { + get { + return ResourceManager.GetString("EmptyScreenText2", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enter Credentials. + /// + public static string EnterCredentialsHeader { + get { + return ResourceManager.GetString("EnterCredentialsHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Общие настройки. + /// + public static string GeneralSettings { + get { + return ResourceManager.GetString("GeneralSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Мелодии приветствия. + /// + public static string GreetingRingtones { + get { + return ResourceManager.GetString("GreetingRingtones", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Incoming Call. + /// + public static string IncomingCall { + get { + return ResourceManager.GetString("IncomingCall", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}Очередь входящих звонков{1} "дальше идет объяснительный текст". + /// + public static string IncomingCallsQueueDscrMsg { + get { + return ResourceManager.GetString("IncomingCallsQueueDscrMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Размер очереди входящих звонков. + /// + public static string IncomingCallsQueueSize { + get { + return ResourceManager.GetString("IncomingCallsQueueSize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to После привязки все вызовы будут обрабатываться текущим порталом.. + /// + public static string LinkNumberBody { + get { + return ResourceManager.GetString("LinkNumberBody", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Привязать купленный номер. + /// + public static string LinkNumberBtn { + get { + return ResourceManager.GetString("LinkNumberBtn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Привязка номера. + /// + public static string LinkNumberHeader { + get { + return ResourceManager.GetString("LinkNumberHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Номер успешно привязан. + /// + public static string LinkNumberSuccessMsg { + get { + return ResourceManager.GetString("LinkNumberSuccessMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Идет загрузка, пожалуйста подождите. + /// + public static string LoadingMsg { + get { + return ResourceManager.GetString("LoadingMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to К сожалению доступных номеров нет, попробуйте позже.. + /// + public static string NoAvailableNumbersMsg { + get { + return ResourceManager.GetString("NoAvailableNumbersMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to У вас пока нет номеров. Срочно купите!. + /// + public static string NoExistingNumbersMsg { + get { + return ResourceManager.GetString("NoExistingNumbersMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to У вас нет номеров. Приобрести номер можно {0}здесь{1}.. + /// + public static string NoNumbersMsg { + get { + return ResourceManager.GetString("NoNumbersMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Нет номеров с такими крутыми циферками. + /// + public static string NoSearchingNumbersMsg { + get { + return ResourceManager.GetString("NoSearchingNumbersMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Операторы номера. + /// + public static string NumberOperators { + get { + return ResourceManager.GetString("NumberOperators", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}Здесь{1} можно их настроить.. + /// + public static string NumbersCountDscrMsg { + get { + return ResourceManager.GetString("NumbersCountDscrMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Количество ваших номеров. + /// + public static string NumbersCountMsg { + get { + return ResourceManager.GetString("NumbersCountMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Оператор. + /// + public static string Operator { + get { + return ResourceManager.GetString("Operator", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Пауза оператора. + /// + public static string OperatorPause { + get { + return ResourceManager.GetString("OperatorPause", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Выключена. + /// + public static string OperatorPauseDisabled { + get { + return ResourceManager.GetString("OperatorPauseDisabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}Пауза оператора{1} "дальше идет объяснительный текст". + /// + public static string OperatorPauseDscrMsg { + get { + return ResourceManager.GetString("OperatorPauseDscrMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Включена. + /// + public static string OperatorPauseEnabled { + get { + return ResourceManager.GetString("OperatorPauseEnabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Исходящие звонки. + /// + public static string OutgoingCalls { + get { + return ResourceManager.GetString("OutgoingCalls", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Мелодии очереди. + /// + public static string QueueRingtones { + get { + return ResourceManager.GetString("QueueRingtones", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Быстрая настройка. + /// + public static string QuickTuning { + get { + return ResourceManager.GetString("QuickTuning", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}Быстрая настройка{1} "дальше идет объяснительный текст". + /// + public static string QuickTuningDscrMsg { + get { + return ResourceManager.GetString("QuickTuningDscrMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Запись звонков. + /// + public static string RecordingCalls { + get { + return ResourceManager.GetString("RecordingCalls", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Записи разговоров хранятся в формате WAV. Ваш браузер не поддерживает воспроизведение аудиофайлов в этом формате, поэтому Вы не сможете прослушать свои записи.. + /// + public static string RecordingsCallsPlayNotSupportedMsg { + get { + return ResourceManager.GetString("RecordingsCallsPlayNotSupportedMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Список браузеров, поддерживающих данную функцию можно найти {0}здесь{1}. + /// + public static string RecordingsCallsPlayNotSupportedTipMsg { + get { + return ResourceManager.GetString("RecordingsCallsPlayNotSupportedTipMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Список мелодий. + /// + public static string RingtonesList { + get { + return ResourceManager.GetString("RingtonesList", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Настройка мелодий. + /// + public static string RingtonesTuning { + get { + return ResourceManager.GetString("RingtonesTuning", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}Настройка мелодий{1} "дальше идет объяснительный текст". + /// + public static string RingtonesTuningDscrMsg { + get { + return ResourceManager.GetString("RingtonesTuningDscrMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}Рекомендации к загружаемой мелодии{1} Размер файла не должен превышать 5 Mb. Поддерживаемые форматы: mp3.. + /// + public static string RingtonesUploadingRecomendationsMsg { + get { + return ResourceManager.GetString("RingtonesUploadingRecomendationsMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Поиск. + /// + public static string SearchLabel { + get { + return ResourceManager.GetString("SearchLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Настройка номера. + /// + public static string TuningNumber { + get { + return ResourceManager.GetString("TuningNumber", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Account Sid. + /// + public static string TwilioAccountSid { + get { + return ResourceManager.GetString("TwilioAccountSid", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Auth Token. + /// + public static string TwilioAuthToken { + get { + return ResourceManager.GetString("TwilioAuthToken", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Не выбрано. + /// + public static string UnspecifiedOption { + get { + return ResourceManager.GetString("UnspecifiedOption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Обновить настройки. + /// + public static string UpdateSettingsBtn { + get { + return ResourceManager.GetString("UpdateSettingsBtn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Виртуальные номера. + /// + public static string VirtualNumbers { + get { + return ResourceManager.GetString("VirtualNumbers", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Виртуальные номера и операторы. + /// + public static string VirtualNumbersAndOperators { + get { + return ResourceManager.GetString("VirtualNumbersAndOperators", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}Виртуальные номера{1} "дальше идет объяснительный текст". + /// + public static string VirtualNumbersDscrMsg { + get { + return ResourceManager.GetString("VirtualNumbersDscrMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Голосовая почта. + /// + public static string Voicemail { + get { + return ResourceManager.GetString("Voicemail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Мелодии голосового письма. + /// + public static string VoicemailRingtones { + get { + return ResourceManager.GetString("VoicemailRingtones", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to VoIP. + /// + public static string VoipModuleDescription { + get { + return ResourceManager.GetString("VoipModuleDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to VoIP. + /// + public static string VoipModuleTitle { + get { + return ResourceManager.GetString("VoipModuleTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Мелодии ожидания. + /// + public static string WaitingRingtones { + get { + return ResourceManager.GetString("WaitingRingtones", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Таймаут ожидания. + /// + public static string WaitingTimeout { + get { + return ResourceManager.GetString("WaitingTimeout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 10 минут. + /// + public static string WaitingTimeout10Minutes { + get { + return ResourceManager.GetString("WaitingTimeout10Minutes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 15 минут. + /// + public static string WaitingTimeout15Minutes { + get { + return ResourceManager.GetString("WaitingTimeout15Minutes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 5 минут. + /// + public static string WaitingTimeout5Minutes { + get { + return ResourceManager.GetString("WaitingTimeout5Minutes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}Таймаут ожидания{1} "дальше идет объяснительный текст". + /// + public static string WaitingTimeoutDscrMsg { + get { + return ResourceManager.GetString("WaitingTimeoutDscrMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Рабочие часы. + /// + public static string WorkingHours { + get { + return ResourceManager.GetString("WorkingHours", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Некорректный интервал. + /// + public static string WorkingHoursFormatErrorMsg { + get { + return ResourceManager.GetString("WorkingHoursFormatErrorMsg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Введите время в формате ##:##. + /// + public static string WorkingHoursFormatTip { + get { + return ResourceManager.GetString("WorkingHoursFormatTip", resourceCulture); + } + } + } +} diff --git a/products/ASC.CRM/Server/Resources/CRMVoipResource.resx b/products/ASC.CRM/Server/Resources/CRMVoipResource.resx new file mode 100644 index 00000000000..19abbf66dbc --- /dev/null +++ b/products/ASC.CRM/Server/Resources/CRMVoipResource.resx @@ -0,0 +1,382 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Добавить оператора + + + Добавить операторов + + + Добавить мелодию + + + Псевдоним + + + {0}Псевдоним{1} "дальше идет объяснительный текст" + + + Введите название для номера + + + Buy phone number + + + Покупка номера + + + Call + + + Agent + + + Client + + + Cost + + + Date/Time + + + Duration + + + Type + + + Waiting Time + + + Отмена + + + Удалить + + + Номер сохранится в twilio, но будет недоступен в рамках текущего портала + + + Удалить номер + + + Удалить номер + + + Удалить мелодию + + + Редактировать + + + Enter Credentials + + + IP Telephony Setup + + + Buy a new IP phone number to add it to the ONLYOFFICE portal. Once added you can use this phone number with portal CRM. + + + + Note: the phone numbers you already have at Twilio website cannot be transferred to the portal. + + + No IP telephony numbers have been added yet + + + Register Account + + + Click the 'Register Account' button to go to Twilio website for registration to be able to use the IP telephony feature. + + + If you already have a Twilio account, enter the API credentials: Account SID and Auth token, which can be found at the {0}settings{1} page. + + + Enter Credentials + + + Общие настройки + + + Мелодии приветствия + + + Incoming Call + + + {0}Очередь входящих звонков{1} "дальше идет объяснительный текст" + + + Размер очереди входящих звонков + + + После привязки все вызовы будут обрабатываться текущим порталом. + + + Привязать купленный номер + + + Привязка номера + + + Номер успешно привязан + + + Идет загрузка, пожалуйста подождите + + + К сожалению доступных номеров нет, попробуйте позже. + + + У вас пока нет номеров. Срочно купите! + + + У вас нет номеров. Приобрести номер можно {0}здесь{1}. + + + Нет номеров с такими крутыми циферками + + + Операторы номера + + + {0}Здесь{1} можно их настроить. + + + Количество ваших номеров + + + Оператор + + + Пауза оператора + + + Выключена + + + {0}Пауза оператора{1} "дальше идет объяснительный текст" + + + Включена + + + Исходящие звонки + + + Мелодии очереди + + + Быстрая настройка + + + {0}Быстрая настройка{1} "дальше идет объяснительный текст" + + + Запись звонков + + + Записи разговоров хранятся в формате WAV. Ваш браузер не поддерживает воспроизведение аудиофайлов в этом формате, поэтому Вы не сможете прослушать свои записи. + + + Список браузеров, поддерживающих данную функцию можно найти {0}здесь{1} + + + Список мелодий + + + Настройка мелодий + + + {0}Настройка мелодий{1} "дальше идет объяснительный текст" + + + {0}Рекомендации к загружаемой мелодии{1} Размер файла не должен превышать 5 Mb. Поддерживаемые форматы: mp3. + + + Поиск + + + Настройка номера + + + Account Sid + + + Auth Token + + + Не выбрано + + + Обновить настройки + + + Виртуальные номера + + + Виртуальные номера и операторы + + + {0}Виртуальные номера{1} "дальше идет объяснительный текст" + + + Голосовая почта + + + Мелодии голосового письма + + + VoIP + + + VoIP + + + Мелодии ожидания + + + Таймаут ожидания + + + 10 минут + + + 15 минут + + + 5 минут + + + {0}Таймаут ожидания{1} "дальше идет объяснительный текст" + + + Рабочие часы + + + Некорректный интервал + + + Введите время в формате ##:## + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Services/NotifyService/CRMPatternResource.Designer.cs b/products/ASC.CRM/Server/Services/NotifyService/CRMPatternResource.Designer.cs new file mode 100644 index 00000000000..116999c9a93 --- /dev/null +++ b/products/ASC.CRM/Server/Services/NotifyService/CRMPatternResource.Designer.cs @@ -0,0 +1,324 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.CRM.Services.NotifyService { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class CRMPatternResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal CRMPatternResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.CRM.Services.NotifyService.CRMPatternResource", typeof(CRMPatternResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to h1. New event added to "$EntityTitle":"${__VirtualRootPath}/${EntityRelativeURL}" + ///$__DateTime "$__AuthorName":"$__AuthorUrl" has added a new event to "$EntityTitle":"${__VirtualRootPath}/${EntityRelativeURL}": + /// $AdditionalData.get_item("EventContent") + /// + /// #foreach($fileInfo in $AdditionalData.get_item("Files").Keys) + /// + /// #beforeall + /// + /// ---------------------------------------- + /// + /// #each + /// /// [rest of string was truncated]";. + /// + public static string pattern_AddRelationshipEvent { + get { + return ResourceManager.GetString("pattern_AddRelationshipEvent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to h1. CRM. New contact created using the 'Website Contact Form' "$EntityTitle":"${__VirtualRootPath}/products/crm/default.aspx?ID=$EntityID" + /// + ///$__DateTime A new contact has been created using the 'Website Contact Form' "$EntityTitle":"${__VirtualRootPath}/products/crm/default.aspx?ID=$EntityID" + /// + ///Contact information: + /// + ///#foreach($contactInfo in $AdditionalData.Keys) + ///#each + /// + ///$contactInfo: $AdditionalData.get_item($contactInfo) + /// + ///#end. + /// + public static string pattern_CreateNewContact { + get { + return ResourceManager.GetString("pattern_CreateNewContact", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to h1. CRM. Data export successfully completed + /// + ///Please, follow this link to download the archive: "exportdata.zip":"${EntityRelativeURL}" + /// + ///*Note*: this link is valid for 24 hours only. + /// + ///If you have any questions or need assistance please feel free to contact us at "support.onlyoffice.com":"http://support.onlyoffice.com" + /// + ///Best regards, + ///ONLYOFFICE™ Support Team + ///"www.onlyoffice.com":"http://onlyoffice.com/". + /// + public static string pattern_ExportCompleted { + get { + return ResourceManager.GetString("pattern_ExportCompleted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to h1. CRM. Data export successfully completed + /// + ///Please, follow this link to download the archive: "${EntityTitle}":"${EntityRelativeURL}" + /// + ///^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. To change the notification type, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^. + /// + public static string pattern_ExportCompletedCustomMode { + get { + return ResourceManager.GetString("pattern_ExportCompletedCustomMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to h1. CRM. Data import successfully completed + /// + ///Go to the "Contacts":"$__VirtualRootPath/products/crm/" list. + /// + ///If you have any questions or need assistance please feel free to contact us at "support.onlyoffice.com":"http://support.onlyoffice.com" + /// + ///Best regards, + ///ONLYOFFICE™ Support Team + ///"www.onlyoffice.com":"http://onlyoffice.com/". + /// + public static string pattern_ImportCompleted { + get { + return ResourceManager.GetString("pattern_ImportCompleted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to h1. CRM. Data import successfully completed + /// + ///Go to the "${Tag_EntityListTitle}":"$__VirtualRootPath/${Tag_EntityListRelativeURL}" list. + /// + ///^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. To change the notification type, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^. + /// + public static string pattern_ImportCompletedCustomMode { + get { + return ResourceManager.GetString("pattern_ImportCompletedCustomMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to h1.You were appointed responsible for the opportunity: "$EntityTitle":"${__VirtualRootPath}/products/crm/deals.aspx?id=$EntityID" + /// + ///$__DateTime "$__AuthorName":"$__AuthorUrl" has appointed you responsible for the opportunity: $EntityTitle . + /// + ///#if($AdditionalData.get_item("OpportunityDescription")&&$AdditionalData.get_item("OpportunityDescription")!="") + /// + ///Opportunity description: + ///$AdditionalData.get_item("OpportunityDescription") + ///#end. + /// + public static string pattern_ResponsibleForOpportunity { + get { + return ResourceManager.GetString("pattern_ResponsibleForOpportunity", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to h1.Task assigned to you: $EntityTitle + /// + ///$__DateTime "$__AuthorName":"$__AuthorUrl" has appointed you responsible for the task: $EntityTitle. + ///#if($AdditionalData.get_item("TaskCategory")) + /// + ///Task category: $AdditionalData.get_item("TaskCategory") + ///#end + ///#if($AdditionalData.get_item("ContactRelativeUrl")) + /// + ///Link with contact: "$AdditionalData.get_item("ContactTitle")":"${__VirtualRootPath}/$AdditionalData.get_item("ContactRelativeUrl")" + ///#end + ///#if($AdditionalData.get_item("CaseRelativeUrl")) + /// + ///Link with [rest of string was truncated]";. + /// + public static string pattern_ResponsibleForTask { + get { + return ResourceManager.GetString("pattern_ResponsibleForTask", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to h1.Access granted to "$EntityTitle":"${__VirtualRootPath}/${EntityRelativeURL}" + /// + ///$__DateTime "$__AuthorName":"$__AuthorUrl" has granted you the access to "$EntityTitle":"${__VirtualRootPath}/${EntityRelativeURL}".. + /// + public static string pattern_SetAccess { + get { + return ResourceManager.GetString("pattern_SetAccess", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to h1. Reminder about the task: $EntityTitle + ///#if($AdditionalData.get_item("TaskCategory")) + /// + ///Task category: $AdditionalData.get_item("TaskCategory") + ///#end + ///#if($AdditionalData.get_item("ContactRelativeUrl")) + /// + ///Link with contact: "$AdditionalData.get_item("ContactTitle")":"${__VirtualRootPath}/$AdditionalData.get_item("ContactRelativeUrl")" + ///#end + ///#if($AdditionalData.get_item("CaseRelativeUrl")) + /// + ///Link with case: "$AdditionalData.get_item("CaseTitle")":"${__VirtualRootPath}/$AdditionalData.get_item("CaseRe [rest of string was truncated]";. + /// + public static string pattern_TaskReminder { + get { + return ResourceManager.GetString("pattern_TaskReminder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <patterns> + /// <formatter type="ASC.Notify.Patterns.NVelocityPatternFormatter, ASC.Common" /> + /// + /// <!--Export is completed--> + /// <pattern id="ExportCompleted" sender="email.sender"> + /// <subject resource="|subject_ExportCompleted|ASC.Web.CRM.Services.NotifyService.CRMPatternResource,ASC.Web.CRM" /> + /// <body styler="ASC.Notify.Textile.TextileStyler,ASC.Notify.Textile" resource="|pattern_ExportCompleted|ASC.Web.CRM.Services.NotifyService.CRMPatternResource,ASC.Web.CRM" /> + /// </pattern> + /// <pattern id="Expor [rest of string was truncated]";. + /// + public static string patterns { + get { + return ResourceManager.GetString("patterns", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CRM. New event added to $EntityTitle. + /// + public static string subject_AddRelationshipEvent { + get { + return ResourceManager.GetString("subject_AddRelationshipEvent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CRM. New contact created using 'Website Contact Form'. + /// + public static string subject_CreateNewContact { + get { + return ResourceManager.GetString("subject_CreateNewContact", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CRM. Data export successfully completed. + /// + public static string subject_ExportCompleted { + get { + return ResourceManager.GetString("subject_ExportCompleted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CRM. Data import successfully completed. + /// + public static string subject_ImportCompleted { + get { + return ResourceManager.GetString("subject_ImportCompleted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CRM. You were appointed as a responsible person for the opportunity: $EntityTitle. + /// + public static string subject_ResponsibleForOpportunity { + get { + return ResourceManager.GetString("subject_ResponsibleForOpportunity", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CRM. Task assigned to you: $EntityTitle. + /// + public static string subject_ResponsibleForTask { + get { + return ResourceManager.GetString("subject_ResponsibleForTask", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CRM. Access granted to $EntityTitle. + /// + public static string subject_SetAccess { + get { + return ResourceManager.GetString("subject_SetAccess", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CRM. Reminder about the task: $EntityTitle. + /// + public static string subject_TaskReminder { + get { + return ResourceManager.GetString("subject_TaskReminder", resourceCulture); + } + } + } +} diff --git a/products/ASC.CRM/Server/Services/NotifyService/CRMPatternResource.resx b/products/ASC.CRM/Server/Services/NotifyService/CRMPatternResource.resx new file mode 100644 index 00000000000..a2fc39aa22d --- /dev/null +++ b/products/ASC.CRM/Server/Services/NotifyService/CRMPatternResource.resx @@ -0,0 +1,306 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + h1. New event added to "$EntityTitle":"${__VirtualRootPath}/${EntityRelativeURL}" +$__DateTime "$__AuthorName":"$__AuthorUrl" has added a new event to "$EntityTitle":"${__VirtualRootPath}/${EntityRelativeURL}": + $AdditionalData.get_item("EventContent") + + #foreach($fileInfo in $AdditionalData.get_item("Files").Keys) + + #beforeall + + ---------------------------------------- + + #each + + "$fileInfo":"$AdditionalData.get_item("Files").get_item($fileInfo)" + + #end + + + h1. CRM. New contact created using the 'Website Contact Form' "$EntityTitle":"${__VirtualRootPath}/products/crm/default.aspx?ID=$EntityID" + +$__DateTime A new contact has been created using the 'Website Contact Form' "$EntityTitle":"${__VirtualRootPath}/products/crm/default.aspx?ID=$EntityID" + +Contact information: + +#foreach($contactInfo in $AdditionalData.Keys) +#each + +$contactInfo: $AdditionalData.get_item($contactInfo) + +#end + + + h1. CRM. Data export successfully completed + +Please, follow this link to download the archive: "exportdata.zip":"${EntityRelativeURL}" + +*Note*: this link is valid for 24 hours only. + +If you have any questions or need assistance please feel free to contact us at "support.onlyoffice.com":"http://support.onlyoffice.com" + +Best regards, +ONLYOFFICE™ Support Team +"www.onlyoffice.com":"http://onlyoffice.com/" + + + h1. CRM. Data import successfully completed + +Go to the "Contacts":"$__VirtualRootPath/products/crm/" list. + +If you have any questions or need assistance please feel free to contact us at "support.onlyoffice.com":"http://support.onlyoffice.com" + +Best regards, +ONLYOFFICE™ Support Team +"www.onlyoffice.com":"http://onlyoffice.com/" + + + h1.You were appointed responsible for the opportunity: "$EntityTitle":"${__VirtualRootPath}/products/crm/deals.aspx?id=$EntityID" + +$__DateTime "$__AuthorName":"$__AuthorUrl" has appointed you responsible for the opportunity: $EntityTitle . + +#if($AdditionalData.get_item("OpportunityDescription")&&$AdditionalData.get_item("OpportunityDescription")!="") + +Opportunity description: +$AdditionalData.get_item("OpportunityDescription") +#end + + + h1.Task assigned to you: $EntityTitle + +$__DateTime "$__AuthorName":"$__AuthorUrl" has appointed you responsible for the task: $EntityTitle. +#if($AdditionalData.get_item("TaskCategory")) + +Task category: $AdditionalData.get_item("TaskCategory") +#end +#if($AdditionalData.get_item("ContactRelativeUrl")) + +Link with contact: "$AdditionalData.get_item("ContactTitle")":"${__VirtualRootPath}/$AdditionalData.get_item("ContactRelativeUrl")" +#end +#if($AdditionalData.get_item("CaseRelativeUrl")) + +Link with case: "$AdditionalData.get_item("CaseTitle")":"${__VirtualRootPath}/$AdditionalData.get_item("CaseRelativeUrl")" +#end +#if($AdditionalData.get_item("DealRelativeUrl")) + +Link with opportunity: "$AdditionalData.get_item("DealTitle")":"${__VirtualRootPath}/$AdditionalData.get_item("DealRelativeUrl")" +#end +#if($AdditionalData.get_item("TaskDescription")&&$AdditionalData.get_item("TaskDescription")!="") + +Task description: +$AdditionalData.get_item("TaskDescription") +#end + +Due date: $AdditionalData.get_item("DueDate") + + +#foreach($fileInfo in $AdditionalData.get_item("Files").Keys) + +#beforeall + +---------------------------------------- + +#each + +"$fileInfo":"$AdditionalData.get_item("Files").get_item($fileInfo)" + +#end + +Go to the "Tasks":"${__VirtualRootPath}/products/crm/tasks.aspx" list. + + + h1.Access granted to "$EntityTitle":"${__VirtualRootPath}/${EntityRelativeURL}" + +$__DateTime "$__AuthorName":"$__AuthorUrl" has granted you the access to "$EntityTitle":"${__VirtualRootPath}/${EntityRelativeURL}". + + + + patterns.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + h1. Reminder about the task: $EntityTitle +#if($AdditionalData.get_item("TaskCategory")) + +Task category: $AdditionalData.get_item("TaskCategory") +#end +#if($AdditionalData.get_item("ContactRelativeUrl")) + +Link with contact: "$AdditionalData.get_item("ContactTitle")":"${__VirtualRootPath}/$AdditionalData.get_item("ContactRelativeUrl")" +#end +#if($AdditionalData.get_item("CaseRelativeUrl")) + +Link with case: "$AdditionalData.get_item("CaseTitle")":"${__VirtualRootPath}/$AdditionalData.get_item("CaseRelativeUrl")" +#end +#if($AdditionalData.get_item("DealRelativeUrl")) + +Link with opportunity: "$AdditionalData.get_item("DealTitle")":"${__VirtualRootPath}/$AdditionalData.get_item("DealRelativeUrl")" +#end +#if($AdditionalData.get_item("TaskDescription")&&$AdditionalData.get_item("TaskDescription")!="") + +Task description: +$AdditionalData.get_item("TaskDescription") +#end + +Due date: $AdditionalData.get_item("DueDate") + +Go to the "Tasks":"${__VirtualRootPath}/products/crm/tasks.aspx" list. + +^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal.^ + + + CRM. New event added to $EntityTitle + + + CRM. New contact created using 'Website Contact Form' + + + CRM. Data export successfully completed + + + CRM. Data import successfully completed + + + CRM. You were appointed as a responsible person for the opportunity: $EntityTitle + + + CRM. Task assigned to you: $EntityTitle + + + CRM. Access granted to $EntityTitle + + + CRM. Reminder about the task: $EntityTitle + + + h1. CRM. Data export successfully completed + +Please, follow this link to download the archive: "${EntityTitle}":"${EntityRelativeURL}" + +^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. To change the notification type, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^ + + + h1. CRM. Data import successfully completed + +Go to the "${Tag_EntityListTitle}":"$__VirtualRootPath/${Tag_EntityListRelativeURL}" list. + +^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. To change the notification type, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^ + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Services/NotifyService/NotifyClient.cs b/products/ASC.CRM/Server/Services/NotifyService/NotifyClient.cs new file mode 100644 index 00000000000..db879363054 --- /dev/null +++ b/products/ASC.CRM/Server/Services/NotifyService/NotifyClient.cs @@ -0,0 +1,544 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Web; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Billing; +using ASC.Core.Tenants; +using ASC.Core.Users; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; +using ASC.Notify; +using ASC.Notify.Patterns; +using ASC.Notify.Recipients; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace ASC.Web.CRM.Services.NotifyService +{ + [Scope] + public class NotifyClient + { + private IServiceProvider _serviceProvider; + + public NotifyClient(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public void SendAboutCreateNewContact(List recipientID, + int contactID, + string contactTitle, NameValueCollection fields) + { + if ((recipientID.Count == 0) || String.IsNullOrEmpty(contactTitle)) return; + + using var scope = _serviceProvider.CreateScope(); + var notifySource = scope.ServiceProvider.GetService(); + var client = WorkContext.NotifyContext.NotifyService.RegisterClient(notifySource, scope); + + client.SendNoticeToAsync( + NotifyConstants.Event_CreateNewContact, + null, + recipientID.ConvertAll(item => notifySource.GetRecipientsProvider().GetRecipient(item.ToString())).ToArray(), + true, + new TagValue(NotifyConstants.Tag_AdditionalData, fields), + new TagValue(NotifyConstants.Tag_EntityTitle, contactTitle), + new TagValue(NotifyConstants.Tag_EntityID, contactID) + ); + } + + public void SendAboutSetAccess(EntityType entityType, int entityID, DaoFactory daoFactory, params Guid[] userID) + { + if (userID.Length == 0) return; + + using var scope = _serviceProvider.CreateScope(); + var notifySource = scope.ServiceProvider.GetService(); + var client = WorkContext.NotifyContext.NotifyService.RegisterClient(notifySource, scope); + + var baseData = ExtractBaseDataFrom(entityType, entityID, daoFactory); + + client.SendNoticeToAsync( + NotifyConstants.Event_SetAccess, + null, + userID.Select(item => notifySource.GetRecipientsProvider().GetRecipient(item.ToString())).ToArray(), + true, + new TagValue(NotifyConstants.Tag_EntityID, baseData["id"]), + new TagValue(NotifyConstants.Tag_EntityTitle, baseData["title"]), + new TagValue(NotifyConstants.Tag_EntityRelativeURL, baseData["entityRelativeURL"]) + ); + } + + private NameValueCollection ExtractBaseDataFrom(EntityType entityType, int entityID, DaoFactory daoFactory) + { + using var scope = _serviceProvider.CreateScope(); + var pathProvider = scope.ServiceProvider.GetService(); + + var result = new NameValueCollection(); + + String title; + String relativeURL; + + switch (entityType) + { + case EntityType.Person: + case EntityType.Company: + case EntityType.Contact: + { + var contact = daoFactory.GetContactDao().GetByID(entityID); + title = contact != null ? contact.GetTitle() : string.Empty; + relativeURL = "default.aspx?id=" + entityID; + break; + } + case EntityType.Opportunity: + { + var deal = daoFactory.GetDealDao().GetByID(entityID); + title = deal != null ? deal.Title : string.Empty; + relativeURL = "deals.aspx?id=" + entityID; + break; + } + case EntityType.Case: + { + var cases = daoFactory.GetCasesDao().GetByID(entityID); + title = cases != null ? cases.Title : string.Empty; + relativeURL = "cases.aspx?id=" + entityID; + break; + } + + default: + throw new ArgumentException(); + } + + result.Add("title", title); + result.Add("id", entityID.ToString()); + result.Add("entityRelativeURL", String.Concat(pathProvider.BaseAbsolutePath, relativeURL)); + + return result; + } + + public void SendAboutAddRelationshipEventAdd(RelationshipEvent entity, Hashtable fileListInfoHashtable, DaoFactory daoFactory, params Guid[] userID) + { + if (userID.Length == 0) return; + + using var scope = _serviceProvider.CreateScope(); + var notifySource = scope.ServiceProvider.GetService(); + var pathProvider = scope.ServiceProvider.GetService(); + var securityContext = scope.ServiceProvider.GetService(); + + var client = WorkContext.NotifyContext.NotifyService.RegisterClient(notifySource, scope); + + NameValueCollection baseEntityData; + + if (entity.EntityID != 0) + { + baseEntityData = ExtractBaseDataFrom(entity.EntityType, entity.EntityID, daoFactory); + } + else + { + var contact = daoFactory.GetContactDao().GetByID(entity.ContactID); + + baseEntityData = new NameValueCollection(); + baseEntityData["title"] = contact.GetTitle(); + baseEntityData["id"] = contact.ID.ToString(); + baseEntityData["entityRelativeURL"] = "default.aspx?id=" + contact.ID; + + if (contact is Person) + baseEntityData["entityRelativeURL"] += "&type=people"; + + baseEntityData["entityRelativeURL"] = String.Concat(pathProvider.BaseAbsolutePath, + baseEntityData["entityRelativeURL"]); + } + + client.BeginSingleRecipientEvent("send about add relationship event add"); + + var interceptor = new InitiatorInterceptor(new DirectRecipient(securityContext.CurrentAccount.ID.ToString(), "")); + + client.AddInterceptor(interceptor); + + try + { + + client.SendNoticeToAsync( + NotifyConstants.Event_AddRelationshipEvent, + null, + userID.Select(item => notifySource.GetRecipientsProvider().GetRecipient(item.ToString())).ToArray(), + true, + new TagValue(NotifyConstants.Tag_EntityTitle, baseEntityData["title"]), + new TagValue(NotifyConstants.Tag_EntityID, baseEntityData["id"]), + new TagValue(NotifyConstants.Tag_EntityRelativeURL, baseEntityData["entityRelativeURL"]), + new TagValue(NotifyConstants.Tag_AdditionalData, + new Hashtable { + { "Files", fileListInfoHashtable }, + {"EventContent", entity.Content}})); + + } + finally + { + client.RemoveInterceptor(interceptor.Name); + client.EndSingleRecipientEvent("send about add relationship event add"); + } + + + } + + public void SendAboutExportCompleted(Guid recipientID, String fileName, String filePath) + { + if (recipientID == Guid.Empty) return; + + using var scope = _serviceProvider.CreateScope(); + var coreBaseSettings = scope.ServiceProvider.GetService(); + var notifySource = scope.ServiceProvider.GetService(); + var client = WorkContext.NotifyContext.NotifyService.RegisterClient(notifySource, scope); + + var recipient = notifySource.GetRecipientsProvider().GetRecipient(recipientID.ToString()); + + client.SendNoticeToAsync(coreBaseSettings.CustomMode ? NotifyConstants.Event_ExportCompletedCustomMode : NotifyConstants.Event_ExportCompleted, + null, + new[] { recipient }, + true, + new TagValue(NotifyConstants.Tag_EntityTitle, fileName), + new TagValue(NotifyConstants.Tag_EntityRelativeURL, filePath)); + + } + + public void SendAboutImportCompleted(Guid recipientID, EntityType entityType) + { + if (recipientID == Guid.Empty) return; + + using var scope = _serviceProvider.CreateScope(); + var notifySource = scope.ServiceProvider.GetService(); + var coreBaseSettings = scope.ServiceProvider.GetService(); + var client = WorkContext.NotifyContext.NotifyService.RegisterClient(notifySource, scope); + + var recipient = notifySource.GetRecipientsProvider().GetRecipient(recipientID.ToString()); + + var entitiyListTitle = ""; + var entitiyListRelativeURL = ""; + + switch (entityType) + { + case EntityType.Contact: + entitiyListTitle = CRMContactResource.Contacts; + entitiyListRelativeURL = "products/crm/"; + break; + case EntityType.Opportunity: + entitiyListTitle = CRMCommonResource.DealModuleName; + entitiyListRelativeURL = "products/crm/deals.aspx"; + break; + case EntityType.Case: + entitiyListTitle = CRMCommonResource.CasesModuleName; + entitiyListRelativeURL = "products/crm/cases.aspx"; + break; + case EntityType.Task: + entitiyListTitle = CRMCommonResource.TaskModuleName; + entitiyListRelativeURL = "products/crm/tasks.aspx"; + break; + default: + throw new ArgumentException(CRMErrorsResource.EntityTypeUnknown); + } + + client.SendNoticeToAsync( + coreBaseSettings.CustomMode ? NotifyConstants.Event_ImportCompletedCustomMode : NotifyConstants.Event_ImportCompleted, + null, + new[] { recipient }, + true, + new TagValue(NotifyConstants.Tag_EntityListRelativeURL, entitiyListRelativeURL), + new TagValue(NotifyConstants.Tag_EntityListTitle, entitiyListTitle)); + } + + public void SendAutoReminderAboutTask(DateTime scheduleDate) + { + using var scope = _serviceProvider.CreateScope(); + + var defaultDao = scope.ServiceProvider.GetService(); + var tenantManager = scope.ServiceProvider.GetService(); + var userManager = scope.ServiceProvider.GetService(); + var securityContext = scope.ServiceProvider.GetService(); + var paymentManager = scope.ServiceProvider.GetService(); + var logger = scope.ServiceProvider.GetService>().Get("ASC.CRM"); + var coreSettings = scope.ServiceProvider.GetService(); + + var execAlert = new List(); + + foreach (var row in defaultDao.GetTaskDao() + .GetInfoForReminder(scheduleDate)) + { + + var tenantId = Convert.ToInt32(row[0]); + var taskId = Convert.ToInt32(row[1]); + var deadline = Convert.ToDateTime(row[2]); + var alertValue = Convert.ToInt32(row[3]); + var responsibleID = !string.IsNullOrEmpty(Convert.ToString(row[4])) + ? new Guid(Convert.ToString(row[4])) + : Guid.Empty; + + var deadlineReminderDate = deadline.AddMinutes(-alertValue); + + if (deadlineReminderDate.Subtract(scheduleDate).Minutes > 1) continue; + + execAlert.Add(taskId); + + var tenant = tenantManager.GetTenant(tenantId); + if (tenant == null || + tenant.Status != TenantStatus.Active || + TariffState.NotPaid <= paymentManager.GetTariff(tenant.TenantId).State) + { + continue; + } + + try + { + tenantManager.SetCurrentTenant(tenant); + securityContext.AuthenticateMe(ASC.Core.Configuration.Constants.CoreSystem); + + var user = userManager.GetUsers(responsibleID); + + if (!(!Constants.LostUser.Equals(user) && user.Status == EmployeeStatus.Active)) continue; + + securityContext.AuthenticateMe(user.ID); + + Thread.CurrentThread.CurrentCulture = user.GetCulture(); + Thread.CurrentThread.CurrentUICulture = user.GetCulture(); + + var dao = defaultDao; + var task = dao.GetTaskDao().GetByID(taskId); + + if (task == null) continue; + + ASC.CRM.Core.Entities.Contact taskContact = null; + ASC.CRM.Core.Entities.Cases taskCase = null; + ASC.CRM.Core.Entities.Deal taskDeal = null; + + if (task.ContactID > 0) + { + taskContact = dao.GetContactDao().GetByID(task.ContactID); + } + + if (task.EntityID > 0) + { + switch (task.EntityType) + { + case EntityType.Case: + taskCase = dao.GetCasesDao().GetByID(task.EntityID); + break; + case EntityType.Opportunity: + taskDeal = dao.GetDealDao().GetByID(task.EntityID); + break; + } + } + + var listItem = dao.GetListItemDao().GetByID(task.CategoryID); + + SendTaskReminder(task, + listItem != null ? listItem.Title : string.Empty, + taskContact, taskCase, taskDeal); + } + catch (Exception ex) + { + logger.Error("SendAutoReminderAboutTask, tenant: " + tenant.GetTenantDomain(coreSettings), ex); + } + } + + defaultDao.GetTaskDao().ExecAlert(execAlert); + + } + + public void SendTaskReminder(Task task, String taskCategoryTitle, Contact taskContact, ASC.CRM.Core.Entities.Cases taskCase, ASC.CRM.Core.Entities.Deal taskDeal) + { + using var scope = _serviceProvider.CreateScope(); + var notifySource = scope.ServiceProvider.GetService(); + var client = WorkContext.NotifyContext.NotifyService.RegisterClient(notifySource, scope); + + var recipient = notifySource.GetRecipientsProvider().GetRecipient(task.ResponsibleID.ToString()); + + if (recipient == null) return; + + + var deadLineString = task.DeadLine.Hour == 0 && task.DeadLine.Minute == 0 + ? task.DeadLine.ToShortDateString() + : task.DeadLine.ToString(CultureInfo.InvariantCulture); + + string taskContactRelativeUrl = null; + string taskContactTitle = null; + + string taskCaseRelativeUrl = null; + string taskCaseTitle = null; + + string taskDealRelativeUrl = null; + string taskDealTitle = null; + + if (taskContact != null) + { + taskContactRelativeUrl = String.Format("products/crm/default.aspx?id={0}{1}", taskContact.ID, taskContact is Person ? "&type=people" : ""); + taskContactTitle = taskContact.GetTitle(); + } + + if (taskCase != null) + { + taskCaseRelativeUrl = String.Format("products/crm/cases.aspx?id={0}", taskCase.ID); + taskCaseTitle = taskCase.Title.HtmlEncode(); + } + + if (taskDeal != null) + { + taskDealRelativeUrl = String.Format("products/crm/deals.aspx?id={0}", taskDeal.ID); + taskDealTitle = taskDeal.Title.HtmlEncode(); + } + + + client.SendNoticeToAsync( + NotifyConstants.Event_TaskReminder, + null, + new[] { recipient }, + true, + new TagValue(NotifyConstants.Tag_EntityTitle, task.Title), + new TagValue(NotifyConstants.Tag_AdditionalData, + new Hashtable { + { "TaskDescription", HttpUtility.HtmlEncode(task.Description) }, + { "TaskCategory", taskCategoryTitle }, + + { "ContactRelativeUrl", taskContactRelativeUrl }, + { "ContactTitle", taskContactTitle }, + + { "CaseRelativeUrl", taskCaseRelativeUrl }, + { "CaseTitle", taskCaseTitle }, + + { "DealRelativeUrl", taskDealRelativeUrl }, + { "DealTitle", taskDealTitle }, + + { "DueDate", deadLineString } + }) + ); + } + + public void SendAboutResponsibleByTask(Task task, String taskCategoryTitle, Contact taskContact, Cases taskCase, ASC.CRM.Core.Entities.Deal taskDeal, Hashtable fileListInfoHashtable) + { + using var scope = _serviceProvider.CreateScope(); + var notifySource = scope.ServiceProvider.GetService(); + var client = WorkContext.NotifyContext.NotifyService.RegisterClient(notifySource, scope); + var tenantUtil = scope.ServiceProvider.GetService(); + + var recipient = notifySource.GetRecipientsProvider().GetRecipient(task.ResponsibleID.ToString()); + + if (recipient == null) return; + + task.DeadLine = tenantUtil.DateTimeFromUtc(task.DeadLine); + + var deadLineString = task.DeadLine.Hour == 0 && task.DeadLine.Minute == 0 + ? task.DeadLine.ToShortDateString() + : task.DeadLine.ToString(); + + + string taskContactRelativeUrl = null; + string taskContactTitle = null; + + string taskCaseRelativeUrl = null; + string taskCaseTitle = null; + + string taskDealRelativeUrl = null; + string taskDealTitle = null; + + if (taskContact != null) + { + taskContactRelativeUrl = String.Format("products/crm/default.aspx?id={0}{1}", taskContact.ID, taskContact is Person ? "&type=people" : ""); + taskContactTitle = taskContact.GetTitle(); + } + + if (taskCase != null) + { + taskCaseRelativeUrl = String.Format("products/crm/cases.aspx?id={0}", taskCase.ID); + taskCaseTitle = taskCase.Title.HtmlEncode(); + } + + if (taskDeal != null) + { + taskDealRelativeUrl = String.Format("products/crm/deals.aspx?id={0}", taskDeal.ID); + taskDealTitle = taskDeal.Title.HtmlEncode(); + } + + client.SendNoticeToAsync( + NotifyConstants.Event_ResponsibleForTask, + null, + new[] { recipient }, + true, + new TagValue(NotifyConstants.Tag_EntityTitle, task.Title), + new TagValue(NotifyConstants.Tag_AdditionalData, + new Hashtable { + { "TaskDescription", HttpUtility.HtmlEncode(task.Description) }, + { "Files", fileListInfoHashtable }, + { "TaskCategory", taskCategoryTitle }, + + { "ContactRelativeUrl", taskContactRelativeUrl }, + { "ContactTitle", taskContactTitle }, + + { "CaseRelativeUrl", taskCaseRelativeUrl }, + { "CaseTitle", taskCaseTitle }, + + { "DealRelativeUrl", taskDealRelativeUrl }, + { "DealTitle", taskDealTitle }, + + { "DueDate", deadLineString } + }) + ); + } + + public void SendAboutResponsibleForOpportunity(Deal deal) + { + using var scope = _serviceProvider.CreateScope(); + var notifySource = scope.ServiceProvider.GetService(); + var securityContext = scope.ServiceProvider.GetService(); + var client = WorkContext.NotifyContext.NotifyService.RegisterClient(notifySource, scope); + + var recipient = notifySource.GetRecipientsProvider().GetRecipient(deal.ResponsibleID.ToString()); + + if (recipient == null) return; + + client.SendNoticeToAsync( + NotifyConstants.Event_ResponsibleForOpportunity, + null, + new[] { recipient }, + true, + new TagValue(NotifyConstants.Tag_EntityTitle, deal.Title), + new TagValue(NotifyConstants.Tag_EntityID, deal.ID), + new TagValue(NotifyConstants.Tag_AdditionalData, + new Hashtable { + { "OpportunityDescription", HttpUtility.HtmlEncode(deal.Description) } + }) + ); + } + } +} diff --git a/products/ASC.CRM/Server/Services/NotifyService/NotifyConstants.cs b/products/ASC.CRM/Server/Services/NotifyService/NotifyConstants.cs new file mode 100644 index 00000000000..e4f0d8e696f --- /dev/null +++ b/products/ASC.CRM/Server/Services/NotifyService/NotifyConstants.cs @@ -0,0 +1,71 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using ASC.Notify.Model; + +#endregion + +namespace ASC.Web.CRM.Services.NotifyService +{ + public static class NotifyConstants + { + + public static readonly INotifyAction Event_SetAccess = new NotifyAction("SetAccess", "set access for users"); + + public static readonly INotifyAction Event_ResponsibleForTask = new NotifyAction("ResponsibleForTask", "responsible for task"); + + public static readonly INotifyAction Event_TaskReminder = new NotifyAction("TaskReminder", "auto reminder about task"); + + public static readonly INotifyAction Event_ResponsibleForOpportunity = new NotifyAction("ResponsibleForOpportunity", "responsible for opportunity"); + + public static readonly INotifyAction Event_AddRelationshipEvent = new NotifyAction("AddRelationshipEvent", "add relationship event"); + + public static readonly INotifyAction Event_ExportCompleted = new NotifyAction("ExportCompleted", "export is completed"); + + public static readonly INotifyAction Event_ExportCompletedCustomMode = new NotifyAction("ExportCompletedCustomMode", "export is completed"); + + public static readonly INotifyAction Event_ImportCompleted = new NotifyAction("ImportCompleted", "import is completed"); + + public static readonly INotifyAction Event_ImportCompletedCustomMode = new NotifyAction("ImportCompletedCustomMode", "import is completed"); + + public static readonly INotifyAction Event_CreateNewContact = new NotifyAction("CreateNewContact", "create new contact"); + + public static readonly string Tag_AdditionalData = "AdditionalData"; + + public static readonly string Tag_EntityID = "EntityID"; + + public static readonly string Tag_EntityTitle = "EntityTitle"; + + public static readonly string Tag_EntityRelativeURL = "EntityRelativeURL"; + + public static readonly string Tag_EntityListRelativeURL = "EntityListRelativeURL"; + + public static readonly string Tag_EntityListTitle = "EntityListTitle"; + + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Services/NotifyService/NotifySource.cs b/products/ASC.CRM/Server/Services/NotifyService/NotifySource.cs new file mode 100644 index 00000000000..74d42301789 --- /dev/null +++ b/products/ASC.CRM/Server/Services/NotifyService/NotifySource.cs @@ -0,0 +1,68 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + +using System; + +using ASC.Core; +using ASC.CRM.Services.NotifyService; +using ASC.Notify.Model; +using ASC.Notify.Patterns; +using ASC.Notify.Recipients; + +using NotifySourceBase = ASC.Core.Notify.NotifySource; + +namespace ASC.Web.CRM.Services.NotifyService +{ + + public class NotifySource : NotifySourceBase + { + + public NotifySource(UserManager userManager, IRecipientProvider recipientsProvider, SubscriptionManager subscriptionManager) + : base(new Guid("{13FF36FB-0272-4887-B416-74F52B0D0B02}"), userManager, recipientsProvider, subscriptionManager) + { + + } + + protected override IActionProvider CreateActionProvider() + { + return new ConstActionProvider( + NotifyConstants.Event_ResponsibleForTask, + NotifyConstants.Event_ResponsibleForOpportunity, + NotifyConstants.Event_AddRelationshipEvent, + NotifyConstants.Event_TaskReminder, + NotifyConstants.Event_SetAccess, + NotifyConstants.Event_ExportCompleted, + NotifyConstants.Event_ExportCompletedCustomMode, + NotifyConstants.Event_ImportCompleted, + NotifyConstants.Event_ImportCompletedCustomMode, + NotifyConstants.Event_CreateNewContact); + } + + protected override IPatternProvider CreatePatternsProvider() + { + return new XmlPatternProvider2(CRMPatternResource.patterns); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Services/NotifyService/patterns.xml b/products/ASC.CRM/Server/Services/NotifyService/patterns.xml new file mode 100644 index 00000000000..7b1bc1025c9 --- /dev/null +++ b/products/ASC.CRM/Server/Services/NotifyService/patterns.xml @@ -0,0 +1,148 @@ + + + + + + + + + + + + ${EntityRelativeURL} + + + + + + + + + + ${EntityRelativeURL} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #if($AdditionalData.get_item("TaskDescription")&&$AdditionalData.get_item("TaskDescription")!="") + + $AdditionalData.get_item("TaskDescription") + #end + + ${__VirtualRootPath}/products/crm/tasks.aspx + + + + + + + + + + + + #if($AdditionalData.get_item("TaskDescription")&&$AdditionalData.get_item("TaskDescription")!="") + + $AdditionalData.get_item("TaskDescription") + #end + + ${__VirtualRootPath}/products/crm/tasks.aspx + + + + + + + + + + + + + #if($AdditionalData.get_item("OpportunityDescription")&&$AdditionalData.get_item("OpportunityDescription")!="") + + $AdditionalData.get_item("OpportunityDescription") + #end + + ${__VirtualRootPath}/products/crm/deals.aspx?id=$EntityID + + + + + + + + + + + + + $AdditionalData.get_item("EventContent") + + #foreach($fileInfo in $AdditionalData.get_item("Files").Keys) + + #beforeall + + ---------------------------------------- + + #each + + "$fileInfo":"$AdditionalData.get_item("Files").get_item($fileInfo)" + + #end + + ${__VirtualRootPath}/${EntityRelativeURL} + + + + + + + + + + + + ${__VirtualRootPath}/${EntityRelativeURL} + + + + + + + + + + + + ${__VirtualRootPath}/products/crm/default.aspx?id=$EntityID + + + + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Startup.cs b/products/ASC.CRM/Server/Startup.cs index f2cb3fac1cc..e38fad6353a 100644 --- a/products/ASC.CRM/Server/Startup.cs +++ b/products/ASC.CRM/Server/Startup.cs @@ -1,42 +1,145 @@ - -using System.Text; - -using ASC.Api.Core; - -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace ASC.CRM -{ - public class Startup : BaseStartup - { - public override string[] LogParams { get => new string[] { "ASC.CRM" }; } - - public Startup(IConfiguration configuration, IHostEnvironment hostEnvironment) - : base(configuration, hostEnvironment) - { - - } - - public override void ConfigureServices(IServiceCollection services) - { - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - - base.ConfigureServices(services); - } - - public override void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - app.UseCors(builder => - builder - .AllowAnyOrigin() - .AllowAnyHeader() - .AllowAnyMethod()); - - base.Configure(app, env); - } - } -} +using System.Collections.Generic; +using System.Text; +using System.Text.Json.Serialization; + +using ASC.Api.Core; +using ASC.Common; +using ASC.CRM.Api; +using ASC.CRM.ApiModels; +using ASC.CRM.HttpHandlers; +using ASC.CRM.Mapping; +using ASC.Web.CRM.Core.Search; +using ASC.Web.CRM.HttpHandlers; + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace ASC.CRM +{ + + public class Startup : BaseStartup + { + public Startup(IConfiguration configuration, IHostEnvironment hostEnvironment) + : base(configuration, hostEnvironment) + { + } + + public override void ConfigureServices(IServiceCollection services) + { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + + base.ConfigureServices(services); + + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + DIHelper.TryAdd(); + } + + public override void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + base.Configure(app, env); + + app.UseMiddleware(); + + app.MapWhen( + context => context.Request.Path.ToString().EndsWith("httphandlers/contactphotohandler.ashx"), + appBranch => + { + appBranch.UseContactPhotoHandler(); + }); + + app.MapWhen( + context => context.Request.Path.ToString().EndsWith("httphandlers/filehandler.ashx"), + appBranch => + { + appBranch.UseFileHandler(); + }); + + app.MapWhen( + context => context.Request.Path.ToString().EndsWith("httphandlers/fileuploaderhandler.ashx"), + appBranch => + { + appBranch.UseFileUploaderHandler(); + }); + + app.MapWhen( + context => context.Request.Path.ToString().EndsWith("httphandlers/importfilehandler.ashx"), + appBranch => + { + appBranch.UseImportFileHandlerHandler(); + }); + + app.MapWhen( + context => context.Request.Path.ToString().EndsWith("httphandlers/organisationlogohandler.ashx"), + appBranch => + { + appBranch.UseOrganisationLogoHandler(); + }); + + + app.MapWhen( + context => context.Request.Path.ToString().EndsWith("httphandlers/webtoleadfromhandler.ashx"), + appBranch => + { + appBranch.UseWebToLeadFromHandlerHandler(); + }); + + } + + public override JsonConverter[] Converters + { + get + { + var jsonConverters = new List + { + new ContactDtoJsonConverter() + }; + + return jsonConverters.ToArray(); + } + } + + } +} + + diff --git a/products/ASC.CRM/Server/Templates/CasesTemplates.html b/products/ASC.CRM/Server/Templates/CasesTemplates.html new file mode 100644 index 00000000000..7cb8e4efb41 --- /dev/null +++ b/products/ASC.CRM/Server/Templates/CasesTemplates.html @@ -0,0 +1,129 @@ + + + + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Templates/CommonCustomFieldsTemplates.html b/products/ASC.CRM/Server/Templates/CommonCustomFieldsTemplates.html new file mode 100644 index 00000000000..288d8d7b7aa --- /dev/null +++ b/products/ASC.CRM/Server/Templates/CommonCustomFieldsTemplates.html @@ -0,0 +1,144 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Templates/CommonTemplates.html b/products/ASC.CRM/Server/Templates/CommonTemplates.html new file mode 100644 index 00000000000..a1c94124bc4 --- /dev/null +++ b/products/ASC.CRM/Server/Templates/CommonTemplates.html @@ -0,0 +1,389 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Templates/ContactInfoCardTemplate.html b/products/ASC.CRM/Server/Templates/ContactInfoCardTemplate.html new file mode 100644 index 00000000000..94f44f28064 --- /dev/null +++ b/products/ASC.CRM/Server/Templates/ContactInfoCardTemplate.html @@ -0,0 +1,87 @@ + + + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Templates/ContactSelectorTemplates.html b/products/ASC.CRM/Server/Templates/ContactSelectorTemplates.html new file mode 100644 index 00000000000..0608eea6e22 --- /dev/null +++ b/products/ASC.CRM/Server/Templates/ContactSelectorTemplates.html @@ -0,0 +1,161 @@ + + + + + + + + + + + diff --git a/products/ASC.CRM/Server/Templates/ContactsTemplates.html b/products/ASC.CRM/Server/Templates/ContactsTemplates.html new file mode 100644 index 00000000000..89f979a995a --- /dev/null +++ b/products/ASC.CRM/Server/Templates/ContactsTemplates.html @@ -0,0 +1,366 @@ + + + + + + + + + + +F + + + + + + + + + + + + + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Templates/DealsSelectorTemplates.html b/products/ASC.CRM/Server/Templates/DealsSelectorTemplates.html new file mode 100644 index 00000000000..63964f8ba46 --- /dev/null +++ b/products/ASC.CRM/Server/Templates/DealsSelectorTemplates.html @@ -0,0 +1,26 @@ + diff --git a/products/ASC.CRM/Server/Templates/DealsTemplates.html b/products/ASC.CRM/Server/Templates/DealsTemplates.html new file mode 100644 index 00000000000..67f44948975 --- /dev/null +++ b/products/ASC.CRM/Server/Templates/DealsTemplates.html @@ -0,0 +1,366 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Templates/InvoicesTemplates.html b/products/ASC.CRM/Server/Templates/InvoicesTemplates.html new file mode 100644 index 00000000000..61d942f169f --- /dev/null +++ b/products/ASC.CRM/Server/Templates/InvoicesTemplates.html @@ -0,0 +1,624 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Templates/SettingsTemplates.html b/products/ASC.CRM/Server/Templates/SettingsTemplates.html new file mode 100644 index 00000000000..835e01fe11f --- /dev/null +++ b/products/ASC.CRM/Server/Templates/SettingsTemplates.html @@ -0,0 +1,806 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Templates/SimpleContactListTemplate.html b/products/ASC.CRM/Server/Templates/SimpleContactListTemplate.html new file mode 100644 index 00000000000..08ef3719a42 --- /dev/null +++ b/products/ASC.CRM/Server/Templates/SimpleContactListTemplate.html @@ -0,0 +1,73 @@ + + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Templates/TasksTemplates.html b/products/ASC.CRM/Server/Templates/TasksTemplates.html new file mode 100644 index 00000000000..e9020fdc503 --- /dev/null +++ b/products/ASC.CRM/Server/Templates/TasksTemplates.html @@ -0,0 +1,283 @@ + + + + + + + + + \ No newline at end of file diff --git a/products/ASC.CRM/Server/Templates/VoipTemplates.html b/products/ASC.CRM/Server/Templates/VoipTemplates.html new file mode 100644 index 00000000000..b54ec35b53f --- /dev/null +++ b/products/ASC.CRM/Server/Templates/VoipTemplates.html @@ -0,0 +1,364 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/products/ASC.CRM/Server/Utils/CurrencyProvider.cs b/products/ASC.CRM/Server/Utils/CurrencyProvider.cs new file mode 100644 index 00000000000..0807d1eec3e --- /dev/null +++ b/products/ASC.CRM/Server/Utils/CurrencyProvider.cs @@ -0,0 +1,357 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Text.RegularExpressions; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.Core.Common.Settings; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; + +using Autofac; + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; + +namespace ASC.Web.CRM.Classes +{ + [Scope] + public class CurrencyProvider + { + private readonly ILog _log; + private readonly object _syncRoot = new object(); + private readonly Dictionary _currencies; + private Dictionary _exchangeRates; + private DateTime _publisherDate; + private const String _formatDate = "yyyy-MM-ddTHH:mm:ss.fffffffK"; + + public CurrencyProvider(IOptionsMonitor logger, + IConfiguration configuration, + SettingsManager settingsManager, + DaoFactory daoFactory) + { + _log = logger.Get("ASC"); + Configuration = configuration; + SettingsManager = settingsManager; + + var daocur = daoFactory.GetCurrencyInfoDao(); + var currencies = daocur.GetAll(); + + if (currencies == null || currencies.Count == 0) + { + currencies = new List + { + new CurrencyInfo("Currency_UnitedStatesDollar", "USD", "$", "US", true, true) + }; + } + + _currencies = currencies.ToDictionary(c => c.Abbreviation); + + } + + public IConfiguration Configuration { get; } + public SettingsManager SettingsManager { get; } + + public DateTime GetPublisherDate + { + get + { + TryToReadPublisherDate(GetExchangesTempPath()); + return _publisherDate; + } + } + + public CurrencyInfo Get(string currencyAbbreviation) + { + if (!_currencies.ContainsKey(currencyAbbreviation)) + return null; + + return _currencies[currencyAbbreviation]; + } + + public List GetAll() + { + return _currencies.Values.OrderBy(v => v.Abbreviation).ToList(); + } + + public List GetBasic() + { + return _currencies.Values.Where(c => c.IsBasic).OrderBy(v => v.Abbreviation).ToList(); + } + + public List GetOther() + { + return _currencies.Values.Where(c => !c.IsBasic).OrderBy(v => v.Abbreviation).ToList(); + } + + public Dictionary MoneyConvert(CurrencyInfo baseCurrency) + { + if (baseCurrency == null) throw new ArgumentNullException("baseCurrency"); + if (!_currencies.ContainsKey(baseCurrency.Abbreviation)) throw new ArgumentOutOfRangeException("baseCurrency", "Not found."); + + var result = new Dictionary(); + var rates = GetExchangeRates(); + foreach (var ci in GetAll()) + { + + if (baseCurrency.Title == ci.Title) + { + result.Add(ci, 1); + + continue; + } + + var key = String.Format("{1}/{0}", baseCurrency.Abbreviation, ci.Abbreviation); + + if (!rates.ContainsKey(key)) + continue; + + result.Add(ci, rates[key]); + } + return result; + } + + + public bool IsConvertable(String abbreviation) + { + var findedItem = _currencies.Keys.ToList().Find(item => String.Compare(abbreviation, item) == 0); + + if (findedItem == null) + throw new ArgumentException(abbreviation); + + return _currencies[findedItem].IsConvertable; + } + + public Decimal MoneyConvert(decimal amount, string from, string to) + { + if (string.IsNullOrEmpty(from) || string.IsNullOrEmpty(to) || string.Compare(from, to, true) == 0) return amount; + + var rates = GetExchangeRates(); + + if (from.Contains('-')) from = new RegionInfo(from).ISOCurrencySymbol; + if (to.Contains('-')) to = new RegionInfo(to).ISOCurrencySymbol; + var key = string.Format("{0}/{1}", to, from); + + return Math.Round(rates[key] * amount, 4, MidpointRounding.AwayFromZero); + } + + public decimal MoneyConvertToDefaultCurrency(decimal amount, string from) + { + + var crmSettings = SettingsManager.Load(); + var defaultCurrency = Get(crmSettings.DefaultCurrency); + + return MoneyConvert(amount, from, defaultCurrency.Abbreviation); + } + + private bool ObsoleteData() + { + return _exchangeRates == null || (DateTime.UtcNow.Date.Subtract(_publisherDate.Date).Days > 0); + } + + private string GetExchangesTempPath() + { + return Path.Combine(Path.GetTempPath(), Path.Combine("onlyoffice", "exchanges")); + } + + private readonly Regex CurRateRegex = new Regex("[a-zA-Z]{3})\">(?[\\d\\.]+)"); + + private Dictionary GetExchangeRates() + { + if (ObsoleteData()) + { + lock (_syncRoot) + { + if (ObsoleteData()) + { + try + { + _exchangeRates = new Dictionary(); + + var tmppath = GetExchangesTempPath(); + + TryToReadPublisherDate(tmppath); + + + + var updateEnable = Configuration["crm:update:currency:info:enable"] != "false"; + var ratesUpdatedFlag = false; + + foreach (var ci in _currencies.Values.Where(c => c.IsConvertable)) + { + var filepath = Path.Combine(tmppath, ci.Abbreviation + ".html"); + + if (updateEnable && 0 < (DateTime.UtcNow.Date - _publisherDate.Date).TotalDays || !File.Exists(filepath)) + { + var filepath_temp = Path.Combine(tmppath, ci.Abbreviation + "_temp.html"); + + DownloadCurrencyPage(ci.Abbreviation, filepath_temp); + + if (File.Exists(filepath_temp)) + { + if (TryGetRatesFromFile(filepath_temp, ci)) + { + ratesUpdatedFlag = true; + File.Copy(filepath_temp, filepath, true); + } + File.Delete(filepath_temp); + continue; + } + } + + if (File.Exists(filepath) && TryGetRatesFromFile(filepath, ci)) + { + ratesUpdatedFlag = true; + } + } + + if (ratesUpdatedFlag) + { + _publisherDate = DateTime.UtcNow; + WritePublisherDate(tmppath); + } + + } + catch (Exception error) + { + _log.Error(error); + _publisherDate = DateTime.UtcNow; + } + } + } + } + + return _exchangeRates; + } + + private bool TryGetRatesFromFile(string filepath, CurrencyInfo curCI) + { + var success = false; + var currencyLines = File.ReadAllLines(filepath); + for (var i = 0; i < currencyLines.Length; i++) + { + var line = currencyLines[i]; + + if (line.Contains("id=\"major-currency-table\"") || line.Contains("id=\"minor-currency-table\"") || line.Contains("id=\"exotic-currency-table\"")) + { + var currencyInfos = CurRateRegex.Matches(line); + + if (currencyInfos.Count > 0) + { + foreach (var curInfo in currencyInfos) + { + _exchangeRates.Add( + String.Format("{0}/{1}", (curInfo as Match).Groups["Currency"].Value.Trim(), curCI.Abbreviation), + Convert.ToDecimal((curInfo as Match).Groups["Rate"].Value.Trim(), CultureInfo.InvariantCulture.NumberFormat)); + + success = true; + } + } + } + } + + return success; + } + + + private void TryToReadPublisherDate(string tmppath) + { + if (_publisherDate == default(DateTime)) + { + try + { + var timefile = Path.Combine(tmppath, "last.time"); + if (File.Exists(timefile)) + { + var dateFromFile = File.ReadAllText(timefile); + _publisherDate = DateTime.ParseExact(dateFromFile, _formatDate, null); + } + } + catch (Exception err) + { + _log.Error(err); + } + } + } + + private void WritePublisherDate(string tmppath) + { + try + { + var timefile = Path.Combine(tmppath, "last.time"); + File.WriteAllText(timefile, _publisherDate.ToString(_formatDate)); + } + catch (Exception err) + { + _log.Error(err); + } + } + + private void DownloadCurrencyPage(string currency, string filepath) + { + + try + { + var dir = Path.GetDirectoryName(filepath); + + if (!Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } + + var destinationURI = new Uri(String.Format("https://themoneyconverter.com/{0}/{0}.aspx", currency)); + + var request = (HttpWebRequest)WebRequest.Create(destinationURI); + request.Method = "GET"; + request.AllowAutoRedirect = true; + request.MaximumAutomaticRedirections = 2; + request.UserAgent = "Mozilla/5.0 (Windows NT 6.1; rv:8.0) Gecko/20100101 Firefox/8.0"; + request.UseDefaultCredentials = true; + + using (var response = (HttpWebResponse)request.GetResponse()) + using (var responseStream = new StreamReader(response.GetResponseStream())) + { + var data = responseStream.ReadToEnd(); + + File.WriteAllText(filepath, data); + + } + } + catch (Exception error) + { + _log.Error(error); + } + } + + } +} diff --git a/products/ASC.CRM/Server/Utils/ExportToCSV.cs b/products/ASC.CRM/Server/Utils/ExportToCSV.cs new file mode 100644 index 00000000000..dacb709e89b --- /dev/null +++ b/products/ASC.CRM/Server/Utils/ExportToCSV.cs @@ -0,0 +1,1308 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Data; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Json; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.Common.Security.Authentication; +using ASC.Common.Threading; +using ASC.Common.Threading.Progress; +using ASC.Core; +using ASC.Core.Users; +using ASC.CRM.Classes; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; +using ASC.Data.Storage; +using ASC.Files.Core; +using ASC.Web.Core.Files; +using ASC.Web.Core.Users; +using ASC.Web.CRM.Services.NotifyService; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Utils; +using ASC.Web.Studio.Utility; + +using ICSharpCode.SharpZipLib.Zip; + +using Microsoft.Extensions.Options; + +namespace ASC.Web.CRM.Classes +{ + [Transient] + public class ExportDataOperation : DistributedTaskProgress, IProgressItem + { + + private readonly DisplayUserSettingsHelper _displayUserSettingsHelper; + private readonly FilesLinkUtility _filesLinkUtility; + private readonly FileMarker _fileMarker; + private readonly IDaoFactory _fileDaoFactory; + private readonly GlobalFolder _globalFolder; + private readonly FileUploader _fileUploader; + private readonly TenantManager _tenantManager; + private readonly CommonLinkUtility _commonLinkUtility; + private readonly SecurityContext _securityContext; + private readonly DaoFactory _daoFactory; + private readonly UserManager _userManager; + private readonly FileUtility _fileUtility; + private readonly int _tenantId; + private readonly IAccount _author; + private readonly IDataStore _dataStore; + private readonly NotifyClient _notifyClient; + private readonly TempStream _tempStream; + private FilterObject _filterObject; + private readonly ILog _log; + private int _totalCount; + + public ExportDataOperation(UserManager userManager, + FileUtility fileUtility, + SecurityContext securityContext, + IOptionsMonitor logger, + TenantManager tenantManager, + Global global, + CommonLinkUtility commonLinkUtility, + NotifyClient notifyClient, + DaoFactory daoFactory, + FileUploader fileUploader, + GlobalFolder globalFolder, + FileMarker fileMarker, + IDaoFactory fileDaoFactory, + FilesLinkUtility filesLinkUtility, + DisplayUserSettingsHelper displayUserSettingsHelper, + TempStream tempStream) + { + _daoFactory = daoFactory; + _userManager = userManager; + _fileUtility = fileUtility; + _fileUploader = fileUploader; + _tenantManager = tenantManager; + _tenantId = tenantManager.GetCurrentTenant().TenantId; + _tempStream = tempStream; + + _author = securityContext.CurrentAccount; + _dataStore = global.GetStore(); + _notifyClient = notifyClient; + + _log = logger.Get("ASC.CRM"); + + Error = null; + Percentage = 0; + IsCompleted = false; + FileUrl = null; + + _securityContext = securityContext; + + _commonLinkUtility = commonLinkUtility; + _globalFolder = globalFolder; + _fileMarker = fileMarker; + _fileDaoFactory = fileDaoFactory; + _filesLinkUtility = filesLinkUtility; + _displayUserSettingsHelper = displayUserSettingsHelper; + } + + public void Configure(object id, FilterObject filterObject, string fileName) + { + FileName = fileName ?? CRMSettingResource.Export + (filterObject == null ? ".zip" : ".csv"); + _filterObject = filterObject; + Id = id.ToString(); + } + + public override bool Equals(object obj) + { + if (obj == null) return false; + + var exportDataOperation = obj as ExportDataOperation; + + if (exportDataOperation == null) return false; + + return Id == exportDataOperation.Id; + } + + public override int GetHashCode() + { + return Id.GetHashCode(); + } + + public object Clone() + { + return MemberwiseClone(); + } + + public object Error { get; set; } + public string FileName { get; set; } + public string FileUrl { get; set; } + + private String WrapDoubleQuote(String value) + { + return "\"" + value.Trim().Replace("\"", "\"\"") + "\""; + } + + private String DataTableToCsv(DataTable dataTable) + { + var result = new StringBuilder(); + + var columnsCount = dataTable.Columns.Count; + + for (var index = 0; index < columnsCount; index++) + { + if (index != columnsCount - 1) + result.Append(dataTable.Columns[index].Caption + ","); + else + result.Append(dataTable.Columns[index].Caption); + } + + result.Append(Environment.NewLine); + + foreach (DataRow row in dataTable.Rows) + { + for (var i = 0; i < columnsCount; i++) + { + var itemValue = WrapDoubleQuote(row[i].ToString()); + + if (i != columnsCount - 1) + result.Append(itemValue + ","); + else + result.Append(itemValue); + } + + result.Append(Environment.NewLine); + } + + return result.ToString(); + } + + protected override void DoJob() + { + try + { + _tenantManager.SetCurrentTenant(_tenantId); + _securityContext.AuthenticateMe(_author); + + var userCulture = _userManager.GetUsers(_securityContext.CurrentAccount.ID).GetCulture(); + + System.Threading.Thread.CurrentThread.CurrentCulture = userCulture; + System.Threading.Thread.CurrentThread.CurrentUICulture = userCulture; + + _log.Debug("Start Export Data"); + + if (_filterObject == null) + ExportAllData(_daoFactory); + else + ExportPartData(_daoFactory); + + Complete(100, DistributedTaskStatus.Completed, null); + + _log.Debug("Export is completed"); + } + catch (OperationCanceledException) + { + Complete(0, DistributedTaskStatus.Completed, null); + + _log.Debug("Export is cancel"); + } + catch (Exception ex) + { + Complete(0, DistributedTaskStatus.Failted, ex.Message); + + _log.Error(ex); + } + } + + private void Complete(double percentage, DistributedTaskStatus status, object error) + { + IsCompleted = true; + Percentage = percentage; + Status = status; + Error = error; + } + + private void ExportAllData(DaoFactory daoFactory) + { + using (var stream = _tempStream.Create()) + { + var contactDao = daoFactory.GetContactDao(); + var contactInfoDao = daoFactory.GetContactInfoDao(); + var dealDao = daoFactory.GetDealDao(); + var casesDao = daoFactory.GetCasesDao(); + var taskDao = daoFactory.GetTaskDao(); + var historyDao = daoFactory.GetRelationshipEventDao(); + var invoiceItemDao = daoFactory.GetInvoiceItemDao(); + + _totalCount += contactDao.GetAllContactsCount(); + _totalCount += dealDao.GetDealsCount(); + _totalCount += casesDao.GetCasesCount(); + _totalCount += taskDao.GetAllTasksCount(); + _totalCount += historyDao.GetAllItemsCount(); + _totalCount += invoiceItemDao.GetInvoiceItemsCount(); + + using (var zipStream = new ZipOutputStream(stream)) + { + zipStream.PutNextEntry(new ZipEntry(CRMContactResource.Contacts + ".csv")); + + var contactData = contactDao.GetAllContacts(); + var contactInfos = new StringDictionary(); + + contactInfoDao.GetAll() + .ForEach(item => + { + var contactInfoKey = String.Format("{0}_{1}_{2}", item.ContactID, (int)item.InfoType, item.Category); + if (contactInfos.ContainsKey(contactInfoKey)) + { + contactInfos[contactInfoKey] += "," + item.Data; + } + else + { + contactInfos.Add(contactInfoKey, item.Data); + } + }); + + using (var zipEntryData = new MemoryStream(Encoding.UTF8.GetBytes(ExportContactsToCsv(contactData, contactInfos, daoFactory)))) + { + zipEntryData.CopyTo(zipStream); + } + + zipStream.PutNextEntry(new ZipEntry(CRMCommonResource.DealModuleName + ".csv")); + + var dealData = dealDao.GetAllDeals(); + + using (var zipEntryData = new MemoryStream(Encoding.UTF8.GetBytes(ExportDealsToCsv(dealData, daoFactory)))) + { + zipEntryData.CopyTo(zipStream); + } + + zipStream.PutNextEntry(new ZipEntry(CRMCommonResource.CasesModuleName + ".csv")); + + var casesData = casesDao.GetAllCases(); + + using (var zipEntryData = new MemoryStream(Encoding.UTF8.GetBytes(ExportCasesToCsv(casesData, daoFactory)))) + { + zipEntryData.CopyTo(zipStream); + } + + zipStream.PutNextEntry(new ZipEntry(CRMCommonResource.TaskModuleName + ".csv")); + + var taskData = taskDao.GetAllTasks(); + + using (var zipEntryData = new MemoryStream(Encoding.UTF8.GetBytes(ExportTasksToCsv(taskData, daoFactory)))) + { + zipEntryData.CopyTo(zipStream); + } + + zipStream.PutNextEntry(new ZipEntry(CRMCommonResource.History + ".csv")); + + var historyData = historyDao.GetAllItems(); + + using (var zipEntryData = new MemoryStream(Encoding.UTF8.GetBytes(ExportHistoryToCsv(historyData, daoFactory)))) + { + zipEntryData.CopyTo(zipStream); + } + + zipStream.PutNextEntry(new ZipEntry(CRMCommonResource.ProductsAndServices + ".csv")); + + var invoiceItemData = invoiceItemDao.GetAll(); + + using (var zipEntryData = new MemoryStream(Encoding.UTF8.GetBytes(ExportInvoiceItemsToCsv(invoiceItemData, daoFactory)))) + { + zipEntryData.CopyTo(zipStream); + } + + zipStream.Flush(); + zipStream.Close(); + + stream.Position = 0; + } + + FileUrl = _commonLinkUtility.GetFullAbsolutePath(_dataStore.SavePrivate(String.Empty, FileName, stream, DateTime.Now.AddDays(1))); + + _notifyClient.SendAboutExportCompleted(_author.ID, FileName, FileUrl); + } + } + + private void ExportPartData(DaoFactory daoFactory) + { + var items = _filterObject.GetItemsByFilter(daoFactory); + + string fileContent; + + _totalCount = items.Count; + + if (_totalCount == 0) + throw new ArgumentException(CRMErrorsResource.ExportToCSVDataEmpty); + + if (items is List) + { + var contactInfoDao = daoFactory.GetContactInfoDao(); + + var contacts = (List)items; + + var contactInfos = new StringDictionary(); + + contactInfoDao.GetAll(contacts.Select(item => item.ID).ToArray()) + .ForEach(item => + { + var contactInfoKey = String.Format("{0}_{1}_{2}", item.ContactID, + (int)item.InfoType, + item.Category); + + if (contactInfos.ContainsKey(contactInfoKey)) + contactInfos[contactInfoKey] += "," + item.Data; + else + contactInfos.Add(contactInfoKey, item.Data); + }); + + fileContent = ExportContactsToCsv(contacts, contactInfos, daoFactory); + } + else if (items is List) + { + fileContent = ExportDealsToCsv((List)items, daoFactory); + } + else if (items is List) + { + fileContent = ExportCasesToCsv((List)items, daoFactory); + } + else if (items is List) + { + fileContent = ExportHistoryToCsv((List)items, daoFactory); + } + else if (items is List) + { + fileContent = ExportTasksToCsv((List)items, daoFactory); + } + else if (items is List) + { + fileContent = ExportInvoiceItemsToCsv((List)items, daoFactory); + } + else + throw new ArgumentException(); + + FileUrl = SaveCsvFileInMyDocument(FileName, fileContent); + } + + private String ExportContactsToCsv(IReadOnlyCollection contacts, StringDictionary contactInfos, DaoFactory daoFactory) + { + var key = (string)Id; + var listItemDao = daoFactory.GetListItemDao(); + var tagDao = daoFactory.GetTagDao(); + var customFieldDao = daoFactory.GetCustomFieldDao(); + var contactDao = daoFactory.GetContactDao(); + + var dataTable = new DataTable(); + + dataTable.Columns.AddRange(new[] + { + new DataColumn + { + Caption = CRMCommonResource.TypeCompanyOrPerson, + ColumnName = "company/person" + }, + new DataColumn + { + Caption = CRMContactResource.FirstName, + ColumnName = "firstname" + }, + new DataColumn + { + Caption = CRMContactResource.LastName, + ColumnName = "lastname" + }, + new DataColumn + { + Caption = CRMContactResource.CompanyName, + ColumnName = "companyname" + }, + new DataColumn + { + Caption = CRMContactResource.JobTitle, + ColumnName = "jobtitle" + }, + new DataColumn + { + Caption = CRMContactResource.About, + ColumnName = "about" + }, + new DataColumn + { + Caption = CRMContactResource.ContactStage, + ColumnName = "contact_stage" + }, + new DataColumn + { + Caption = CRMContactResource.ContactType, + ColumnName = "contact_type" + }, + new DataColumn + { + Caption = CRMContactResource.ContactTagList, + ColumnName = "contact_tag_list" + } + }); + + foreach (ContactInfoType infoTypeEnum in Enum.GetValues(typeof(ContactInfoType))) + foreach (Enum categoryEnum in Enum.GetValues(ContactInfo.GetCategory(infoTypeEnum))) + { + var localTitle = String.Format("{1} ({0})", categoryEnum.ToLocalizedString().ToLower(), infoTypeEnum.ToLocalizedString()); + + if (infoTypeEnum == ContactInfoType.Address) + dataTable.Columns.AddRange((from AddressPart addressPartEnum in Enum.GetValues(typeof(AddressPart)) + select new DataColumn + { + Caption = String.Format(localTitle + " {0}", addressPartEnum.ToLocalizedString().ToLower()), + ColumnName = String.Format("contactInfo_{0}_{1}_{2}", (int)infoTypeEnum, categoryEnum, (int)addressPartEnum) + }).ToArray()); + + else + dataTable.Columns.Add(new DataColumn + { + Caption = localTitle, + ColumnName = String.Format("contactInfo_{0}_{1}", (int)infoTypeEnum, categoryEnum) + }); + } + + var fieldsDescription = customFieldDao.GetFieldsDescription(EntityType.Company); + + customFieldDao.GetFieldsDescription(EntityType.Person).ForEach(item => + { + var alreadyContains = fieldsDescription.Any(field => field.ID == item.ID); + + if (!alreadyContains) + fieldsDescription.Add(item); + }); + + fieldsDescription.ForEach( + item => + { + if (item.Type == CustomFieldType.Heading) return; + + dataTable.Columns.Add( + new DataColumn + { + Caption = item.Label, + ColumnName = "customField_" + item.ID + } + ); + }); + + var customFieldEntity = (contacts.Where(x => x is Company).Any() ? customFieldDao.GetEnityFields(EntityType.Company, contacts.Where(x => x is Company).Select(x => x.ID).ToArray()) : new List()) + .Union(contacts.Where(x => x is Person).Any() ? customFieldDao.GetEnityFields(EntityType.Person, contacts.Where(x => x is Person).Select(x => x.ID).ToArray()) : new List()) + .GroupBy(x => x.EntityID) + .ToDictionary(x => x.Key, x => x.ToList()); + + + var tags = tagDao.GetEntitiesTags(EntityType.Contact); + + foreach (var contact in contacts) + { + Percentage += 1.0 * 100 / _totalCount; + PublishChanges(); + + + var isCompany = contact is Company; + + var compPersType = (isCompany) ? CRMContactResource.Company : CRMContactResource.Person; + + var contactTags = String.Empty; + + if (tags.ContainsKey(contact.ID)) + contactTags = String.Join(",", tags[contact.ID].OrderBy(x => x)); + + String firstName; + String lastName; + + String companyName; + String title; + + if (contact is Company) + { + firstName = String.Empty; + lastName = String.Empty; + title = String.Empty; + companyName = ((Company)contact).CompanyName; + } + else + { + var people = (Person)contact; + + firstName = people.FirstName; + lastName = people.LastName; + title = people.JobTitle; + + companyName = String.Empty; + + if (people.CompanyID > 0) + { + var personCompany = contacts.SingleOrDefault(item => item.ID == people.CompanyID) ?? + contactDao.GetByID(people.CompanyID); + + if (personCompany != null) + companyName = personCompany.GetTitle(); + } + } + + var contactStatus = String.Empty; + + if (contact.StatusID > 0) + { + + var listItem = listItemDao.GetByID(contact.StatusID); + + if (listItem != null) + contactStatus = listItem.Title; + } + + var contactType = String.Empty; + + if (contact.ContactTypeID > 0) + { + + var listItem = listItemDao.GetByID(contact.ContactTypeID); + + if (listItem != null) + contactType = listItem.Title; + } + + var dataRowItems = new List + { + compPersType, + firstName, + lastName, + companyName, + title, + contact.About, + contactStatus, + contactType, + contactTags + }; + + foreach (ContactInfoType infoTypeEnum in Enum.GetValues(typeof(ContactInfoType))) + foreach (Enum categoryEnum in Enum.GetValues(ContactInfo.GetCategory(infoTypeEnum))) + { + var contactInfoKey = String.Format("{0}_{1}_{2}", contact.ID, + (int)infoTypeEnum, + Convert.ToInt32(categoryEnum)); + + var columnValue = ""; + + if (contactInfos.ContainsKey(contactInfoKey)) + columnValue = contactInfos[contactInfoKey]; + + if (infoTypeEnum == ContactInfoType.Address) + { + if (!String.IsNullOrEmpty(columnValue)) + { + var addresses = JsonSerializer.Deserialize>>(String.Concat("[", columnValue, "]")); + + dataRowItems.AddRange(from AddressPart addressPartEnum in Enum.GetValues(typeof(AddressPart)) + select String.Join(",", addresses.Select(x => x[addressPartEnum.ToString().ToLower()]).ToArray())); + } + else + { + dataRowItems.AddRange(new[] { "", "", "", "", "" }); + } + } + else + { + dataRowItems.Add(columnValue); + } + } + + var dataRow = dataTable.Rows.Add(dataRowItems.ToArray()); + + if (customFieldEntity.ContainsKey(contact.ID)) + customFieldEntity[contact.ID].ForEach(item => dataRow["customField_" + item.ID] = item.Value); + } + + return DataTableToCsv(dataTable); + } + + private String ExportDealsToCsv(IEnumerable deals, DaoFactory daoFactory) + { + var key = (string)Id; + var tagDao = daoFactory.GetTagDao(); + var customFieldDao = daoFactory.GetCustomFieldDao(); + var dealMilestoneDao = daoFactory.GetDealMilestoneDao(); + var contactDao = daoFactory.GetContactDao(); + + var dataTable = new DataTable(); + + dataTable.Columns.AddRange(new[] + { + new DataColumn + { + Caption = CRMDealResource.NameDeal, + ColumnName = "title" + }, + new DataColumn + { + Caption = CRMDealResource.ClientDeal, + ColumnName = "client_deal" + }, + new DataColumn + { + Caption = CRMDealResource.DescriptionDeal, + ColumnName = "description" + }, + new DataColumn + { + Caption = CRMCommonResource.Currency, + ColumnName = "currency" + }, + new DataColumn + { + Caption = CRMDealResource.DealAmount, + ColumnName = "amount" + }, + new DataColumn + { + Caption = CRMDealResource.BidType, + ColumnName = "bid_type" + }, + new DataColumn + { + Caption = CRMDealResource.BidTypePeriod, + ColumnName = "bid_type_period" + }, + new DataColumn + { + Caption = CRMJSResource.ExpectedCloseDate, + ColumnName = "expected_close_date" + }, + new DataColumn + { + Caption = CRMJSResource.ActualCloseDate, + ColumnName = "actual_close_date" + }, + new DataColumn + { + Caption = CRMDealResource.ResponsibleDeal, + ColumnName = "responsible_deal" + }, + new DataColumn + { + Caption = CRMDealResource.CurrentDealMilestone, + ColumnName = "current_deal_milestone" + }, + new DataColumn + { + Caption = CRMDealResource.DealMilestoneType, + ColumnName = "deal_milestone_type" + }, + new DataColumn + { + Caption = (CRMDealResource.ProbabilityOfWinning + " %"), + ColumnName = "probability_of_winning" + }, + new DataColumn + { + Caption = (CRMDealResource.DealTagList), + ColumnName = "tag_list" + } + }); + + customFieldDao.GetFieldsDescription(EntityType.Opportunity).ForEach( + item => + { + if (item.Type == CustomFieldType.Heading) return; + + dataTable.Columns.Add(new DataColumn + { + Caption = item.Label, + ColumnName = "customField_" + item.ID + }); + }); + + var customFieldEntity = customFieldDao.GetEnityFields(EntityType.Opportunity, deals.Select(x => x.ID).ToArray()) + .GroupBy(x => x.EntityID) + .ToDictionary(x => x.Key, x => x.ToList()); + + + var tags = tagDao.GetEntitiesTags(EntityType.Opportunity); + + foreach (var deal in deals) + { + + Percentage += 1.0 * 100 / _totalCount; + PublishChanges(); + + var contactTags = String.Empty; + + if (tags.ContainsKey(deal.ID)) + contactTags = String.Join(",", tags[deal.ID].OrderBy(x => x)); + + String bidType; + + switch (deal.BidType) + { + case BidType.FixedBid: + bidType = CRMDealResource.BidType_FixedBid; + break; + case BidType.PerDay: + bidType = CRMDealResource.BidType_PerDay; + break; + case BidType.PerHour: + bidType = CRMDealResource.BidType_PerHour; + break; + case BidType.PerMonth: + bidType = CRMDealResource.BidType_PerMonth; + break; + case BidType.PerWeek: + bidType = CRMDealResource.BidType_PerWeek; + break; + case BidType.PerYear: + bidType = CRMDealResource.BidType_PerYear; + break; + default: + throw new ArgumentException(); + } + + var currentDealMilestone = dealMilestoneDao.GetByID(deal.DealMilestoneID); + var currentDealMilestoneStatus = currentDealMilestone.Status.ToLocalizedString(); + var contactTitle = String.Empty; + + if (deal.ContactID != 0) + contactTitle = contactDao.GetByID(deal.ContactID).GetTitle(); + + var dataRow = dataTable.Rows.Add(new object[] + { + deal.Title, + contactTitle, + deal.Description, + deal.BidCurrency, + deal.BidValue.ToString(CultureInfo.InvariantCulture), + bidType, + deal.PerPeriodValue == 0 ? "" : deal.PerPeriodValue.ToString(CultureInfo.InvariantCulture), + deal.ExpectedCloseDate.Date == DateTime.MinValue.Date ? "" : deal.ExpectedCloseDate.ToString(), + deal.ActualCloseDate.Date == DateTime.MinValue.Date ? "" : deal.ActualCloseDate.ToString(), + //deal.ExpectedCloseDate.Date == DateTime.MinValue.Date ? "" : deal.ExpectedCloseDate.ToString(DateTimeExtension.DateFormatPattern), + //deal.ActualCloseDate.Date == DateTime.MinValue.Date ? "" : deal.ActualCloseDate.ToString(DateTimeExtension.DateFormatPattern), + _userManager.GetUsers(deal.ResponsibleID).DisplayUserName(_displayUserSettingsHelper), + currentDealMilestone.Title, + currentDealMilestoneStatus, + deal.DealMilestoneProbability.ToString(CultureInfo.InvariantCulture), + contactTags + }); + + if (customFieldEntity.ContainsKey(deal.ID)) + customFieldEntity[deal.ID].ForEach(item => dataRow["customField_" + item.ID] = item.Value); + } + + return DataTableToCsv(dataTable); + } + + private String ExportCasesToCsv(IEnumerable cases, DaoFactory daoFactory) + { + var key = (string)Id; + var tagDao = daoFactory.GetTagDao(); + var customFieldDao = daoFactory.GetCustomFieldDao(); + + var dataTable = new DataTable(); + + dataTable.Columns.AddRange(new[] + { + new DataColumn + { + Caption = CRMCasesResource.CaseTitle, + ColumnName = "title" + }, + new DataColumn(CRMCasesResource.CasesTagList) + { + Caption = CRMCasesResource.CasesTagList, + ColumnName = "tag_list" + } + }); + + customFieldDao.GetFieldsDescription(EntityType.Case).ForEach( + item => + { + if (item.Type == CustomFieldType.Heading) return; + + dataTable.Columns.Add(new DataColumn + { + Caption = item.Label, + ColumnName = "customField_" + item.ID + }); + }); + + var customFieldEntity = customFieldDao.GetEnityFields(EntityType.Case, cases.Select(x => x.ID).ToArray()) + .GroupBy(x => x.EntityID) + .ToDictionary(x => x.Key, x => x.ToList()); + + var tags = tagDao.GetEntitiesTags(EntityType.Case); + + foreach (var item in cases) + { + Percentage += 1.0 * 100 / _totalCount; + PublishChanges(); + + var contactTags = String.Empty; + + if (tags.ContainsKey(item.ID)) + contactTags = String.Join(",", tags[item.ID].OrderBy(x => x)); + + var dataRow = dataTable.Rows.Add(new object[] + { + item.Title, + contactTags + }); + + if (customFieldEntity.ContainsKey(item.ID)) + customFieldEntity[item.ID].ForEach(row => dataRow["customField_" + row.ID] = row.Value); + } + + return DataTableToCsv(dataTable); + } + + private String ExportHistoryToCsv(IEnumerable events, DaoFactory daoFactory) + { + var key = (string)Id; + var listItemDao = daoFactory.GetListItemDao(); + var dealDao = daoFactory.GetDealDao(); + var casesDao = daoFactory.GetCasesDao(); + var contactDao = daoFactory.GetContactDao(); + + var dataTable = new DataTable(); + + dataTable.Columns.AddRange(new[] + { + new DataColumn + { + Caption = (CRMContactResource.Content), + ColumnName = "content" + }, + new DataColumn + { + Caption = (CRMCommonResource.Category), + ColumnName = "category" + }, + new DataColumn + { + Caption = (CRMContactResource.ContactTitle), + ColumnName = "contact_title" + }, + new DataColumn + { + Caption = (CRMContactResource.RelativeEntity), + ColumnName = "relative_entity" + }, + new DataColumn + { + Caption = (CRMCommonResource.Author), + ColumnName = "author" + }, + new DataColumn + { + Caption = (CRMCommonResource.CreateDate), + ColumnName = "create_date" + } + }); + + foreach (var item in events) + { + Percentage += 1.0 * 100 / _totalCount; + PublishChanges(); + + var entityTitle = String.Empty; + + if (item.EntityID > 0) + switch (item.EntityType) + { + case EntityType.Case: + var casesObj = casesDao.GetByID(item.EntityID); + + if (casesObj != null) + entityTitle = String.Format("{0}: {1}", CRMCasesResource.Case, + casesObj.Title); + break; + case EntityType.Opportunity: + var dealObj = dealDao.GetByID(item.EntityID); + + if (dealObj != null) + entityTitle = String.Format("{0}: {1}", CRMDealResource.Deal, + dealObj.Title); + break; + } + + var contactTitle = String.Empty; + + if (item.ContactID > 0) + { + var contactObj = contactDao.GetByID(item.ContactID); + + if (contactObj != null) + contactTitle = contactObj.GetTitle(); + } + + var categoryTitle = String.Empty; + + if (item.CategoryID > 0) + { + var categoryObj = listItemDao.GetByID(item.CategoryID); + + if (categoryObj != null) + categoryTitle = categoryObj.Title; + + } + else if (item.CategoryID == (int)HistoryCategorySystem.TaskClosed) + categoryTitle = HistoryCategorySystem.TaskClosed.ToLocalizedString(); + else if (item.CategoryID == (int)HistoryCategorySystem.FilesUpload) + categoryTitle = HistoryCategorySystem.FilesUpload.ToLocalizedString(); + else if (item.CategoryID == (int)HistoryCategorySystem.MailMessage) + categoryTitle = HistoryCategorySystem.MailMessage.ToLocalizedString(); + + dataTable.Rows.Add(new object[] + { + item.Content, + categoryTitle, + contactTitle, + entityTitle, + _userManager.GetUsers(item.CreateBy).DisplayUserName(_displayUserSettingsHelper), + // item.CreateOn.ToShortString() + item.CreateOn + }); + } + + return DataTableToCsv(dataTable); + } + + private String ExportTasksToCsv(IEnumerable tasks, DaoFactory daoFactory) + { + var key = (string)Id; + var listItemDao = daoFactory.GetListItemDao(); + var dealDao = daoFactory.GetDealDao(); + var casesDao = daoFactory.GetCasesDao(); + var contactDao = daoFactory.GetContactDao(); + + var dataTable = new DataTable(); + + dataTable.Columns.AddRange(new[] + { + new DataColumn + { + Caption = (CRMTaskResource.TaskTitle), + ColumnName = "title" + }, + new DataColumn + { + Caption = (CRMTaskResource.Description), + ColumnName = "description" + }, + new DataColumn + { + Caption = (CRMTaskResource.DueDate), + ColumnName = "due_date" + }, + new DataColumn + { + Caption = (CRMTaskResource.Responsible), + ColumnName = "responsible" + }, + new DataColumn + { + Caption = (CRMContactResource.ContactTitle), + ColumnName = "contact_title" + }, + new DataColumn + { + Caption = (CRMTaskResource.TaskStatus), + ColumnName = "task_status" + }, + new DataColumn + { + Caption = (CRMTaskResource.TaskCategory), + ColumnName = "task_category" + }, + new DataColumn + { + Caption = (CRMContactResource.RelativeEntity), + ColumnName = "relative_entity" + }, + new DataColumn + { + Caption = (CRMCommonResource.Alert), + ColumnName = "alert_value" + } + }); + + foreach (var item in tasks) + { + Percentage += 1.0 * 100 / _totalCount; + PublishChanges(); + + var entityTitle = String.Empty; + + if (item.EntityID > 0) + switch (item.EntityType) + { + case EntityType.Case: + var caseObj = casesDao.GetByID(item.EntityID); + + if (caseObj != null) + entityTitle = String.Format("{0}: {1}", CRMCasesResource.Case, caseObj.Title); + break; + case EntityType.Opportunity: + var dealObj = dealDao.GetByID(item.EntityID); + + if (dealObj != null) + entityTitle = String.Format("{0}: {1}", CRMDealResource.Deal, dealObj.Title); + break; + } + + var contactTitle = String.Empty; + + if (item.ContactID > 0) + { + var contact = contactDao.GetByID(item.ContactID); + + if (contact != null) + contactTitle = contact.GetTitle(); + } + + dataTable.Rows.Add(new object[] + { + item.Title, + item.Description, + item.DeadLine == DateTime.MinValue + ? "" +// : item.DeadLine.ToShortString(), + : item.DeadLine.ToString(), + _userManager.GetUsers(item.ResponsibleID).DisplayUserName(_displayUserSettingsHelper), + contactTitle, + item.IsClosed + ? CRMTaskResource.TaskStatus_Closed + : CRMTaskResource.TaskStatus_Open, + listItemDao.GetByID(item.CategoryID).Title, + entityTitle, + item.AlertValue.ToString(CultureInfo.InvariantCulture) + }); + } + + return DataTableToCsv(dataTable); + } + + private String ExportInvoiceItemsToCsv(IEnumerable invoiceItems, DaoFactory daoFactory) + { + var key = (string)Id; + var taxes = daoFactory.GetInvoiceTaxDao().GetAll(); + var dataTable = new DataTable(); + + dataTable.Columns.AddRange(new[] + { + new DataColumn + { + Caption = (CRMInvoiceResource.InvoiceItemName), + ColumnName = "title" + }, + new DataColumn + { + Caption = (CRMSettingResource.Description), + ColumnName = "description" + }, + new DataColumn + { + Caption = (CRMInvoiceResource.StockKeepingUnit), + ColumnName = "sku" + }, + new DataColumn + { + Caption = (CRMInvoiceResource.InvoiceItemPrice), + ColumnName = "price" + }, + new DataColumn + { + Caption = (CRMInvoiceResource.FormInvoiceItemStockQuantity), + ColumnName = "stock_quantity" + }, + new DataColumn + { + Caption = (CRMInvoiceResource.TrackInventory), + ColumnName = "track_inventory" + }, + new DataColumn + { + Caption = (CRMInvoiceResource.Currency), + ColumnName = "currency" + }, + + new DataColumn + { + Caption = (CRMInvoiceResource.InvoiceTax1Name), + ColumnName = "tax1_name" + }, + new DataColumn + { + Caption = (CRMInvoiceResource.InvoiceTax1Rate), + ColumnName = "tax1_rate" + }, + new DataColumn + { + Caption = (CRMInvoiceResource.InvoiceTax2Name), + ColumnName = "tax2_name" + }, + new DataColumn + { + Caption = (CRMInvoiceResource.InvoiceTax2Rate), + ColumnName = "tax2_rate" + } + + }); + + + foreach (var item in invoiceItems) + { + Percentage += 1.0 * 100 / _totalCount; + PublishChanges(); + + var tax1 = item.InvoiceTax1ID != 0 ? taxes.Find(t => t.ID == item.InvoiceTax1ID) : null; + var tax2 = item.InvoiceTax2ID != 0 ? taxes.Find(t => t.ID == item.InvoiceTax2ID) : null; + + dataTable.Rows.Add(new object[] + { + item.Title, + item.Description, + item.StockKeepingUnit, + item.Price.ToString(CultureInfo.InvariantCulture), + item.StockQuantity.ToString(CultureInfo.InvariantCulture), + item.TrackInventory.ToString(), + item.Currency, + tax1 != null ? tax1.Name : "", + tax1 != null ? tax1.Rate.ToString(CultureInfo.InvariantCulture) : "", + tax2 != null ? tax2.Name : "", + tax2 != null ? tax2.Rate.ToString(CultureInfo.InvariantCulture) : "" + }); + } + + return DataTableToCsv(dataTable); + } + + private String SaveCsvFileInMyDocument(String title, String data) + { + string fileUrl; + + using (var memStream = new MemoryStream(Encoding.UTF8.GetBytes(data))) + { + var file = _fileUploader.Exec(_globalFolder.GetFolderMy(_fileMarker, _fileDaoFactory).ToString(), title, memStream.Length, memStream, true); + + if (_fileUtility.CanWebView(title) || _fileUtility.CanWebEdit(title)) + { + fileUrl = _filesLinkUtility.GetFileWebEditorUrl(file.ID); + fileUrl += string.Format("&options={{\"delimiter\":{0},\"codePage\":{1}}}", + (int)FileUtility.CsvDelimiter.Comma, + Encoding.UTF8.CodePage); + } + else + { + fileUrl = _filesLinkUtility.GetFileDownloadUrl(file.ID); + } + } + + return fileUrl; + } + } + + public class ExportToCsv + { + private readonly object Locker = new object(); + private readonly DistributedTaskQueue _queue; + private readonly ExportDataOperation _exportDataOperation; + private readonly SecurityContext _securityContext; + protected readonly int _tenantID; + + public ExportToCsv(SecurityContext securityContext, + TenantManager tenantManager, + DistributedTaskQueueOptionsManager queueOptions, + ExportDataOperation exportDataOperation) + { + _securityContext = securityContext; + _tenantID = tenantManager.GetCurrentTenant().TenantId; + _queue = queueOptions.Get(); + _exportDataOperation = exportDataOperation; + } + + public IProgressItem GetStatus(bool partialDataExport) + { + var key = GetKey(partialDataExport); + + var operation = _queue.GetTasks().FirstOrDefault(x => x.Id == key); + + return operation; + } + + public IProgressItem Start(FilterObject filterObject, string fileName) + { + lock (Locker) + { + var key = GetKey(filterObject != null); + + var operation = _queue.GetTasks().FirstOrDefault(x => x.Id == key); + + if (operation != null && operation.IsCompleted) + { + _queue.RemoveTask(operation.Id); + operation = null; + } + + if (operation == null) + { + _exportDataOperation.Configure(key, filterObject, fileName); + + _queue.QueueTask(_exportDataOperation); + } + + return operation; + } + } + + public void Cancel(bool partialDataExport) + { + lock (Locker) + { + var key = GetKey(partialDataExport); + + var findedItem = _queue.GetTasks().FirstOrDefault(x => x.Id == key); + + if (findedItem != null) + { + _queue.RemoveTask(findedItem.Id); + } + } + } + + public string GetKey(bool partialDataExport) + { + return string.Format("{0}_{1}", _tenantID, + partialDataExport ? _securityContext.CurrentAccount.ID : Guid.Empty); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Utils/FileHelper.cs b/products/ASC.CRM/Server/Utils/FileHelper.cs new file mode 100644 index 00000000000..6db9cbcd5f9 --- /dev/null +++ b/products/ASC.CRM/Server/Utils/FileHelper.cs @@ -0,0 +1,94 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.IO; +using System.Reflection; + +namespace ASC.Web.CRM.Classes +{ + public class FileHelper + { + public static void WriteTextToFile(string path, string text) + { + File.WriteAllText(path, text); + } + + public static string ReadTextFromFile(string path) + { + return File.Exists(path) ? File.ReadAllText(path) : null; + } + + public static string ReadTextFromEmbeddedResource(string resourceName) + { + var result = string.Empty; + var assembly = Assembly.GetExecutingAssembly(); + + using (var stream = assembly.GetManifestResourceStream(resourceName)) + { + if (stream != null) + { + using (var sr = new StreamReader(stream)) + { + result = sr.ReadToEnd(); + } + } + } + + return result; + } + + public static byte[] ReadBytesFromFile(string path) + { + return File.Exists(path) ? File.ReadAllBytes(path) : null; + } + + public static byte[] ReadBytesFromEmbeddedResource(string resourceName) + { + byte[] result = null; + var assembly = Assembly.GetExecutingAssembly(); + + using (var stream = assembly.GetManifestResourceStream(resourceName)) + { + if (stream != null) + { + using (var memoryStream = new MemoryStream()) + { + stream.CopyTo(memoryStream); + result = memoryStream.ToArray(); + } + } + } + + return result; + } + + public static void DeleteFile(string path) + { + if (File.Exists(path)) + File.Delete(path); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Utils/Import/CSV/ImportBase.cs b/products/ASC.CRM/Server/Utils/Import/CSV/ImportBase.cs new file mode 100644 index 00000000000..6469ce2b4ac --- /dev/null +++ b/products/ASC.CRM/Server/Utils/Import/CSV/ImportBase.cs @@ -0,0 +1,49 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +namespace LumenWorks.Framework.IO.Csv +{ + public static class CsvReaderExtension + { + public static String[] GetCurrentRowFields(this CsvReader csvReader, bool htmlEncodeColumn) + { + var fieldCount = csvReader.FieldCount; + var result = new String[fieldCount]; + + for (int index = 0; index < fieldCount; index++) + { + if (htmlEncodeColumn) + result[index] = csvReader[index].HtmlEncode().ReplaceSingleQuote(); + else + result[index] = csvReader[index]; + } + + return result; + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Utils/Import/CSV/ImportCSVSettings.cs b/products/ASC.CRM/Server/Utils/Import/CSV/ImportCSVSettings.cs new file mode 100644 index 00000000000..673e868bb4e --- /dev/null +++ b/products/ASC.CRM/Server/Utils/Import/CSV/ImportCSVSettings.cs @@ -0,0 +1,94 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json; + +using ASC.CRM.Core.Enums; + +namespace ASC.Web.CRM.Classes +{ + public class ImportCSVSettings + { + public ImportCSVSettings(String jsonStr) + { + var jsonDocument = JsonDocument.Parse(jsonStr); + + if (jsonDocument == null) return; + + var jsonElement = jsonDocument.RootElement; + + HasHeader = jsonElement.GetProperty("has_header").GetBoolean(); + DelimiterCharacter = Convert.ToChar(jsonElement.GetProperty("delimiter_character").GetInt32()); + Encoding = Encoding.GetEncoding(jsonElement.GetProperty("encoding").GetInt32()); + QuoteType = Convert.ToChar(jsonElement.GetProperty("quote_character").GetInt32()); + + JsonElement columnMappingToken; + if (jsonElement.TryGetProperty("column_mapping", out columnMappingToken)) + ColumnMapping = columnMappingToken; + + JsonElement duplicateRecordRuleToken; + if (jsonElement.TryGetProperty("removing_duplicates_behavior", out duplicateRecordRuleToken)) + DuplicateRecordRule = duplicateRecordRuleToken.GetInt32(); + + JsonElement isPrivateToken; + if (jsonElement.TryGetProperty("is_private", out isPrivateToken)) + { + IsPrivate = isPrivateToken.GetBoolean(); + AccessList = jsonElement.GetProperty("access_list").EnumerateArray().Select(x => x.GetGuid()).ToList(); + } + + JsonElement shareTypeToken; + if (jsonElement.TryGetProperty("share_type", out shareTypeToken)) + { + ShareType = (ShareType)(shareTypeToken.GetInt32()); + ContactManagers = jsonElement.GetProperty("contact_managers").EnumerateArray().Select(x => x.GetGuid()).ToList(); + } + + JsonElement tagsToken; + if (jsonElement.TryGetProperty("tags", out tagsToken)) + { + Tags = tagsToken.EnumerateArray().Select(x => x.GetString()).ToList(); + } + } + + public bool IsPrivate { get; set; } + public ShareType ShareType { get; set; } + public int DuplicateRecordRule { get; set; } + public bool HasHeader { get; set; } + public char DelimiterCharacter { get; set; } + public char QuoteType { get; set; } + public Encoding Encoding { get; set; } + public List AccessList { get; set; } + public List ContactManagers { get; set; } + public JsonElement ColumnMapping { get; set; } + public List Tags { get; set; } + } + +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Utils/Import/CSV/ImportCases.cs b/products/ASC.CRM/Server/Utils/Import/CSV/ImportCases.cs new file mode 100644 index 00000000000..3c7931d531a --- /dev/null +++ b/products/ASC.CRM/Server/Utils/Import/CSV/ImportCases.cs @@ -0,0 +1,191 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; + +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; + +using LumenWorks.Framework.IO.Csv; + +#endregion + +namespace ASC.Web.CRM.Classes +{ + public partial class ImportDataOperation + { + private void ImportCaseData(DaoFactory _daoFactory) + { + using (var CSVFileStream = _dataStore.GetReadStream("temp", _csvFileURI)) + using (CsvReader csv = _importFromCSV.CreateCsvReaderInstance(CSVFileStream, _importSettings)) + { + int currentIndex = 0; + + var casesDao = _daoFactory.GetCasesDao(); + var customFieldDao = _daoFactory.GetCustomFieldDao(); + var tagDao = _daoFactory.GetTagDao(); + + var findedTags = new Dictionary>(); + var findedCustomField = new List(); + var findedCases = new List(); + var findedCasesMembers = new Dictionary>(); + + while (csv.ReadNextRecord()) + { + _columns = csv.GetCurrentRowFields(false); + + var objCases = new ASC.CRM.Core.Entities.Cases(); + + objCases.ID = currentIndex; + + objCases.Title = GetPropertyValue("title"); + + if (String.IsNullOrEmpty(objCases.Title)) continue; + + foreach (JsonProperty jToken in _importSettings.ColumnMapping.EnumerateObject()) + { + var propertyValue = GetPropertyValue(jToken.Name); + + if (String.IsNullOrEmpty(propertyValue)) continue; + + if (!jToken.Name.StartsWith("customField_")) continue; + + var fieldID = Convert.ToInt32(jToken.Name.Split(new[] { '_' })[1]); + + var field = customFieldDao.GetFieldDescription(fieldID); + + if (field != null) + { + findedCustomField.Add(new CustomField + { + EntityID = objCases.ID, + EntityType = EntityType.Case, + ID = fieldID, + Value = field.Type == CustomFieldType.CheckBox ? (propertyValue == "on" || propertyValue == "true" ? "true" : "false") : propertyValue + }); + } + + } + + var tag = GetPropertyValue("tag"); + + if (!String.IsNullOrEmpty(tag)) + { + var tagList = tag.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); + tagList.AddRange(_importSettings.Tags); + tagList = tagList.Distinct().ToList(); + findedTags.Add(objCases.ID, tagList); + } + else if (_importSettings.Tags.Count != 0) + { + findedTags.Add(objCases.ID, _importSettings.Tags); + } + + var localMembersCases = new List(); + + var members = GetPropertyValue("member"); + + if (!String.IsNullOrEmpty(members)) + { + var membersList = members.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + + foreach (var item in membersList) + { + var findedMember = _daoFactory.GetContactDao().GetContactsByName(item, true); + + if (findedMember.Count > 0) + { + localMembersCases.Add(findedMember[0].ID); + } + else + { + findedMember = _daoFactory.GetContactDao().GetContactsByName(item, false); + if (findedMember.Count > 0) + { + localMembersCases.Add(findedMember[0].ID); + } + } + + } + } + + if (localMembersCases.Count > 0) + findedCasesMembers.Add(objCases.ID, localMembersCases); + + objCases.ID = currentIndex; + + findedCases.Add(objCases); + + if (currentIndex + 1 > _importFromCSV.MaxRoxCount) break; + + currentIndex++; + } + + Percentage = 62.5; + PublishChanges(); + + var newIDs = casesDao.SaveCasesList(findedCases); + findedCases.ForEach(d => d.ID = newIDs[d.ID]); + + findedCustomField.ForEach(item => item.EntityID = newIDs[item.EntityID]); + + customFieldDao.SaveList(findedCustomField); + + Percentage += 12.5; + PublishChanges(); + + foreach (var findedCasesMemberKey in findedCasesMembers.Keys) + { + _daoFactory.GetDealDao().SetMembers(newIDs[findedCasesMemberKey], findedCasesMembers[findedCasesMemberKey].ToArray()); + } + + Percentage += 12.5; + PublishChanges(); + + + foreach (var findedTagKey in findedTags.Keys) + { + tagDao.SetTagToEntity(EntityType.Case, newIDs[findedTagKey], findedTags[findedTagKey].ToArray()); + } + + if (_importSettings.IsPrivate) + findedCases.ForEach(dealItem => _crmSecurity.SetAccessTo(dealItem, _importSettings.AccessList)); + + + Percentage += 12.5; + PublishChanges(); + } + + Complete(); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Utils/Import/CSV/ImportContacts.cs b/products/ASC.CRM/Server/Utils/Import/CSV/ImportContacts.cs new file mode 100644 index 00000000000..ac24dc0d0a1 --- /dev/null +++ b/products/ASC.CRM/Server/Utils/Import/CSV/ImportContacts.cs @@ -0,0 +1,646 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; + +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; + +using LumenWorks.Framework.IO.Csv; + +#endregion + +namespace ASC.Web.CRM.Classes +{ + public partial class ImportDataOperation + { + private readonly Int32 DaoIterationStep = 200; + + private void ImportContactsData(DaoFactory _daoFactory) + { + var index = 0; + + var personFakeIdCompanyNameHash = new Dictionary(); + + var contactDao = _daoFactory.GetContactDao(); + var contactInfoDao = _daoFactory.GetContactInfoDao(); + var customFieldDao = _daoFactory.GetCustomFieldDao(); + var tagDao = _daoFactory.GetTagDao(); + + var findedContacts = new Dictionary(); + var findedTags = new Dictionary>(); + var findedCustomField = new List(); + var findedContactInfos = new List(); + + #region Read csv + using (var CSVFileStream = _dataStore.GetReadStream("temp", _csvFileURI)) + { + CsvReader csv = _importFromCSV.CreateCsvReaderInstance(CSVFileStream, _importSettings); + + int currentIndex = 0; + + while (csv.ReadNextRecord()) + { + _columns = csv.GetCurrentRowFields(false); + Contact contact = null; + + #region Common data + + if (!_CommonData(currentIndex, _daoFactory, ref contact, ref personFakeIdCompanyNameHash)) + continue; + + findedContacts.Add(contact.ID, contact); + + #endregion + + #region Read tags + + _ReadTags(ref findedTags, contact); + + #endregion + + #region Custom fields Y contact infos + + var primaryFields = new List(); + + foreach (JsonProperty jProperty in _importSettings.ColumnMapping.EnumerateObject()) + { + var propertyValue = GetPropertyValue(jProperty.Name); + if (String.IsNullOrEmpty(propertyValue)) continue; + + if (jProperty.Name.StartsWith("customField_")) + { + _ReadCustomField(jProperty, propertyValue, contact, ref findedCustomField, customFieldDao); + } + else if (jProperty.Name.StartsWith("contactInfo_")) + { + var addressTemplate = new Dictionary(); + + foreach (String addressPartName in Enum.GetNames(typeof(AddressPart))) + addressTemplate.Add(addressPartName.ToLower(), ""); + + var addressTemplateStr = addressTemplate.ToString(); + + _ReadContactInfo(jProperty, propertyValue, contact, ref findedContactInfos, ref primaryFields, addressTemplateStr); + } + } + #endregion + + if (currentIndex + 1 > _importFromCSV.MaxRoxCount) break; + currentIndex++; + } + } + _log.InfoFormat("ImportContactsData. Reading {0} findedContacts complete", findedContacts.Count); + + #endregion + + Percentage = 37.5; + PublishChanges(); + + #region Processing duplicate rule + + _DuplicateRecordRuleProcess(_daoFactory, ref findedContacts, ref personFakeIdCompanyNameHash, ref findedContactInfos, ref findedCustomField, ref findedTags); + + _log.Info("ImportContactsData. _DuplicateRecordRuleProcess. End"); + + if (IsCompleted) + { + return; + } + + #endregion + + Percentage += 12.5; + PublishChanges(); + + #region Manipulation for saving Companies for persons + CRMSecurity + + var findedCompanies = findedContacts.Where(x => x.Value is Company).ToDictionary(x => x.Key, y => y.Value); + var findedPeoples = findedContacts.Where(x => x.Value is Person).ToDictionary(x => x.Key, y => y.Value); + + var fakeRealContactIdHash = new Dictionary(); + var companyNameRealIdHash = new Dictionary(); + + + var findedCompaniesList = findedCompanies.Values.ToList(); + if (findedCompaniesList.Count != 0) + { + index = 0; + while (index < findedCompaniesList.Count) + { + var portion = findedCompaniesList.Skip(index).Take(DaoIterationStep).ToList();// Get next step + + fakeRealContactIdHash = fakeRealContactIdHash.Union( + contactDao.SaveContactList(portion)) + .ToDictionary(item => item.Key, item => item.Value); + + + #region CRMSecurity set -by every item- + + portion.ForEach(ct => _crmSecurity.SetAccessTo(ct, _importSettings.ContactManagers)); + + #endregion + + + index += DaoIterationStep; + if (index > findedCompaniesList.Count) + { + index = findedCompaniesList.Count; + } + } + } + + + foreach (Company item in findedCompanies.Values) + { + if (companyNameRealIdHash.ContainsKey(item.CompanyName)) continue; + + companyNameRealIdHash.Add(item.CompanyName, item.ID); + } + + foreach (var item in personFakeIdCompanyNameHash) + { + var person = (Person)findedPeoples[item.Key]; + + if (companyNameRealIdHash.ContainsKey(item.Value)) + { + person.CompanyID = companyNameRealIdHash[item.Value]; + } + else + { + var findedCompany = contactDao.GetContactsByName(item.Value, true).FirstOrDefault(); + + // Why ??? + if (findedCompany == null) + { + #region create COMPANY for person in csv + + findedCompany = new Company + { + CompanyName = item.Value, + ShareType = _importSettings.ShareType + }; + findedCompany.ID = contactDao.SaveContact(findedCompany); + + person.CompanyID = findedCompany.ID; + _crmSecurity.SetAccessTo(findedCompany, _importSettings.ContactManagers); + + if (_importSettings.Tags.Count != 0) + { + tagDao.SetTagToEntity(EntityType.Contact, person.CompanyID, _importSettings.Tags.ToArray()); + } + + #endregion + } + else + { + person.CompanyID = findedCompany.ID; + } + + companyNameRealIdHash.Add(item.Value, person.CompanyID); + } + } + #endregion + + #region Saving People common data -by portions- + CRMSecurity + + var findedPeopleList = findedPeoples.Values.ToList(); + if (findedPeopleList.Count != 0) + { + index = 0; + while (index < findedPeopleList.Count) + { + var portion = findedPeopleList.Skip(index).Take(DaoIterationStep).ToList();// Get next step + + fakeRealContactIdHash = fakeRealContactIdHash.Union( + contactDao.SaveContactList(portion)) + .ToDictionary(item => item.Key, item => item.Value); + + #region CRMSecurity set -by every item- + + portion.ForEach(ct => _crmSecurity.SetAccessTo(ct, _importSettings.ContactManagers)); + + #endregion + + + index += DaoIterationStep; + if (index > findedPeopleList.Count) + { + index = findedPeopleList.Count; + } + } + } + _log.Info("ImportContactsData. Contacts common data saved"); + #endregion + + Percentage += 12.5; + PublishChanges(); + + #region Save contact infos -by portions- + + if (findedContactInfos.Count != 0) + { + findedContactInfos.ForEach(item => item.ContactID = fakeRealContactIdHash[item.ContactID]); + + index = 0; + while (index < findedContactInfos.Count) + { + var portion = findedContactInfos.Skip(index).Take(DaoIterationStep).ToList();// Get next step + + contactInfoDao.SaveList(portion); + + index += DaoIterationStep; + if (index > findedContactInfos.Count) + { + index = findedContactInfos.Count; + } + } + } + _log.Info("ImportContactsData. Contacts infos saved"); + #endregion + + Percentage += 12.5; + PublishChanges(); + + #region Save custom fields -by portions- + + if (findedCustomField.Count != 0) + { + findedCustomField.ForEach(item => item.EntityID = fakeRealContactIdHash[item.EntityID]); + + index = 0; + while (index < findedCustomField.Count) + { + var portion = findedCustomField.Skip(index).Take(DaoIterationStep).ToList();// Get next step + + customFieldDao.SaveList(portion); + + index += DaoIterationStep; + if (index > findedCustomField.Count) + { + index = findedCustomField.Count; + } + } + } + _log.Info("ImportContactsData. Custom fields saved"); + #endregion + + Percentage += 12.5; + PublishChanges(); + + #region Save tags + + var findedTagsValues = new List(); + + findedTags.Values.ToList().ForEach(t => { findedTagsValues.AddRange(t); }); + findedTagsValues = findedTagsValues.Distinct().ToList(); + + var allTagsForImport = tagDao.GetAndAddTags(EntityType.Contact, findedTagsValues.Distinct().ToArray()); + + foreach (var findedTagKey in findedTags.Keys) + { + var curTagNames = findedTags[findedTagKey]; + var curTagIds = curTagNames.ConvertAll(n => allTagsForImport.ContainsKey(n) ? allTagsForImport[n] : 0).Where(id => id != 0).ToArray(); + + tagDao.AddTagToEntity(EntityType.Contact, fakeRealContactIdHash[findedTagKey], curTagIds); + } + _log.Info("ImportContactsData. Tags saved"); + #endregion + + Percentage += 12.5; + PublishChanges(); + + Complete(); + } + + private bool _CommonData(int currentIndex, DaoFactory _daoFactory, ref Contact contact, ref Dictionary personFakeIdCompanyNameHash) + { + var firstName = GetPropertyValue("firstName"); + var lastName = GetPropertyValue("lastName"); + var companyName = GetPropertyValue("companyName"); + + if (String.IsNullOrEmpty(firstName) && String.IsNullOrEmpty(lastName) && String.IsNullOrEmpty(companyName)) + return false; + + Percentage += 1.0 * 100 / (_importFromCSV.MaxRoxCount * 3); + PublishChanges(); + + + var listItemDao = _daoFactory.GetListItemDao(); + + + if (!String.IsNullOrEmpty(firstName) || !String.IsNullOrEmpty(lastName)) + { + var person = new Person + { + ID = currentIndex, + FirstName = !String.IsNullOrEmpty(firstName) ? firstName : lastName, + LastName = !String.IsNullOrEmpty(firstName) ? lastName : String.Empty, + JobTitle = GetPropertyValue("title") + }; + + if (!(String.IsNullOrEmpty(companyName))) + personFakeIdCompanyNameHash.Add(person.ID, companyName); + + contact = person; + } + else + { + contact = new Company + { + ID = currentIndex + }; + + ((Company)contact).CompanyName = companyName; + } + + contact.About = GetPropertyValue("notes"); + contact.ShareType = _importSettings.ShareType; + + var contactStageName = GetPropertyValue("contactStage"); + var contactTypeName = GetPropertyValue("contactType"); + if (!String.IsNullOrEmpty(contactStageName)) + { + var contactStage = listItemDao.GetByTitle(ListType.ContactStatus, contactStageName); + if (contactStage != null) + { + contact.StatusID = contactStage.ID; + } + else + { + contact.StatusID = listItemDao.CreateItem(ListType.ContactStatus, new ListItem() + { + Title = contactStageName, + Color = "#77cf9a", + Description = "" + }); + } + } + + if (!String.IsNullOrEmpty(contactTypeName)) + { + var contactType = listItemDao.GetByTitle(ListType.ContactType, contactTypeName); + if (contactType != null) + { + contact.ContactTypeID = contactType.ID; + } + else + { + contact.ContactTypeID = listItemDao.CreateItem(ListType.ContactType, new ListItem() + { + Title = contactTypeName, + Description = "" + }); + } + } + + return true; + } + + #region Read methods + + private void _ReadTags(ref Dictionary> findedTags, Contact contact) + { + var tag = GetPropertyValue("tag"); + + if (!String.IsNullOrEmpty(tag)) + { + var tagList = tag.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); + tagList.AddRange(_importSettings.Tags); + tagList = tagList.Distinct().ToList(); + findedTags.Add(contact.ID, tagList); + } + else if (_importSettings.Tags.Count != 0) + { + findedTags.Add(contact.ID, _importSettings.Tags); + } + } + + private void _ReadCustomField(JsonProperty jToken, String propertyValue, Contact contact, ref List findedCustomField, CustomFieldDao customFieldDao) + { + var fieldID = Convert.ToInt32(jToken.Name.Split(new[] { '_' })[1]); + var field = customFieldDao.GetFieldDescription(fieldID); + + if (field != null) + { + findedCustomField.Add(new CustomField + { + EntityID = contact.ID, + EntityType = contact is Person ? EntityType.Person : EntityType.Company, + ID = fieldID, + Value = field.Type == CustomFieldType.CheckBox ? (propertyValue == "on" || propertyValue == "true" ? "true" : "false") : propertyValue + }); + } + } + + private void _ReadContactInfo(JsonProperty jToken, String propertyValue, Contact contact, ref List findedContactInfos, ref List primaryFields, String addressTemplateStr) + { + var nameParts = jToken.Name.Split(new[] { '_' }).Skip(1).ToList(); + var contactInfoType = + (ContactInfoType)Enum.Parse(typeof(ContactInfoType), nameParts[0]); + + if (contactInfoType == ContactInfoType.Email) + { + var validEmails = propertyValue + .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + .Where(email => email.TestEmailRegex()).ToArray(); + + if (!validEmails.Any()) + return; + + propertyValue = string.Join(",", validEmails); + } + + var category = Convert.ToInt32(nameParts[1]); + + var isPrimary = false; + + if ((contactInfoType == ContactInfoType.Email || + contactInfoType == ContactInfoType.Phone || + contactInfoType == ContactInfoType.Address) && + (!primaryFields.Contains((int)contactInfoType))) + { + isPrimary = true; + + primaryFields.Add((int)contactInfoType); + } + + if (contactInfoType == ContactInfoType.Address) + { + var addressPart = (AddressPart)Enum.Parse(typeof(AddressPart), nameParts[2]); + + var findedAddress = + findedContactInfos.Find( + item => + (category == item.Category) && (item.InfoType == ContactInfoType.Address) && + (item.ContactID == contact.ID)); + + if (findedAddress == null) + { + findedAddress = new ContactInfo + { + Category = category, + InfoType = contactInfoType, + Data = addressTemplateStr, + ContactID = contact.ID, + IsPrimary = isPrimary + }; + + findedContactInfos.Add(findedAddress); + } + + var addressParts = JsonSerializer.Deserialize>(findedAddress.Data); + + addressParts[addressPart.ToString().ToLower()] = propertyValue; + + findedAddress.Data = JsonSerializer.Serialize(addressParts); + + return; + } + + var items = propertyValue.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + + foreach (var item in items) + { + findedContactInfos.Add(new ContactInfo + { + Category = category, + InfoType = contactInfoType, + Data = item, + ContactID = contact.ID, + IsPrimary = isPrimary + }); + + isPrimary = false; + } + } + + #endregion + + private void _DuplicateRecordRuleProcess(DaoFactory _daoFactory, + ref Dictionary findedContacts, + ref Dictionary personFakeIdCompanyNameHash, + ref List findedContactInfos, + ref List findedCustomField, + ref Dictionary> findedTags) + { + var contactDao = _daoFactory.GetContactDao(); + + _log.Info("_DuplicateRecordRuleProcess. Start"); + + switch (_importSettings.DuplicateRecordRule) + { + case 1: // Skip + { + var emails = findedContactInfos.Where(item => item.InfoType == ContactInfoType.Email).ToList(); + + if (emails.Count == 0) break; + + var index = 0; + while (index < emails.Count) + { + var emailsIteration = emails.Skip(index).Take(DaoIterationStep).ToList();// Get next step + + var duplicateContactsID = contactDao.FindDuplicateByEmail(emailsIteration, false) + .Distinct() + .ToList(); + + if (duplicateContactsID.Count != 0) + { + findedContacts = findedContacts.Where(item => !duplicateContactsID.Contains(item.Key)).ToDictionary(x => x.Key, y => y.Value); + + personFakeIdCompanyNameHash = personFakeIdCompanyNameHash.Where(item => !duplicateContactsID.Contains(item.Key)).ToDictionary(x => x.Key, y => y.Value); + + if (findedContacts.Count == 0) + { + Complete(); + return; + } + + findedContactInfos = findedContactInfos.Where(item => !duplicateContactsID.Contains(item.ContactID)).ToList(); + findedCustomField = findedCustomField.Where(item => !duplicateContactsID.Contains(item.EntityID)).ToList(); + + foreach (var exceptID in duplicateContactsID) + { + if (findedTags.ContainsKey(exceptID)) findedTags.Remove(exceptID); + } + } + + index += DaoIterationStep; + if (index > emails.Count) + { + index = emails.Count; + } + } + } + break; + case 2: // Overwrite + { + var emailContactInfos = findedContactInfos.Where(item => item.InfoType == ContactInfoType.Email).ToList(); + if (emailContactInfos.Count == 0) break; + + _log.InfoFormat("_DuplicateRecordRuleProcess. Overwrite. Start. All emeails count = {0}", emailContactInfos.Count); + + var index = 0; + while (index < emailContactInfos.Count) + { + var emailsIteration = emailContactInfos.Skip(index).Take(DaoIterationStep).ToList();// Get next step + + _log.InfoFormat("_DuplicateRecordRuleProcess. Overwrite. Portion from index = {0}. count = {1}", index, emailsIteration.Count); + var duplicateContactsID = contactDao.FindDuplicateByEmail(emailsIteration, true) + .Distinct() + .ToArray(); + + _log.InfoFormat("_DuplicateRecordRuleProcess. Overwrite. FindDuplicateByEmail result count = {0}", duplicateContactsID.Length); + var deleted = contactDao.DeleteBatchContact(duplicateContactsID); + + _log.InfoFormat("_DuplicateRecordRuleProcess. Overwrite. DeleteBatchContact. Was deleted {0} contacts", deleted != null ? deleted.Count : 0); + + index += DaoIterationStep; + if (index > emailContactInfos.Count) + { + index = emailContactInfos.Count; + } + } + + break; + } + case 3: // Clone + break; + default: + break; + } + } + + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Utils/Import/CSV/ImportDataOperation.cs b/products/ASC.CRM/Server/Utils/Import/CSV/ImportDataOperation.cs new file mode 100644 index 00000000000..f8712f5a66f --- /dev/null +++ b/products/ASC.CRM/Server/Utils/Import/CSV/ImportDataOperation.cs @@ -0,0 +1,208 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Linq; +using System.Text.Json; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.Common.Security.Authentication; +using ASC.Common.Threading; +using ASC.Common.Threading.Progress; +using ASC.Core; +using ASC.Core.Common.Settings; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; +using ASC.Data.Storage; +using ASC.Web.CRM.Services.NotifyService; + +using Microsoft.Extensions.Options; + +namespace ASC.Web.CRM.Classes +{ + [Transient] + public partial class ImportDataOperation : DistributedTaskProgress, IProgressItem + { + private readonly ILog _log; + private readonly IDataStore _dataStore; + private readonly IAccount _author; + private readonly int _tenantID; + private string _csvFileURI; + private ImportCSVSettings _importSettings; + private EntityType _entityType; + private string[] _columns; + private bool _IsConfigure; + + private readonly CurrencyProvider _currencyProvider; + private readonly NotifyClient _notifyClient; + private readonly SettingsManager _settingsManager; + private readonly CrmSecurity _crmSecurity; + private readonly TenantManager _tenantManager; + private readonly SecurityContext _securityContext; + private readonly UserManager _userManager; + private readonly DaoFactory _daoFactory; + private readonly ILog _logManager; + + public ImportDataOperation(Global global, + TenantManager tenantManager, + IOptionsMonitor logger, + UserManager userManager, + CrmSecurity crmSecurity, + NotifyClient notifyClient, + SettingsManager settingsManager, + CurrencyProvider currencyProvider, + DaoFactory daoFactory, + SecurityContext securityContext + ) + { + _userManager = userManager; + + _securityContext = securityContext; + _dataStore = global.GetStore(); + + _tenantID = tenantManager.GetCurrentTenant().TenantId; + _author = _securityContext.CurrentAccount; + + _notifyClient = notifyClient; + + Id = String.Format("{0}_{1}", _tenantID, (int)_entityType); + + _log = logger.Get("ASC.CRM"); + + _crmSecurity = crmSecurity; + _settingsManager = settingsManager; + _currencyProvider = currencyProvider; + _daoFactory = daoFactory; + } + + public void Configure(EntityType entityType, + string CSVFileURI, + string importSettingsJSON) + { + + _entityType = entityType; + _csvFileURI = CSVFileURI; + + if (!String.IsNullOrEmpty(importSettingsJSON)) + _importSettings = new ImportCSVSettings(importSettingsJSON); + + _IsConfigure = true; + } + + public override bool Equals(object obj) + { + if (obj == null) return false; + if (!(obj is ImportDataOperation)) return false; + + var dataOperation = (ImportDataOperation)obj; + + if (_tenantID == dataOperation._tenantID && _entityType == dataOperation._entityType) return true; + + return base.Equals(obj); + } + + public override int GetHashCode() + { + return _tenantID.GetHashCode() + _entityType.GetHashCode(); + } + + public object Clone() + { + return MemberwiseClone(); + } + + + public object Error { get; set; } + + private String GetPropertyValue(String propertyName) + { + JsonElement jsonElement; + + if (!_importSettings.ColumnMapping.TryGetProperty(propertyName, out jsonElement)) return String.Empty; + + var values = jsonElement.EnumerateArray().Select(x => x.GetInt32()).ToList().ConvertAll(columnIndex => _columns[columnIndex]); + + values.RemoveAll(item => item == String.Empty); + + return String.Join(",", values.ToArray()); + } + + private void Complete() + { + IsCompleted = true; + + Percentage = 100; + + _log.Debug("Import is completed"); + + _notifyClient.SendAboutImportCompleted(_author.ID, _entityType); + } + + protected override void DoJob() + { + try + { + if (!_IsConfigure) + throw new Exception("Is not configure. Please, call configure method."); + + _tenantManager.SetCurrentTenant(_tenantID); + _securityContext.AuthenticateMe(_author); + + var userCulture = _userManager.GetUsers(_securityContext.CurrentAccount.ID).GetCulture(); + + System.Threading.Thread.CurrentThread.CurrentCulture = userCulture; + System.Threading.Thread.CurrentThread.CurrentUICulture = userCulture; + + switch (_entityType) + { + case EntityType.Contact: + ImportContactsData(_daoFactory); + break; + case EntityType.Opportunity: + ImportOpportunityData(_daoFactory); + break; + case EntityType.Case: + ImportCaseData(_daoFactory); + break; + case EntityType.Task: + ImportTaskData(_daoFactory); + break; + default: + throw new ArgumentException(CRMErrorsResource.EntityTypeUnknown); + } + + } + catch (OperationCanceledException) + { + _log.Debug("Queue canceled"); + } + } + } + +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Utils/Import/CSV/ImportDeals.cs b/products/ASC.CRM/Server/Utils/Import/CSV/ImportDeals.cs new file mode 100644 index 00000000000..92fe4b6a69b --- /dev/null +++ b/products/ASC.CRM/Server/Utils/Import/CSV/ImportDeals.cs @@ -0,0 +1,315 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; + +using ASC.Core.Users; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; + +using LumenWorks.Framework.IO.Csv; + +#endregion + +namespace ASC.Web.CRM.Classes +{ + public partial class ImportDataOperation + { + private void ImportOpportunityData(DaoFactory _daoFactory) + { + var allUsers = _userManager.GetUsers(EmployeeStatus.All).ToList(); + + using (var CSVFileStream = _dataStore.GetReadStream("temp", _csvFileURI)) + using (CsvReader csv = _importFromCSV.CreateCsvReaderInstance(CSVFileStream, _importSettings)) + { + int currentIndex = 0; + + var customFieldDao = _daoFactory.GetCustomFieldDao(); + var contactDao = _daoFactory.GetContactDao(); + var tagDao = _daoFactory.GetTagDao(); + var dealDao = _daoFactory.GetDealDao(); + var dealMilestoneDao = _daoFactory.GetDealMilestoneDao(); + + var findedTags = new Dictionary>(); + var findedCustomField = new List(); + var findedDeals = new List(); + var findedDealMembers = new Dictionary>(); + + var dealMilestones = dealMilestoneDao.GetAll(); + + while (csv.ReadNextRecord()) + { + _columns = csv.GetCurrentRowFields(false); + + var obj = new Deal(); + + obj.ID = currentIndex; + + obj.Title = GetPropertyValue("title"); + + if (String.IsNullOrEmpty(obj.Title)) continue; + + obj.Description = GetPropertyValue("description"); + + var csvResponsibleValue = GetPropertyValue("responsible"); + var responsible = allUsers.Where(n => n.DisplayUserName(_displayUserSettingsHelper).Equals(csvResponsibleValue)).FirstOrDefault(); + + if (responsible != null) + obj.ResponsibleID = responsible.ID; + else + obj.ResponsibleID = Constants.LostUser.ID; + + DateTime actualCloseDate; + + DateTime expectedCloseDate; + + if (DateTime.TryParse(GetPropertyValue("actual_close_date"), out actualCloseDate)) + obj.ActualCloseDate = actualCloseDate; + + if (DateTime.TryParse(GetPropertyValue("expected_close_date"), out expectedCloseDate)) + obj.ExpectedCloseDate = expectedCloseDate; + + var currency = _currencyProvider.Get(GetPropertyValue("bid_currency")); + + var crmSettings = _settingsManager.Load(); + var defaultCurrency = _currencyProvider.Get(crmSettings.DefaultCurrency); + + if (currency != null) + obj.BidCurrency = currency.Abbreviation; + else + obj.BidCurrency = defaultCurrency.Abbreviation; + + decimal bidValue; + + var bidValueStr = GetPropertyValue("bid_amount"); + + if (Decimal.TryParse(bidValueStr, out bidValue)) + obj.BidValue = bidValue; + else + obj.BidValue = 0; + + + var bidTypeStr = GetPropertyValue("bid_type"); + + BidType bidType = BidType.FixedBid; + + if (!string.IsNullOrEmpty(bidTypeStr)) + { + if (String.Compare(CRMDealResource.BidType_FixedBid, bidTypeStr, true) == 0) + bidType = BidType.FixedBid; + else if (String.Compare(CRMDealResource.BidType_PerDay, bidTypeStr, true) == 0) + bidType = BidType.PerDay; + else if (String.Compare(CRMDealResource.BidType_PerHour, bidTypeStr, true) == 0) + bidType = BidType.PerHour; + else if (String.Compare(CRMDealResource.BidType_PerMonth, bidTypeStr, true) == 0) + bidType = BidType.PerMonth; + else if (String.Compare(CRMDealResource.BidType_PerWeek, bidTypeStr, true) == 0) + bidType = BidType.PerWeek; + else if (String.Compare(CRMDealResource.BidType_PerYear, bidTypeStr, true) == 0) + bidType = BidType.PerYear; + } + + obj.BidType = bidType; + + if (obj.BidType != BidType.FixedBid) + { + int perPeriodValue; + + if (int.TryParse(GetPropertyValue("per_period_value"), out perPeriodValue)) + obj.PerPeriodValue = perPeriodValue; + } + + int probabilityOfWinning; + + if (int.TryParse(GetPropertyValue("probability_of_winning"), out probabilityOfWinning)) + obj.DealMilestoneProbability = probabilityOfWinning; + + var dealMilestoneTitle = GetPropertyValue("deal_milestone"); + + var tag = GetPropertyValue("tag"); + + + if (!String.IsNullOrEmpty(tag)) + { + var tagList = tag.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); + tagList.AddRange(_importSettings.Tags); + tagList = tagList.Distinct().ToList(); + findedTags.Add(obj.ID, tagList); + } + else if (_importSettings.Tags.Count != 0) + { + findedTags.Add(obj.ID, _importSettings.Tags); + } + + + if (String.IsNullOrEmpty(dealMilestoneTitle)) + obj.DealMilestoneID = dealMilestones[0].ID; + else + { + var dealMilestone = dealMilestones.Find(item => String.Compare(item.Title, dealMilestoneTitle, true) == 0); + + if (dealMilestone == null) + obj.DealMilestoneID = dealMilestones[0].ID; + else + obj.DealMilestoneID = dealMilestone.ID; + } + + var contactName = GetPropertyValue("client"); + + var localMembersDeal = new List(); + + if (!String.IsNullOrEmpty(contactName)) + { + var contacts = contactDao.GetContactsByName(contactName, true); + + if (contacts.Count > 0) + { + obj.ContactID = contacts[0].ID; + localMembersDeal.Add(obj.ContactID); + } + else + { + contacts = contactDao.GetContactsByName(contactName, false); + if (contacts.Count > 0) + { + obj.ContactID = contacts[0].ID; + localMembersDeal.Add(obj.ContactID); + } + } + } + + var members = GetPropertyValue("member"); + + if (!String.IsNullOrEmpty(members)) + { + var membersList = members.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + + foreach (var item in membersList) + { + var findedMember = contactDao.GetContactsByName(item, true); + + if (findedMember.Count > 0) + { + localMembersDeal.Add(findedMember[0].ID); + } + else + { + findedMember = _daoFactory.GetContactDao().GetContactsByName(item, false); + if (findedMember.Count > 0) + { + localMembersDeal.Add(findedMember[0].ID); + } + } + } + } + + if (localMembersDeal.Count > 0) + findedDealMembers.Add(obj.ID, localMembersDeal); + + + foreach (JsonProperty jToken in _importSettings.ColumnMapping.EnumerateObject()) + { + var propertyValue = GetPropertyValue(jToken.Name); + + if (String.IsNullOrEmpty(propertyValue)) continue; + + if (!jToken.Name.StartsWith("customField_")) continue; + + var fieldID = Convert.ToInt32(jToken.Name.Split(new[] { '_' })[1]); + var field = customFieldDao.GetFieldDescription(fieldID); + + if (field != null) + { + findedCustomField.Add(new CustomField + { + EntityID = obj.ID, + EntityType = EntityType.Opportunity, + ID = fieldID, + Value = field.Type == CustomFieldType.CheckBox ? (propertyValue == "on" || propertyValue == "true" ? "true" : "false") : propertyValue + }); + } + } + + Percentage += 1.0 * 100 / (_importFromCSV.MaxRoxCount * 2); + PublishChanges(); + + findedDeals.Add(obj); + + if (currentIndex + 1 > _importFromCSV.MaxRoxCount) break; + + currentIndex++; + + } + + Percentage = 50; + PublishChanges(); + + + var newDealIDs = dealDao.SaveDealList(findedDeals); + findedDeals.ForEach(d => d.ID = newDealIDs[d.ID]); + + Percentage += 12.5; + PublishChanges(); + + findedCustomField.ForEach(item => item.EntityID = newDealIDs[item.EntityID]); + + customFieldDao.SaveList(findedCustomField); + + Percentage += 12.5; + PublishChanges(); + + foreach (var findedDealMemberKey in findedDealMembers.Keys) + { + dealDao.SetMembers(newDealIDs[findedDealMemberKey], findedDealMembers[findedDealMemberKey].ToArray()); + } + + Percentage += 12.5; + PublishChanges(); + + foreach (var findedTagKey in findedTags.Keys) + { + tagDao.SetTagToEntity(EntityType.Opportunity, newDealIDs[findedTagKey], findedTags[findedTagKey].ToArray()); + } + + if (_importSettings.IsPrivate) + findedDeals.ForEach(dealItem => _crmSecurity.SetAccessTo(dealItem, _importSettings.AccessList)); + + Percentage += 12.5; + PublishChanges(); + } + + Complete(); + } + + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Utils/Import/CSV/ImportFromCSV.cs b/products/ASC.CRM/Server/Utils/Import/CSV/ImportFromCSV.cs new file mode 100644 index 00000000000..353b453c2b7 --- /dev/null +++ b/products/ASC.CRM/Server/Utils/Import/CSV/ImportFromCSV.cs @@ -0,0 +1,154 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.IO; +using System.Linq; +using System.Text.Json; + +using ASC.Common; +using ASC.Common.Threading; +using ASC.Common.Threading.Progress; +using ASC.Core; +using ASC.CRM.Core.Enums; + +using LumenWorks.Framework.IO.Csv; + +namespace ASC.Web.CRM.Classes +{ + [Scope] + public class ImportFromCSV + { + private readonly ImportDataOperation _importDataOperation; + private readonly int _tenantId; + private readonly object _syncObj = new object(); + private readonly DistributedTaskQueue _importQueue; + public readonly int MaxRoxCount = 10000; + + public ImportFromCSV(TenantManager tenantProvider, + DistributedTaskQueueOptionsManager progressQueueOptionsManager, + ImportDataOperation importDataOperation) + { + _tenantId = tenantProvider.GetCurrentTenant().TenantId; + _importQueue = progressQueueOptionsManager.Get(); + _importDataOperation = importDataOperation; + } + + + public int GetQuotas() + { + return MaxRoxCount; + } + + public CsvReader CreateCsvReaderInstance(Stream CSVFileStream, ImportCSVSettings importCsvSettings) + { + var result = new CsvReader( + new StreamReader(CSVFileStream, importCsvSettings.Encoding, true), + importCsvSettings.HasHeader, importCsvSettings.DelimiterCharacter, importCsvSettings.QuoteType, '"', '#', ValueTrimmingOptions.UnquotedOnly) + { SkipEmptyLines = true, SupportsMultiline = true, DefaultParseErrorAction = ParseErrorAction.AdvanceToNextLine, MissingFieldAction = MissingFieldAction.ReplaceByEmpty }; + + return result; + } + + public String GetRow(Stream CSVFileStream, int index, String jsonSettings) + { + var importCSVSettings = new ImportCSVSettings(jsonSettings); + + CsvReader csv = CreateCsvReaderInstance(CSVFileStream, importCSVSettings); + + int countRows = 0; + + index++; + + while (countRows++ != index && csv.ReadNextRecord()) ; + + return JsonSerializer.Serialize(new + { + data = csv.GetCurrentRowFields(false).ToArray(), + isMaxIndex = csv.EndOfStream + }); + } + + public JsonDocument GetInfo(Stream CSVFileStream, String jsonSettings) + { + var importCSVSettings = new ImportCSVSettings(jsonSettings); + + CsvReader csv = CreateCsvReaderInstance(CSVFileStream, importCSVSettings); + + csv.ReadNextRecord(); + + var firstRowFields = csv.GetCurrentRowFields(false); + + String[] headerRowFields = csv.GetFieldHeaders().ToArray(); + + if (!importCSVSettings.HasHeader) + headerRowFields = firstRowFields; + + return JsonDocument.Parse(JsonSerializer.Serialize(new + { + headerColumns = headerRowFields, + firstContactFields = firstRowFields, + isMaxIndex = csv.EndOfStream + })); + } + + protected String GetKey(EntityType entityType) + { + return String.Format("{0}_{1}", _tenantId, (int)entityType); + } + + public IProgressItem GetStatus(EntityType entityType) + { + var operation = _importQueue.GetTasks().FirstOrDefault(x => x.Id == GetKey(entityType)); + + return operation; + } + + public IProgressItem Start(EntityType entityType, String CSVFileURI, String importSettingsJSON) + { + lock (_syncObj) + { + var operation = _importQueue.GetTasks().FirstOrDefault(x => x.Id == GetKey(entityType)); + + if (operation != null && operation.IsCompleted) + { + _importQueue.RemoveTask(operation.Id); + + operation = null; + + } + + if (operation == null) + { + _importDataOperation.Configure(entityType, CSVFileURI, importSettingsJSON); + _importQueue.QueueTask(_importDataOperation); + } + + return operation; + } + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Utils/Import/CSV/ImportTasks.cs b/products/ASC.CRM/Server/Utils/Import/CSV/ImportTasks.cs new file mode 100644 index 00000000000..208b1c1a0d4 --- /dev/null +++ b/products/ASC.CRM/Server/Utils/Import/CSV/ImportTasks.cs @@ -0,0 +1,186 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Core.Tenants; +using ASC.Core.Users; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; +using ASC.Web.Core.Users; + +using LumenWorks.Framework.IO.Csv; + +#endregion + +namespace ASC.Web.CRM.Classes +{ + public partial class ImportDataOperation + { + private ImportFromCSV _importFromCSV; + private TenantUtil _tenantUtil; + private DisplayUserSettingsHelper _displayUserSettingsHelper; + + public ImportDataOperation(TenantUtil tenantUtil, + ImportFromCSV importFromCSV, + DisplayUserSettingsHelper displayUserSettingsHelper) + { + _tenantUtil = tenantUtil; + _importFromCSV = importFromCSV; + _displayUserSettingsHelper = displayUserSettingsHelper; + } + + private void ImportTaskData(DaoFactory _daoFactory) + { + using (var CSVFileStream = _dataStore.GetReadStream("temp", _csvFileURI)) + using (CsvReader csv = _importFromCSV.CreateCsvReaderInstance(CSVFileStream, _importSettings)) + { + int currentIndex = 0; + + var contactDao = _daoFactory.GetContactDao(); + var listItemDao = _daoFactory.GetListItemDao(); + var taskDao = _daoFactory.GetTaskDao(); + + var findedTasks = new List(); + var taskCategories = listItemDao.GetItems(ListType.TaskCategory); + + var allUsers = _userManager.GetUsers(EmployeeStatus.All).ToList(); + + while (csv.ReadNextRecord()) + { + _columns = csv.GetCurrentRowFields(false); + + var obj = new Task(); + + obj.ID = currentIndex; + + obj.Title = GetPropertyValue("title"); + + if (String.IsNullOrEmpty(obj.Title)) continue; + + obj.Description = GetPropertyValue("description"); + + DateTime deadline; + + if (DateTime.TryParse(GetPropertyValue("due_date"), out deadline)) + obj.DeadLine = deadline; + else + obj.DeadLine = _tenantUtil.DateTimeNow(); + + var csvResponsibleValue = GetPropertyValue("responsible"); + var responsible = allUsers.Where(n => n.DisplayUserName(_displayUserSettingsHelper).Equals(csvResponsibleValue)).FirstOrDefault(); + + if (responsible != null) + obj.ResponsibleID = responsible.ID; + else + obj.ResponsibleID = Constants.LostUser.ID; + + var categoryTitle = GetPropertyValue("taskCategory"); + + if (!String.IsNullOrEmpty(categoryTitle)) + { + var findedCategory = taskCategories.Find(item => String.Compare(item.Title, categoryTitle) == 0); + + if (findedCategory == null) + { + obj.CategoryID = taskCategories[0].ID; + } + else + obj.CategoryID = findedCategory.ID; + } + else + obj.CategoryID = taskCategories[0].ID; + + var contactName = GetPropertyValue("contact"); + + if (!String.IsNullOrEmpty(contactName)) + { + var contacts = contactDao.GetContactsByName(contactName, true); + + if (contacts.Count > 0) + { + obj.ContactID = contacts[0].ID; + } + else + { + contacts = contactDao.GetContactsByName(contactName, false); + if (contacts.Count > 0) + { + obj.ContactID = contacts[0].ID; + } + } + } + + obj.IsClosed = false; + + var taskStatus = GetPropertyValue("status"); + + if (!String.IsNullOrEmpty(taskStatus)) + { + if (String.Compare(taskStatus, CRMTaskResource.TaskStatus_Closed, true) == 0) + obj.IsClosed = true; + + } + + var alertValue = GetPropertyValue("alertValue"); + int alertIntVal = 0; + + if (Int32.TryParse(alertValue, out alertIntVal)) + obj.AlertValue = alertIntVal; + else + obj.AlertValue = 0; + + + findedTasks.Add(obj); + + if ((currentIndex + 1) > _importFromCSV.MaxRoxCount) break; + + currentIndex++; + + } + + Percentage = 50; + PublishChanges(); + + taskDao.SaveTaskList(findedTasks); + + Percentage += 12.5; + PublishChanges(); + + Complete(); + + } + + } + + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Utils/MailSender.cs b/products/ASC.CRM/Server/Utils/MailSender.cs new file mode 100644 index 00000000000..faa4254a0fa --- /dev/null +++ b/products/ASC.CRM/Server/Utils/MailSender.cs @@ -0,0 +1,958 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +#region Import + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Sockets; + +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.RegularExpressions; +using System.Threading; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.Common.Threading; +using ASC.Common.Threading.Progress; +using ASC.Core; +using ASC.Core.Common.Settings; +using ASC.Core.Tenants; +using ASC.CRM.Classes; +using ASC.CRM.Core; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; +using ASC.Web.Files.Api; + +using MailKit; +using MailKit.Net.Smtp; +using MailKit.Security; + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; + +using MimeKit; + +using File = System.IO.File; +using SmtpClient = MailKit.Net.Smtp.SmtpClient; + +#endregion + +namespace ASC.Web.CRM.Classes +{ + [Transient] + public class SendBatchEmailsOperation : DistributedTaskProgress, IProgressItem, IDisposable + { + private bool _storeInHistory; + private readonly ILog _log; + private readonly SMTPServerSetting _smtpSetting; + private readonly Guid _currUser; + private readonly int _tenantID; + private List _contactID; + private String _subject; + private String _bodyTempate; + private List _fileID; + private int historyCategory; + private double _exactPercentageValue = 0; + + private DaoFactory _daoFactory; + private FilesIntegration _filesIntegration; + private AuthManager _authManager; + private UserManager _userManager; + private TenantManager _tenantManager; + private SecurityContext _securityContext; + private TenantUtil _tenantUtil; + + public object Error { get; set; } + + private SendBatchEmailsOperation() + { + } + + public SendBatchEmailsOperation( + TenantUtil tenantUtil, + IOptionsMonitor logger, + Global global, + SecurityContext securityContext, + TenantManager tenantManager, + UserManager userManager, + AuthManager authManager, + SettingsManager settingsManager, + DaoFactory daoFactory, + CoreConfiguration coreConfiguration, + FilesIntegration filesIntegration + ) + { + _tenantUtil = tenantUtil; + _securityContext = securityContext; + + Percentage = 0; + + _log = logger.Get("ASC.CRM.MailSender"); + + _tenantID = tenantManager.GetCurrentTenant().TenantId; + + var _crmSettings = settingsManager.Load(); + + _smtpSetting = new SMTPServerSetting(coreConfiguration.SmtpSettings); + _currUser = _securityContext.CurrentAccount.ID; + + _authManager = authManager; + _userManager = userManager; + _daoFactory = daoFactory; + _tenantManager = tenantManager; + _filesIntegration = filesIntegration; + } + + + public void Configure(List fileID, + List contactID, + String subject, + String bodyTempate, + bool storeInHistory) + { + _fileID = fileID ?? new List(); + _contactID = contactID ?? new List(); + _subject = subject; + _bodyTempate = bodyTempate; + _storeInHistory = storeInHistory; + + SetProperty("RecipientCount", _contactID.Count); + SetProperty("EstimatedTime", 0); + SetProperty("DeliveryCount", 0); + } + + private void AddToHistory(int contactID, String content, DaoFactory _daoFactory) + { + if (contactID == 0 || String.IsNullOrEmpty(content)) return; + + var historyEvent = new RelationshipEvent() + { + ContactID = contactID, + Content = content, + CreateBy = _currUser, + CreateOn = _tenantUtil.DateTimeNow(), + }; + if (historyCategory == 0) + { + var listItemDao = _daoFactory.GetListItemDao(); + + // HACK + var listItem = listItemDao.GetItems(ListType.HistoryCategory).Find(item => item.AdditionalParams == "event_category_email.png"); + if (listItem == null) + { + listItemDao.CreateItem( + ListType.HistoryCategory, + new ListItem { AdditionalParams = "event_category_email.png", Title = CRMCommonResource.HistoryCategory_Note }); + } + historyCategory = listItem.ID; + } + + historyEvent.CategoryID = historyCategory; + + var relationshipEventDao = _daoFactory.GetRelationshipEventDao(); + + historyEvent = relationshipEventDao.CreateItem(historyEvent); + + if (historyEvent.ID > 0 && _fileID != null && _fileID.Count > 0) + { + relationshipEventDao.AttachFiles(historyEvent.ID, _fileID.ToArray()); + } + } + + protected override void DoJob() + { + SmtpClient smtpClient = null; + try + { + _tenantManager.SetCurrentTenant(_tenantID); + _securityContext.AuthenticateMe(_authManager.GetAccountByID(_tenantID, _currUser)); + + smtpClient = GetSmtpClient(); + + var userCulture = _userManager.GetUsers(_currUser).GetCulture(); + + Thread.CurrentThread.CurrentCulture = userCulture; + Thread.CurrentThread.CurrentUICulture = userCulture; + + var contactCount = _contactID.Count; + + if (contactCount == 0) + { + Complete(); + return; + } + + var from = new MailboxAddress(_smtpSetting.SenderDisplayName, _smtpSetting.SenderEmailAddress); + var filePaths = new List(); + var fileDao = _filesIntegration.DaoFactory.GetFileDao(); + + foreach (var fileID in _fileID) + { + var fileObj = fileDao.GetFile(fileID); + if (fileObj == null) continue; + using (var fileStream = fileDao.GetFileStream(fileObj)) + { + var directoryPath = Path.Combine(Path.GetTempPath(), "teamlab", _tenantID.ToString(), + "crm/files/mailsender/"); + + if (!Directory.Exists(directoryPath)) + { + Directory.CreateDirectory(directoryPath); + } + + var filePath = Path.Combine(directoryPath, fileObj.Title); + + using (var newFileStream = File.Create(filePath)) + { + fileStream.CopyTo(newFileStream); + } + + filePaths.Add(filePath); + + } + } + + var templateManager = new MailTemplateManager(_daoFactory); + var deliveryCount = 0; + + try + { + Error = string.Empty; + foreach (var contactID in _contactID) + { + _exactPercentageValue += 100.0 / contactCount; + Percentage = Math.Round(_exactPercentageValue); + PublishChanges(); + + if (IsCompleted) break; // User selected cancel + + var contactInfoDao = _daoFactory.GetContactInfoDao(); + + var startDate = DateTime.Now; + + var contactEmails = contactInfoDao.GetList(contactID, ContactInfoType.Email, null, true); + if (contactEmails.Count == 0) + { + continue; + } + + var recipientEmail = contactEmails[0].Data; + + if (!recipientEmail.TestEmailRegex()) + { + Error += string.Format(CRMCommonResource.MailSender_InvalidEmail, recipientEmail) + + "
    "; + continue; + } + + var to = new MailboxAddress(recipientEmail); + + var mimeMessage = new MimeMessage + { + Subject = _subject + }; + + mimeMessage.From.Add(from); + mimeMessage.To.Add(to); + + var bodyBuilder = new BodyBuilder + { + HtmlBody = templateManager.Apply(_bodyTempate, contactID) + }; + + foreach (var filePath in filePaths) + { + bodyBuilder.Attachments.Add(filePath); + } + + mimeMessage.Body = bodyBuilder.ToMessageBody(); + + mimeMessage.Headers.Add("Auto-Submitted", "auto-generated"); + + _log.Debug(GetLoggerRow(mimeMessage)); + + var success = false; + + try + { + smtpClient.Send(mimeMessage); + + success = true; + } + catch (SmtpCommandException ex) + { + _log.Error(Error, ex); + + Error += string.Format(CRMCommonResource.MailSender_FailedDeliverException, recipientEmail) + "
    "; + } + + if (success) + { + if (_storeInHistory) + { + AddToHistory(contactID, string.Format(CRMCommonResource.MailHistoryEventTemplate, mimeMessage.Subject), _daoFactory); + } + + var endDate = DateTime.Now; + var waitInterval = endDate.Subtract(startDate); + + deliveryCount++; + + var estimatedTime = + TimeSpan.FromTicks(waitInterval.Ticks * (_contactID.Count - deliveryCount)); + + SetProperty("RecipientCount", _contactID.Count); + SetProperty("EstimatedTime", estimatedTime.ToString()); + SetProperty("DeliveryCount", deliveryCount); + } + + if (Percentage > 100) + { + Percentage = 100; + PublishChanges(); + } + } + } + catch (OperationCanceledException) + { + _log.Debug("cancel mail sender"); + } + finally + { + foreach (var filePath in filePaths) + { + if (File.Exists(filePath)) + { + File.Delete(filePath); + } + } + } + + SetProperty("RecipientCount", _contactID.Count); + SetProperty("EstimatedTime", TimeSpan.Zero.ToString()); + SetProperty("DeliveryCount", deliveryCount); + } + catch (SocketException e) + { + Error = e.Message; + _log.Error(Error); + } + finally + { + if (smtpClient != null) + { + smtpClient.Dispose(); + } + Complete(); + } + } + + public string GetLoggerRow(MimeMessage mailMessage) + { + if (mailMessage == null) + return String.Empty; + + var result = new StringBuilder(); + + result.AppendLine("From:" + mailMessage.From); + result.AppendLine("To:" + mailMessage.To[0]); + result.AppendLine("Subject:" + mailMessage.Subject); + result.AppendLine("Body:" + mailMessage.Body); + result.AppendLine("TenantID:" + _tenantID); + + foreach (var attachment in mailMessage.Attachments) + { + result.AppendLine("Attachment: " + attachment.ContentDisposition.FileName); + } + + return result.ToString(); + } + + public object Clone() + { + var cloneObj = new SendBatchEmailsOperation(); + + cloneObj.Error = Error; + cloneObj.Id = Id; + cloneObj.IsCompleted = IsCompleted; + cloneObj.Percentage = Percentage; + cloneObj.Status = Status; + + return cloneObj; + } + + private void DeleteFiles() + { + if (_fileID == null || _fileID.Count == 0) return; + + var fileDao = _filesIntegration.DaoFactory.GetFileDao(); + + foreach (var fileID in _fileID) + { + var fileObj = fileDao.GetFile(fileID); + if (fileObj == null) continue; + + fileDao.DeleteFile(fileObj.ID); + } + + } + + private SmtpClient GetSmtpClient() + { + var client = new SmtpClient + { + ServerCertificateValidationCallback = (sender, certificate, chain, errors) => + WorkContext.IsMono || MailKit.MailService.DefaultServerCertificateValidationCallback(sender, certificate, chain, errors), + Timeout = (int)TimeSpan.FromSeconds(30).TotalMilliseconds + }; + + client.Connect(_smtpSetting.Host, _smtpSetting.Port, + _smtpSetting.EnableSSL ? SecureSocketOptions.Auto : SecureSocketOptions.None); + + if (_smtpSetting.RequiredHostAuthentication) + { + client.Authenticate(_smtpSetting.HostLogin, _smtpSetting.HostPassword); + } + + return client; + } + + private void Complete() + { + IsCompleted = true; + Percentage = 100; + _log.Debug("Completed"); + } + + public override bool Equals(object obj) + { + if (obj == null || !(obj is SendBatchEmailsOperation)) return false; + + var curOperation = (SendBatchEmailsOperation)obj; + return (curOperation.Id == Id) && (curOperation._tenantID == _tenantID); + } + + public override int GetHashCode() + { + return Id.GetHashCode() ^ _tenantID.GetHashCode(); + } + + public void Dispose() + { + DeleteFiles(); + } + } + + [Scope] + public class MailSender + { + private readonly Object _syncObj = new Object(); + private readonly DistributedTaskQueue _mailQueue; + private readonly int quotas = 50; + private readonly TenantManager _tenantManager; + private readonly SettingsManager _settingsManager; + private readonly SendBatchEmailsOperation _sendBatchEmailsOperation; + private readonly int _tenantID; + private readonly CoreConfiguration _coreConfiguration; + private readonly IOptionsMonitor _logManager; + + + public MailSender( + IConfiguration configuration, + TenantManager tenantManager, + SettingsManager settingsManager, + DistributedTaskQueueOptionsManager progressQueueOptionsManager, + SendBatchEmailsOperation sendBatchEmailsOperation, + CoreConfiguration coreConfiguration, + IOptionsMonitor logger + ) + { + _sendBatchEmailsOperation = sendBatchEmailsOperation; + _tenantID = tenantManager.GetCurrentTenant().TenantId; + _mailQueue = progressQueueOptionsManager.Get(); + _coreConfiguration = coreConfiguration; + _logManager = logger; + + int parsed; + + if (int.TryParse(configuration["crm:mailsender:quotas"], out parsed)) + { + quotas = parsed; + } + + _tenantManager = tenantManager; + _settingsManager = settingsManager; + + // LogManager = logger.Get(); + } + + public int GetQuotas() + { + return quotas; + } + + public IProgressItem Start(List fileID, List contactID, String subject, String bodyTemplate, bool storeInHistory) + { + lock (_syncObj) + { + var operation = _mailQueue.GetTasks().FirstOrDefault(x => Convert.ToInt32(x.Id) == _tenantID); + + if (operation != null && operation.IsCompleted) + { + _mailQueue.RemoveTask(operation.Id); + operation = null; + } + + if (operation == null) + { + if (fileID == null) + { + fileID = new List(); + } + if (contactID == null || contactID.Count == 0 || + String.IsNullOrEmpty(subject) || String.IsNullOrEmpty(bodyTemplate)) + { + return null; + } + + if (contactID.Count > GetQuotas()) + { + contactID = contactID.Take(GetQuotas()).ToList(); + } + + _sendBatchEmailsOperation.Configure(fileID, contactID, subject, bodyTemplate, storeInHistory); + + _mailQueue.QueueTask(_sendBatchEmailsOperation); + } + + return operation; + } + } + + private SmtpClient GetSmtpClient(SMTPServerSetting smtpSetting) + { + var client = new SmtpClient + { + ServerCertificateValidationCallback = (sender, certificate, chain, errors) => MailService.DefaultServerCertificateValidationCallback(sender, certificate, chain, errors), + Timeout = (int)TimeSpan.FromSeconds(30).TotalMilliseconds + }; + + client.Connect(smtpSetting.Host, smtpSetting.Port, + smtpSetting.EnableSSL ? SecureSocketOptions.Auto : SecureSocketOptions.None); + + if (smtpSetting.RequiredHostAuthentication) + { + client.Authenticate(smtpSetting.HostLogin, smtpSetting.HostPassword); + } + + return client; + } + + public void StartSendTestMail(string recipientEmail, string mailSubj, string mailBody) + { + var log = _logManager.Get("ASC.CRM.MailSender"); + + if (!recipientEmail.TestEmailRegex()) + { + throw new Exception(string.Format(CRMCommonResource.MailSender_InvalidEmail, recipientEmail)); + } + + _tenantManager.SetCurrentTenant(_tenantID); + var smtpSetting = new SMTPServerSetting(_coreConfiguration.SmtpSettings); + + ThreadPool.QueueUserWorkItem(_ => + { + try + { + var toAddress = new MailboxAddress(recipientEmail); + var fromAddress = new MailboxAddress(smtpSetting.SenderDisplayName, smtpSetting.SenderEmailAddress); + + var mimeMessage = new MimeMessage + { + Subject = mailSubj + }; + + mimeMessage.From.Add(fromAddress); + + mimeMessage.To.Add(toAddress); + + var bodyBuilder = new BodyBuilder + { + TextBody = mailBody + }; + + mimeMessage.Body = bodyBuilder.ToMessageBody(); + + mimeMessage.Headers.Add("Auto-Submitted", "auto-generated"); + + using (var smtpClient = GetSmtpClient(smtpSetting)) + { + smtpClient.Send(FormatOptions.Default, mimeMessage, CancellationToken.None); + } + } + catch (Exception ex) + { + log.Error(ex); + } + }); + } + + public IProgressItem GetStatus() + { + var findedItem = _mailQueue.GetTasks().FirstOrDefault(x => Convert.ToInt32(x.Id) == _tenantID); + + return findedItem; + } + + public void Cancel() + { + lock (_syncObj) + { + var findedItem = _mailQueue.GetTasks().FirstOrDefault(x => Convert.ToInt32(x.Id) == _tenantID); + + if (findedItem == null) return; + + _mailQueue.RemoveTask(findedItem.Id); + + } + } + } + + public class MailTemplateTag + { + [JsonPropertyName("sysname")] + public String SysName { get; set; } + + [JsonPropertyName("display_name")] + public String DisplayName { get; set; } + + [JsonPropertyName("category")] + public String Category { get; set; } + + [JsonPropertyName("is_company")] + public bool isCompany { get; set; } + + [JsonPropertyName("name")] + public String Name { get; set; } + } + + public class MailTemplateManager + { + private readonly Dictionary> _templateTagsCache = new Dictionary>(); + + private readonly DaoFactory _daoFactory; + + public MailTemplateManager(DaoFactory daoFactory) + { + _daoFactory = daoFactory; + } + + private IEnumerable GetTagsFrom(String template) + { + if (_templateTagsCache.ContainsKey(template)) return _templateTagsCache[template]; + + var tags = GetAllTags(); + + var result = new List(); + + var _regex = new Regex("\\$\\((Person|Company)\\.[^<>\\)]*\\)"); + + + if (!_regex.IsMatch(template)) + return new List(); + + foreach (Match match in _regex.Matches(template)) + { + var findedTag = tags.Find(item => String.Compare(item.Name, match.Value) == 0); + + if (findedTag == null) continue; + + if (!result.Contains(findedTag)) + result.Add(findedTag); + } + + _templateTagsCache.Add(template, result); + + return result; + } + + private String Apply(String template, IEnumerable templateTags, int contactID) + { + var result = template; + + + var contactDao = _daoFactory.GetContactDao(); + var contactInfoDao = _daoFactory.GetContactInfoDao(); + var customFieldDao = _daoFactory.GetCustomFieldDao(); + + var contact = contactDao.GetByID(contactID); + + if (contact == null) + throw new ArgumentException(CRMErrorsResource.ContactNotFound); + + foreach (var tag in templateTags) + { + var tagParts = tag.SysName.Split(new[] { '_' }); + + var source = tagParts[0]; + + var tagValue = String.Empty; + + switch (source) + { + case "common": + + if (contact is Person) + { + + var person = (Person)contact; + + switch (tagParts[1]) + { + + case "firstName": + tagValue = person.FirstName; + + break; + case "lastName": + tagValue = person.LastName; + + break; + case "jobTitle": + tagValue = person.JobTitle; + break; + case "companyName": + var relativeCompany = contactDao.GetByID(((Person)contact).CompanyID); + + if (relativeCompany != null) + tagValue = relativeCompany.GetTitle(); + + + break; + default: + tagValue = String.Empty; + break; + + } + + } + else + { + + var company = (Company)contact; + + switch (tagParts[1]) + { + case "companyName": + tagValue = company.CompanyName; + break; + default: + tagValue = String.Empty; + break; + } + } + + break; + case "customField": + var tagID = Convert.ToInt32(tagParts[tagParts.Length - 1]); + + var entityType = contact is Company ? EntityType.Company : EntityType.Person; + + tagValue = customFieldDao.GetValue(entityType, contactID, tagID); + + break; + case "contactInfo": + var contactInfoType = (ContactInfoType)Enum.Parse(typeof(ContactInfoType), tagParts[1]); + var category = Convert.ToInt32(tagParts[2]); + var contactInfos = contactInfoDao.GetList(contactID, contactInfoType, category, true); + + if (contactInfos == null || contactInfos.Count == 0) break; + + var contactInfo = contactInfos[0]; + + if (contactInfoType == ContactInfoType.Address) + { + var addressPart = (AddressPart)Enum.Parse(typeof(AddressPart), tagParts[3]); + + tagValue = JsonDocument.Parse(contactInfo.Data).RootElement.GetProperty(addressPart.ToString().ToLower()).GetString(); + } + else + tagValue = contactInfo.Data; + + break; + default: + throw new ArgumentException(tag.SysName); + } + + result = result.Replace(tag.Name, tagValue); + } + + return result; + } + + public String Apply(String template, int contactID) + { + return Apply(template, GetTagsFrom(template), contactID); + } + + private String ToTagName(String value, bool isCompany) + { + return String.Format("$({0}.{1})", isCompany ? "Company" : "Person", value); + } + + private List GetAllTags() + { + return GetTags(true).Union(GetTags(false)).ToList(); + } + + public List GetTags(bool isCompany) + { + var result = new List(); + + if (isCompany) + { + + result.Add(new MailTemplateTag + { + DisplayName = CRMContactResource.CompanyName, + SysName = "common_companyName", + Category = CRMContactResource.GeneralInformation, + isCompany = isCompany, + Name = ToTagName("Company Name", isCompany) + }); + + } + else + { + result.Add(new MailTemplateTag + { + DisplayName = CRMContactResource.FirstName, + SysName = "common_firstName", + Category = CRMContactResource.GeneralInformation, + isCompany = false, + Name = ToTagName("First Name", isCompany) + }); + + result.Add(new MailTemplateTag + { + DisplayName = CRMContactResource.LastName, + SysName = "common_lastName", + Category = CRMContactResource.GeneralInformation, + isCompany = false, + Name = ToTagName("Last Name", isCompany) + }); + + result.Add(new MailTemplateTag + { + DisplayName = CRMContactResource.JobTitle, + SysName = "common_jobTitle", + Category = CRMContactResource.GeneralInformation, + isCompany = false, + Name = ToTagName("Job Title", isCompany) + }); + + + result.Add(new MailTemplateTag + { + DisplayName = CRMContactResource.CompanyName, + SysName = "common_companyName", + Category = CRMContactResource.GeneralInformation, + isCompany = false, + Name = ToTagName("Company Name", isCompany) + }); + + } + + foreach (ContactInfoType infoTypeEnum in Enum.GetValues(typeof(ContactInfoType))) + { + + var localName = String.Format("contactInfo_{0}_{1}", infoTypeEnum, ContactInfo.GetDefaultCategory(infoTypeEnum)); + var localTitle = infoTypeEnum.ToLocalizedString(); + + if (infoTypeEnum == ContactInfoType.Address) + foreach (AddressPart addressPartEnum in Enum.GetValues(typeof(AddressPart))) + result.Add(new MailTemplateTag + { + SysName = String.Format(localName + "_{0}_{1}", addressPartEnum, (int)AddressCategory.Work), + DisplayName = String.Format(localTitle + " {0}", addressPartEnum.ToLocalizedString()), + Category = CRMContactResource.GeneralInformation, + isCompany = isCompany, + Name = ToTagName(String.Format("{0} {1}", infoTypeEnum.ToString(), addressPartEnum.ToString()), isCompany) + }); + else + result.Add(new MailTemplateTag + { + SysName = localName, + DisplayName = localTitle, + Category = CRMContactResource.GeneralInformation, + isCompany = isCompany, + Name = ToTagName(infoTypeEnum.ToString(), isCompany) + }); + } + + var entityType = isCompany ? EntityType.Company : EntityType.Person; + + var customFieldsDao = _daoFactory.GetCustomFieldDao(); + + var customFields = customFieldsDao.GetFieldsDescription(entityType); + + var category = CRMContactResource.GeneralInformation; + + foreach (var customField in customFields) + { + if (customField.Type == CustomFieldType.SelectBox) continue; + if (customField.Type == CustomFieldType.CheckBox) continue; + + if (customField.Type == CustomFieldType.Heading) + { + if (!String.IsNullOrEmpty(customField.Label)) + category = customField.Label; + + continue; + } + + result.Add(new MailTemplateTag + { + SysName = "customField_" + customField.ID, + DisplayName = customField.Label.HtmlEncode(), + Category = category, + isCompany = isCompany, + Name = ToTagName(customField.Label, isCompany) + }); + } + + return result; + } + + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Utils/PdfCreator.cs b/products/ASC.CRM/Server/Utils/PdfCreator.cs new file mode 100644 index 00000000000..639716f077e --- /dev/null +++ b/products/ASC.CRM/Server/Utils/PdfCreator.cs @@ -0,0 +1,804 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Globalization; +using System.IO; +using System.Net; +using System.Text; +using System.Xml; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Entities; +using ASC.CRM.Resources; +using ASC.Files.Core; +using ASC.Web.Files.Services.DocumentService; + +using ICSharpCode.SharpZipLib.Zip; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace ASC.Web.CRM.Classes +{ + [Scope] + public class PdfCreator + { + private InvoiceFormattedData _invoiceFormattedData; + private DaoFactory _daoFactory; + private IServiceProvider _serviceProvider; + private DocumentServiceConnector _documentServiceConnector; + private OrganisationLogoManager _organisationLogoManager; + private Files.Classes.PathProvider _filesPathProvider; + private ILog _logger; + + + public PdfCreator(IOptionsMonitor logger, + Files.Classes.PathProvider filesPathProvider, + DocumentServiceConnector documentServiceConnector, + IServiceProvider serviceProvider, + OrganisationLogoManager organisationLogoManager, + DaoFactory daoFactory, + InvoiceFormattedData invoiceFormattedData) + { + _filesPathProvider = filesPathProvider; + + _logger = logger.Get("ASC.CRM"); + + _documentServiceConnector = documentServiceConnector; + _serviceProvider = serviceProvider; + _organisationLogoManager = organisationLogoManager; + _daoFactory = daoFactory; + _invoiceFormattedData = invoiceFormattedData; + } + + + private Stream Template + { + get + { + var bytes = FileHelper.ReadBytesFromEmbeddedResource("ASC.Web.CRM.InvoiceTemplates.template.docx"); + return new MemoryStream(bytes); + } + } + + + private const string FormatPdf = ".pdf"; + private const string FormatDocx = ".docx"; + private const string DocumentXml = "word/document.xml"; + private const string DocumentLogoImage = "word/media/logo.jpeg"; + + public void CreateAndSaveFile(int invoiceId) + { + _logger.DebugFormat("PdfCreator. CreateAndSaveFile. Invoice ID = {0}", invoiceId); + + try + { + var invoice = _daoFactory.GetInvoiceDao().GetByID(invoiceId); + + if (invoice == null) + { + _logger.Warn(CRMErrorsResource.InvoiceNotFound + ". Invoice ID = " + invoiceId); + + return; + } + + _logger.DebugFormat("PdfCreator. CreateAndSaveFile. Invoice ID = {0}. Convertation", invoiceId); + + string urlToFile; + + using (var docxStream = GetStreamDocx(invoice)) + { + urlToFile = GetUrlToFile(docxStream); + } + + _logger.DebugFormat("PdfCreator. CreateAndSaveFile. Invoice ID = {0}. UrlToFile = {1}", invoiceId, + urlToFile); + + var file = _serviceProvider.GetService>(); + + file.Title = string.Format("{0}{1}", invoice.Number, FormatPdf); + file.FolderID = _daoFactory.GetFileDao().GetRoot(); + + var request = WebRequest.Create(urlToFile); + + using (var response = request.GetResponse()) + using (var stream = response.GetResponseStream()) + { + file.ContentLength = response.ContentLength; + + _logger.DebugFormat("PdfCreator. CreateAndSaveFile. Invoice ID = {0}. SaveFile", invoiceId); + file = _daoFactory.GetFileDao().SaveFile(file, stream); + } + + if (file == null) + { + throw new Exception(CRMErrorsResource.FileCreateError); + } + + invoice.FileID = Int32.Parse(file.ID.ToString()); + + _logger.DebugFormat("PdfCreator. CreateAndSaveFile. Invoice ID = {0}. UpdateInvoiceFileID. FileID = {1}", invoiceId, file.ID); + + _daoFactory.GetInvoiceDao().UpdateInvoiceFileID(invoice.ID, invoice.FileID); + + _logger.DebugFormat("PdfCreator. CreateAndSaveFile. Invoice ID = {0}. AttachFiles. FileID = {1}", invoiceId, file.ID); + + _daoFactory.GetRelationshipEventDao().AttachFiles(invoice.ContactID, invoice.EntityType, invoice.EntityID, new[] { invoice.FileID }); + } + catch (Exception e) + { + _logger.Error(e); + } + } + + public File CreateFile(Invoice data, DaoFactory daoFactory) + { + try + { + using (var docxStream = GetStreamDocx(data)) + { + var urlToFile = GetUrlToFile(docxStream); + + return SaveFile(data, urlToFile, daoFactory); + } + } + catch (Exception e) + { + _logger.Error(e); + + throw; + } + } + + private string GetUrlToFile(Stream docxStream) + { + var externalUri = _filesPathProvider.GetTempUrl(docxStream, FormatDocx); + + externalUri = _documentServiceConnector.ReplaceCommunityAdress(externalUri); + + _logger.DebugFormat("PdfCreator. GetUrlToFile. externalUri = {0}", externalUri); + + var revisionId = DocumentServiceConnector.GenerateRevisionId(Guid.NewGuid().ToString()); + + string urlToFile; + + _documentServiceConnector.GetConvertedUri(externalUri, FormatDocx, FormatPdf, revisionId, null, null, null, false, out urlToFile); + + _logger.DebugFormat("PdfCreator. GetUrlToFile. urlToFile = {0}", urlToFile); + + return urlToFile; + + } + + public ConverterData StartCreationFileAsync(Invoice data) + { + using (var docxStream = GetStreamDocx(data)) + { + var externalUri = _filesPathProvider.GetTempUrl(docxStream, FormatDocx); + + externalUri = _documentServiceConnector.ReplaceCommunityAdress(externalUri); + + var revisionId = DocumentServiceConnector.GenerateRevisionId(Guid.NewGuid().ToString()); + + string urlToFile; + + _documentServiceConnector.GetConvertedUri(externalUri, FormatDocx, FormatPdf, revisionId, null, null, null, true, out urlToFile); + + return new ConverterData + { + StorageUrl = externalUri, + RevisionId = revisionId, + InvoiceId = data.ID, + }; + } + } + + public File GetConvertedFile(ConverterData data, DaoFactory daoFactory) + { + if (string.IsNullOrEmpty(data.StorageUrl) || string.IsNullOrEmpty(data.RevisionId)) + { + return null; + } + + string urlToFile; + + _documentServiceConnector.GetConvertedUri(data.StorageUrl, FormatDocx, FormatPdf, data.RevisionId, null, null, null, true, out urlToFile); + + if (string.IsNullOrEmpty(urlToFile)) + { + return null; + } + + var invoice = _daoFactory.GetInvoiceDao().GetByID(data.InvoiceId); + + return SaveFile(invoice, urlToFile, daoFactory); + } + + private File SaveFile(Invoice data, string url, DaoFactory daoFactory) + { + File file = null; + + var request = (HttpWebRequest)WebRequest.Create(url); + + using (var response = request.GetResponse()) + { + using (var stream = response.GetResponseStream()) + { + if (stream != null) + { + var document = _serviceProvider.GetService>(); + + document.Title = string.Format("{0}{1}", data.Number, FormatPdf); + document.FolderID = _daoFactory.GetFileDao().GetRoot(); + document.ContentLength = response.ContentLength; + + if (data.GetInvoiceFile(daoFactory) != null) + { + document.ID = data.FileID; + } + + file = _daoFactory.GetFileDao().SaveFile(document, stream); + } + } + } + + return file; + } + + private Stream GetStreamDocx(Invoice data) + { + var invoiceData = _invoiceFormattedData.GetData(data, 0, 0); + var logo = new byte[] { }; + + if (!string.IsNullOrEmpty(invoiceData.LogoBase64)) + { + logo = Convert.FromBase64String(invoiceData.LogoBase64); + } + else if (invoiceData.LogoBase64Id != 0) + { + logo = Convert.FromBase64String(_organisationLogoManager.GetOrganisationLogoBase64(invoiceData.LogoBase64Id)); + } + + var result = new MemoryStream(); + + using (var zipOutputStream = new ZipOutputStream(Template)) + using (var zipInputStream = new ZipInputStream(Template)) + { + ZipEntry zipEntry; + + while ((zipEntry = zipInputStream.GetNextEntry()) != null) + { + zipOutputStream.PutNextEntry(new ZipEntry(zipEntry.Name)); + + if (zipEntry.Name == DocumentXml) + { + var documentXmlStream = new MemoryStream(); + + zipInputStream.CopyTo(documentXmlStream); + + documentXmlStream.Position = 0; + + var document = new XmlDocument(); + + document.Load(documentXmlStream); + + var documentStr = GenerateDocumentXml(document, invoiceData, logo); + + var documentStrAsStream = new MemoryStream(Encoding.UTF8.GetBytes(documentStr)); + + documentStrAsStream.CopyTo(zipOutputStream); + + continue; + + } + + if (zipEntry.Name == DocumentLogoImage && logo.Length > 0) + { + var logoAsStream = new MemoryStream(logo); + + logoAsStream.CopyTo(zipOutputStream); + + continue; + } + + zipInputStream.CopyTo(zipOutputStream); + } + + zipOutputStream.Position = 0; + + zipOutputStream.CopyTo(result); + } + + return result; + } + + private string GenerateDocumentXml(XmlDocument xDocument, InvoiceFormattedData data, byte[] logo) + { + XmlNodeList nodeList; + XmlNode parent; + XmlNode child; + + + #region Seller + + nodeList = xDocument.SelectNodes("//*[@ascid='seller']"); + parent = nodeList != null && nodeList.Count > 0 ? nodeList[0] : null; + if (parent != null) + { + if (data.Seller == null) + { + parent.RemoveAll(); + } + else + { + var newText = parent.CloneNode(true).OuterXml; + newText = newText + .Replace("${label}", EncodeAndReplaceLineBreaks(data.Seller.Item1)) + .Replace("${value}", EncodeAndReplaceLineBreaks(data.Seller.Item2)); + var newEl = new XmlDocument(); + newEl.LoadXml(newText); + if (parent.ParentNode != null) + { + if (newEl.DocumentElement != null) + { + parent.ParentNode.InsertBefore(xDocument.ImportNode(newEl.DocumentElement, true), parent); + } + parent.ParentNode.RemoveChild(parent); + } + } + } + + #endregion + + + #region Logo + + nodeList = xDocument.SelectNodes("//*[@ascid='logo']"); + parent = nodeList != null && nodeList.Count > 0 ? nodeList[0] : null; + if (parent != null) + { + if (logo.Length <= 0) + { + parent.RemoveAll(); + } + else + { + using (var stream = new MemoryStream(logo)) + using (var img = System.Drawing.Image.FromStream(stream)) + { + var cx = img.Width * 9525; //1px = 9525emu + var cy = img.Height * 9525; //1px = 9525emu + + var newText = parent.CloneNode(true).OuterXml; + newText = newText + .Replace("${width}", cx.ToString(CultureInfo.InvariantCulture)) + .Replace("${height}", cy.ToString(CultureInfo.InvariantCulture)); + var newEl = new XmlDocument(); + newEl.LoadXml(newText); + if (parent.ParentNode != null) + { + if (newEl.DocumentElement != null) + { + parent.ParentNode.InsertBefore(xDocument.ImportNode(newEl.DocumentElement, true), + parent); + } + parent.ParentNode.RemoveChild(parent); + } + } + } + } + + #endregion + + + #region Number + + nodeList = xDocument.SelectNodes("//*[@ascid='number']"); + parent = nodeList != null && nodeList.Count > 0 ? nodeList[0] : null; + if (parent != null) + { + if (data.Number == null) + { + parent.RemoveAll(); + } + else + { + var newText = parent.CloneNode(true).OuterXml; + newText = newText + .Replace("${label}", EncodeAndReplaceLineBreaks(data.Number.Item1)) + .Replace("${value}", EncodeAndReplaceLineBreaks(data.Number.Item2)); + var newEl = new XmlDocument(); + newEl.LoadXml(newText); + if (parent.ParentNode != null) + { + if (newEl.DocumentElement != null) + { + parent.ParentNode.InsertBefore(xDocument.ImportNode(newEl.DocumentElement, true), parent); + } + parent.ParentNode.RemoveChild(parent); + } + } + } + + #endregion + + + #region Invoice + + nodeList = xDocument.SelectNodes("//*[@ascid='invoice']"); + parent = nodeList != null && nodeList.Count > 0 ? nodeList[0] : null; + if (parent != null) + { + nodeList = xDocument.SelectNodes("//*[@ascid='invoice']//*[@ascid='row']"); + child = nodeList != null && nodeList.Count > 0 ? nodeList[0] : null; + if (child != null) + { + if (data.Invoice == null || data.Invoice.Count <= 0) + { + if (parent.ParentNode != null) + { + parent.ParentNode.RemoveChild(parent); + } + } + else + { + foreach (var line in data.Invoice) + { + var newText = child.CloneNode(true).OuterXml; + newText = newText + .Replace("${label}", EncodeAndReplaceLineBreaks(line.Item1)) + .Replace("${value}", EncodeAndReplaceLineBreaks(line.Item2)); + var newEl = new XmlDocument(); + newEl.LoadXml(newText); + if (newEl.DocumentElement != null) + { + parent.InsertBefore(xDocument.ImportNode(newEl.DocumentElement, true), child); + } + } + parent.RemoveChild(child); + } + } + } + + #endregion + + + #region Customer + + nodeList = xDocument.SelectNodes("//*[@ascid='customer']"); + parent = nodeList != null && nodeList.Count > 0 ? nodeList[0] : null; + if (parent != null) + { + if (data.Customer == null) + { + if (parent.ParentNode != null) + { + parent.ParentNode.RemoveChild(parent); + } + } + else + { + var newText = parent.CloneNode(true).OuterXml; + newText = newText + .Replace("${label}", EncodeAndReplaceLineBreaks(data.Customer.Item1)) + .Replace("${value}", EncodeAndReplaceLineBreaks(data.Customer.Item2)); + var newEl = new XmlDocument(); + newEl.LoadXml(newText); + if (parent.ParentNode != null) + { + if (newEl.DocumentElement != null) + { + parent.ParentNode.InsertBefore(xDocument.ImportNode(newEl.DocumentElement, true), parent); + } + parent.ParentNode.RemoveChild(parent); + } + } + } + + #endregion + + + nodeList = xDocument.SelectNodes("//*[@ascid='table']"); + parent = nodeList != null && nodeList.Count > 0 ? nodeList[0] : null; + if (parent != null) + { + #region TableHeaderRow + + nodeList = xDocument.SelectNodes("//*[@ascid='table']//*[@ascid='headerRow']"); + child = nodeList != null && nodeList.Count > 0 ? nodeList[0] : null; + if (child != null) + { + if (data.TableHeaderRow == null || data.TableHeaderRow.Count <= 0) + { + if (parent.ParentNode != null) + parent.ParentNode.RemoveChild(parent); + } + else + { + var newText = child.CloneNode(true).OuterXml; + for (var i = 0; i < data.TableHeaderRow.Count; i++) + { + newText = newText + .Replace("${label" + i + "}", EncodeAndReplaceLineBreaks(data.TableHeaderRow[i])); + } + var newEl = new XmlDocument(); + newEl.LoadXml(newText); + if (newEl.DocumentElement != null) + { + parent.InsertBefore(xDocument.ImportNode(newEl.DocumentElement, true), child); + } + parent.RemoveChild(child); + } + } + + #endregion + + + #region TableBodyRows + + nodeList = xDocument.SelectNodes("//*[@ascid='table']//*[@ascid='bodyRow']"); + child = nodeList != null && nodeList.Count > 0 ? nodeList[0] : null; + if (child != null) + { + if (data.TableBodyRows == null || data.TableBodyRows.Count <= 0) + { + if (parent.ParentNode != null) + parent.ParentNode.RemoveChild(parent); + } + else + { + foreach (var line in data.TableBodyRows) + { + var newText = child.CloneNode(true).OuterXml; + for (var i = 0; i < line.Count; i++) + { + newText = newText + .Replace("${value" + i + "}", EncodeAndReplaceLineBreaks(line[i])); + } + var newEl = new XmlDocument(); + newEl.LoadXml(newText); + if (newEl.DocumentElement != null) + { + parent.InsertBefore(xDocument.ImportNode(newEl.DocumentElement, true), child); + } + } + parent.RemoveChild(child); + } + } + + #endregion + + + #region TableFooterRows + + nodeList = xDocument.SelectNodes("//*[@ascid='table']//*[@ascid='footerRow']"); + child = nodeList != null && nodeList.Count > 0 ? nodeList[0] : null; + if (child != null) + { + if (data.TableFooterRows == null || data.TableFooterRows.Count <= 0) + { + if (parent.ParentNode != null) + parent.ParentNode.RemoveChild(parent); + } + else + { + foreach (var line in data.TableFooterRows) + { + var newText = child.CloneNode(true).OuterXml; + newText = newText + .Replace("${label}", EncodeAndReplaceLineBreaks(line.Item1)) + .Replace("${value}", EncodeAndReplaceLineBreaks(line.Item2)); + var newEl = new XmlDocument(); + newEl.LoadXml(newText); + if (newEl.DocumentElement != null) + { + parent.InsertBefore(xDocument.ImportNode(newEl.DocumentElement, true), child); + } + } + parent.RemoveChild(child); + } + } + + #endregion + + + #region TableTotalRow + + nodeList = xDocument.SelectNodes("//*[@ascid='table']//*[@ascid='totalRow']"); + child = nodeList != null && nodeList.Count > 0 ? nodeList[0] : null; + if (child != null) + { + if (data.TableTotalRow == null) + { + if (parent.ParentNode != null) + parent.ParentNode.RemoveChild(parent); + } + else + { + var newText = child.CloneNode(true).OuterXml; + newText = newText + .Replace("${label}", EncodeAndReplaceLineBreaks(data.TableTotalRow.Item1)) + .Replace("${value}", EncodeAndReplaceLineBreaks(data.TableTotalRow.Item2)); + var newEl = new XmlDocument(); + newEl.LoadXml(newText); + if (newEl.DocumentElement != null) + { + parent.InsertBefore(xDocument.ImportNode(newEl.DocumentElement, true), child); + } + parent.RemoveChild(child); + } + } + + #endregion + } + + + #region Terms + + nodeList = xDocument.SelectNodes("//*[@ascid='terms']"); + parent = nodeList != null && nodeList.Count > 0 ? nodeList[0] : null; + if (parent != null) + { + if (data.Terms == null) + { + if (parent.ParentNode != null) + parent.ParentNode.RemoveChild(parent); + } + else + { + var newText = parent.CloneNode(true).OuterXml; + newText = newText + .Replace("${label}", EncodeAndReplaceLineBreaks(data.Terms.Item1)) + .Replace("${value}", EncodeAndReplaceLineBreaks(data.Terms.Item2)); + var newEl = new XmlDocument(); + newEl.LoadXml(newText); + if (parent.ParentNode != null) + { + if (newEl.DocumentElement != null) + { + parent.ParentNode.InsertBefore(xDocument.ImportNode(newEl.DocumentElement, true), parent); + } + parent.ParentNode.RemoveChild(parent); + } + } + } + + #endregion + + + #region Notes + + nodeList = xDocument.SelectNodes("//*[@ascid='notes']"); + parent = nodeList != null && nodeList.Count > 0 ? nodeList[0] : null; + if (parent != null) + { + if (data.Notes == null) + { + if (parent.ParentNode != null) + parent.ParentNode.RemoveChild(parent); + } + else + { + var newText = parent.CloneNode(true).OuterXml; + newText = newText + .Replace("${label}", EncodeAndReplaceLineBreaks(data.Notes.Item1)) + .Replace("${value}", EncodeAndReplaceLineBreaks(data.Notes.Item2)); + var newEl = new XmlDocument(); + newEl.LoadXml(newText); + if (parent.ParentNode != null) + { + if (newEl.DocumentElement != null) + { + parent.ParentNode.InsertBefore(xDocument.ImportNode(newEl.DocumentElement, true), parent); + } + parent.ParentNode.RemoveChild(parent); + } + } + } + + #endregion + + + #region Consignee + + nodeList = xDocument.SelectNodes("//*[@ascid='consignee']"); + parent = nodeList != null && nodeList.Count > 0 ? nodeList[0] : null; + if (parent != null) + { + if (data.Consignee == null) + { + if (parent.ParentNode != null) + { + parent.ParentNode.RemoveChild(parent); + } + } + else + { + var newText = parent.CloneNode(true).OuterXml; + newText = newText + .Replace("${label}", EncodeAndReplaceLineBreaks(data.Consignee.Item1)) + .Replace("${value}", EncodeAndReplaceLineBreaks(data.Consignee.Item2)); + var newEl = new XmlDocument(); + newEl.LoadXml(newText); + if (parent.ParentNode != null) + { + if (newEl.DocumentElement != null) + { + parent.ParentNode.InsertBefore(xDocument.ImportNode(newEl.DocumentElement, true), parent); + } + parent.ParentNode.RemoveChild(parent); + } + } + } + + #endregion + + + return xDocument.InnerXml; + } + + private string EncodeAndReplaceLineBreaks(string str) + { + return str + .Replace("&", "&") + .Replace("'", "'") + .Replace("<", "<") + .Replace(">", ">") + .Replace("\"", """) + .Replace("\r\n", "") + .Replace("\n", "") + .Replace("\r", ""); + } + } + + public class ConverterData + { + public string StorageUrl { get; set; } + public string RevisionId { get; set; } + public int InvoiceId { get; set; } + public int FileId { get; set; } + } + + class CustomStaticDataSource : IStaticDataSource + { + private Stream _stream; + // Implement method from IStaticDataSource + public Stream GetSource() + { + return _stream; + } + + // Call this to provide the memorystream + public void SetStream(Stream inputStream) + { + _stream = inputStream; + _stream.Position = 0; + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Utils/PdfQueueWorker.cs b/products/ASC.CRM/Server/Utils/PdfQueueWorker.cs new file mode 100644 index 00000000000..bfe95c9fdc8 --- /dev/null +++ b/products/ASC.CRM/Server/Utils/PdfQueueWorker.cs @@ -0,0 +1,213 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Linq; +using System.Web; + +using ASC.Common; +using ASC.Common.Threading; +using ASC.Common.Threading.Progress; +using ASC.Core; + +using log4net; + +using Microsoft.AspNetCore.Http; + +namespace ASC.Web.CRM.Classes +{ + [Transient] + public class PdfQueueWorker + { + private readonly DistributedTaskQueue _queue; + private readonly int _tenantId; + private readonly Guid _userId; + private readonly object Locker = new object(); + private PdfProgressItem _pdfProgressItem; + + public PdfQueueWorker(DistributedTaskQueueOptionsManager queueOptions, + PdfProgressItem pdfProgressItem, + TenantManager tenantProvider, + SecurityContext securityContext) + { + _queue = queueOptions.Get(); + _pdfProgressItem = pdfProgressItem; + _tenantId = tenantProvider.GetCurrentTenant().TenantId; + _userId = securityContext.CurrentAccount.ID; + } + + + public string GetTaskId(int tenantId, int invoiceId) + { + return string.Format("{0}_{1}", tenantId, invoiceId); + } + + public PdfProgressItem GetTaskStatus(int tenantId, int invoiceId) + { + var id = GetTaskId(tenantId, invoiceId); + + var findedItem = _queue.GetTasks().FirstOrDefault(x => x.Id == id); + + return findedItem; + } + + public void TerminateTask(int invoiceId) + { + var item = GetTaskStatus(_tenantId, invoiceId); + + if (item != null) + _queue.RemoveTask(item.Id); + } + + public PdfProgressItem StartTask(int invoiceId) + { + lock (Locker) + { + var task = GetTaskStatus(_tenantId, invoiceId); + + if (task != null && task.IsCompleted) + { + _queue.RemoveTask(task.Id); + task = null; + } + + if (task == null) + { + _pdfProgressItem.Configure(GetTaskId(_tenantId, invoiceId), _tenantId, _userId, invoiceId); + _queue.QueueTask(_pdfProgressItem); + + } + + return task; + } + } + } + + [Transient] + public class PdfProgressItem : DistributedTaskProgress, IProgressItem + { + private readonly string _contextUrl; + private int _tenantId; + private int _invoiceId; + private Guid _userId; + + public object Error { get; set; } + private readonly PdfCreator PdfCreator; + private readonly SecurityContext SecurityContext; + private readonly TenantManager TenantManager; + + public PdfProgressItem(IHttpContextAccessor httpContextAccessor) + { + _contextUrl = httpContextAccessor.HttpContext != null ? httpContextAccessor.HttpContext.Request.GetUrlRewriter().ToString() : null; + + Error = null; + Percentage = 0; + IsCompleted = false; + } + + public void Configure(object id, + int tenantId, + Guid userId, + int invoiceId) + { + Id = id.ToString(); + _tenantId = tenantId; + _invoiceId = invoiceId; + _userId = userId; + } + + + protected override void DoJob() + { + try + { + Percentage = 0; + + TenantManager.SetCurrentTenant(_tenantId); + + SecurityContext.AuthenticateMe(_userId); + + //if (HttpContext.Current == null && !WorkContext.IsMono) + //{ + // HttpContext.Current = new HttpContext( + // new HttpRequest("hack", _contextUrl, string.Empty), + // new HttpResponse(new System.IO.StringWriter())); + //} + + PdfCreator.CreateAndSaveFile(_invoiceId); + + Percentage = 100; + PublishChanges(); + + Status = DistributedTaskStatus.Completed; + } + catch (Exception ex) + { + LogManager.GetLogger("ASC.Web").Error(ex); + + Percentage = 0; + Status = DistributedTaskStatus.Failted; + Error = ex.Message; + } + finally + { + // fake httpcontext break configuration manager for mono + if (!WorkContext.IsMono) + { + //if (HttpContext.Current != null) + //{ + // new DisposableHttpContext(HttpContext.Current).Dispose(); + // HttpContext.Current = null; + //} + } + + IsCompleted = true; + } + } + + public override bool Equals(object obj) + { + if (obj == null) return false; + + if (obj is PdfProgressItem) + { + return ((PdfProgressItem)obj).Id == Id; + } + + return base.Equals(obj); + } + + public override int GetHashCode() + { + return Id.GetHashCode(); + } + + public object Clone() + { + return MemberwiseClone(); + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/Utils/ReportHelper.cs b/products/ASC.CRM/Server/Utils/ReportHelper.cs new file mode 100644 index 00000000000..a01547114ef --- /dev/null +++ b/products/ASC.CRM/Server/Utils/ReportHelper.cs @@ -0,0 +1,277 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that + * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights. + * + * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR + * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Net; +using System.Text.Json; + +using ASC.Common; +using ASC.Core; +using ASC.Core.Common.Settings; +using ASC.Core.Tenants; +using ASC.CRM.Core.Dao; +using ASC.CRM.Core.Enums; +using ASC.CRM.Resources; +using ASC.Files.Core; +using ASC.Web.Files.Services.DocumentService; + +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace ASC.Web.CRM.Classes +{ + [Scope] + public class ReportHelper + { + private CurrencyProvider _currencyProvider; + private IHttpContextAccessor _httpContext; + private SecurityContext _securityContext; + private DocbuilderReportsUtilityHelper _docbuilderReportsUtilityHelper; + private DaoFactory _daoFactory; + private IServiceProvider _serviceProvider; + private Global _global; + private SettingsManager _settingsManager; + private TenantUtil _tenantUtil; + private TenantManager _tenantManager; + + public ReportHelper(TenantManager tenantManager, + TenantUtil tenantUtil, + Global global, + DocbuilderReportsUtilityHelper docbuilderReportsUtilityHelper, + SettingsManager settingsManager, + DaoFactory daoFactory, + SecurityContext securityContext, + IServiceProvider serviceProvider, + IHttpContextAccessor httpContextAccessor, + CurrencyProvider currencyProvider + ) + { + _tenantManager = tenantManager; + _tenantUtil = tenantUtil; + _global = global; + _settingsManager = settingsManager; + _serviceProvider = serviceProvider; + _daoFactory = daoFactory; + _docbuilderReportsUtilityHelper = docbuilderReportsUtilityHelper; + _securityContext = securityContext; + _httpContext = httpContextAccessor; + _currencyProvider = currencyProvider; + } + + private string GetFileName(ReportType reportType) + { + string reportName; + + switch (reportType) + { + case ReportType.SalesByManagers: + reportName = CRMReportResource.SalesByManagersReport; + break; + case ReportType.SalesForecast: + reportName = CRMReportResource.SalesForecastReport; + break; + case ReportType.SalesFunnel: + reportName = CRMReportResource.SalesFunnelReport; + break; + case ReportType.WorkloadByContacts: + reportName = CRMReportResource.WorkloadByContactsReport; + break; + case ReportType.WorkloadByTasks: + reportName = CRMReportResource.WorkloadByTasksReport; + break; + case ReportType.WorkloadByDeals: + reportName = CRMReportResource.WorkloadByDealsReport; + break; + case ReportType.WorkloadByInvoices: + reportName = CRMReportResource.WorkloadByInvoicesReport; + break; + case ReportType.WorkloadByVoip: + reportName = CRMReportResource.WorkloadByVoipReport; + break; + case ReportType.SummaryForThePeriod: + reportName = CRMReportResource.SummaryForThePeriodReport; + break; + case ReportType.SummaryAtThisMoment: + reportName = CRMReportResource.SummaryAtThisMomentReport; + break; + default: + reportName = string.Empty; + break; + } + + return string.Format("{0} ({1} {2}).xlsx", + reportName, + _tenantUtil.DateTimeNow().ToShortDateString(), + _tenantUtil.DateTimeNow().ToShortTimeString()); + } + + public bool CheckReportData(ReportType reportType, ReportTimePeriod timePeriod, Guid[] managers) + { + var reportDao = _daoFactory.GetReportDao(); + + throw new NotImplementedException(); + + //switch (reportType) + //{ + // case ReportType.SalesByManagers: + // return reportDao.CheckSalesByManagersReportData(timePeriod, managers); + // case ReportType.SalesForecast: + // return reportDao.CheckSalesForecastReportData(timePeriod, managers); + // case ReportType.SalesFunnel: + // return reportDao.CheckSalesFunnelReportData(timePeriod, managers); + // case ReportType.WorkloadByContacts: + // return reportDao.CheckWorkloadByContactsReportData(timePeriod, managers); + // case ReportType.WorkloadByTasks: + // return reportDao.CheckWorkloadByTasksReportData(timePeriod, managers); + // case ReportType.WorkloadByDeals: + // return reportDao.CheckWorkloadByDealsReportData(timePeriod, managers); + // case ReportType.WorkloadByInvoices: + // return reportDao.CheckWorkloadByInvoicesReportData(timePeriod, managers); + // case ReportType.WorkloadByVoip: + // return reportDao.CheckWorkloadByViopReportData(timePeriod, managers); + // case ReportType.SummaryForThePeriod: + // return reportDao.CheckSummaryForThePeriodReportData(timePeriod, managers); + // case ReportType.SummaryAtThisMoment: + // return reportDao.CheckSummaryAtThisMomentReportData(timePeriod, managers); + // default: + // return false; + //} + } + + public List GetMissingRates(ReportType reportType) + { + var reportDao = _daoFactory.GetReportDao(); + + if (reportType == ReportType.WorkloadByTasks || reportType == ReportType.WorkloadByInvoices || + reportType == ReportType.WorkloadByContacts || reportType == ReportType.WorkloadByVoip) return null; + + var crmSettings = _settingsManager.Load(); + var defaultCurrency = _currencyProvider.Get(crmSettings.DefaultCurrency); + + return reportDao.GetMissingRates(defaultCurrency.Abbreviation); + } + + private object GetReportData(ReportType reportType, ReportTimePeriod timePeriod, Guid[] managers) + { + var crmSettings = _settingsManager.Load(); + + var reportDao = _daoFactory.GetReportDao(); + var defaultCurrency = _currencyProvider.Get(crmSettings.DefaultCurrency).Abbreviation; + + switch (reportType) + { + case ReportType.SalesByManagers: + return reportDao.GetSalesByManagersReportData(timePeriod, managers, defaultCurrency); + case ReportType.SalesForecast: + return reportDao.GetSalesForecastReportData(timePeriod, managers, defaultCurrency); + case ReportType.SalesFunnel: + return reportDao.GetSalesFunnelReportData(timePeriod, managers, defaultCurrency); + case ReportType.WorkloadByContacts: + return reportDao.GetWorkloadByContactsReportData(timePeriod, managers); + case ReportType.WorkloadByTasks: + return reportDao.GetWorkloadByTasksReportData(timePeriod, managers); + case ReportType.WorkloadByDeals: + return reportDao.GetWorkloadByDealsReportData(timePeriod, managers, defaultCurrency); + case ReportType.WorkloadByInvoices: + return reportDao.GetWorkloadByInvoicesReportData(timePeriod, managers); + case ReportType.WorkloadByVoip: + return reportDao.GetWorkloadByViopReportData(timePeriod, managers); + case ReportType.SummaryForThePeriod: + return reportDao.GetSummaryForThePeriodReportData(timePeriod, managers, defaultCurrency); + case ReportType.SummaryAtThisMoment: + return reportDao.GetSummaryAtThisMomentReportData(timePeriod, managers, defaultCurrency); + default: + return null; + } + } + + private string GetReportScript(object data, ReportType type, string fileName) + { + var script = + FileHelper.ReadTextFromEmbeddedResource(string.Format("ASC.Web.CRM.ReportTemplates.{0}.docbuilder", type)); + + if (string.IsNullOrEmpty(script)) + throw new Exception(CRMReportResource.BuildErrorEmptyDocbuilderTemplate); + + return script.Replace("${outputFilePath}", fileName) + .Replace("${reportData}", JsonSerializer.Serialize(data)); + } + + private void SaveReportFile(ReportState state, string url) + { + var data = new WebClient().DownloadData(url); + + using (var stream = new System.IO.MemoryStream(data)) + { + var document = _serviceProvider.GetService>(); + + document.Title = state.FileName; + document.FolderID = _daoFactory.GetFileDao().GetRoot(); + document.ContentLength = stream.Length; + + var file = _daoFactory.GetFileDao().SaveFile(document, stream); + + _daoFactory.GetReportDao().SaveFile((int)file.ID, state.ReportType); + + state.FileId = (int)file.ID; + } + + } + + public ReportState RunGenareteReport(ReportType reportType, ReportTimePeriod timePeriod, Guid[] managers) + { + var reportData = GetReportData(reportType, timePeriod, managers); + + if (reportData == null) + throw new Exception(CRMReportResource.ErrorNullReportData); + + var tmpFileName = DocbuilderReportsUtility.TmpFileName; + + var script = GetReportScript(reportData, reportType, tmpFileName); + + if (string.IsNullOrEmpty(script)) + throw new Exception(CRMReportResource.ErrorNullReportScript); + + var reportStateData = new ReportStateData( + GetFileName(reportType), + tmpFileName, + script, + (int)reportType, + ReportOrigin.CRM, + SaveReportFile, + null, + _tenantManager.GetCurrentTenant().TenantId, + _securityContext.CurrentAccount.ID); + + var state = new ReportState(_serviceProvider, reportStateData, _httpContext); + + _docbuilderReportsUtilityHelper.Enqueue(state); + + return state; + } + } +} \ No newline at end of file diff --git a/products/ASC.CRM/Server/appsettings.json b/products/ASC.CRM/Server/appsettings.json index c88dcaae5a9..9bf702e3108 100644 --- a/products/ASC.CRM/Server/appsettings.json +++ b/products/ASC.CRM/Server/appsettings.json @@ -1,3 +1,3 @@ -{ - "pathToConf": "..\\..\\..\\config" -} +{ + "pathToConf": "..\\..\\..\\config" +} diff --git a/products/ASC.CRM/Server/proto/ContactPhotoManagerCacheItem.proto b/products/ASC.CRM/Server/proto/ContactPhotoManagerCacheItem.proto new file mode 100644 index 00000000000..44218c86354 --- /dev/null +++ b/products/ASC.CRM/Server/proto/ContactPhotoManagerCacheItem.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; + +package ASC.Web.CRM.Classes; + +message ContactPhotoManagerCacheItem { + int32 id = 1; +} + \ No newline at end of file diff --git a/products/ASC.Files/Core/Core/FilesIntegration.cs b/products/ASC.Files/Core/Core/FilesIntegration.cs index c73dbb2aa23..63c88b4de43 100644 --- a/products/ASC.Files/Core/Core/FilesIntegration.cs +++ b/products/ASC.Files/Core/Core/FilesIntegration.cs @@ -39,7 +39,7 @@ public class FilesIntegration { private static readonly IDictionary providers = new Dictionary(); - private IDaoFactory DaoFactory { get; } + public IDaoFactory DaoFactory { get; } public FilesIntegration(IDaoFactory daoFactory) { diff --git a/products/ASC.Files/Core/Services/DocumentService/DocbuilderReportsUtility.cs b/products/ASC.Files/Core/Services/DocumentService/DocbuilderReportsUtility.cs index d7504a0b699..c5df750875f 100644 --- a/products/ASC.Files/Core/Services/DocumentService/DocbuilderReportsUtility.cs +++ b/products/ASC.Files/Core/Services/DocumentService/DocbuilderReportsUtility.cs @@ -28,8 +28,9 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using System.Web; - +using System.Web; + +using ASC.Common; using ASC.Common.Logging; using ASC.Common.Threading; using ASC.Core; @@ -250,7 +251,7 @@ protected void FillDistributedTask() TaskInfo.SetProperty("exception", Exception); } } - + [Scope] public class DocbuilderReportsUtility { private readonly DistributedTaskQueue tasks; @@ -321,7 +322,8 @@ internal static string GetCacheKey(ReportOrigin origin, int tenantId, Guid userI return $"{tenantId}_{userId}_{(int)origin}"; } } - + + [Scope] public class DocbuilderReportsUtilityHelper { public DocbuilderReportsUtility DocbuilderReportsUtility { get; } diff --git a/web/ASC.Web.Core/Calendars/RecurrenceRule.cs b/web/ASC.Web.Core/Calendars/RecurrenceRule.cs index 53f15e49073..d89a288a287 100644 --- a/web/ASC.Web.Core/Calendars/RecurrenceRule.cs +++ b/web/ASC.Web.Core/Calendars/RecurrenceRule.cs @@ -73,6 +73,12 @@ public static int GetDayOfWeekInMonth(this DateTime date) } return count; } + + public static string ToShortString(this DateTime targetDateTime) + { + return String.Format("{0} {1}", targetDateTime.ToShortDateString(), targetDateTime.ToShortTimeString()); + } + } public enum Frequency diff --git a/web/ASC.Web.Core/Utility/IFileUploadHandler.cs b/web/ASC.Web.Core/Utility/IFileUploadHandler.cs index 0d4fae9fde1..959a48797e7 100644 --- a/web/ASC.Web.Core/Utility/IFileUploadHandler.cs +++ b/web/ASC.Web.Core/Utility/IFileUploadHandler.cs @@ -30,9 +30,12 @@ namespace ASC.Web.Core.Utility { public class FileUploadResult { + public bool Success { get; set; } public object Data { get; set; } public string Message { get; set; } + public string FileName { get; set; } + public string FileURL { get; set; } } public interface IFileUploadHandler