diff --git a/OutOfSchool/OutOfSchool.BusinessLogic/Models/StudySubjects/StudySubjectDto.cs b/OutOfSchool/OutOfSchool.BusinessLogic/Models/StudySubjects/StudySubjectDto.cs
new file mode 100644
index 000000000..f3dff27fe
--- /dev/null
+++ b/OutOfSchool/OutOfSchool.BusinessLogic/Models/StudySubjects/StudySubjectDto.cs
@@ -0,0 +1,24 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace OutOfSchool.BusinessLogic.Models;
+public class StudySubjectDto
+{
+ public int Id { get; set; }
+ ///
+ /// Name in Ukrainian
+ ///
+ [Required(ErrorMessage = "The name in Ukrainian is required.")]
+ public string NameInUkrainian { get; set; }
+
+ ///
+ /// Name in the language of instruction
+ ///
+ [Required(ErrorMessage = "The name in the language of instruction is required.")]
+ public string NameInInstructionLanguage { get; set; }
+
+ ///
+ /// Language of instruction (allows multiple selection)
+ ///
+ [Required(ErrorMessage = "The language of instruction is required.")]
+ public List Languages { get; set; }
+}
diff --git a/OutOfSchool/OutOfSchool.BusinessLogic/Services/ISubjectService.cs b/OutOfSchool/OutOfSchool.BusinessLogic/Services/ISubjectService.cs
new file mode 100644
index 000000000..0af18ee3d
--- /dev/null
+++ b/OutOfSchool/OutOfSchool.BusinessLogic/Services/ISubjectService.cs
@@ -0,0 +1,43 @@
+using OutOfSchool.BusinessLogic.Enums;
+using OutOfSchool.BusinessLogic.Models;
+
+namespace OutOfSchool.BusinessLogic.Services;
+public interface ISubjectService
+{
+ ///
+ /// Get all entities.
+ ///
+ /// Localization: Ua - 0, En - 1.
+ /// List of all Subjects.
+ Task> GetAll(LocalizationType localization = LocalizationType.Ua);
+
+ ///
+ /// Get entity by it's key.
+ ///
+ /// /// Key in the table.
+ /// Localization: Ua - 0, En - 1.
+ /// Subject.
+ Task GetById(long id, LocalizationType localization = LocalizationType.Ua);
+
+ ///
+ /// Add entity.
+ ///
+ /// Tag entity to add.
+ /// A representing the result of the asynchronous operation.
+ Task Create(StudySubjectDto dto);
+
+ ///
+ /// Update entity.
+ ///
+ /// /// Subject entity to add.
+ /// Localization: Ua - 0, En - 1.
+ /// A representing the result of the asynchronous operation.
+ Task Update(StudySubjectDto dto, LocalizationType localization = LocalizationType.Ua);
+
+ ///
+ /// Delete entity.
+ ///
+ /// Subject key.
+ /// A representing the result of the asynchronous operation.
+ Task Delete(long id);
+}
diff --git a/OutOfSchool/OutOfSchool.BusinessLogic/Services/SubjectService.cs b/OutOfSchool/OutOfSchool.BusinessLogic/Services/SubjectService.cs
new file mode 100644
index 000000000..c2db7980c
--- /dev/null
+++ b/OutOfSchool/OutOfSchool.BusinessLogic/Services/SubjectService.cs
@@ -0,0 +1,128 @@
+using AutoMapper;
+using Microsoft.Extensions.Localization;
+using OutOfSchool.BusinessLogic.Enums;
+using OutOfSchool.BusinessLogic.Models;
+using OutOfSchool.BusinessLogic.Models.Tag;
+using OutOfSchool.Services.Repository.Base.Api;
+
+namespace OutOfSchool.BusinessLogic.Services;
+public class SubjectService : ISubjectService
+{
+ private readonly IEntityRepository repository;
+ private readonly ILogger logger;
+ private readonly IStringLocalizer localizer;
+ private readonly IMapper mapper;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Repository.
+ /// Logger.
+ /// Localizer.
+ /// Mapper.
+ public SubjectService(
+ IEntityRepository repository,
+ ILogger logger,
+ IStringLocalizer localizer,
+ IMapper mapper)
+ {
+ this.localizer = localizer ?? throw new ArgumentNullException(nameof(localizer));
+ this.repository = repository ?? throw new ArgumentNullException(nameof(repository));
+ this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ this.mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
+ }
+
+ ///
+ public async Task Create(StudySubjectDto dto)
+ {
+ logger.LogDebug("StudySubject creating was started.");
+
+ var tag = mapper.Map(dto);
+
+ var newTag = await repository.Create(tag).ConfigureAwait(false);
+
+ logger.LogDebug($"StudySubject with Id = {newTag?.Id} created successfully.");
+
+ return mapper.Map(newTag);
+ }
+
+ ///
+ public async Task Delete(long id)
+ {
+ logger.LogInformation($"Deleting Subject with Id = {id} started.");
+
+ var entity = await repository.GetById(id).ConfigureAwait(false);
+
+ if (entity is null)
+ {
+ logger.LogWarning($"Subject with Id = {id} was not found.");
+ throw new KeyNotFoundException($"Subject with Id = {id} does not exist.");
+ }
+
+ try
+ {
+ await repository.Delete(entity).ConfigureAwait(false);
+
+ logger.LogInformation($"Subject with Id = {id} successfully deleted.");
+ }
+ catch (DbUpdateConcurrencyException)
+ {
+ logger.LogError($"Deleting Subject with Id = {id} failed.");
+ throw;
+ }
+ }
+
+ ///
+ public async Task> GetAll(LocalizationType localization = LocalizationType.Ua)
+ {
+ logger.LogInformation("Getting all Subjects started.");
+
+ var subjects = await repository.GetAll().ConfigureAwait(false);
+
+ logger.LogInformation(!subjects.Any()
+ ? "Subject table is empty."
+ : $"All {subjects.Count()} records were successfully received from the Subject table");
+
+ return subjects.Select(subject => mapper.Map(subject)).ToList();
+ }
+
+ ///
+ public async Task GetById(long id, LocalizationType localization = LocalizationType.Ua)
+ {
+ logger.LogInformation($"Getting Subject by Id started. Looking Id = {id}.");
+
+ var subject = await repository.GetById(id).ConfigureAwait(false);
+
+ if (subject == null)
+ {
+ throw new ArgumentException(
+ nameof(id),
+ paramName: $"There are no recors in subjects table with such id - {id}.");
+ }
+
+ logger.LogInformation($"Got a Subject with Id = {id}.");
+
+ return mapper.Map(subject);
+ }
+
+ ///
+ public async Task Update(StudySubjectDto dto, LocalizationType localization = LocalizationType.Ua)
+ {
+ logger.LogDebug($"Updating StudySubject with Id = {dto.Id}, {localization} localization, started.");
+
+ var Localized = await repository.GetById(dto.Id).ConfigureAwait(false);
+
+ if (Localized == null)
+ {
+ logger.LogError($"Updating failed. Tag with Id = {dto.Id} doesn't exist in the system.");
+
+ return null;
+ }
+
+ var tag = await repository.Update(Localized).ConfigureAwait(false);
+
+ logger.LogDebug($"Tag with Id = {tag.Id} updated succesfully.");
+
+ return mapper.Map(tag);
+ }
+}
diff --git a/OutOfSchool/OutOfSchool.DataAccess/Extensions/ModelBuilderExtension.cs b/OutOfSchool/OutOfSchool.DataAccess/Extensions/ModelBuilderExtension.cs
index 9f8df0cf5..49ffb0aa5 100644
--- a/OutOfSchool/OutOfSchool.DataAccess/Extensions/ModelBuilderExtension.cs
+++ b/OutOfSchool/OutOfSchool.DataAccess/Extensions/ModelBuilderExtension.cs
@@ -430,6 +430,20 @@ public static void Seed(this ModelBuilder builder)
Title = "Певний місяць або місяці року",
TitleEn = "A certain month or months of the year",
});
+
+ builder.Entity().HasData(
+ new Language {
+ Id = 1,
+ Title = "English",
+ Code = "en",
+ },
+ new Language
+ {
+ Id = 2,
+ Title = "Українська",
+ Code = "uk",
+
+ });
}
///
diff --git a/OutOfSchool/OutOfSchool.DataAccess/Models/Configurations/SubjectConfiguration.cs b/OutOfSchool/OutOfSchool.DataAccess/Models/Configurations/SubjectConfiguration.cs
new file mode 100644
index 000000000..b904fbb68
--- /dev/null
+++ b/OutOfSchool/OutOfSchool.DataAccess/Models/Configurations/SubjectConfiguration.cs
@@ -0,0 +1,17 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+namespace OutOfSchool.Services.Models.Configurations;
+public class SubjectConfiguration : IEntityTypeConfiguration
+{
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.HasKey(x => x.Id);
+
+ builder.HasIndex(x => x.IsDeleted);
+
+ builder.Property(x => x.IsDeleted).HasDefaultValue(false);
+
+ //TO DO
+ }
+}
diff --git a/OutOfSchool/OutOfSchool.DataAccess/Models/Language.cs b/OutOfSchool/OutOfSchool.DataAccess/Models/Language.cs
new file mode 100644
index 000000000..c9a82f862
--- /dev/null
+++ b/OutOfSchool/OutOfSchool.DataAccess/Models/Language.cs
@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+
+namespace OutOfSchool.Services.Models;
+
+///
+/// Represents a language used in the educational system.
+///
+public class Language : IKeyedEntity
+{
+ public long Id { get; set; }
+ public string Code { get; set; }
+ public string Title { get; set; }
+
+ public virtual List EducationalDisciplines { get; set; }
+}
+
diff --git a/OutOfSchool/OutOfSchool.DataAccess/Models/StudySubject.cs b/OutOfSchool/OutOfSchool.DataAccess/Models/StudySubject.cs
new file mode 100644
index 000000000..2e31da346
--- /dev/null
+++ b/OutOfSchool/OutOfSchool.DataAccess/Models/StudySubject.cs
@@ -0,0 +1,36 @@
+
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace OutOfSchool.Services.Models;
+
+///
+/// Represents a subject in the educational system.
+///
+public class StudySubject : IKeyedEntity, ISoftDeleted
+{
+ public long Id { get; set; }
+ public bool IsDeleted { get; set; }
+
+ ///
+ /// Name in Ukrainian
+ ///
+ [Required(ErrorMessage = "The name in Ukrainian is required.")]
+ public string NameInUkrainian { get; set; }
+
+ ///
+ /// Name in the language of instruction
+ ///
+ [Required(ErrorMessage = "The name in the language of instruction is required.")]
+ public string NameInInstructionLanguage { get; set; }
+
+ ///
+ /// Language of instruction (allows multiple selection)
+ ///
+ [Required(ErrorMessage = "The language of instruction is required.")]
+ public virtual List Languages { get; set; }
+
+ public virtual Workshop WorkshopID{ get; set; }
+ public virtual Workshop Workshop { get; set; }
+}
+
diff --git a/OutOfSchool/OutOfSchool.WebApi/Controllers/V1/StudySubjectController.cs b/OutOfSchool/OutOfSchool.WebApi/Controllers/V1/StudySubjectController.cs
new file mode 100644
index 000000000..25813a1d2
--- /dev/null
+++ b/OutOfSchool/OutOfSchool.WebApi/Controllers/V1/StudySubjectController.cs
@@ -0,0 +1,138 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Localization;
+using OutOfSchool.BusinessLogic.Models;
+using OutOfSchool.BusinessLogic.Services.ProviderServices;
+
+namespace OutOfSchool.WebApi.Controllers.V1;
+
+[ApiController]
+[AspApiVersion(1)]
+[Route("api/v{version:apiVersion}/[controller]/[action]")]
+public class StudySubjectController : ControllerBase
+{
+ private readonly ISubjectService _studySubjectService;
+ private readonly IStringLocalizer _localizer;
+ private readonly IProviderService _providerService;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Service for StudySubject model.
+ /// Localizer.
+ /// Service for Provider.
+ public StudySubjectController(
+ ISubjectService studySubjectService,
+ IStringLocalizer localizer,
+ IProviderService providerService)
+ {
+ _providerService = providerService;
+ _localizer = localizer;
+ _studySubjectService = studySubjectService;
+ }
+
+ ///
+ /// Get all StudySubjects from the database.
+ ///
+ /// List of StudySubjects.
+ [Authorize]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable))]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+ [HttpGet]
+ public async Task Get()
+ {
+ var studySubjects = await _studySubjectService.GetAll().ConfigureAwait(false);
+
+ if (!studySubjects.Any())
+ {
+ return NoContent();
+ }
+
+ return Ok(studySubjects);
+ }
+
+ ///
+ /// Get StudySubject by it's id.
+ ///
+ /// StudySubject's id.
+ /// StudySubject.
+ [Authorize]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(StudySubjectDto))]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+ [HttpGet("{id}")]
+ public async Task GetById(long id)
+ {
+ try
+ {
+ var studySubjectDto = await _studySubjectService.GetById(id).ConfigureAwait(false);
+ return Ok(studySubjectDto);
+ }
+ catch (ArgumentException e)
+ {
+ return BadRequest(e.Message);
+ }
+ }
+
+ ///
+ /// Add a new StudySubject to the database.
+ ///
+ /// Entity to add.
+ /// A representing the result of the asynchronous operation.
+ [Authorize]
+ [ProducesResponseType(StatusCodes.Status201Created)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+ [HttpPost]
+ public async Task Create(StudySubjectDto dto)
+ {
+ if (!ModelState.IsValid)
+ {
+ return BadRequest(ModelState);
+ }
+
+ var creationResult = await _studySubjectService.Create(dto).ConfigureAwait(false);
+
+ return CreatedAtAction(
+ nameof(GetById),
+ new { id = creationResult.Id, },
+ creationResult);
+ }
+
+ ///
+ /// Update info about a specific StudySubject in the database.
+ ///
+ /// StudySubject to update.
+ /// StudySubject.
+ [Authorize]
+ [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(StudySubjectDto))]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+ [HttpPut]
+ public async Task Update(StudySubjectDto dto)
+ {
+ if (!ModelState.IsValid)
+ {
+ return BadRequest(ModelState);
+ }
+
+ return Ok(await _studySubjectService.Update(dto).ConfigureAwait(false));
+ }
+
+ ///
+ /// Delete a specific StudySubject entity from the database.
+ ///
+ /// StudySubject's id.
+ /// A representing the result of the asynchronous operation.
+ [Authorize]
+ [ProducesResponseType(StatusCodes.Status204NoContent)]
+ [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+ [HttpDelete("{id}")]
+ public async Task Delete(long id)
+ {
+ this.ValidateId(id, _localizer);
+
+ await _studySubjectService.Delete(id).ConfigureAwait(false);
+ return NoContent();
+ }
+}