Compare commits

...

140 Commits

Author SHA1 Message Date
385937ee68 удалил бесполезный код 2023-05-24 00:07:06 +04:00
55a9363a09 так симпатичнее 2023-05-20 08:54:31 +04:00
1aca0b53bb бд для дарьи 2023-05-19 22:54:53 +04:00
256f8f98af теперь 1 метод на всё и на это, и на это, и на это, и на то 2023-05-19 22:47:00 +04:00
e4e9447a0b сделали 13 этап 2023-05-19 22:07:03 +04:00
f07b35cd87 Merge branch 'Storekeeper' into Worker_Raspaev 2023-05-19 20:02:31 +04:00
10fb62b17e добавили десятый график. сделали 12 этап курсовой работы 2023-05-19 20:01:29 +04:00
d5438a4090 fix 2023-05-19 20:00:42 +04:00
85f38e9539 fix 2023-05-19 18:58:40 +04:00
b48124b0b9 DASHA SPASIBO CHTO ZALILA 2023-05-19 17:41:42 +04:00
6f449c505b КОЛЯ ДЕРЖИ ИЗМЕНЕНИЯ КАК ТЫ И ПРОСИЛ 2023-05-19 17:30:44 +04:00
60c507bb4b обновление политки конфиденциальности 2023-05-19 16:49:20 +04:00
2f80dcf920 редактирование 2023-05-19 15:49:30 +04:00
c15757969f Merge branch 'Worker_Raspaev' into Storekeeper 2023-05-19 15:29:03 +04:00
ce13c36817 изменение политики безопасности 2023-05-19 15:26:48 +04:00
1258a43786 fix 2023-05-19 14:39:35 +04:00
51c8a7da5a fa 2023-05-19 14:19:42 +04:00
b426a6373d fix frontend and update user 2023-05-19 14:06:33 +04:00
38f53ae91e fix front 2023-05-19 11:36:34 +04:00
5bd38a465f fix report 2023-05-19 11:25:45 +04:00
ed4a896db8 не до конца пофикшенный фронт 2023-05-18 21:01:22 +04:00
842531bccd не дозолил 2023-05-18 20:38:36 +04:00
9e470873d8 оно работает 2023-05-18 20:38:05 +04:00
13f475d64b pdf 2023-05-18 20:26:26 +04:00
cd21f23715 commit 2023-05-18 19:39:29 +04:00
9c61562368 merge 2023-05-18 19:38:56 +04:00
2b92074b02 отправление на почту нужно доделать 2023-05-18 19:24:35 +04:00
66292d362b создание пдфки не работает 2023-05-18 19:21:42 +04:00
d43bff822f send message 2023-05-18 19:02:48 +04:00
67257c30ca fix 2023-05-18 18:07:49 +04:00
3afab0dee9 merge 2023-05-18 18:05:48 +04:00
610c76a402 mail worker 2023-05-18 18:04:23 +04:00
b686555cf7 pdf 2023-05-18 18:01:28 +04:00
f6657eaa0d pdf 2023-05-18 17:21:54 +04:00
acdd3e1554 вывод отчета на форму 2023-05-18 16:32:53 +04:00
5333817ea8 формирование списков работает 2023-05-18 14:40:36 +04:00
a166e976de Merge branch 'Storekeeper' into Worker_Raspaev 2023-05-18 13:37:29 +04:00
4997506a44 формат 2023-05-18 12:58:44 +04:00
e376e9d8f2 files download 2023-05-17 22:40:46 +04:00
2bec90ed3d формирование списка в ворде работает криво, но работает 2023-05-17 22:19:18 +04:00
41f189f0ee Merge branch 'Storekeeper' into Worker_Raspaev 2023-05-17 20:17:27 +04:00
8ed579f623 чето есть чето нет 2023-05-17 20:16:45 +04:00
64bdab2d57 CRUD для покупок готов 2023-05-17 19:36:40 +04:00
f3d161527a Merge branch 'Storekeeper' into Worker_Raspaev 2023-05-17 17:15:40 +04:00
fe06bc28e1 fix linkpurchase 2023-05-17 17:14:45 +04:00
f1a9439c55 rename 2023-05-17 15:45:20 +04:00
786d2de669 а может даже так 2023-05-17 15:37:25 +04:00
5a027d33ef не все имена были переименованы 2023-05-17 15:28:09 +04:00
47534e6672 исправление имени 2023-05-17 15:27:11 +04:00
fbff900bef добавление excel и doc логики без формирования отчетов 2023-05-17 15:22:18 +04:00
e343e4638f валидация? 2023-05-17 14:43:35 +04:00
f663bfe663 cruds 2023-05-17 12:28:05 +04:00
b64aa50fc8 написал логику обновления 2023-05-17 12:11:21 +04:00
70f28e3c66 реализованно добавление и удаление сборок из покупок 2023-05-17 11:54:30 +04:00
d3b455117e пофиксил обновление цены при привязке сборки к покупке 2023-05-17 11:00:56 +04:00
b84b593b88 fix 2023-05-16 23:01:02 +04:00
d498e507ff fix purchase 2023-05-16 19:49:58 +04:00
f032fc0f44 добавлена смена статуса у покупки 2023-05-16 18:35:08 +04:00
8cc02e4532 поломанный fontawesome 2023-05-16 18:11:20 +04:00
a2b22658c3 merge 2023-05-16 17:59:07 +04:00
10271ea4bd fix 2023-05-16 17:57:43 +04:00
0f35bf9e19 fix 2023-05-16 17:54:45 +04:00
f8845a84c0 fa 2023-05-16 17:52:38 +04:00
011fa8a2c3 fix 2023-05-16 17:48:39 +04:00
bfc1dc3313 fix updateOrder 2023-05-16 17:48:39 +04:00
a3bd2346ca CRUD для комментариев готов 2023-05-16 17:48:39 +04:00
7ee3f34450 CRUD для сборки сделан 2023-05-16 17:48:38 +04:00
1774b0c758 реализовал редактирование сборки 2023-05-16 17:48:38 +04:00
190968e7b5 сделал отображение сборок на стрнаице и их создание, нужно реализовать выделение строк таблицы и их последующее удаление и изменение 2023-05-16 17:48:38 +04:00
70e9005df6 init controller 2023-05-16 17:48:37 +04:00
bc7055257d регистрация работает 2023-05-16 17:48:37 +04:00
cade6df340 попытки фиксить регистрацию 2023-05-16 17:48:37 +04:00
4684d4665b fix 2023-05-16 15:57:27 +04:00
aeef227dfc fix updateOrder 2023-05-16 15:57:14 +04:00
70ae0159af Merge branch 'Storekeeper' into Worker_Raspaev 2023-05-16 15:42:10 +04:00
b984fdfb05 table select index && create 2023-05-16 15:41:04 +04:00
b3d59578e7 CRUD для комментариев готов 2023-05-16 14:38:49 +04:00
f72779704e CRUD для сборки сделан 2023-05-16 12:48:47 +04:00
b9119331fe реализовал редактирование сборки 2023-05-16 10:40:26 +04:00
4121d5ec1c сделал отображение сборок на стрнаице и их создание, нужно реализовать выделение строк таблицы и их последующее удаление и изменение 2023-05-15 22:33:12 +04:00
1c79c41e0f init controller 2023-05-15 19:26:52 +04:00
ffe3f57242 регистрация работает 2023-05-15 19:12:19 +04:00
f6c5157c5d попытки фиксить регистрацию 2023-05-15 18:59:57 +04:00
b0ea2fb11e register 2023-05-15 18:24:46 +04:00
a6aa2d3e35 mama say mama sa mama coo sa 2023-04-08 17:24:05 +04:00
a48d6d1e44 помогите 2023-04-08 17:14:55 +04:00
856735868d убили футер 2023-04-08 17:11:21 +04:00
a7b13bef7a Последние изменения 2023-04-08 17:03:47 +04:00
34849c0377 Merge branch 'Worker_Raspaev' into Storekeeper 2023-04-08 16:57:38 +04:00
417a8c9dcf fix logic purchase 2023-04-08 16:57:03 +04:00
879d0d858b Промежуточные изменения 2023-04-08 16:56:53 +04:00
923d110e40 Исправление отчётов 2023-04-08 15:51:12 +04:00
0b0e6abcde исправление для отчётов 2023-04-07 18:48:40 +04:00
db7f79ed81 мини изменения 2023-04-07 01:56:19 +04:00
07ebb8d635 Исправление отчета + бага бд 2023-04-06 21:51:10 +04:00
3244f9d2ea Миграция 2023-04-06 21:28:58 +04:00
51a8119546 Вроде норм отчеты 2023-04-06 21:18:30 +04:00
6011ed3f90 Удаление лишнего 2023-04-06 20:58:24 +04:00
b1f517a3e2 спасибо за отчет миру 2023-04-05 00:21:12 +04:00
eb08556e88 Merge branch 'Worker_Raspaev' into Storekeeper 2023-04-04 18:49:24 +04:00
576d8eef75 Исправление моделей 2023-04-04 18:48:23 +04:00
1ea5be4788 фронт вроде готов 2023-04-04 13:29:56 +04:00
d3b137fbdb footer исправлен 2023-04-04 13:14:18 +04:00
6dd59c47ad Разделение проектов 2023-04-04 02:09:53 +04:00
ce1fd40875 чепуха какая-то но пусть пока будет так 2023-04-04 01:23:14 +04:00
1c67a1004f слияние типа 2023-04-04 00:22:03 +04:00
c2d673f9a3 я ничего не знаю я дебил 2023-04-04 00:18:03 +04:00
0f7148fbe5 Получение данных для формирования отчёта готово 2023-04-03 23:59:08 +04:00
76c9fa6580 удаление лишних инклудов 2023-04-03 19:52:40 +04:00
5c849f7d59 исправление фронта 2023-04-03 19:46:50 +04:00
ea30411791 Лишние инклуды 2023-04-03 19:32:56 +04:00
0a57d3ebf3 Неплохой фронтенд вроде 2023-04-03 19:29:30 +04:00
bab1b63323 Merge branch 'Worker_Raspaev' into Storekeeper 2023-04-03 18:25:30 +04:00
2a7782eea0 Остался отчет 2023-04-03 18:24:24 +04:00
998e352d2d Исправленна бизнес логика для удаления и изменения покупки. Также добавлен ещё один способ фильтрации для покупки 2023-04-03 18:23:22 +04:00
ff8041fe1a исправленный фронтенд работника. Даша спасибо за хедер 2023-04-03 18:03:17 +04:00
2e505618bc модель 2023-04-03 13:43:42 +04:00
1bcd3a5d0b слияние 2023-04-03 13:41:11 +04:00
8cddd1067c Исправлен переход после регистрации 2023-04-03 10:45:33 +04:00
26966f9c8c merge 2023-04-03 10:39:43 +04:00
d0aa59742c Ну чето красивое 2023-04-03 00:56:06 +04:00
800a21389a крутые изменения 2023-04-02 22:52:24 +04:00
88504d8bf1 Переключение через header 2023-04-02 21:00:44 +04:00
0d9306c597 fix 2023-04-02 19:52:02 +04:00
f749e8fcb4 fix 2023-04-02 19:30:57 +04:00
3db96eaaa1 Подсчет суммы и цены 2023-04-02 19:27:58 +04:00
6789b86723 Merge branch 'Worker_Raspaev' into Storekeeper 2023-04-02 17:43:33 +04:00
4e6b34a77d Уборка лишнего 2023-04-02 17:42:07 +04:00
e7fff05911 обновление суммы 2023-04-02 17:41:03 +04:00
3dd37697c8 вроде всё 2023-04-02 17:15:03 +04:00
2debfe4ba8 бред 2023-04-02 17:13:48 +04:00
cf82bd4fe7 fix 2023-04-02 16:37:37 +04:00
03d8c6db0a merge 2023-04-02 16:36:18 +04:00
ff2f0d57db fix 2023-04-02 16:32:22 +04:00
1160862303 Исправление хранилищ 2023-04-02 16:22:58 +04:00
8203d22849 fix 2023-04-02 16:10:21 +04:00
934080cd54 fix 2023-04-02 15:43:05 +04:00
c4c8069645 переделанный макет работника 2023-04-02 14:53:07 +04:00
410ee5cb9f fix 2023-04-02 13:47:04 +04:00
9ab410c5af Макеты для работника готовы 2023-04-02 13:41:20 +04:00
262 changed files with 92397 additions and 2102 deletions

View File

@ -1,5 +1,5 @@
using HardwareShopContracts.BindingModels;
using HardwareShopContracts.BuisnessLogicsContracts;
using HardwareShopContracts.BusinessLogicsContracts;
using HardwareShopContracts.SearchModels;
using HardwareShopContracts.StoragesContracts;
using HardwareShopContracts.ViewModels;

View File

@ -1,5 +1,5 @@
using HardwareShopContracts.BindingModels;
using HardwareShopContracts.BuisnessLogicsContracts;
using HardwareShopContracts.BusinessLogicsContracts;
using HardwareShopContracts.SearchModels;
using HardwareShopContracts.StoragesContracts;
using HardwareShopContracts.ViewModels;

View File

@ -120,5 +120,17 @@ namespace HardwareShopBusinessLogic.BusinessLogics.Storekeeper
{
return StatusUpdate(model, OrderStatus.Выдан);
}
public bool Delete(OrderBindingModel model)
{
CheckModel(model, false);
_logger.LogInformation("Delete. Id: {Id}", model.Id);
if (_orderStorage.Delete(model) == null)
{
_logger.LogWarning("Delete operation failed");
return false;
}
return true;
}
}
}

View File

@ -0,0 +1,144 @@
using HardwareShopBusinessLogic.MailWorker;
using HardwareShopBusinessLogic.OfficePackage;
using HardwareShopBusinessLogic.OfficePackage.HelperModels;
using HardwareShopContracts.BindingModels;
using HardwareShopContracts.BusinessLogicsContracts;
using HardwareShopContracts.StoragesContracts;
using HardwareShopContracts.ViewModels;
namespace HardwareShopBusinessLogic.BusinessLogics.Storekeeper
{
public class ReportStorekeeperLogic : IReportStorekeeperLogic
{
private readonly IComponentStorage _componentStorage;
private readonly IGoodStorage _goodStorage;
private readonly AbstractSaveToExcel _saveToExcel;
private readonly AbstractSaveToWord _saveToWord;
private readonly AbstractSaveToPdf _saveToPdf;
private readonly AbstractMailWorker _mailWorker;
public ReportStorekeeperLogic(IComponentStorage componentStorage, AbstractSaveToExcel abstractSaveToExcel, AbstractSaveToWord abstractSaveToWord, IGoodStorage goodStorage, AbstractMailWorker abstractMailWorker, AbstractSaveToPdf saveToPdf)
{
_componentStorage = componentStorage;
_saveToExcel = abstractSaveToExcel;
_saveToWord = abstractSaveToWord;
_goodStorage = goodStorage;
_mailWorker = abstractMailWorker;
_saveToPdf = saveToPdf;
}
public List<ReportBuildGoodViewModel> GetBuildGood(List<GoodViewModel> goods)
{
var result = new List<ReportBuildGoodViewModel>();
foreach (var g in goods)
{
var good = _goodStorage.GetElement(new() { Id = g.Id })!;
var builds = good.GoodComponents
//получили сборки и количество компонентов
.Select(x => _componentStorage.GetComponentBuilds(new() { Id = x.Key })
//если кол-во компонентов в товаре == кол-ву в сборке
.Where(y => x.Value.Item2 == y.Item2))
.SelectMany(x => x.Select(x => x.Item1))
.Distinct()
.ToList();
ReportBuildGoodViewModel record = new()
{
GoodName = good.GoodName,
Builds = builds
};
result.Add(record);
}
return result;
}
/// Получение сведений по комплектующим за период,
/// с указанием в каких товарах и сборках они использовались
public List<ReportComponentsViewModel> GetComponents(ReportBindingModel model)
{
var result = new List<ReportComponentsViewModel>();
var components = _componentStorage.GetFilteredList(new()
{
UserId = model.UserId,
DateFrom = model.DateFrom,
DateTo = model.DateTo
});
foreach (var component in components)
{
var builds = component.ComponentBuilds
.Select(x => Tuple.Create(x.Value.Item1.BuildName, x.Value.Item2))
.ToList();
var goods = _componentStorage
.GetComponentGoods(new() { Id = component.Id })
.ToList();
ReportComponentsViewModel record = new()
{
ComponentName = component.ComponentName,
TotalCount = builds.Sum(x => x.Item2) + goods.Sum(x => x.Item2),
GoodOrBuilds = builds.Concat(goods).ToList()
};
result.Add(record);
}
return result;
}
public byte[] SaveBuildGoodToWordFile(ReportBindingModel model, List<GoodViewModel> goods)
{
_saveToWord.CreateBuildGoodReport(new WordInfo
{
FileName = model.FileName,
Title = "Cписок сборок по выбранным товарам",
BuildGood = GetBuildGood(goods)
});
byte[] file = File.ReadAllBytes(model.FileName);
File.Delete(model.FileName);
return file;
}
public byte[] SaveBuildGoodToExcelFile(ReportBindingModel model, List<GoodViewModel> goods)
{
_saveToExcel.CreateBuildGoodReport(new ExcelInfo
{
FileName = model.FileName,
Title = "Cписок сборок по выбранным товарам",
BuildGood = GetBuildGood(goods)
});
byte[] file = File.ReadAllBytes(model.FileName);
File.Delete(model.FileName);
return file;
}
public bool SendReportOnMail(ReportBindingModel model)
{
model.FileName = "temp.pdf";
_saveToPdf.CreateComponentsReport(new PdfInfo
{
FileName = model.FileName,
Title = "Отчет по комплектующим",
DateFrom = model.DateFrom,
DateTo = model.DateTo,
ReportComponents = GetComponents(model)
});
byte[] file = File.ReadAllBytes(model.FileName);
File.Delete(model.FileName);
_mailWorker.MailSendAsync(new()
{
MailAddress = model.UserEmail,
Subject = "Отчет по комплектующим",
Text = $"Отчет по полученным вами комлектующим за период с {model.DateFrom} по {model.DateTo} в формате Pdf.",
File = file
});
return true;
}
}
}

View File

@ -4,6 +4,7 @@ using HardwareShopContracts.SearchModels;
using HardwareShopContracts.StoragesContracts;
using HardwareShopContracts.ViewModels;
using Microsoft.Extensions.Logging;
using System.Text.RegularExpressions;
namespace HardwareShopBusinessLogic.BusinessLogics
{
@ -93,17 +94,21 @@ namespace HardwareShopBusinessLogic.BusinessLogics
{
return;
}
if (string.IsNullOrEmpty(model.Login))
if (string.IsNullOrEmpty(model.Login) || model.Login.Length > 40)
{
throw new ArgumentNullException("Нет логина пользователя", nameof(model.Login));
throw new ArgumentNullException("Нет логина пользователя или длина превышает 40 символов", nameof(model.Login));
}
if (string.IsNullOrEmpty(model.Email))
if (string.IsNullOrEmpty(model.Email) || model.Email.Length > 40)
{
throw new ArgumentNullException("Нет почты пользователя", nameof(model.Email));
throw new ArgumentNullException("Нет почты пользователя или длина превышает 40 символов", nameof(model.Email));
}
if (string.IsNullOrEmpty(model.Password))
if (!Regex.IsMatch(model.Email, @"^[^@\s]+@[^@\s]+\.[^@\s]+$", RegexOptions.IgnoreCase))
{
throw new ArgumentNullException("Нет пароля пользователя", nameof(model.Password));
throw new ArgumentException("Неправильно введенная почта", nameof(model.Email));
}
if (string.IsNullOrEmpty(model.Password) || model.Password.Length > 40 || model.Password.Contains(' '))
{
throw new ArgumentNullException("Нет пароля пользователя или пароль содержит пробелы", nameof(model.Password));
}
_logger.LogInformation("User. Login: {Login}. Email: {Email}. Id: {Id}",
model.Login, model.Email, model.Id);

View File

@ -10,10 +10,12 @@ namespace HardwareShopContracts.BusinessLogicsContracts
{
private readonly ILogger _logger;
private readonly IBuildStorage _buildStorage;
public BuildLogic(ILogger<BuildLogic> logger, IBuildStorage buildStorage)
private readonly IComponentStorage _componentStorage;
public BuildLogic(ILogger<BuildLogic> logger, IBuildStorage buildStorage, IComponentStorage componentStorage)
{
_logger = logger;
_buildStorage = buildStorage;
_componentStorage = componentStorage;
}
public List<BuildViewModel>? ReadList(BuildSearchModel? model)
@ -94,10 +96,6 @@ namespace HardwareShopContracts.BusinessLogicsContracts
{
throw new ArgumentNullException("Нет названия сборки", nameof(model.BuildName));
}
if (model.Price <= 0)
{
throw new ArgumentNullException("Цена компонента должна быть больше 0", nameof(model.Price));
}
if (model.UserId < 0)
{
throw new ArgumentNullException("Некорректный идентификатор у клиента", nameof(model.UserId));
@ -106,7 +104,7 @@ namespace HardwareShopContracts.BusinessLogicsContracts
{
throw new ArgumentNullException("Некорректный идентификатор у сборки", nameof(model.Id));
}
_logger.LogInformation("Build. BuildName:{BuildName}. Price:{Price}. Id:{Id}", model.BuildName, model.Price, model.Id);
_logger.LogInformation("Build. UserId:{UserId}. BuildName:{BuildName}. Price:{Price}. Id:{Id}", model.UserId, model.BuildName, model.Price, model.Id);
var element = _buildStorage.GetElement(new BuildSearchModel
{
BuildName = model.BuildName

View File

@ -100,8 +100,12 @@ namespace HardwareShopContracts.BusinessLogicsContracts
{
throw new ArgumentNullException("Некорректный идентификатор у комментария", nameof(model.Id));
}
if (model.UserId < 0)
{
throw new ArgumentNullException("Некорректный идентификатор у клиента", nameof(model.UserId));
}
_logger.LogInformation("Comment. BuildId:{BuildId}. Id:{Id}", model.BuildId, model.Id);
_logger.LogInformation("Comment. UserId:{UserId}. BuildId:{BuildId}. Id:{Id}", model.UserId, model.BuildId, model.Id);
}
}
}

View File

@ -30,9 +30,34 @@ namespace HardwareShopContracts.BusinessLogicsContracts
return list;
}
public CommentViewModel? ReadElement(CommentSearchModel model)
public List<PurchaseViewModel>? ReadOrderList(PurchaseSearchModel model)
{
throw new NotImplementedException();
_logger.LogInformation("ReadOrderList. Id:{Id}", model.Id);
var list = _purchaseStorage.GetReportFilteredList(model);
if (list == null)
{
_logger.LogWarning("ReadOrderList return null list");
return null;
}
_logger.LogInformation("ReadOrderList. Count:{Count}", list.Count);
return list;
}
public PurchaseViewModel? ReadElement(PurchaseSearchModel model)
{
if (model == null)
{
throw new ArgumentNullException(nameof(model));
}
_logger.LogInformation("ReadElement. Id:{Id}", model.Id);
var element = _purchaseStorage.GetElement(model);
if (element == null)
{
_logger.LogWarning("ReadElement element not found");
return null;
}
_logger.LogInformation("ReadElement find. Id:{Id}", element.Id);
return element;
}
public bool DeliveryPurchase(PurchaseBindingModel model)
@ -50,7 +75,7 @@ namespace HardwareShopContracts.BusinessLogicsContracts
model.PurchaseStatus = PurchaseStatus.Выдан;
model.DatePurchase = DateTime.Now;
CheckModel(model, false);
if (_purchaseStorage.Update(model) == null)
if (_purchaseStorage.Update(model, false) == null)
{
_logger.LogWarning("Change status operation failed");
return false;
@ -78,6 +103,11 @@ namespace HardwareShopContracts.BusinessLogicsContracts
public bool Update(PurchaseBindingModel model)
{
if (model.PurchaseStatus == PurchaseStatus.Выдан)
{
_logger.LogWarning("Update status operation failed");
return false;
}
CheckModel(model);
if (_purchaseStorage.Update(model) == null)
{
@ -89,6 +119,11 @@ namespace HardwareShopContracts.BusinessLogicsContracts
public bool Delete(PurchaseBindingModel model)
{
if (model.PurchaseStatus == PurchaseStatus.Выдан)
{
_logger.LogWarning("Delete status operation failed");
return false;
}
CheckModel(model, false);
_logger.LogInformation("Delete. Id:{Id}", model.Id);
if (_purchaseStorage.Delete(model) == null)
@ -121,7 +156,7 @@ namespace HardwareShopContracts.BusinessLogicsContracts
{
throw new ArgumentNullException("Сумма заказа должна быть больше 0", nameof(model.Sum));
}
_logger.LogInformation("Purchase. PurchaseID:{Id}. Sum:{ Sum}", model.Id, model.Sum);
_logger.LogInformation("Purchase. UserId:{UserId}. PurchaseID:{Id}. Sum:{ Sum}", model.UserId, model.Id, model.Sum);
}
}
}

View File

@ -0,0 +1,177 @@

using HardwareShopBusinessLogic.MailWorker;
using HardwareShopBusinessLogic.OfficePackage;
using HardwareShopBusinessLogic.OfficePackage.HelperModels;
using HardwareShopBusinessLogic.OfficePackage.Implements;
using HardwareShopContracts.BindingModels;
using HardwareShopContracts.SearchModels;
using HardwareShopContracts.StoragesContracts;
using HardwareShopContracts.ViewModels;
using HardwareShopDatabaseImplement.Implements.Worker;
using HardwareShopDatabaseImplement.Models.Worker;
using System.Reflection.PortableExecutable;
namespace HardwareShopContracts.BusinessLogicsContracts
{
public class WorkerReportLogic : IWorkerReportLogic
{
private readonly IPurchaseStorage _purchaseStorage;
private readonly AbstractSaveToExcel _saveToExcel;
private readonly AbstractSaveToWord _saveToWord;
private readonly AbstractSaveToPdf _saveToPdf;
private readonly AbstractMailWorker _mailKitWorker;
public WorkerReportLogic(IPurchaseStorage purchaseStorage, AbstractMailWorker mailKitWorker, AbstractSaveToPdf saveToPdf, AbstractSaveToExcel saveToExcel, AbstractSaveToWord saveToWord)
{
_purchaseStorage = purchaseStorage;
_saveToExcel = saveToExcel;
_saveToWord = saveToWord;
_saveToPdf = saveToPdf;
_mailKitWorker = mailKitWorker;
}
/// <summary>
/// Получение списка компонент с указанием, в каких покупках используются
/// </summary>
/// <returns></returns>
public List<ReportPurchaseComponentViewModel> GetPurchaseComponent(List<PurchaseViewModel> purchaseList)
{
var list = new List<ReportPurchaseComponentViewModel>();
foreach (var p in purchaseList)
{
var purchase = _purchaseStorage.GetElement(new() { Id = p.Id })!;
var record = new ReportPurchaseComponentViewModel
{
Id = purchase.Id,
Builds = new List<(string Build, int count, List<(string Component, int count)>)>(),
TotalCount = 0,
TotalCost = purchase.Sum,
};
foreach (var build in purchase.PurchaseBuilds)
{
List<(string Component, int count)> componentList = new List<(string Component, int count)>();
int buildTotalCount = 0;
foreach (var component in build.Value.Item1.BuildComponents)
{
componentList.Add(new(component.Value.Item1.ComponentName, component.Value.Item2));
buildTotalCount += component.Value.Item2;
}
record.Builds.Add(new(build.Value.Item1.BuildName, build.Value.Item2, componentList));
record.TotalCount += buildTotalCount * build.Value.Item2;
}
list.Add(record);
}
return list;
}
/// <summary>
/// Получение списка покупок за определенный период
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public List<ReportPurchaseViewModel> GetPurchase(ReportBindingModel model)
{
var list = new List<ReportPurchaseViewModel>();
var purchases = _purchaseStorage.GetFilteredList(new PurchaseSearchModel { DateFrom = model.DateFrom, DateTo = model.DateTo, UserId = model.UserId });
foreach (var p in purchases)
{
var purchase = _purchaseStorage.GetElement(new() { Id = p.Id })!;
List<string> commentList = new List<string>();
List<string> componentList = new List<string>();
foreach (var build in purchase.PurchaseBuilds)
{
foreach (var comment in build.Value.Item1.BuildComments)
{
commentList.Add(new(comment.Value.Text));
}
foreach (var component in build.Value.Item1.BuildComponents)
{
componentList.Add(component.Value.Item1.ComponentName);
}
}
var record = new ReportPurchaseViewModel
{
Id = purchase.Id,
PurchaseDate = (DateTime)p.DatePurchase,
PurchaseSum = p.Sum,
Comments = commentList,
Components = componentList.Distinct().ToList()
};
list.Add(record);
}
return list;
}
/// <summary>
/// Сохранение компонент с указаеним покупок в файл-Word
/// </summary>
/// <param name="model"></param>
public byte[] SavePurchasesToWordFile(ReportBindingModel model, List<PurchaseViewModel> purchases)
{
_saveToWord.CreateBuildPurchaseReport(new WordInfo
{
FileName = model.FileName,
Title = "Список Компонентов",
PurchaseComponent = GetPurchaseComponent(purchases)
});
byte[] file = File.ReadAllBytes(model.FileName);
File.Delete(model.FileName);
return file;
}
/// <summary>
/// Сохранение компонент с указаеним покупок в файл-Excel
/// </summary>
/// <param name="model"></param>
public byte[] SavePurchasesToExcelFile(ReportBindingModel model, List<PurchaseViewModel> purchases)
{
_saveToExcel.CreatePurchaseComponentReport(new ExcelInfo
{
FileName = model.FileName,
Title = "Список Компонентов",
PurchaseComponent = GetPurchaseComponent(purchases)
});
byte[] file = File.ReadAllBytes(model.FileName);
File.Delete(model.FileName);
return file;
}
/// <summary>
/// Сохранение отчёта по покупкам в файл-Pdf
/// </summary>
/// <param name="model"></param>
public void SendByMailPurchaseReport(ReportBindingModel model)
{
model.FileName = "temp.pdf";
_saveToPdf.GetPurchaseReportFile(new()
{
FileName = model.FileName,
Title = "Отчет по покупкам",
DateFrom = model.DateFrom,
DateTo = model.DateTo,
ReportPurchases = GetPurchase(model)
});
byte[] file = File.ReadAllBytes(model.FileName);
File.Delete(model.FileName);
_mailKitWorker.MailSendAsync(new()
{
MailAddress = model.UserEmail,
Subject = "Отчет по покупкам",
Text = $"За период с {model.DateFrom.ToShortDateString()} " +
$"по {model.DateTo.ToShortDateString()}.",
File = file
});
}
}
}

View File

@ -7,11 +7,17 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DocumentFormat.OpenXml" Version="2.20.0" />
<PackageReference Include="MailKit" Version="4.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
<PackageReference Include="PdfSharp.MigraDoc.Standard" Version="1.51.15" />
<PackageReference Include="System.Text.Encoding" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\HardwareShopContracts\HardwareShopContracts.csproj" />
<ProjectReference Include="..\HardwareShopDatabaseImplement\HardwareShopDatabaseImplement.csproj" />
<ProjectReference Include="..\HardwareShopDataModels\HardwareShopDataModels.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,62 @@
using HardwareShopContracts.BindingModels;
using Microsoft.Extensions.Logging;
namespace HardwareShopBusinessLogic.MailWorker
{
public abstract class AbstractMailWorker
{
protected string _mailLogin = string.Empty;
protected string _mailPassword = string.Empty;
protected string _smtpClientHost = string.Empty;
protected int _smtpClientPort;
protected string _popHost = string.Empty;
protected int _popPort;
private readonly ILogger _logger;
public AbstractMailWorker(ILogger<AbstractMailWorker> logger)
{
_logger = logger;
}
public void MailConfig(MailConfigBindingModel config)
{
_mailLogin = config.MailLogin;
_mailPassword = config.MailPassword;
_smtpClientHost = config.SmtpClientHost;
_smtpClientPort = config.SmtpClientPort;
_popHost = config.PopHost;
_popPort = config.PopPort;
_logger.LogDebug("Config: {login}, {password}, {clientHost}, {clientPort}, {popHost}, {popPort}",
_mailLogin, _mailPassword, _smtpClientHost, _smtpClientPort, _popHost, _popPort);
}
public async void MailSendAsync(MailSendInfoBindingModel info)
{
if (string.IsNullOrEmpty(_mailLogin) || string.IsNullOrEmpty(_mailPassword))
{
return;
}
if (string.IsNullOrEmpty(_smtpClientHost) || _smtpClientPort == 0)
{
return;
}
if (string.IsNullOrEmpty(info.MailAddress) || string.IsNullOrEmpty(info.Subject) || string.IsNullOrEmpty(info.Text))
{
return;
}
_logger.LogDebug("Send Mail: {To}, {Subject}", info.MailAddress, info.Subject);
await SendMailAsync(info);
}
protected abstract Task SendMailAsync(MailSendInfoBindingModel info);
}
}

View File

@ -0,0 +1,44 @@
using HardwareShopContracts.BindingModels;
using HardwareShopContracts.BusinessLogicsContracts;
using MailKit.Net.Pop3;
using MailKit.Security;
using Microsoft.Extensions.Logging;
using System.Net;
using System.Net.Mail;
using System.Text;
namespace HardwareShopBusinessLogic.MailWorker
{
public class MailKitWorker : AbstractMailWorker
{
public MailKitWorker(ILogger<MailKitWorker> logger) : base(logger) { }
protected override async Task SendMailAsync(MailSendInfoBindingModel info)
{
using var objMailMessage = new MailMessage();
using var objSmtpClient = new SmtpClient(_smtpClientHost, _smtpClientPort);
try
{
objMailMessage.From = new MailAddress(_mailLogin);
objMailMessage.To.Add(new MailAddress(info.MailAddress));
objMailMessage.Subject = info.Subject;
objMailMessage.Body = info.Text;
objMailMessage.SubjectEncoding = Encoding.UTF8;
objMailMessage.BodyEncoding = Encoding.UTF8;
MemoryStream ms = new(info.File);
objMailMessage.Attachments.Add(new Attachment(ms, "report.pdf", "application/pdf"));
objSmtpClient.UseDefaultCredentials = false;
objSmtpClient.EnableSsl = true;
objSmtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
objSmtpClient.Credentials = new NetworkCredential(_mailLogin, _mailPassword);
await Task.Run(() => objSmtpClient.Send(objMailMessage));
}
catch (Exception)
{
throw;
}
}
}
}

View File

@ -0,0 +1,161 @@
using HardwareShopBusinessLogic.OfficePackage.HelperEnums;
using HardwareShopBusinessLogic.OfficePackage.HelperModels;
namespace HardwareShopBusinessLogic.OfficePackage
{
public abstract class AbstractSaveToExcel
{
/// <summary>
/// Создание отчета по сборкам в выбранных товарах
/// </summary>
/// <param name="info"></param>
public void CreateBuildGoodReport(ExcelInfo info)
{
CreateExcel(info);
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "A",
RowIndex = 1,
Text = info.Title,
StyleInfo = ExcelStyleInfoType.Title
});
MergeCells(new ExcelMergeParameters
{
CellFromName = "A1",
CellToName = "B1"
});
uint rowIndex = 2;
foreach (var bg in info.BuildGood)
{
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "A",
RowIndex = rowIndex,
Text = bg.GoodName,
StyleInfo = ExcelStyleInfoType.TextWithBroder
});
rowIndex++;
foreach (var build in bg.Builds)
{
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "B",
RowIndex = rowIndex,
Text = build,
StyleInfo = ExcelStyleInfoType.TextWithBroder
});
rowIndex++;
}
}
SaveExcel(info);
}
/// <summary>
/// Создание отчета по сборкам в выбранных товарах
/// </summary>
/// <param name="info"></param>
public void CreatePurchaseComponentReport(ExcelInfo info)
{
CreateExcel(info);
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "A",
RowIndex = 1,
Text = info.Title,
StyleInfo = ExcelStyleInfoType.Title
});
MergeCells(new ExcelMergeParameters
{
CellFromName = "A1",
CellToName = "E1"
});
uint rowIndex = 2;
foreach (var bg in info.PurchaseComponent)
{
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "A",
RowIndex = rowIndex,
Text = bg.Id.ToString(),
StyleInfo = ExcelStyleInfoType.Text
});
rowIndex++;
foreach (var build in bg.Builds)
{
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "B",
RowIndex = rowIndex,
Text = build.Build,
StyleInfo = ExcelStyleInfoType.TextWithBroder
});
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "C",
RowIndex = rowIndex,
Text = build.count.ToString(),
StyleInfo = ExcelStyleInfoType.TextWithBroder
});
rowIndex++;
foreach (var component in build.Item3)
{
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "D",
RowIndex = rowIndex,
Text = component.Component,
StyleInfo = ExcelStyleInfoType.TextWithBroder
});
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "E",
RowIndex = rowIndex,
Text = component.count.ToString(),
StyleInfo = ExcelStyleInfoType.TextWithBroder
});
rowIndex++;
}
}
}
SaveExcel(info);
}
/// <summary>
/// Создание excel-файла
/// </summary>
/// <param name="info"></param>
protected abstract void CreateExcel(ExcelInfo info);
/// <summary>
/// Добавляем новую ячейку в лист
/// </summary>
/// <param name="cellParameters"></param>
protected abstract void InsertCellInWorksheet(ExcelCellParameters excelParams);
/// <summary>
/// Объединение ячеек
/// </summary>
/// <param name="mergeParameters"></param>
protected abstract void MergeCells(ExcelMergeParameters excelParams);
/// <summary>
/// Сохранение файла
/// </summary>
/// <param name="info"></param>
protected abstract void SaveExcel(ExcelInfo info);
}
}

View File

@ -0,0 +1,155 @@
using DocumentFormat.OpenXml.EMMA;
using HardwareShopBusinessLogic.OfficePackage.HelperEnums;
using HardwareShopBusinessLogic.OfficePackage.HelperModels;
using MigraDoc.Rendering;
namespace HardwareShopBusinessLogic.OfficePackage
{
public abstract class AbstractSaveToPdf
{
public void GetPurchaseReportFile(PdfInfo info)
{
CreatePdf(info);
CreateParagraph(new PdfParagraph
{
Text = info.Title,
Style = "NormalTitle",
ParagraphAlignment = PdfParagraphAlignmentType.Center
});
CreateParagraph(new PdfParagraph
{
Text = $"за период с {info.DateFrom.ToShortDateString()} " +
$"по {info.DateTo.ToShortDateString()}",
Style = "Normal",
ParagraphAlignment = PdfParagraphAlignmentType.Center
});
CreateTable(new List<string> { "3cm", "4cm", "3cm", "4cm", "4cm" });
CreateRow(new PdfRowParameters
{
Texts = new List<string> { "Покупка", "Дата покупки", "Цена", "Комментарии", "Комплектующие" },
Style = "NormalTitle",
ParagraphAlignment = PdfParagraphAlignmentType.Center
});
foreach (var record in info.ReportPurchases)
{
List<string> comments = record.Comments;
List<string> components = record.Components;
int recordHeight = Math.Max(comments.Count + 1, components.Count + 1);
for (int i = 0; i < recordHeight; i++)
{
List<string> cellsData = new() { "", "", "", "", "" };
if (i == 0)
{
cellsData[0] = record.Id.ToString();
cellsData[1] = record.PurchaseDate.ToShortDateString();
cellsData[2] = record.PurchaseSum.ToString("0.00") + " р.";
CreateRow(new PdfRowParameters
{
Texts = cellsData,
Style = "Normal",
ParagraphAlignment = PdfParagraphAlignmentType.Left
});
continue;
}
int k = i - 1;
if (k < comments.Count)
{
cellsData[3] = comments[k];
}
if (k < components.Count)
{
cellsData[4] = components[k];
}
CreateRow(new PdfRowParameters
{
Texts = cellsData,
Style = "Normal",
ParagraphAlignment = PdfParagraphAlignmentType.Left
});
}
}
CreateParagraph(new PdfParagraph { Text = $"Итого: {info.ReportPurchases.Sum(x => x.PurchaseSum)}\t", Style = "Normal", ParagraphAlignment = PdfParagraphAlignmentType.Left });
SavePdf(info);
}
public void CreateComponentsReport(PdfInfo info)
{
CreatePdf(info);
CreateParagraph(new PdfParagraph { Text = info.Title, Style = "NormalTitle", ParagraphAlignment = PdfParagraphAlignmentType.Center });
CreateParagraph(new PdfParagraph { Text = $"с {info.DateFrom.ToShortDateString()} по {info.DateTo.ToShortDateString()}", Style = "Normal", ParagraphAlignment = PdfParagraphAlignmentType.Center });
CreateTable(new List<string> { "5cm", "5cm", "3cm" });
CreateRow(new PdfRowParameters
{
Texts = new List<string> { "Комплектующее", "Товар/Сборка", "Количество" },
Style = "NormalTitle",
ParagraphAlignment = PdfParagraphAlignmentType.Center
});
foreach (var record in info.ReportComponents)
{
CreateRow(new PdfRowParameters
{
Texts = new List<string> { record.ComponentName, "", "" },
Style = "Normal",
ParagraphAlignment = PdfParagraphAlignmentType.Left
});
foreach (var goodOrBuild in record.GoodOrBuilds)
{
CreateRow(new PdfRowParameters
{
Texts = new List<string> { "", goodOrBuild.Item1, goodOrBuild.Item2.ToString() },
Style = "Normal",
ParagraphAlignment = PdfParagraphAlignmentType.Left
});
}
CreateRow(new PdfRowParameters
{
Texts = new List<string> { "Итого", "", record.TotalCount.ToString() },
Style = "Normal",
ParagraphAlignment = PdfParagraphAlignmentType.Left
});
}
SavePdf(info);
}
/// <summary>
/// Создание doc-файла
/// </summary>
/// <param name="info"></param>
protected abstract void CreatePdf(PdfInfo info);
/// <summary>
/// Создание параграфа с текстом
/// </summary>
/// <param name="title"></param>
/// <param name="style"></param>
protected abstract void CreateParagraph(PdfParagraph paragraph);
/// <summary>
/// Создание таблицы
/// </summary>
/// <param name="title"></param>
/// <param name="style"></param>
protected abstract void CreateTable(List<string> columns);
/// <summary>
/// Создание и заполнение строки
/// </summary>
/// <param name="rowParameters"></param>
protected abstract void CreateRow(PdfRowParameters rowParameters);
/// <summary>
/// Сохранение файла
/// </summary>
/// <param name="info"></param>
protected abstract void SavePdf(PdfInfo info);
}
}

View File

@ -0,0 +1,160 @@
using HardwareShopBusinessLogic.OfficePackage.HelperEnums;
using HardwareShopBusinessLogic.OfficePackage.HelperModels;
namespace HardwareShopBusinessLogic.OfficePackage
{
public abstract class AbstractSaveToWord
{
public void CreateBuildGoodReport(WordInfo info)
{
CreateWord(info);
CreateParagraph(new WordParagraph
{
Texts = new List<(string, WordTextProperties)> { (info.Title, new WordTextProperties { Bold = true, Size = "24" }) },
TextProperties = new WordTextProperties
{
Size = "24",
JustificationType = WordJustificationType.Center
}
});
List<WordRow> rows = new List<WordRow>();
rows.Add(new WordRow
{
Rows = new List<(string, WordTextProperties)> {
("Товары", new WordTextProperties { Size = "24", Bold = true }),
("Сборки", new WordTextProperties { Size = "24", Bold = true })
}
});
var reportRecords = info.BuildGood;
foreach (var reportRecord in reportRecords)
{
rows.Add(new WordRow
{
Rows = new List<(string, WordTextProperties)>
{
(reportRecord.GoodName, new WordTextProperties { Size = "24" }),
("", new WordTextProperties { })
}
});
for (int i = 0; i < reportRecord.Builds.Count; i++)
{
rows.Add(new WordRow
{
Rows = new List<(string, WordTextProperties)>
{
("", new WordTextProperties { }),
(reportRecord.Builds[i], new WordTextProperties { Size = "24" })
}
});
}
}
CreateTable(rows);
SaveWord(info);
}
public void CreateBuildPurchaseReport(WordInfo info)
{
CreateWord(info);
CreateParagraph(new WordParagraph
{
Texts = new List<(string, WordTextProperties)> { (info.Title, new WordTextProperties { Bold = true, Size = "24" }) },
TextProperties = new WordTextProperties
{
Size = "24",
JustificationType = WordJustificationType.Center
}
});
List<WordRow> rows = new List<WordRow>();
rows.Add(new WordRow
{
Rows = new List<(string, WordTextProperties)> {
("Покупки", new WordTextProperties { Size = "24", Bold = true }),
("Сборки", new WordTextProperties { Size = "24", Bold = true }),
("Количество", new WordTextProperties { Size = "24", Bold = true }),
("Компоненты", new WordTextProperties { Size = "24", Bold = true }),
("Количество", new WordTextProperties { Size = "24", Bold = true })
}
});
var reportRecords = info.PurchaseComponent;
foreach (var reportRecord in reportRecords)
{
rows.Add(new WordRow
{
Rows = new List<(string, WordTextProperties)>
{
(reportRecord.Id.ToString(), new WordTextProperties { }),
("", new WordTextProperties { }),
("", new WordTextProperties { }),
("", new WordTextProperties { }),
("", new WordTextProperties { })
}
});
for (int i = 0; i < reportRecord.Builds.Count; i++)
{
rows.Add(new WordRow
{
Rows = new List<(string, WordTextProperties)>
{
("", new WordTextProperties { }),
(reportRecord.Builds[i].Build, new WordTextProperties { }),
(reportRecord.Builds[i].count.ToString(), new WordTextProperties { }),
("", new WordTextProperties { }),
("", new WordTextProperties { })
}
});
for(int j = 0; j < reportRecord.Builds[i].Item3.Count; j++)
{
rows.Add(new WordRow
{
Rows = new List<(string, WordTextProperties)>
{
("", new WordTextProperties { }),
("", new WordTextProperties { }),
("", new WordTextProperties { }),
(reportRecord.Builds[i].Item3[j].Component, new WordTextProperties { }),
(reportRecord.Builds[i].Item3[j].count.ToString(), new WordTextProperties { })
}
});
}
}
}
CreateTable(rows);
SaveWord(info);
}
/// <summary>
/// Создание doc-файла
/// </summary>
/// <param name="info"></param>
protected abstract void CreateWord(WordInfo info);
/// <summary>
/// Создание таблицы
/// </summary>
/// <param name="rows"></param>
protected abstract void CreateTable(List<WordRow> rows);
/// <summary>
/// Создание абзаца с текстом
/// </summary>
/// <param name="paragraph"></param>
/// <returns></returns>
protected abstract void CreateParagraph(WordParagraph paragraph);
/// <summary>
/// Сохранение файла
/// </summary>
/// <param name="info"></param>
protected abstract void SaveWord(WordInfo info);
}
}

View File

@ -0,0 +1,11 @@
namespace HardwareShopBusinessLogic.OfficePackage.HelperEnums
{
public enum ExcelStyleInfoType
{
Title,
Text,
TextWithBroder
}
}

View File

@ -0,0 +1,11 @@
namespace HardwareShopBusinessLogic.OfficePackage.HelperEnums
{
public enum PdfParagraphAlignmentType
{
Center,
Left,
Rigth
}
}

View File

@ -0,0 +1,9 @@
namespace HardwareShopBusinessLogic.OfficePackage.HelperEnums
{
public enum WordJustificationType
{
Center,
Both
}
}

View File

@ -0,0 +1,17 @@
using HardwareShopBusinessLogic.OfficePackage.HelperEnums;
namespace HardwareShopBusinessLogic.OfficePackage.HelperModels
{
public class ExcelCellParameters
{
public string ColumnName { get; set; } = string.Empty;
public uint RowIndex { get; set; }
public string Text { get; set; } = string.Empty;
public string CellReference => $"{ColumnName}{RowIndex}";
public ExcelStyleInfoType StyleInfo { get; set; }
}
}

View File

@ -0,0 +1,15 @@
using HardwareShopContracts.ViewModels;
namespace HardwareShopBusinessLogic.OfficePackage.HelperModels
{
public class ExcelInfo
{
public string FileName { get; set; } = string.Empty;
public string Title { get; set; } = string.Empty;
public List<ReportBuildGoodViewModel> BuildGood { get; set; } = new();
public List<ReportPurchaseComponentViewModel> PurchaseComponent { get; set; } = new();
}
}

View File

@ -0,0 +1,11 @@
namespace HardwareShopBusinessLogic.OfficePackage.HelperModels
{
public class ExcelMergeParameters
{
public string CellFromName { get; set; } = string.Empty;
public string CellToName { get; set; } = string.Empty;
public string Merge => $"{CellFromName}:{CellToName}";
}
}

View File

@ -0,0 +1,20 @@

using HardwareShopContracts.ViewModels;
namespace HardwareShopBusinessLogic.OfficePackage.HelperModels
{
public class PdfInfo
{
public string FileName { get; set; } = string.Empty;
public string Title { get; set; } = string.Empty;
public DateTime DateFrom { get; set; }
public DateTime DateTo { get; set; }
public List<ReportComponentsViewModel> ReportComponents { get; set; } = new();
public List<ReportPurchaseViewModel> ReportPurchases { get; set; } = new();
}
}

View File

@ -0,0 +1,13 @@
using HardwareShopBusinessLogic.OfficePackage.HelperEnums;
namespace HardwareShopBusinessLogic.OfficePackage.HelperModels
{
public class PdfParagraph
{
public string Text { get; set; } = string.Empty;
public string Style { get; set; } = string.Empty;
public PdfParagraphAlignmentType ParagraphAlignment { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using HardwareShopBusinessLogic.OfficePackage.HelperEnums;
namespace HardwareShopBusinessLogic.OfficePackage.HelperModels
{
public class PdfRowParameters
{
public List<string> Texts { get; set; } = new();
public string Style { get; set; } = string.Empty;
public PdfParagraphAlignmentType ParagraphAlignment { get; set; }
}
}

View File

@ -0,0 +1,15 @@
using HardwareShopContracts.ViewModels;
namespace HardwareShopBusinessLogic.OfficePackage.HelperModels
{
public class WordInfo
{
public string FileName { get; set; } = string.Empty;
public string Title { get; set; } = string.Empty;
public List<ReportBuildGoodViewModel> BuildGood { get; set; } = new();
public List<ReportPurchaseComponentViewModel> PurchaseComponent { get; set; } = new();
}
}

View File

@ -0,0 +1,9 @@
namespace HardwareShopBusinessLogic.OfficePackage.HelperModels
{
public class WordParagraph
{
public List<(string, WordTextProperties)> Texts { get; set; } = new();
public WordTextProperties? TextProperties { get; set; }
}
}

View File

@ -0,0 +1,7 @@
namespace HardwareShopBusinessLogic.OfficePackage.HelperModels
{
public class WordRow
{
public List<(string, WordTextProperties)> Rows { get; set; } = new();
}
}

View File

@ -0,0 +1,13 @@
using HardwareShopBusinessLogic.OfficePackage.HelperEnums;
namespace HardwareShopBusinessLogic.OfficePackage.HelperModels
{
public class WordTextProperties
{
public string Size { get; set; } = string.Empty;
public bool Bold { get; set; }
public WordJustificationType JustificationType { get; set; }
}
}

View File

@ -0,0 +1,292 @@
using HardwareShopBusinessLogic.OfficePackage.HelperEnums;
using HardwareShopBusinessLogic.OfficePackage.HelperModels;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Office2010.Excel;
using DocumentFormat.OpenXml.Office2013.Excel;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
namespace HardwareShopBusinessLogic.OfficePackage.Implements
{
public class SaveToExcel : AbstractSaveToExcel
{
private SpreadsheetDocument? _spreadsheetDocument;
private SharedStringTablePart? _shareStringPart;
private Worksheet? _worksheet;
/// <summary>
/// Настройка стилей для файла
/// </summary>
/// <param name="workbookpart"></param>
private static void CreateStyles(WorkbookPart workbookpart)
{
var sp = workbookpart.AddNewPart<WorkbookStylesPart>();
sp.Stylesheet = new Stylesheet();
var fonts = new Fonts() { Count = 2U, KnownFonts = true };
var fontUsual = new Font();
fontUsual.Append(new FontSize() { Val = 12D });
fontUsual.Append(new DocumentFormat.OpenXml.Office2010.Excel.Color() { Theme = 1U });
fontUsual.Append(new FontName() { Val = "Times New Roman" });
fontUsual.Append(new FontFamilyNumbering() { Val = 2 });
fontUsual.Append(new FontScheme() { Val = FontSchemeValues.Minor });
var fontTitle = new Font();
fontTitle.Append(new Bold());
fontTitle.Append(new FontSize() { Val = 14D });
fontTitle.Append(new DocumentFormat.OpenXml.Office2010.Excel.Color() { Theme = 1U });
fontTitle.Append(new FontName() { Val = "Times New Roman" });
fontTitle.Append(new FontFamilyNumbering() { Val = 2 });
fontTitle.Append(new FontScheme() { Val = FontSchemeValues.Minor });
fonts.Append(fontUsual);
fonts.Append(fontTitle);
var fills = new Fills() { Count = 2U };
var fill1 = new Fill();
fill1.Append(new PatternFill() { PatternType = PatternValues.None });
var fill2 = new Fill();
fill2.Append(new PatternFill() { PatternType = PatternValues.Gray125 });
fills.Append(fill1);
fills.Append(fill2);
var borders = new Borders() { Count = 2U };
var borderNoBorder = new Border();
borderNoBorder.Append(new LeftBorder());
borderNoBorder.Append(new RightBorder());
borderNoBorder.Append(new TopBorder());
borderNoBorder.Append(new BottomBorder());
borderNoBorder.Append(new DiagonalBorder());
var borderThin = new Border();
var leftBorder = new LeftBorder() { Style = BorderStyleValues.Thin };
leftBorder.Append(new DocumentFormat.OpenXml.Office2010.Excel.Color() { Indexed = 64U });
var rightBorder = new RightBorder() { Style = BorderStyleValues.Thin };
rightBorder.Append(new DocumentFormat.OpenXml.Office2010.Excel.Color() { Indexed = 64U });
var topBorder = new TopBorder() { Style = BorderStyleValues.Thin };
topBorder.Append(new DocumentFormat.OpenXml.Office2010.Excel.Color() { Indexed = 64U });
var bottomBorder = new BottomBorder() { Style = BorderStyleValues.Thin };
bottomBorder.Append(new DocumentFormat.OpenXml.Office2010.Excel.Color() { Indexed = 64U });
borderThin.Append(leftBorder);
borderThin.Append(rightBorder);
borderThin.Append(topBorder);
borderThin.Append(bottomBorder);
borderThin.Append(new DiagonalBorder());
borders.Append(borderNoBorder);
borders.Append(borderThin);
var cellStyleFormats = new CellStyleFormats() { Count = 1U };
var cellFormatStyle = new CellFormat() { NumberFormatId = 0U, FontId = 0U, FillId = 0U, BorderId = 0U };
cellStyleFormats.Append(cellFormatStyle);
var cellFormats = new CellFormats() { Count = 3U };
var cellFormatFont = new CellFormat() { NumberFormatId = 0U, FontId = 0U, FillId = 0U, BorderId = 0U, FormatId = 0U, ApplyFont = true };
var cellFormatFontAndBorder = new CellFormat() { NumberFormatId = 0U, FontId = 0U, FillId = 0U, BorderId = 1U, FormatId = 0U, ApplyFont = true, ApplyBorder = true };
var cellFormatTitle = new CellFormat() { NumberFormatId = 0U, FontId = 1U, FillId = 0U, BorderId = 0U, FormatId = 0U, Alignment = new Alignment() { Vertical = VerticalAlignmentValues.Center, WrapText = true, Horizontal = HorizontalAlignmentValues.Center }, ApplyFont = true };
cellFormats.Append(cellFormatFont);
cellFormats.Append(cellFormatFontAndBorder);
cellFormats.Append(cellFormatTitle);
var cellStyles = new CellStyles() { Count = 1U };
cellStyles.Append(new CellStyle() { Name = "Normal", FormatId = 0U, BuiltinId = 0U });
var differentialFormats = new DocumentFormat.OpenXml.Office2013.Excel.DifferentialFormats() { Count = 0U };
var tableStyles = new TableStyles() { Count = 0U, DefaultTableStyle = "TableStyleMedium2", DefaultPivotStyle = "PivotStyleLight16" };
var stylesheetExtensionList = new StylesheetExtensionList();
var stylesheetExtension1 = new StylesheetExtension() { Uri = "{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}" };
stylesheetExtension1.AddNamespaceDeclaration("x14", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main");
stylesheetExtension1.Append(new SlicerStyles() { DefaultSlicerStyle = "SlicerStyleLight1" });
var stylesheetExtension2 = new StylesheetExtension() { Uri = "{9260A510-F301-46a8-8635-F512D64BE5F5}" };
stylesheetExtension2.AddNamespaceDeclaration("x15", "http://schemas.microsoft.com/office/spreadsheetml/2010/11/main");
stylesheetExtension2.Append(new TimelineStyles() { DefaultTimelineStyle = "TimeSlicerStyleLight1" });
stylesheetExtensionList.Append(stylesheetExtension1);
stylesheetExtensionList.Append(stylesheetExtension2);
sp.Stylesheet.Append(fonts);
sp.Stylesheet.Append(fills);
sp.Stylesheet.Append(borders);
sp.Stylesheet.Append(cellStyleFormats);
sp.Stylesheet.Append(cellFormats);
sp.Stylesheet.Append(cellStyles);
sp.Stylesheet.Append(differentialFormats);
sp.Stylesheet.Append(tableStyles);
sp.Stylesheet.Append(stylesheetExtensionList);
}
/// <summary>
/// Получение номера стиля из типа
/// </summary>
/// <param name="styleInfo"></param>
/// <returns></returns>
private static uint GetStyleValue(ExcelStyleInfoType styleInfo)
{
return styleInfo switch
{
ExcelStyleInfoType.Title => 2U,
ExcelStyleInfoType.TextWithBroder => 1U,
ExcelStyleInfoType.Text => 0U,
_ => 0U,
};
}
protected override void CreateExcel(ExcelInfo info)
{
_spreadsheetDocument = SpreadsheetDocument.Create(info.FileName, SpreadsheetDocumentType.Workbook);
// Создаем книгу (в ней хранятся листы)
var workbookpart = _spreadsheetDocument.AddWorkbookPart();
workbookpart.Workbook = new Workbook();
CreateStyles(workbookpart);
// Получаем/создаем хранилище текстов для книги
_shareStringPart = _spreadsheetDocument.WorkbookPart!.GetPartsOfType<SharedStringTablePart>().Any()
? _spreadsheetDocument.WorkbookPart.GetPartsOfType<SharedStringTablePart>().First()
: _spreadsheetDocument.WorkbookPart.AddNewPart<SharedStringTablePart>();
// Создаем SharedStringTable, если его нет
if (_shareStringPart.SharedStringTable == null)
{
_shareStringPart.SharedStringTable = new SharedStringTable();
}
// Создаем лист в книгу
var worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet(new SheetData());
// Добавляем лист в книгу
var sheets = _spreadsheetDocument.WorkbookPart.Workbook.AppendChild(new Sheets());
var sheet = new Sheet()
{
Id = _spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart),
SheetId = 1,
Name = "Лист"
};
sheets.Append(sheet);
_worksheet = worksheetPart.Worksheet;
}
protected override void InsertCellInWorksheet(ExcelCellParameters excelParams)
{
if (_worksheet == null || _shareStringPart == null)
{
return;
}
var sheetData = _worksheet.GetFirstChild<SheetData>();
if (sheetData == null)
{
return;
}
// Ищем строку, либо добавляем ее
Row row;
if (sheetData.Elements<Row>().Where(r => r.RowIndex! == excelParams.RowIndex).Any())
{
row = sheetData.Elements<Row>().Where(r => r.RowIndex! == excelParams.RowIndex).First();
}
else
{
row = new Row() { RowIndex = excelParams.RowIndex };
sheetData.Append(row);
}
// Ищем нужную ячейку
Cell cell;
if (row.Elements<Cell>().Where(c => c.CellReference!.Value == excelParams.CellReference).Any())
{
cell = row.Elements<Cell>().Where(c => c.CellReference!.Value == excelParams.CellReference).First();
}
else
{
// Все ячейки должны быть последовательно друг за другом расположены
// нужно определить, после какой вставлять
Cell? refCell = null;
foreach (Cell rowCell in row.Elements<Cell>())
{
if (string.Compare(rowCell.CellReference!.Value, excelParams.CellReference, true) > 0)
{
refCell = rowCell;
break;
}
}
var newCell = new Cell() { CellReference = excelParams.CellReference };
row.InsertBefore(newCell, refCell);
cell = newCell;
}
// вставляем новый текст
_shareStringPart.SharedStringTable.AppendChild(new SharedStringItem(new Text(excelParams.Text)));
_shareStringPart.SharedStringTable.Save();
cell.CellValue = new CellValue((_shareStringPart.SharedStringTable.Elements<SharedStringItem>().Count() - 1).ToString());
cell.DataType = new EnumValue<CellValues>(CellValues.SharedString);
cell.StyleIndex = GetStyleValue(excelParams.StyleInfo);
}
protected override void MergeCells(ExcelMergeParameters excelParams)
{
if (_worksheet == null)
{
return;
}
MergeCells mergeCells;
if (_worksheet.Elements<MergeCells>().Any())
{
mergeCells = _worksheet.Elements<MergeCells>().First();
}
else
{
mergeCells = new MergeCells();
if (_worksheet.Elements<CustomSheetView>().Any())
{
_worksheet.InsertAfter(mergeCells, _worksheet.Elements<CustomSheetView>().First());
}
else
{
_worksheet.InsertAfter(mergeCells, _worksheet.Elements<SheetData>().First());
}
}
var mergeCell = new MergeCell()
{
Reference = new StringValue(excelParams.Merge)
};
mergeCells.Append(mergeCell);
}
protected override void SaveExcel(ExcelInfo info)
{
if (_spreadsheetDocument == null)
{
return;
}
_spreadsheetDocument.WorkbookPart!.Workbook.Save();
_spreadsheetDocument.Close();
}
}
}

View File

@ -0,0 +1,116 @@
using HardwareShopBusinessLogic.OfficePackage.HelperEnums;
using HardwareShopBusinessLogic.OfficePackage.HelperModels;
using MigraDoc.DocumentObjectModel;
using MigraDoc.DocumentObjectModel.Tables;
using MigraDoc.Rendering;
namespace HardwareShopBusinessLogic.OfficePackage.Implements
{
public class SaveToPdf : AbstractSaveToPdf
{
private Document? _document;
private Section? _section;
private Table? _table;
private static ParagraphAlignment GetParagraphAlignment(PdfParagraphAlignmentType type)
{
return type switch
{
PdfParagraphAlignmentType.Center => ParagraphAlignment.Center,
PdfParagraphAlignmentType.Left => ParagraphAlignment.Left,
PdfParagraphAlignmentType.Rigth => ParagraphAlignment.Right,
_ => ParagraphAlignment.Justify,
};
}
/// <summary>
/// Создание стилей для документа
/// </summary>
/// <param name="document"></param>
private static void DefineStyles(Document document)
{
var style = document.Styles["Normal"];
style.Font.Name = "Times New Roman";
style.Font.Size = 14;
style = document.Styles.AddStyle("NormalTitle", "Normal");
style.Font.Bold = true;
}
protected override void CreatePdf(PdfInfo info)
{
_document = new Document();
DefineStyles(_document);
_section = _document.AddSection();
}
protected override void CreateParagraph(PdfParagraph pdfParagraph)
{
if (_section == null)
{
return;
}
var paragraph = _section.AddParagraph(pdfParagraph.Text);
paragraph.Format.SpaceAfter = "1cm";
paragraph.Format.Alignment = GetParagraphAlignment(pdfParagraph.ParagraphAlignment);
paragraph.Style = pdfParagraph.Style;
}
protected override void CreateTable(List<string> columns)
{
if (_document == null)
{
return;
}
_table = _document.LastSection.AddTable();
foreach (var elem in columns)
{
_table.AddColumn(elem);
}
}
protected override void CreateRow(PdfRowParameters rowParameters)
{
if (_table == null)
{
return;
}
var row = _table.AddRow();
for (int i = 0; i < rowParameters.Texts.Count; ++i)
{
row.Cells[i].AddParagraph(rowParameters.Texts[i]);
if (!string.IsNullOrEmpty(rowParameters.Style))
{
row.Cells[i].Style = rowParameters.Style;
}
Unit borderWidth = 0.5;
row.Cells[i].Borders.Left.Width = borderWidth;
row.Cells[i].Borders.Right.Width = borderWidth;
row.Cells[i].Borders.Top.Width = borderWidth;
row.Cells[i].Borders.Bottom.Width = borderWidth;
row.Cells[i].Format.Alignment = GetParagraphAlignment(rowParameters.ParagraphAlignment);
row.Cells[i].VerticalAlignment = VerticalAlignment.Center;
}
}
protected override void SavePdf(PdfInfo info)
{
var renderer = new PdfDocumentRenderer(true)
{
Document = _document
};
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
renderer.RenderDocument();
renderer.PdfDocument.Save(info.FileName);
}
}
}

View File

@ -0,0 +1,189 @@
using HardwareShopBusinessLogic.OfficePackage.HelperEnums;
using HardwareShopBusinessLogic.OfficePackage.HelperModels;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
namespace HardwareShopBusinessLogic.OfficePackage.Implements
{
public class SaveToWord : AbstractSaveToWord
{
private WordprocessingDocument? _wordDocument;
private Body? _docBody;
/// <summary>
/// Получение типа выравнивания
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static JustificationValues GetJustificationValues(WordJustificationType type)
{
return type switch
{
WordJustificationType.Both => JustificationValues.Both,
WordJustificationType.Center => JustificationValues.Center,
_ => JustificationValues.Left,
};
}
/// <summary>
/// Настройки страницы
/// </summary>
/// <returns></returns>
private static SectionProperties CreateSectionProperties()
{
var properties = new SectionProperties();
var pageSize = new PageSize
{
Orient = PageOrientationValues.Portrait
};
properties.AppendChild(pageSize);
return properties;
}
/// <summary>
/// Задание форматирования для абзаца
/// </summary>
/// <param name="paragraphProperties"></param>
/// <returns></returns>
private static ParagraphProperties? CreateParagraphProperties(WordTextProperties? paragraphProperties)
{
if (paragraphProperties == null)
{
return null;
}
var properties = new ParagraphProperties();
properties.AppendChild(new Justification()
{
Val = GetJustificationValues(paragraphProperties.JustificationType)
});
properties.AppendChild(new SpacingBetweenLines
{
LineRule = LineSpacingRuleValues.Auto
});
properties.AppendChild(new Indentation());
var paragraphMarkRunProperties = new ParagraphMarkRunProperties();
if (!string.IsNullOrEmpty(paragraphProperties.Size))
{
paragraphMarkRunProperties.AppendChild(new FontSize { Val = paragraphProperties.Size });
}
properties.AppendChild(paragraphMarkRunProperties);
return properties;
}
protected override void CreateWord(WordInfo info)
{
_wordDocument = WordprocessingDocument.Create(info.FileName, WordprocessingDocumentType.Document);
MainDocumentPart mainPart = _wordDocument.AddMainDocumentPart();
mainPart.Document = new Document();
_docBody = mainPart.Document.AppendChild(new Body());
}
protected override void CreateTable(List<WordRow> data)
{
if (_docBody == null || data == null)
{
return;
}
Table table = new Table();
var tableProp = new TableProperties();
tableProp.AppendChild(new TableLayout { Type = TableLayoutValues.Fixed });
tableProp.AppendChild(new TableBorders(
new TopBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 4 },
new LeftBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 4 },
new RightBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 4 },
new BottomBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 4 },
new InsideHorizontalBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 4 },
new InsideVerticalBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 4 }
));
tableProp.AppendChild(new TableWidth { Type = TableWidthUnitValues.Auto });
table.AppendChild(tableProp);
TableGrid tableGrid = new TableGrid();
for (int j = 0; j < data[0].Rows.Count; ++j)
{
tableGrid.AppendChild(new GridColumn() { Width = "1600" });
}
table.AppendChild(tableGrid);
for (int i = 0; i < data.Count; ++i)
{
TableRow docRow = new TableRow();
for (int j = 0; j < data[i].Rows.Count; ++j)
{
var docParagraph = new Paragraph();
var docRun = new Run();
var runProperties = new RunProperties();
docParagraph.AppendChild(CreateParagraphProperties(data[i].Rows[j].Item2));
runProperties.AppendChild(new RunFonts() { Ascii = "Times New Roman", ComplexScript = "Times New Roman", HighAnsi = "Times New Roman" });
runProperties.AppendChild(new FontSize { Val = data[i].Rows[j].Item2.Size == null ? data[i].Rows[j].Item2.Size : "24" });
if (data[i].Rows[j].Item2.Bold)
runProperties.AppendChild(new Bold());
docRun.AppendChild(runProperties);
docRun.AppendChild(new Text { Text = data[i].Rows[j].Item1.ToString(), Space = SpaceProcessingModeValues.Preserve });
docParagraph.AppendChild(docRun);
TableCell docCell = new TableCell();
docCell.AppendChild(docParagraph);
docRow.AppendChild(docCell);
}
table.AppendChild(docRow);
}
_docBody.AppendChild(table);
}
protected override void CreateParagraph(WordParagraph paragraph)
{
if (_docBody == null || paragraph == null)
{
return;
}
var docParagraph = new Paragraph();
docParagraph.AppendChild(CreateParagraphProperties(paragraph.TextProperties));
foreach (var run in paragraph.Texts)
{
var docRun = new Run();
var properties = new RunProperties();
properties.AppendChild(new FontSize { Val = run.Item2.Size });
if (run.Item2.Bold)
{
properties.AppendChild(new Bold());
}
docRun.AppendChild(properties);
docRun.AppendChild(new Text { Text = run.Item1, Space = SpaceProcessingModeValues.Preserve });
docParagraph.AppendChild(docRun);
}
_docBody.AppendChild(docParagraph);
}
protected override void SaveWord(WordInfo info)
{
if (_docBody == null || _wordDocument == null)
{
return;
}
_docBody.AppendChild(CreateSectionProperties());
_wordDocument.MainDocumentPart!.Document.Save();
_wordDocument.Close();
}
}
}

View File

@ -1,33 +0,0 @@
using Microsoft.AspNetCore.Mvc;
namespace HardwareShopClientApi.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
}

View File

@ -1,13 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
</ItemGroup>
</Project>

View File

@ -1,25 +0,0 @@
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

View File

@ -1,13 +0,0 @@
namespace HardwareShopClientApi
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string? Summary { get; set; }
}
}

View File

@ -3,7 +3,7 @@ using Newtonsoft.Json;
using System.Net.Http.Headers;
using System.Text;
namespace HardwareShopClientApp
namespace HardwareShopStorekeeperApp
{
//Клиент который общается с API, а не наша сущность
public class APIClient
@ -46,5 +46,23 @@ namespace HardwareShopClientApp
throw new Exception(result);
}
}
public static R? PostRequestWithResult<T, R>(string requestUrl, T model)
{
var json = JsonConvert.SerializeObject(model);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var response = _client.PostAsync(requestUrl, data);
var result = response.Result.Content.ReadAsStringAsync().Result;
if (response.Result.IsSuccessStatusCode)
{
return JsonConvert.DeserializeObject<R>(result);
}
else
{
return default;
}
}
}
}

View File

@ -1,12 +1,11 @@
using HardwareShopClientApp.Models;
using HardwareShopStorekeeperApp.Models;
using HardwareShopContracts.BindingModels;
using HardwareShopContracts.ViewModels;
using HardwareShopDataModels.Enums;
using Microsoft.AspNetCore.Mvc;
using System.Data;
using System.Diagnostics;
namespace HardwareShopClientApp.Controllers
namespace HardwareShopStorekeeperApp.Controllers
{
public class HomeController : Controller
{
@ -17,25 +16,24 @@ namespace HardwareShopClientApp.Controllers
_logger = logger;
}
[HttpGet]
public IActionResult Register()
{
return View();
}
[HttpPost]
public void Register(string login, string email, string password, int role)
public void Register(string login, string email, string password)
{
if (string.IsNullOrEmpty(login) || string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password) || role <= 0)
if (string.IsNullOrEmpty(login) || string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password))
{
throw new Exception("Введите логин, email, пароль и роль");
throw new Exception("Введите логин, email, пароль");
}
APIClient.PostRequest("api/client/register", new UserBindingModel
APIClient.PostRequest("api/user/register", new UserBindingModel
{
Login = login,
Email = email,
Password = password,
Role = (UserRole)role
Role = UserRole.Кладовщик
});
Response.Redirect("Enter");
return;
@ -43,17 +41,43 @@ namespace HardwareShopClientApp.Controllers
public IActionResult Index()
{
//return RedirectToAction("MainWorker", "Worker");
return View();
}
[HttpGet]
public IActionResult Privacy()
{
if (APIClient.User == null)
{
return Redirect("~/Home/Enter");
}
return View();
return View(APIClient.User);
}
public IActionResult Privacy()
[HttpPost]
public IActionResult Privacy(string login, string email, string password)
{
return View();
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
if (string.IsNullOrEmpty(login) || string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password))
{
throw new Exception("Введите логин, пароль и почту");
}
APIClient.PostRequest("api/user/updatedata", new UserBindingModel
{
Id = APIClient.User.Id,
Login = login,
Email = email,
Password = password
});
APIClient.User.Login = login;
APIClient.User.Email = email;
APIClient.User.Password = password;
return RedirectToAction("MainStorekeeper", "Storekeeper");
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
@ -62,8 +86,6 @@ namespace HardwareShopClientApp.Controllers
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
[HttpGet]
public IActionResult Enter()
{
return View();
@ -74,30 +96,14 @@ namespace HardwareShopClientApp.Controllers
{
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password))
{
throw new Exception("Введите логин и пароль");
throw new Exception("Введите почту и пароль");
}
APIClient.User = APIClient.GetRequest<UserViewModel>($"api/client/login?email={email}&password={password}");
if (APIClient.User == null)
APIClient.User = APIClient.GetRequest<UserViewModel>($"api/user/login?email={email}&password={password}");
if (APIClient.User == null || APIClient.User.Role != UserRole.Кладовщик)
{
throw new Exception("Неверный логин/пароль");
throw new Exception("Неверные почта и/или пароль");
}
if ((int)APIClient.User.Role == 1)
{
//TempData["UserId"] = APIClient.User.Id;
return RedirectToAction("WorkerReport", "Worker");
}
else
{
return Redirect("Index");
}
return RedirectToAction("MainStorekeeper", "Storekeeper");
}
[HttpGet]
public IActionResult MainWorker()
{
return View();
}
}
}

View File

@ -0,0 +1,456 @@
using HardwareShopContracts.BindingModels;
using HardwareShopContracts.ViewModels;
using HardwareShopDatabaseImplement.Models;
using HardwareShopDatabaseImplement.Models.Storekeeper;
using HardwareShopDataModels.Enums;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel;
namespace HardwareShopStorekeeperApp.Controllers
{
public class StorekeeperController : Controller
{
private readonly ILogger<HomeController> _logger;
public StorekeeperController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult CreateOrder()
{
if (APIClient.User == null)
{
return Redirect("~/Home/Enter");
}
ViewBag.Goods = APIClient.GetRequest<List<GoodViewModel>>($"api/good/getgoods?userId={APIClient.User.Id}");
return View();
}
[HttpPost]
public void CreateOrder(int good, int count, string sum)
{
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
if (good <= 0)
{
throw new Exception("Некорректный идентификатор товара");
}
if (count <= 0)
{
throw new Exception("Количество должно быть больше 0");
}
if (Convert.ToDouble(sum.Replace('.', ',')) <= 0)
{
throw new Exception("Цена должна быть больше 0");
}
APIClient.PostRequest("api/order/createorder", new OrderBindingModel
{
UserId = APIClient.User.Id,
GoodId = good,
Count = count,
Sum = Convert.ToDouble(sum.Replace('.', ','))
});
Response.Redirect("Orders");
}
[HttpPost]
public void DeleteOrder(int Id)
{
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
if (Id <= 0)
{
throw new Exception("Некорректный идентификатор");
}
APIClient.PostRequest("api/order/deleteorder", new OrderBindingModel
{
Id = Id
});
Response.Redirect("Orders");
}
[HttpPost]
public void UpdateOrder(int id, int status)
{
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
if (id <= 0)
{
throw new Exception("Некорректный идентификатор");
}
if (status <= 0)
{
throw new Exception("Некорректный статус");
}
APIClient.PostRequest("api/order/updatedata", new OrderBindingModel
{
Id = id,
Status = (OrderStatus)status
});
Response.Redirect("Orders");
}
[HttpPost]
public double Calc(int count, int good)
{
var prod = APIClient.GetRequest<GoodViewModel>($"api/good/getgood?id={good}");
double result = Math.Round(count * (prod?.Price ?? 1), 2);
return result;
}
public IActionResult CreateGood()
{
if (APIClient.User == null)
{
return Redirect("~/Home/Enter");
}
ViewBag.Components = APIClient.GetRequest<List<ComponentViewModel>>($"api/component/getcomponents?userId={APIClient.User.Id}");
return View();
}
[HttpPost]
public void CreateGood([FromBody] GoodBindingModel goodModel)
{
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
if (string.IsNullOrEmpty(goodModel.GoodName))
{
throw new Exception("Название не должно быть пустым");
}
if (goodModel.Price <= 0)
{
throw new Exception("Цена должна быть больше 0");
}
goodModel.UserId = APIClient.User.Id;
APIClient.PostRequest("api/good/creategood", goodModel);
}
public IActionResult UpdateGood(int goodid)
{
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
ViewBag.Components = APIClient.GetRequest<List<ComponentViewModel>>($"api/component/getcomponents?userId={APIClient.User.Id}");
return View(goodid);
}
[HttpPost]
public void UpdateGood([FromBody] GoodBindingModel goodModel)
{
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
if (string.IsNullOrEmpty(goodModel.GoodName))
{
throw new Exception("Название не должно быть пустым");
}
if (goodModel.Price <= 0)
{
throw new Exception("Цена должна быть больше 0");
}
goodModel.UserId = APIClient.User.Id;
APIClient.PostRequest("api/good/updatedata", goodModel);
}
[HttpGet]
public GoodViewModel? GetGood(int Id)
{
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
if (Id <= 0)
{
throw new Exception($"Идентификатор товара не может быть меньше или равен 0");
}
var result = APIClient.GetRequest<GoodViewModel>($"api/good/getgood?id={Id}");
return result;
}
[HttpGet]
public Tuple<GoodViewModel, List<Tuple<ComponentViewModel?, int>>>? GetGoodUpdate(int goodid)
{
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
var result = APIClient.GetRequest<Tuple<GoodViewModel,
List<Tuple<ComponentViewModel?, int>>>?>($"api/good/getgoodupdate?id={goodid}&userId={APIClient.User.Id}");
return result;
}
[HttpPost]
public void DeleteGood(int good)
{
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
if (good <= 0)
{
throw new Exception($"Идентификатор товара не может быть меньше или равен 0");
}
APIClient.PostRequest("api/good/deletegood", new GoodBindingModel
{
Id = good
});
}
public IActionResult LinkBuilds(int componentid)
{
if (APIClient.User == null)
{
return Redirect("~/Home/Enter");
}
ViewBag.Builds = APIClient.GetRequest<List<BuildViewModel>>($"api/build/getbuilds");
return View(componentid);
}
[HttpPost]
public void LinkBuilds([FromBody] ComponentBindingModel componentModel)
{
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
componentModel.UserId = APIClient.User.Id;
APIClient.PostRequest($"api/component/updatedata", componentModel);
}
[HttpGet]
public BuildViewModel? GetBuild(int buildId)
{
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
if (buildId <= 0)
{
throw new Exception($"Идентификатор сборки не может быть меньше или равен 0");
}
var result = APIClient.GetRequest<BuildViewModel>($"api/build/getbuild?buildId={buildId}");
return result;
}
[HttpGet]
public List<Tuple<BuildViewModel, int>>? GetComponentBuilds(int componentid)
{
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
var result = APIClient.GetRequest<List<Tuple<BuildViewModel, int>>?>($"api/component/getcomponentbuilds?id={componentid}");
return result;
}
public IActionResult CreateComponent()
{
if (APIClient.User == null)
{
return Redirect("~/Home/Enter");
}
return View();
}
[HttpPost]
public void CreateComponent(string name, string cost)
{
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
if (string.IsNullOrEmpty(name))
{
throw new Exception("Название не должно быть пустым");
}
if (string.IsNullOrEmpty(cost) || Convert.ToDouble(cost.Replace('.', ',')) <= 0)
{
throw new Exception("Цена должна быть больше 0");
}
APIClient.PostRequest("api/component/createcomponent", new ComponentBindingModel
{
UserId = APIClient.User.Id,
ComponentName = name,
Cost = Convert.ToDouble(cost.Replace('.', ','))
});
Response.Redirect("Components");
}
[HttpGet]
public ComponentViewModel? GetComponent(int Id)
{
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
var result = APIClient.GetRequest<ComponentViewModel?>($"api/component/getcomponent?id={Id}");
if (result == null)
{
return default;
}
return result;
}
[HttpPost]
public void UpdateComponent(string name, string cost, DateTime date, int component)
{
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
if (component <= 0)
{
throw new Exception($"Идентификатор комплектующего не может быть меньше или равен 0");
}
if (string.IsNullOrEmpty(name))
{
throw new Exception($"Имя комплектующего не должно быть пустым");
}
if (Convert.ToDouble(cost.Replace('.', ',')) <= 0)
{
throw new Exception($"Цена комплектующего не может быть меньше или равна 0");
}
APIClient.PostRequest("api/component/updatecomponent", new ComponentBindingModel
{
Id = component,
ComponentName = name,
Cost = Convert.ToDouble(cost.Replace('.', ',')),
UserId = APIClient.User.Id,
});
Response.Redirect("Components");
}
[HttpPost]
public void DeleteComponent(int component)
{
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
if (component <= 0)
{
throw new Exception($"Идентификатор комплектующего не может быть меньше или равен 0");
}
APIClient.PostRequest("api/component/deletecomponent", new ComponentBindingModel
{
Id = component
});
}
public IActionResult MainStorekeeper()
{
if (APIClient.User == null)
{
return Redirect("~/Home/Enter");
}
return View();
}
public IActionResult Components()
{
if (APIClient.User == null)
{
return Redirect("~/Home/Enter");
}
return View(APIClient.GetRequest<List<ComponentViewModel>>($"api/component/getcomponents?userId={APIClient.User.Id}"));
}
public IActionResult Goods()
{
if (APIClient.User == null)
{
return Redirect("~/Home/Enter");
}
return View(APIClient.GetRequest<List<GoodViewModel>>($"api/good/getgoods?userId={APIClient.User.Id}"));
}
public IActionResult Orders()
{
if (APIClient.User == null)
{
return Redirect("~/Home/Enter");
}
return View(APIClient.GetRequest<List<OrderViewModel>>($"api/order/getorders?userId={APIClient.User.Id}"));
}
public IActionResult ListBuilds()
{
if (APIClient.User == null)
{
return Redirect("~/Home/Enter");
}
ViewBag.Goods = APIClient.GetRequest<List<GoodViewModel>>($"api/good/getgoods?userId={APIClient.User.Id}");
return View();
}
[HttpPost]
public int[]? ListBuilds([FromBody] GoodBindingModel goodModel, [FromQuery] string format)
{
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
if (string.IsNullOrEmpty(format))
{
throw new FormatException("Неправильный формат файла");
}
byte[]? file = APIClient.PostRequestWithResult<GoodBindingModel, byte[]>($"api/report/buildgoodreport?format={format}", goodModel);
return file!.Select(b => (int)b).ToArray();
}
public IActionResult Report()
{
if (APIClient.User == null)
{
return Redirect("~/Home/Enter");
}
return View();
}
[HttpPost]
public List<ReportComponentsViewModel> Report([FromBody] ReportBindingModel reportModel)
{
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
reportModel.UserId = APIClient.User.Id;
List<ReportComponentsViewModel>? list = APIClient.PostRequestWithResult
<ReportBindingModel, List<ReportComponentsViewModel>>("api/report/componentsreport", reportModel);
return list!;
}
[HttpPost]
public void ReportSendOnMail([FromBody] ReportBindingModel reportModel)
{
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
reportModel.UserId = APIClient.User.Id;
reportModel.UserEmail = APIClient.User.Email;
APIClient.PostRequest("api/report/componentsreportsendonmail", reportModel);
}
[HttpGet]
public List<CommentViewModel> GetCommentsOnBuild(int buildId)
{
if (APIClient.User == null)
{
throw new Exception("Вы как сюда попали? Сюда вход только авторизованным");
}
return APIClient.GetRequest<List<CommentViewModel>>($"api/comment/GetCommentsOnBuild?buildId={buildId}")!;
}
}
}

View File

@ -1,73 +0,0 @@
using HardwareShopClientApp.Models;
using HardwareShopContracts.BindingModels;
using HardwareShopContracts.ViewModels;
using HardwareShopDataModels.Enums;
using Microsoft.AspNetCore.Mvc;
using System.Data;
using System.Diagnostics;
namespace HardwareShopClientApp.Controllers
{
public class WorkerController : Controller
{
private readonly ILogger<WorkerController> _logger;
public WorkerController(ILogger<WorkerController> logger)
{
_logger = logger;
}
public IActionResult Builds()
{
return View();
}
public IActionResult Comments()
{
return View();
}
public IActionResult listComponents()
{
return View();
}
public IActionResult MainWorker()
{
return View();
}
[HttpGet]
public IActionResult Purchases()
{
//return View();
//string login = (string)TempData["UserId"];
return View(APIClient.GetRequest<List<PurchaseViewModel>>($"api/client/getpurchases?UserId={APIClient.User.Id}"));
}
[HttpPost]
public void Purchases(int id)
{
//return View();
//string login = (string)TempData["UserId"];
Response.Redirect("Purchase");
}
[HttpGet]
public IActionResult Purchase()
{
//return View();
//string login = (string)TempData["UserId"];
return View();
}
public IActionResult WorkerReport()
{
return View();
}
}
}

View File

@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\HardwareShopContracts\HardwareShopContracts.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.13" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\HardwareShopBusinessLogic\HardwareShopBusinessLogic.csproj" />
<ProjectReference Include="..\HardwareShopContracts\HardwareShopContracts.csproj" />
</ItemGroup>
</Project>

View File

@ -1,4 +1,4 @@
namespace HardwareShopClientApp.Models
namespace HardwareShopStorekeeperApp.Models
{
public class ErrorViewModel
{

View File

@ -1,4 +1,4 @@
using HardwareShopClientApp;
using HardwareShopStorekeeperApp;
var builder = WebApplication.CreateBuilder(args);
@ -7,6 +7,7 @@ builder.Services.AddControllersWithViews();
var app = builder.Build();
APIClient.Connect(builder.Configuration);
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
@ -26,4 +27,4 @@ app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
app.Run();

View File

@ -8,7 +8,7 @@
}
},
"profiles": {
"HardwareShopClientApp": {
"HardwareShopStorekeeperApp": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,

View File

@ -1,9 +1,20 @@
@{
ViewData["Title"] = "Enter";
}
@section Header {}
@section Header {
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">Магазин компьютерной техники "Ты ж программист"</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
</div>
</nav>
</header>
}
<div class="text-center">
<h2 class="display-4">Вход в приложение</h2>
<h2 class="display-4">Вход</h2>
</div>
<form method="post" class="d-flex flex-column align-items-center">
<div class="col-sm-3">
@ -14,10 +25,8 @@
<label class="form-label">Пароль</label>
<input type="password" class="form-control" name="password">
</div>
<div class="col-sm-2 d-flex justify-content-evenly align-items-baseline">
<button type="submit" class="btn btn-primary mt-3 px-4">Submit</button>
<a asp-action="Register">Регистрация</a>
</div>
<div class="col-sm-2 d-flex justify-content-evenly align-items-baseline">
<button type="submit" class="btn btn-primary mt-3 px-4">Подтвердить</button>
</div><a asp-action="Register">Регистрация</a>
</form>

View File

@ -1,99 +1,32 @@
@{
ViewData["Title"] = "Home Page";
ViewData["Title"] = "Enter";
}
@using HardwareShopContracts.ViewModels
@model List<PurchaseViewModel>
@section Header {
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Purchases">Purchases</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Builds">Builds</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Comments">Comments</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="listComponents">listComponents</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="WorkerReport">WorkerReport</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">Магазин компьютерной техники "Ты ж программист"</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Enter">Вход</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Register">Регистрация</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Storekeeper" asp-action="MainStorekeeper">Кладовщик</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
}
<div class="text-center">
<h2 class="display-4">Регистрация</h2>
</div>
<div class="text-center">
@{
if (Model == null)
{
<h3 class="display-4">Авторизируйтесь</h3>
return;
}
<table class="table">
<thead>
<tr>
<th>
Номер
</th>
<th>
Блюдо
</th>
<th>
Дата создания
</th>
<th>
Количество
</th>
<th>
Сумма
</th>
<th>
Статус
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Id)
</td>
<td>
@Html.DisplayFor(modelItem => item.DatePurchase)
</td>
<td>
@Html.DisplayFor(modelItem => item.Sum)
</td>
<td>
@Html.DisplayFor(modelItem => item.PurchaseStatus)
</td>
<td>
@Html.DisplayFor(modelItem => item.UserLogin)
</td>
</tr>
}
</tbody>
</table>
}
</div>
<div class="d-flex justify-content-center">
<img src="images/logo.png"/>
</div>

View File

@ -1,6 +1,29 @@
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
@using HardwareShopContracts.ViewModels;
<p>Use this page to detail your site's privacy policy.</p>
@model UserViewModel
@{
ViewData["Title"] = "Privacy Policy";
Layout = "~/Views/Shared/_LayoutStorekeeper.cshtml";
}
<div class="text-center">
<h2 class="display-4">Личные данные</h2>
</div>
<form method="post" class="d-flex flex-column align-items-center">
<div class="col-sm-3">
<label class="form-label">Почта:</label>
<input type="text" class="form-control" name="email" value="@Model.Email">
</div>
<div class="col-sm-3">
<label class="form-label">Пароль:</label>
<input type="text" class="form-control" name="password" value="@Model.Password">
</div>
<div class="col-sm-3">
<label class="form-label">Логин:</label>
<input type="text" class="form-control" name="login" value="@Model.Login">
</div>
<div class="col-sm-2 d-flex justify-content-evenly align-items-baseline">
<button type="submit" class="btn btn-primary mt-3 px-4">Подтвердить</button>
</div>
</form>

View File

@ -1,31 +1,36 @@
@{
ViewData["Title"] = "Register";
}
@section Header {}
@section Header {
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">Магазин компьютерной техники "Ты ж программист"</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
</div>
</nav>
</header>
}
<div class="text-center">
<h2 class="display-4">Регистрация</h2>
</div>
<form method="post" class="d-flex flex-column align-items-center">
<div class="col-sm-3">
<label class="form-label">Логин</label>
<input type="text" class="form-control" aria-describedby="emailHelp" name="login">
</div>
<div class="col-sm-3">
<label class="form-label">Почта</label>
<input type="text" class="form-control" name="email">
</div>
<div class="col-sm-3">
<label class="form-label">Логин</label>
<input type="text" class="form-control" aria-describedby="emailHelp" name="login">
</div>
<div class="col-sm-3">
<label class="form-label">Пароль</label>
<input type="password" class="form-control" name="password">
</div>
<div class="col-sm-3">
<label class="form-label">Роль</label>
<select class="form-select" name="role">
<option value="1">Работник</option>
<option value="2">Кладовщик</option>
</select>
</div>
<button type="submit" class="btn btn-primary mt-3 px-4">Submit</button>
<button type="submit" class="btn btn-primary mt-3 px-4">Подтвердить</button>
<a asp-action="Enter">Вернуться на вход</a>
</form>

View File

@ -3,10 +3,11 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - HardwareShopClientApp</title>
<title>@ViewData["Title"] - Магазин компьютерной техники</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/HardwareShopClientApp.styles.css" asp-append-version="true" />
<link rel="stylesheet" href="~/HardwareShopStorekeeperApp.styles.css" asp-append-version="true" />
<link rel="stylesheet" href="~/lib/font-awesome-4.7.0/css/font-awesome.min.css">
</head>
<body>
@RenderSection("Header")
@ -16,11 +17,6 @@
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
&copy; 2023 - HardwareShopClientApp - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>

View File

@ -0,0 +1,57 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Магазин компьютерной техники</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/HardwareShopStorekeeperApp.styles.css" asp-append-version="true" />
<link rel="stylesheet" href="~/lib/font-awesome-4.7.0/css/font-awesome.min.css">
@await RenderSectionAsync("Styles", required: false)
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid"><a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">Магазин компьютерной техники "Ты ж программист"</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Storekeeper" asp-action="Components">Комплектующие</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Storekeeper" asp-action="Goods">Товары</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Storekeeper" asp-action="Orders">Заказы</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Storekeeper" asp-action="ListBuilds">Получение списка</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Storekeeper" asp-action="Report">Отчёт</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Личные данные</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>

View File

@ -0,0 +1,127 @@
@using HardwareShopContracts.ViewModels
@model List<ComponentViewModel>
@{
ViewData["Title"] = "Комплектующие";
Layout = "~/Views/Shared/_LayoutStorekeeper.cshtml";
}
<div class="text-center">
<h1 class="display-4">Комплектующие</h1>
</div>
<div class="text-center">
@{
<p>
<a asp-action="CreateComponent" class="btn btn-primary mx-2">Создать комплектующее</a>
</p>
<table class="table">
<thead>
<tr>
<th>
Название
</th>
<th>
Цена
</th>
<th>
Дата приобретения
</th>
<th>
Действия
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.ComponentName)
</td>
<td>
@Html.DisplayFor(modelItem => item.Cost)
</td>
<td>
@Html.DisplayFor(modelItem => item.DateCreate)
</td>
<td>
<div>
<a asp-controller="Storekeeper"
asp-action="LinkBuilds"
asp-route-componentid="@item.Id"
class="btn btn-success">
<i class="fa fa-paperclip fa-rotate-90" aria-hidden="true"></i>
</a>
<button onclick="getComponent(@item.Id)" type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#updateModal">
<i class="fa fa-pencil" aria-hidden="true"></i>
</button>
<button onclick="deleteComponent(@item.Id)" type="button" class="btn btn-danger">
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</div>
</td>
</tr>
}
</tbody>
</table>
}
</div>
<form method="post" asp-controller="Storekeeper" asp-action="UpdateComponent">
<div class="modal fade" id="updateModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Комплектующая</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
</div>
<div class="modal-body">
<div class="col">
<label class="form-label">Название</label>
<input type="text" class="form-control" name="name" id="name" required>
</div>
<div class="col">
<label class="form-label">Стоимость</label>
<input type="number" step="0.01" class="form-control" name="cost" id="cost" min="0.01" required>
</div>
</div>
<input type="hidden" id="component" name="component" />
<div class="modal-footer">
<input type="submit" class="btn btn-primary" value="Сохранить">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
</div>
</div>
</div>
</div>
</form>
@section Scripts
{
<script>
function getComponent(componentId) {
$.ajax({
method: "GET",
url: "/Storekeeper/GetComponent",
data: { Id: componentId },
success: function (result) {
if (result != null)
{
$('#name').val(result.componentName);
$('#cost').val(result.cost);
$('#component').val(result.id);
}
}
});
}
function deleteComponent(componentId) {
$.ajax({
method: "POST",
url: "/Storekeeper/DeleteComponent",
data: { component: componentId }
}).done(() => window.location.href = "/Storekeeper/Components");
}
</script>
}

View File

@ -0,0 +1,22 @@
@{
ViewData["Title"] = "Создание комплектующего";
Layout = "~/Views/Shared/_LayoutStorekeeper.cshtml";
}
<div class="text-center">
<h2 class="display-4">Создание комплектующего</h2>
</div>
<form method="post" class="d-flex flex-column align-items-center">
<div class="col-sm-3">
<label class="form-label">Название</label>
<input type="text" class="form-control" name="name" required>
</div>
<div class="col-sm-3">
<label class="form-label">Стоимость</label>
<input type="number" step="0.01" class="form-control" name="cost" min="0.01" required>
</div>
<div class="col-sm-2 d-flex justify-content-evenly align-items-baseline">
<button type="submit" class="btn btn-primary mt-3 px-4">Сохранить</button>
</div>
</form>

View File

@ -0,0 +1,152 @@
@using HardwareShopContracts.ViewModels
@{
ViewData["Title"] = "Создание товара";
Layout = "~/Views/Shared/_LayoutStorekeeper.cshtml";
}
<div class="text-center">
<h2 class="display-4">Создание товара</h2>
</div>
<div class="d-flex flex-column align-items-center">
<div class="col-sm-3">
<label class="form-label">Название</label>
<input type="text" class="form-control" name="name" id="name" >
</div>
<div class="col-sm-3">
<label class="form-label">Цена</label>
<input type="number" step="0.01" class="form-control" name="price" id="price" readonly min="0.01" value="0" >
</div>
<h1 class="display-6">Комплектующие</h1>
<div class="d-flex justify-content-center">
<button type="button" class="btn btn-primary mx-2 mt-3" data-bs-toggle="modal" data-bs-target="#exampleModal">Добавить</button>
</div>
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Комплектующее</th>
<th scope="col">Стоимость</th>
<th scope="col">Количество</th>
<th scope="col">Сумма</th>
<th scope="col">Действие</th>
</tr>
</thead>
<tbody id="result">
</tbody>
</table>
<div class="col-sm-2 d-flex justify-content-evenly align-items-baseline">
<button type="button" class="btn btn-primary mt-3 px-4" id="creategood">Сохранить</button>
</div>
</div>
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Комплектующее</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
</div>
<div class="modal-body">
<label class="form-label">Комплектующее</label>
<select id="component" name="component" class="form-control" asp-items="@(new SelectList(@ViewBag.Components, "Id", "ComponentName"))"></select>
<label class="form-label">Количество</label>
<input type="number" class="form-control" name="count" id="count" min="1" value="1" >
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" id="savecomponent">Сохранить</button>
</div>
</div>
</div>
</div>
@section Scripts
{
<script>
let list = [];
const name = document.getElementById("name");
const submitComponentBtn = document.getElementById("savecomponent");
const saveBtn = document.getElementById("creategood");
const countElem = document.getElementById("count");
const resultTable = document.getElementById("result");
const totalPrice = document.getElementById("price");
submitComponentBtn.addEventListener("click", () => {
console.log('try to add component')
var count = $('#count').val();
var component = $('#component').val();
if (component && count && count > 0) {
$.ajax({
method: "GET",
url: `/Storekeeper/GetComponent`,
data: { Id: component },
success: function (result) {
let flag = false
if (list.length > 0) {
list.forEach((elem) => {
if (elem.component.id === parseInt(result.id)) {
console.log('component already added')
flag = true
}
})
}
if (!flag) list.push({ component: result, count: count })
reloadTable()
countElem.value = '1'
}
}).fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
})
}
})
saveBtn.addEventListener("click", () => {
console.log('try to add good')
if (list.length == 0) {
alert('failed add good. components are empty')
return
}
let components = []
let counts = []
list.forEach((x) => {
components.push(x.component);
counts.push(parseInt(x.count))
})
$.ajax(
{
url: `/Storekeeper/CreateGood`,
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({"GoodName": name.value,"Price": parseFloat(totalPrice.value),
"GoodComponentsComponents": components, "GoodComponentsCounts": counts })
}
).done(() => window.location.href = '/Storekeeper/Goods')
.fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
})
})
function reloadTable() {
resultTable.innerHTML = ''
let price = 0;
let count = 0;
list.forEach((elem) => {
resultTable.innerHTML += `<tr><td>${elem.component.componentName}</td><td>${elem.component.cost}</td><td>${elem.count}</td><td>${Math.round(elem.component.cost * elem.count * 100) / 100}</td><td> \
<div> \
<button onclick="deleteComponent(${count})" type="button" class="btn btn-danger"> \
<i class="fa fa-trash" aria-hidden="true"></i> \
</button> \
</div><td/></tr>`
count++;
price += elem.component.cost * elem.count
})
totalPrice.value = Math.round(price * 100) / 100
}
function deleteComponent(id) {
list = list.filter(value => value.component.componentName != resultTable.rows[id].cells[0].innerText)
reloadTable()
}
</script>
}

View File

@ -0,0 +1,57 @@
@{
ViewData["Title"] = "Создание заказа";
Layout = "~/Views/Shared/_LayoutStorekeeper.cshtml";
}
<div class="text-center">
<h2 class="display-4">Создание заказа</h2>
</div>
<form method="post" class="d-flex flex-column align-items-center">
<div class="col-sm-3">
<label class="form-label">Товар</label>
<select id="good" name="good" class="form-control" asp-items="@(new SelectList(@ViewBag.Goods, "Id", "GoodName"))" required></select>
</div>
<div class="col-sm-3">
<label class="form-label">Количество</label>
<input type="number" class="form-control" name="count" id="count" min="1" value="1" required>
</div>
<div class="col-sm-3">
<label class="form-label">Сумма</label>
<input type="number" step="0.01" class="form-control" name="sum" id="sum" value="0" min="0,01" readonly required>
</div>
<div class="col-sm-2 d-flex justify-content-evenly align-items-baseline">
<button type="submit" class="btn btn-primary mt-3 px-4">Сохранить</button>
</div>
</form>
@section Scripts
{
<script>
$('#good').on('change', function () {
check();
});
$('#count').on('input', function () {
check();
});
function check() {
var count = $('#count').val();
var good = $('#good').val();
if (count && good) {
$.ajax({
method: "POST",
url: "/Storekeeper/Calc",
data: { count: count, good: good },
success: function (result) {
$("#sum").val(result);
}
})
.fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
});
};
}
check()
</script>
}

View File

@ -0,0 +1,72 @@
@using HardwareShopContracts.ViewModels
@model List<GoodViewModel>
@{
ViewData["Title"] = "Товары";
Layout = "~/Views/Shared/_LayoutStorekeeper.cshtml";
}
<div class="text-center">
<h1 class="display-4">Товары</h1>
</div>
<div class="text-center">
<p>
<a asp-action="CreateGood" class="btn btn-primary mx-2">Создать товар</a>
</p>
<table class="table">
<thead>
<tr>
<th>
Название
</th>
<th>
Цена
</th>
<th>
Действия
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.GoodName)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
<div>
<a asp-controller="Storekeeper"
asp-action="UpdateGood"
asp-route-goodid="@item.Id"
class="btn btn-primary">
<i class="fa fa-pencil" aria-hidden="true"></i>
</a>
<button onclick="deleteGood(@item.Id)" type="button" class="btn btn-danger">
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
@section Scripts
{
<script>
function deleteGood(goodId) {
$.ajax({
method: "POST",
url: "/Storekeeper/DeleteGood",
data: { good: goodId }
}).done(() => window.location.href = "/Storekeeper/Goods");
}
</script>
}

View File

@ -0,0 +1,183 @@
@using HardwareShopContracts.ViewModels
@{
ViewData["Title"] = "Привязка сборок";
Layout = "~/Views/Shared/_LayoutStorekeeper.cshtml";
}
<div class="text-center">
<h1 class="display-4">Привязка сборок</h1>
</div>
<div class="d-flex flex-column align-items-center">
<div class="col-sm-3">
<div class="d-flex justify-content-center">
<button type="button" class="btn btn-primary mx-2 mt-3" data-bs-toggle="modal" data-bs-target="#exampleModal">Привязать</button>
</div>
</div>
<h1 class="display-6">Привязанные сборки</h1>
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Сборка</th>
<th scope="col">Количество</th>
<th scope="col">Действие</th>
</tr>
</thead>
<tbody id="result">
</tbody>
</table>
<div class="col-sm-2 d-flex justify-content-evenly align-items-baseline">
<button type="button" class="btn btn-primary mt-3 px-4" id="linkbuilds">Сохранить</button>
</div>
</div>
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Привязка сборки</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
</div>
<div class="modal-body">
<label class="form-label">Сборка</label>
<select id="build" name="build" class="form-control" asp-items="@(new SelectList(@ViewBag.Builds, "Id", "BuildName"))" required></select>
<table class="table table-hover">
<thead><tr><th scope="col">Комментарии</th></tr></thead>
<tbody id="comments"></tbody>
<label class="form-label">Количество</label>
<input type="number" class="form-control" name="count" id="count" min="1" value="1" required>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" id="savebuild">Сохранить</button>
</div>
</div>
</div>
</div>
@section Scripts
{
<script>
const componentid = @Model;
let component;
let list = [];
const submitBuildBtn = document.getElementById("savebuild");
const saveBtn = document.getElementById("linkbuilds");
const countElem = document.getElementById("count");
const resultTable = document.getElementById("result");
const selectBuilds = document.getElementById("build");
const comments = document.getElementById("comments");
selectBuilds.addEventListener('change', function() { getCommentsOnBuild() });
function getCommentsOnBuild() {
$.ajax({
method: "GET",
url: `/Storekeeper/GetCommentsOnBuild`,
data: { buildId: selectBuilds.value },
success: function (result) {
reloadCommentsTable(result)
}
});
}
submitBuildBtn.addEventListener("click", () => {
console.log('try to add build')
var count = $('#count').val();
var build = $('#build').val();
$.ajax({
method: "GET",
url: `/Storekeeper/GetBuild`,
data: { buildId: build },
success: function (result) {
let flag = false
if (list.length > 0) {
list.forEach((elem) => {
if (elem.build.id === parseInt(result.id)) {
console.log('build already added')
flag = true
}
})
}
if (!flag) list.push({ build: result, count: count })
reloadTable()
countElem.value = '1'
}
});
})
saveBtn.addEventListener("click", () => {
console.log('try to link builds')
let builds = []
let counts = []
list.forEach((x) => {
builds.push(x.build);
counts.push(parseInt(x.count))
})
$.ajax(
{
url: `/Storekeeper/LinkBuilds`,
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ "Id": componentid, "ComponentName": component.componentName,
"Cost": component.cost, "ComponentBuildsBuilds": builds, "ComponentBuildsCounts": counts })
}
).done(() => window.location.href = '/Storekeeper/Components')
})
function reloadTable() {
resultTable.innerHTML = ''
let count = 0;
list.forEach((elem) => {
resultTable.innerHTML += `<tr><td>${elem.build.buildName}</td><td>${elem.count}</td><td> \
<div> \
<button onclick="deleteBuild(${count})" type="button" class="btn btn-danger"> \
<i class="fa fa-trash" aria-hidden="true"></i> \
</button> \
</div><td/></tr>`
count++;
})
}
function reloadCommentsTable(result) {
comments.innerHTML = ''
result.forEach((elem) => {
comments.innerHTML += `<tr><td>${elem.text}</td></tr>`
})
}
function deleteBuild(id) {
list = list.filter(value => value.build.buildName != resultTable.rows[id].cells[0].innerText)
reloadTable()
}
function getComponentBuilds() {
if (componentid) {
$.ajax({
method: "GET",
url: "/Storekeeper/GetComponent",
data: { Id: componentid },
success: function (result) {
component = result
}
});
$.ajax({
method: "GET",
url: "/Storekeeper/GetComponentBuilds",
data: { componentid: componentid },
success: function (result) {
if (result) {
result.forEach(elem => {
list.push({ build: elem.item1, count: elem.item2 })
})
reloadTable()
}
}
});
};
}
getComponentBuilds();
getCommentsOnBuild();
</script>
}

View File

@ -0,0 +1,152 @@
@using HardwareShopContracts.ViewModels
@{
ViewData["Title"] = "Получение списка";
Layout = "~/Views/Shared/_LayoutStorekeeper.cshtml";
}
<div class="text-center">
<h1 class="display-4">Получение списка сборок</h1>
</div>
<div class="d-flex flex-column align-items-center">
<h1 class="display-6">Выбранные товары</h1>
<div class="d-flex justify-content-center">
<button type="button" class="btn btn-primary mx-2 mt-3" data-bs-toggle="modal" data-bs-target="#exampleModal">Добавить товар</button>
</div>
<table class="table">
<thead>
<tr>
<th scope="col">Товар</th>
<th scope="col">Действия</th>
</tr>
</thead>
<tbody id="result">
</tbody>
</table>
<div class="col d-flex justify-content-evenly align-items-baseline">
<button type="button" class="btn btn-primary m-2" id="savedoc">Сохранить в doc-формате</button>
<button type="button" class="btn btn-primary m-2" id="saveexcel">Сохранить в xls-формате</button>
</div>
</div>
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Товар</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
</div>
<div class="modal-body">
<label class="form-label">Товар</label>
<select id="good" name="good" class="form-control" asp-items="@(new SelectList(@ViewBag.Goods, "Id", "GoodName"))"></select>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" id="savegood">Сохранить</button>
</div>
</div>
</div>
</div>
@section Scripts
{
<script>
let list = [];
const submitGoodBtn = document.getElementById("savegood");
const resultTable = document.getElementById("result");
const saveDocBtn = document.getElementById("savedoc");
const saveExcelBtn = document.getElementById("saveexcel");
submitGoodBtn.addEventListener("click", () => {
console.log('try to add good')
var good = $('#good').val();
if (good)
$.ajax({
method: "GET",
url: `/Storekeeper/GetGood`,
data: { Id: good },
success: function (result) {
let flag = false
if (list.length > 0) {
list.forEach((elem) => {
if (elem.id === parseInt(result.id)) {
console.log('good already added')
flag = true
}
})
}
if (!flag) list.push(result)
reloadTable()
}
}).fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
})
})
saveDocBtn.addEventListener("click", async () => {
send('docx')
})
saveExcelBtn.addEventListener("click", async () => {
send('xlsx')
})
function send(format) {
console.log(`try to save in ${format} format`)
if (list.length == 0) {
alert('operation failed. goods are empty')
return
}
$.ajax({
url: `/Storekeeper/ListBuilds?format=${format}`,
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ "Goods" : list })
}).done((file) => {
let byteArray = new Uint8Array(file);
saveFile(byteArray, format);
})
.fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
})
}
async function saveFile(bytes, format) {
if (window.showSaveFilePicker) {
const opts = {
suggestedName: `listbuilds.${format}`,
types: [{
description: `${format} file`,
accept:
{
[`text/${format}`]: [`.${format}`]
},
}],
};
const handle = await showSaveFilePicker(opts);
const writable = await handle.createWritable();
await writable.write(bytes);
writable.close();
alert('done')
}
}
function reloadTable() {
resultTable.innerHTML = ''
let count = 0;
list.forEach((elem) => {
resultTable.innerHTML += `<tr><td>${elem.goodName}</td><td> \
<div> \
<button onclick="deleteGood(${count})" type="button" class="btn btn-danger"> \
<i class="fa fa-trash" aria-hidden="true"></i> \
</button> \
</div></td></tr>`
count++;
})
}
function deleteGood(id) {
list = list.filter(value => value.goodName != resultTable.rows[id].cells[0].innerText)
reloadTable()
}
</script>
}

View File

@ -0,0 +1,8 @@
@{
ViewData["Title"] = "Основная - Кладовщик";
Layout = "~/Views/Shared/_LayoutStorekeeper.cshtml";
}
<div class="d-flex justify-content-center">
<img src="../images/logo.png"/>
</div>

View File

@ -0,0 +1,168 @@
@using HardwareShopContracts.ViewModels
@model List<OrderViewModel>
@{
ViewData["Title"] = "Заказы";
Layout = "~/Views/Shared/_LayoutStorekeeper.cshtml";
}
<div class="text-center">
<h1 class="display-4">Заказы</h1>
</div>
<div class="text-center">
<p>
<a asp-action="CreateOrder" class="btn btn-primary mx-2">Создать заказ</a>
<button type="button" class="btn btn-primary mx-2" id="delete">Удалить заказ</button>
<button type="button" class="btn btn-primary mx-2" id="inwork">Выполняется</button>
<button type="button" class="btn btn-primary mx-2" id="ready">Готов</button>
<button type="button" class="btn btn-primary mx-2" id="done">Выдан</button>
</p>
<table class="table" id="table">
<thead>
<tr>
<th>
Номер
</th>
<th>
Товар
</th>
<th>
Количество
</th>
<th>
Сумма
</th>
<th>
Статус
</th>
<th>
Дата создания
</th>
<th>
Дата выполнения
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Id)
</td>
<td>
@Html.DisplayFor(modelItem => item.GoodName)
</td>
<td>
@Html.DisplayFor(modelItem => item.Count)
</td>
<td>
@Html.DisplayFor(modelItem => item.Sum)
</td>
<td>
@Html.DisplayFor(modelItem => item.Status)
</td>
<td>
@Html.DisplayFor(modelItem => item.DateCreate)
</td>
<td>
@Html.DisplayFor(modelItem => item.DateImplement)
</td>
</tr>
}
</tbody>
</table>
</div>
@section Styles
{
<style>
tr{cursor: pointer;}
.selected{background-color: #0d6efd; color: white;}
</style>
}
@section Scripts
{
<script>
// get selected row
// display selected row data in text input
var table = document.getElementById("table");
var remove = document.getElementById("delete");
var inwork = document.getElementById("inwork");
var ready = document.getElementById("ready");
var done = document.getElementById("done");
for(var i = 1; i < table.rows.length; i++)
{
table.rows[i].onclick = function()
{
// remove the background from the previous selected row
if(typeof index !== "undefined") {
table.rows[index].classList.toggle("selected");
}
// get the selected row index
index = this.rowIndex;
// add class selected to the row
this.classList.toggle("selected");
var order = parseInt(this.cells[0].innerText)
remove.addEventListener("click", () => {
console.log('try to delete order')
$.ajax(
{
url: `/Storekeeper/DeleteOrder`,
type: 'POST',
data: { id: order }
}
).done(() => window.location.href='/Storekeeper/Orders')
.fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
})
})
inwork.addEventListener("click", () => {
console.log('try to update order status')
$.ajax(
{
url: `/Storekeeper/UpdateOrder`,
type: 'POST',
data: { id : order, status : 1 }
}
).done((result) => window.location.href='/Storekeeper/Orders')
.fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
})
})
ready.addEventListener("click", () => {
console.log('try to update order status')
$.ajax(
{
url: `/Storekeeper/UpdateOrder`,
type: 'POST',
data: { id : order, status : 2 }
}
).done(() => window.location.href='/Storekeeper/Orders')
.fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
})
})
done.addEventListener("click", () => {
console.log('try to update order status')
$.ajax(
{
url: `/Storekeeper/UpdateOrder`,
type: 'POST',
data: { id : order, status : 3 }
}
).done(() => window.location.href='/Storekeeper/Orders')
.fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
})
})
};
}
</script>
}

View File

@ -0,0 +1,94 @@
@using HardwareShopContracts.ViewModels
@{
ViewData["Title"] = "Отчет";
Layout = "~/Views/Shared/_LayoutStorekeeper.cshtml";
}
<div class="d-flex flex-column align-items-center">
<div class="col-sm-3">
<label class="form-label">С</label>
<input type="date" class="form-control" name="dateFrom" id="dateFrom">
<label class="form-label">По</label>
<input type="date" class="form-control" name="dateTo" id="dateTo">
</div>
<button type="submit" class="btn btn-primary mt-3 px-4" id="page">Вывод на страницу</button>
<button type="submit" class="btn btn-primary mt-3 px-4" id="mail">Отправить на почту</button>
</div>
<table class="table">
<thead>
<tr>
<th scope="col">Комплектующее</th>
<th scope="col">Товар/Сборка</th>
<th scope="col">Количество</th>
</tr>
</thead>
<tbody id="result">
</tbody>
</table>
@section Scripts
{
<script>
let list = []
const from = document.getElementById("dateFrom");
const to = document.getElementById("dateTo");
const onpage = document.getElementById("page");
const onmail = document.getElementById("mail");
const resultTable = document.getElementById("result");
onpage.addEventListener("click", () => {
console.log('try to get report')
if (from.value && to.value && from.value !== '' && to.value !== '') {
const dateFrom = new Date(from.value);
const dateTo = new Date(to.value);
if (dateFrom.getTime() > dateTo.getTime())
alert("Неправильные даты")
const reportModel = { "DateFrom": dateFrom, "DateTo": dateTo }
$.ajax({
method: "POST",
contentType: "application/json",
url: `/Storekeeper/Report`,
data: JSON.stringify(reportModel),
success: function (result) {
list = result
console.log(list)
reloadTable()
}
}).fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
})
} else { alert("Пустые поля") }
})
onmail.addEventListener("click", () => {
console.log('try to send email')
if (from.value && to.value && from.value !== '' && to.value !== '') {
const dateFrom = new Date(from.value);
const dateTo = new Date(to.value);
if (dateFrom.getTime() > dateTo.getTime())
alert("Неправильные даты")
const reportModel = { "DateFrom": dateFrom, "DateTo": dateTo }
$.ajax({
method: "POST",
contentType: "application/json",
url: `/Storekeeper/ReportSendOnMail`,
data: JSON.stringify(reportModel)
}).fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
})
} else { alert("Пустые поля") }
})
function reloadTable() {
resultString = '';
list.forEach((elem) => {
resultString += `<tr><td>${elem.componentName}</td><td></td><td></td></tr>`;
elem.goodOrBuilds.forEach((goodOrBuild) => {
resultString += `<tr><td></td><td>${goodOrBuild.item1}</td><td>${goodOrBuild.item2}</td></tr>`;
})
resultString += `<tr><td>Итого</td><td></td><td>${elem.totalCount}</td></tr>`;
})
resultTable.innerHTML = resultString
}
</script>
}

View File

@ -0,0 +1,181 @@
@using HardwareShopContracts.ViewModels
@model int
@{
ViewData["Title"] = "Товар";
Layout = "~/Views/Shared/_LayoutStorekeeper.cshtml";
}
<div class="text-center">
<h2 class="display-4">Редактирование товара</h2>
</div>
<div class="d-flex flex-column align-items-center">
<div class="col-sm-3">
<label class="form-label">Название</label>
<input type="text" class="form-control" name="name" id="name" required>
</div>
<div class="col-sm-3">
<label class="form-label">Цена</label>
<input type="number" step="0.01" class="form-control" name="price" id="price" readonly min="0.01" value="0" required>
</div>
<h1 class="display-6">Комплектующие</h1>
<div class="d-flex justify-content-center">
<button type="button" class="btn btn-primary mx-2 mt-3" data-bs-toggle="modal" data-bs-target="#exampleModal">Добавить</button>
</div>
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Комплектующее</th>
<th scope="col">Стоимость</th>
<th scope="col">Количество</th>
<th scope="col">Сумма</th>
<th scope="col">Действие</th>
</tr>
</thead>
<tbody id="result">
</tbody>
</table>
<div class="col-sm-2 d-flex justify-content-evenly align-items-baseline">
<button type="button" class="btn btn-primary mt-3 px-4" id="editgood">Сохранить</button>
</div>
</div>
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Комплектующее</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
</div>
<div class="modal-body">
<label class="form-label">Комплектующее</label>
<select id="component" name="component" class="form-control" asp-items="@(new SelectList(@ViewBag.Components, "Id", "ComponentName"))"></select>
<label class="form-label">Количество</label>
<input type="number" class="form-control" name="count" id="count" min="1" value="1" required>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" id="savecomponent">Сохранить</button>
</div>
</div>
</div>
</div>
@section Scripts
{
<script>
const goodid = @Model
// components & counts
let list = [];
const name = document.getElementById("name");
const submitComponentBtn = document.getElementById("savecomponent");
const saveBtn = document.getElementById("editgood");
const countElem = document.getElementById("count");
const resultTable = document.getElementById("result");
const totalPrice = document.getElementById("price");
submitComponentBtn.addEventListener("click", () => {
console.log('try to add component')
var count = $('#count').val();
var component = $('#component').val();
if (component && count && count > 0)
$.ajax({
method: "GET",
url: `/Storekeeper/GetComponent`,
data: { Id: component },
success: function (result) {
let flag = false
if (list.length > 0) {
list.forEach((elem) => {
if (elem.component.id === parseInt(result.id)) {
console.log('component already added')
flag = true
}
})
}
if (!flag) list.push({ component: result, count: count })
reloadTable()
countElem.value = '1'
}
}).fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
});
})
saveBtn.addEventListener("click", () => {
console.log('try to update good')
if (list.length == 0) {
console.log('failed update good')
return
}
let components = []
let counts = []
list.forEach((x) => {
components.push(x.component);
counts.push(parseInt(x.count))
})
$.ajax(
{
url: `/Storekeeper/UpdateGood`,
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ "Id": goodid, "GoodName": name.value,"Price": parseFloat(totalPrice.value),
"GoodComponentsComponents": components, "GoodComponentsCounts": counts })
}
).done(() => window.location.href = '/Storekeeper/Goods')
.fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
})
})
function reloadTable() {
resultTable.innerHTML = ''
let price = 0;
let count = 0;
list.forEach((elem) => {
resultTable.innerHTML += `<tr><td>${elem.component.componentName}</td><td>${elem.component.cost}</td><td>${elem.count}</td><td>${Math.round(elem.component.cost * elem.count * 100) / 100}</td><td> \
<div> \
<button onclick="deleteComponent(${count})" type="button" class="btn btn-danger"> \
<i class="fa fa-trash" aria-hidden="true"></i> \
</button> \
</div><td/></tr>`
count++;
price += elem.component.cost * elem.count
})
totalPrice.value = Math.round(price * 100) / 100
}
function deleteComponent(id) {
list = list.filter(value => value.component.componentName != resultTable.rows[id].cells[0].innerText)
reloadTable()
}
function getGood() {
if (goodid) {
$.ajax({
method: "GET",
url: "/Storekeeper/GetGoodUpdate",
data: { goodid: goodid },
success: function (result) {
if (result) {
name.value = result.item1.goodName
totalPrice.value = result.item1.price
result.item2.forEach(elem => {
list.push({ component: elem.item1, count: elem.item2 })
})
reloadTable()
}
else
alert("Ошибка получения товара")
}
})
.fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
})
};
}
getGood();
</script>
}

View File

@ -1,62 +0,0 @@
@{
ViewData["Title"] = "Builds";
}
@section Header {
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Purchases">Purchases</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Builds">Builds</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Comments">Comments</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="listComponents">listComponents</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="WorkerReport">WorkerReport</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
}
<div class="text-center">
<h2 class="display-4">Регистрация</h2>
</div>
<form method="post" class="d-flex flex-column align-items-center">
<div class="col-sm-3">
<label class="form-label">Логин</label>
<input type="text" class="form-control" aria-describedby="emailHelp" name="login">
</div>
<div class="col-sm-3">
<label class="form-label">Почта</label>
<input type="text" class="form-control" name="email">
</div>
<div class="col-sm-3">
<label class="form-label">Пароль</label>
<input type="password" class="form-control" name="password">
</div>
<div class="col-sm-3">
<label class="form-label">Роль</label>
<select class="form-select" name="role">
<option value="1">Работник</option>
<option value="2">Кладовщик</option>
</select>
</div>
<button type="submit" class="btn btn-primary mt-3 px-4">Submit</button>
</form>

View File

@ -1,62 +0,0 @@
@{
ViewData["Title"] = "Comments";
}
@section Header {
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Purchases">Purchases</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Builds">Builds</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Comments">Comments</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="listComponents">listComponents</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="WorkerReport">WorkerReport</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
}
<div class="text-center">
<h2 class="display-4">Регистрация</h2>
</div>
<form method="post" class="d-flex flex-column align-items-center">
<div class="col-sm-3">
<label class="form-label">Логин</label>
<input type="text" class="form-control" aria-describedby="emailHelp" name="login">
</div>
<div class="col-sm-3">
<label class="form-label">Почта</label>
<input type="text" class="form-control" name="email">
</div>
<div class="col-sm-3">
<label class="form-label">Пароль</label>
<input type="password" class="form-control" name="password">
</div>
<div class="col-sm-3">
<label class="form-label">Роль</label>
<select class="form-select" name="role">
<option value="1">Работник</option>
<option value="2">Кладовщик</option>
</select>
</div>
<button type="submit" class="btn btn-primary mt-3 px-4">Submit</button>
</form>

View File

@ -1,36 +0,0 @@
@{
ViewData["Title"] = "MainWorker";
}
@section Header {
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Purchases">Purchases</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Builds">Builds</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Comments">Comments</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="listComponents">listComponents</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="WorkerReport">WorkerReport</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
}

View File

@ -1,106 +0,0 @@
@using HardwareShopContracts.ViewModels
@{
ViewData["Title"] = "Purchase";
}
@model List<GoodViewModel>
@section Header {
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Purchases">Purchases</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Builds">Builds</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Comments">Comments</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="listComponents">listComponents</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="WorkerReport">WorkerReport</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
}
<form class="d-flex justify-content-evenly">
<div class=" col-sm-8">
<div class="text-center">
<h2 class="display-4">Товары</h2>
</div>
<div class="text-center" name="id">
<table class="table">
<thead>
<tr>
<th>
Номер
</th>
<th>
Название товара
</th>
<th>
Цена
</th>
<th>
Пользователь
</th>
<th>
Количество
</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
<div class="text-center d-flex flex-column mt-5">
<button type="button" class="btn btn-primary btn-lg mb-5" data-bs-toggle="modal" data-bs-target="#exampleModal">Добавить</button>
<button type="button" class="btn btn-primary btn-lg mb-5" data-bs-toggle="modal" data-bs-target="#exampleModal">Изменить</button>
<button type="button" class="btn btn-primary btn-lg mb-5">Удалить</button>
<button type="button" class="btn btn-primary btn-lg mb-5">Обновить</button>
</div>
<!-- Модальное окно -->
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Заголовок модального окна</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
</div>
<div class="modal-body">
<div class="col-sm-3">
<label class="form-label">Товар</label>
<select class="form-select" name="role">
<option value="1">Товар 1</option>
<option value="2">Товар 2</option>
</select>
</div>
<div class="col-sm-3">
<label class="form-label">Количество</label>
<input type="number" class="form-control" name="count">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="button" class="btn btn-primary">Сохранить изменения</button>
</div>
</div>
</div>
</div>
</form>

View File

@ -1,103 +0,0 @@
@using HardwareShopContracts.ViewModels
@{
ViewData["Title"] = "Purchases";
}
@model List<PurchaseViewModel>
@section Header {
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Purchases">Purchases</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Builds">Builds</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Comments">Comments</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="listComponents">listComponents</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="WorkerReport">WorkerReport</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
}
<form method="post" class="d-flex justify-content-evenly">
<div class=" col-sm-8">
<div class="text-center">
<h2 class="display-4">Покупки</h2>
</div>
<div class="text-center" name="id">
@{
if (Model == null)
{
<h3 class="display-4">Авторизируйтесь</h3>
return;
}
<table class="table">
<thead>
<tr>
<th>
Номер
</th>
<th>
Дата оплаты
</th>
<th>
Сумма
</th>
<th>
Статус
</th>
<th>
Пользователь
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Id)
</td>
<td>
@Html.DisplayFor(modelItem => item.DatePurchase)
</td>
<td>
@Html.DisplayFor(modelItem => item.Sum)
</td>
<td>
@Html.DisplayFor(modelItem => item.PurchaseStatus)
</td>
<td>
@Html.DisplayFor(modelItem => item.UserLogin)
</td>
</tr>
}
</tbody>
</table>
}
</div>
</div>
<div class="text-center d-flex flex-column mt-5">
<button type="submit" class="btn btn-primary btn-lg mb-5">Добавить</button>
<button type="submit" class="btn btn-primary btn-lg mb-5">Изменить</button>
<button type="button" class="btn btn-primary btn-lg mb-5">Удалить</button>
<button type="button" class="btn btn-primary btn-lg mb-5">Обновить</button>
</div>
</form>

View File

@ -1,38 +0,0 @@
@using HardwareShopContracts.ViewModels
@{
ViewData["Title"] = "WorkerReport";
}
@section Header {
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Purchases">Purchases</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Builds">Builds</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Comments">Comments</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="listComponents">listComponents</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="WorkerReport">WorkerReport</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
}

View File

@ -1,62 +0,0 @@
@{
ViewData["Title"] = "listComponents";
}
@section Header {
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Purchases">Purchases</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Builds">Builds</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="Comments">Comments</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="listComponents">listComponents</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Worker" asp-action="WorkerReport">WorkerReport</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
}
<div class="text-center">
<h2 class="display-4">Регистрация</h2>
</div>
<form method="post" class="d-flex flex-column align-items-center">
<div class="col-sm-3">
<label class="form-label">Логин</label>
<input type="text" class="form-control" aria-describedby="emailHelp" name="login">
</div>
<div class="col-sm-3">
<label class="form-label">Почта</label>
<input type="text" class="form-control" name="email">
</div>
<div class="col-sm-3">
<label class="form-label">Пароль</label>
<input type="password" class="form-control" name="password">
</div>
<div class="col-sm-3">
<label class="form-label">Роль</label>
<select class="form-select" name="role">
<option value="1">Работник</option>
<option value="2">Кладовщик</option>
</select>
</div>
<button type="submit" class="btn btn-primary mt-3 px-4">Submit</button>
</form>

View File

@ -1,3 +1,3 @@
@using HardwareShopClientApp
@using HardwareShopClientApp.Models
@using HardwareShopStorekeeperApp
@using HardwareShopStorekeeperApp.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

View File

@ -7,5 +7,5 @@
},
"AllowedHosts": "*",
"IPAddress": "http://localhost:5205/"
"IPAddress": "http://localhost:5254/"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 434 KiB

View File

@ -1,17 +1,21 @@
using HardwareShopDataModels.Models;
using System.ComponentModel;
namespace HardwareShopContracts.BindingModels
{
public class BuildBindingModel : IBuildModel
{
public int Id { get; set; }
public decimal Price { get; set; }
public double Price { get; set; }
public string BuildName { get; set; } = string.Empty;
public int UserId { get; set; }
public Dictionary<int, (IComponentModel, int)>? BuildComponents { get; set; }
public Dictionary<int, (IPurchaseModel, int)> BuildPurchases { get; set; } = new();
public Dictionary<int, (IComponentModel, int)> BuildComponents { get; set; } = new();
public Dictionary<int, ICommentModel> BuildComments { get; set; } = new();
}
}

View File

@ -1,5 +1,4 @@
using HardwareShopDataModels.Models;
using System.ComponentModel;
namespace HardwareShopContracts.BindingModels
{

View File

@ -1,4 +1,5 @@
using HardwareShopDataModels.Models;
using HardwareShopContracts.ViewModels;
using HardwareShopDataModels.Models;
namespace HardwareShopContracts.BindingModels
{
@ -11,5 +12,25 @@ namespace HardwareShopContracts.BindingModels
public double Cost { get; set; }
public int UserId { get; set; }
public DateTime DateCreate { get; set; } = DateTime.Now;
public Dictionary<int, (IBuildModel, int)> ComponentBuilds
{
get;
set;
} = new();
public List<BuildViewModel> ComponentBuildsBuilds
{
get;
set;
} = new();
public List<int> ComponentBuildsCounts
{
get;
set;
} = new();
}
}

View File

@ -1,4 +1,5 @@
using HardwareShopDataModels.Models;
using HardwareShopContracts.ViewModels;
using HardwareShopDataModels.Models;
namespace HardwareShopContracts.BindingModels
{
@ -12,10 +13,31 @@ namespace HardwareShopContracts.BindingModels
public int UserId { get; set; }
public Dictionary<int, (IComponentModel, int)> GoodComponents
{
get;
set;
public Dictionary<int, (IComponentModel, int)> GoodComponents
{
get;
set;
} = new();
// for dictionary item1
public List<ComponentViewModel> GoodComponentsComponents
{
get;
set;
} = new();
// for dictionary item2
public List<int> GoodComponentsCounts
{
get;
set;
} = new();
// for report list
public List<GoodViewModel> Goods
{
get;
set;
} = new();
}
}

View File

@ -0,0 +1,17 @@
namespace HardwareShopContracts.BindingModels
{
public class MailConfigBindingModel
{
public string MailLogin { get; set; } = string.Empty;
public string MailPassword { get; set; } = string.Empty;
// откуда отправляем письма
public string SmtpClientHost { get; set; } = string.Empty;
public int SmtpClientPort { get; set; }
// откуда получаем
public string PopHost { get; set; } = string.Empty;
public int PopPort { get; set; }
}
}

View File

@ -0,0 +1,13 @@
namespace HardwareShopContracts.BindingModels
{
public class MailSendInfoBindingModel
{
public string MailAddress { get; set; } = string.Empty;
public string Subject { get; set; } = string.Empty;
public string Text { get; set; } = string.Empty;
public byte[] File { get; set; } = Array.Empty<byte>();
}
}

View File

@ -1,13 +1,14 @@
using HardwareShopDataModels.Enums;
using HardwareShopContracts.ViewModels;
using HardwareShopDataModels.Enums;
using HardwareShopDataModels.Models;
using System.ComponentModel;
namespace HardwareShopContracts.BindingModels
{
public class PurchaseBindingModel : IPurchaseModel
{
public int Id { get; set; }
public decimal Sum { get; set; }
public double Sum { get; set; }
public PurchaseStatus PurchaseStatus { get; set; } = PurchaseStatus.Неизвестен;
@ -15,8 +16,26 @@ namespace HardwareShopContracts.BindingModels
public int UserId { get; set; }
public Dictionary<int, (IBuildModel, int)>? PurchaseBuilds { get; set; }
public Dictionary<int, (IGoodModel, int)> PurchaseGoods { get; set; } = new();
}
public Dictionary<int, (IBuildModel, int)> PurchaseBuilds { get; set; } = new();
public List<GoodViewModel> ListPurchaseGoods
{
get;
set;
} = new();
public List<int> PurchaseGoodsCounts
{
get;
set;
} = new();
public List<PurchaseViewModel> Purchases
{
get;
set;
} = new();
}
}

View File

@ -0,0 +1,15 @@
namespace HardwareShopContracts.BindingModels
{
public class ReportBindingModel
{
public string UserEmail { get; set; } = string.Empty;
public string FileName { get; set; } = string.Empty;
public DateTime DateFrom { get; set; }
public DateTime DateTo { get; set; }
public int UserId { get; set; }
}
}

View File

@ -1,5 +1,5 @@
using HardwareShopDataModels.Models;
using HardwareShopDataModels.Enums;
using HardwareShopDataModels.Enums;
using HardwareShopDataModels.Models;
namespace HardwareShopContracts.BindingModels
{

View File

@ -2,7 +2,7 @@
using HardwareShopContracts.SearchModels;
using HardwareShopContracts.ViewModels;
namespace HardwareShopContracts.BuisnessLogicsContracts
namespace HardwareShopContracts.BusinessLogicsContracts
{
public interface IComponentLogic
{

View File

@ -2,7 +2,7 @@
using HardwareShopContracts.SearchModels;
using HardwareShopContracts.ViewModels;
namespace HardwareShopContracts.BuisnessLogicsContracts
namespace HardwareShopContracts.BusinessLogicsContracts
{
public interface IGoodLogic
{

View File

@ -11,5 +11,6 @@ namespace HardwareShopContracts.BuisnessLogicsContracts
bool TakeOrderInWork(OrderBindingModel model);
bool FinishOrder(OrderBindingModel model);
bool DeliveryOrder(OrderBindingModel model);
bool Delete(OrderBindingModel model);
}
}

View File

@ -7,7 +7,8 @@ namespace HardwareShopContracts.BusinessLogicsContracts
public interface IPurchaseLogic
{
List<PurchaseViewModel>? ReadList(PurchaseSearchModel? model);
CommentViewModel? ReadElement(CommentSearchModel model);
List<PurchaseViewModel>? ReadOrderList(PurchaseSearchModel model);
PurchaseViewModel? ReadElement(PurchaseSearchModel model);
bool Create(PurchaseBindingModel model);
bool Update(PurchaseBindingModel model);
bool Delete(PurchaseBindingModel model);

View File

@ -0,0 +1,41 @@
using HardwareShopContracts.BindingModels;
using HardwareShopContracts.ViewModels;
namespace HardwareShopContracts.BusinessLogicsContracts
{
public interface IReportStorekeeperLogic
{
/// <summary>
/// Получение списка сборок с указанием, в каких товарах используются
/// </summary>
/// <returns></returns>
List<ReportBuildGoodViewModel> GetBuildGood(List<GoodViewModel> goods);
/// <summary>
/// Получение сведений по комплектующим за период,
/// с указанием в каких товарах и сборках они использовались
/// </summary>
/// <param name="model"></param>
/// <param name="user"></param>
/// <returns></returns>
List<ReportComponentsViewModel> GetComponents(ReportBindingModel model);
/// <summary>
/// Сохранение списка сборок по выбранным товарам в файл-Word
/// </summary>
/// <param name="model"></param>
byte[] SaveBuildGoodToWordFile(ReportBindingModel model, List<GoodViewModel> goods);
/// <summary>
/// Сохранение списка сборок по выбранным товарам в файл-Excel
/// </summary>
/// <param name="model"></param>
byte[] SaveBuildGoodToExcelFile(ReportBindingModel model, List<GoodViewModel> goods);
/// <summary>
/// Отправление отчета на почту
/// </summary>
/// <param name="model"></param>
bool SendReportOnMail(ReportBindingModel model);
}
}

View File

@ -7,13 +7,9 @@ namespace HardwareShopContracts.BusinessLogicsContracts
public interface IUserLogic
{
List<UserViewModel>? ReadList(UserSearchModel? model);
UserViewModel? ReadElement(UserSearchModel model);
bool Create(UserBindingModel model);
bool Update(UserBindingModel model);
bool Delete(UserBindingModel model);
}
}

View File

@ -0,0 +1,40 @@

using HardwareShopContracts.BindingModels;
using HardwareShopContracts.ViewModels;
namespace HardwareShopContracts.BusinessLogicsContracts
{
public interface IWorkerReportLogic
{
/// <summary>
/// Получение списка компонент с указанием, в каких покупках используются
/// </summary>
/// <returns></returns>
List<ReportPurchaseComponentViewModel> GetPurchaseComponent(List<PurchaseViewModel> purchaseList);
/// <summary>
/// Получение списка покупок за определенный период
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
List<ReportPurchaseViewModel> GetPurchase(ReportBindingModel model);
/// <summary>
/// Сохранение компонент с указаеним покупок в файл-Word
/// </summary>
/// <param name="model"></param>
byte[] SavePurchasesToWordFile(ReportBindingModel model, List<PurchaseViewModel> purchases);
/// <summary>
/// Сохранение компонент с указаеним покупок в файл-Excel
/// </summary>
/// <param name="model"></param>
byte[] SavePurchasesToExcelFile(ReportBindingModel model, List<PurchaseViewModel> purchases);
/// <summary>
/// Сохранение отчёта по покупкам в файл-Pdf
/// </summary>
/// <param name="model"></param>
void SendByMailPurchaseReport(ReportBindingModel model);
}
}

View File

@ -1,5 +1,4 @@
using System.ComponentModel;
namespace HardwareShopContracts.SearchModels
namespace HardwareShopContracts.SearchModels
{
public class BuildSearchModel
{

View File

@ -1,7 +1,4 @@

using System.ComponentModel;
namespace HardwareShopContracts.SearchModels
namespace HardwareShopContracts.SearchModels
{
public class CommentSearchModel
{

View File

@ -4,5 +4,8 @@
{
public int? Id { get; set; }
public string? ComponentName { get; set; }
public int? UserId { get; set; }
public DateTime? DateFrom { get; set; }
public DateTime? DateTo { get; set; }
}
}

View File

@ -4,5 +4,6 @@
{
public int? Id { get; set; }
public string? GoodName { get; set; }
public int? UserId { get; set; }
}
}

View File

@ -3,7 +3,6 @@
public class OrderSearchModel
{
public int? Id { get; set; }
public DateTime? DateFrom { get; set; }
public DateTime? DateTo { get; set; }
public int? UserId { get; set; }
}
}

Some files were not shown because too many files have changed in this diff Show More