Merge branch 'Storekeeper' into Worker_Raspaev

This commit is contained in:
Николай 2023-05-17 17:15:40 +04:00
commit f3d161527a
33 changed files with 1421 additions and 63 deletions

View File

@ -1,4 +1,6 @@
using HardwareShopContracts.BindingModels; using HardwareShopBusinessLogic.OfficePackage;
using HardwareShopBusinessLogic.OfficePackage.HelperModels;
using HardwareShopContracts.BindingModels;
using HardwareShopContracts.BusinessLogicsContracts; using HardwareShopContracts.BusinessLogicsContracts;
using HardwareShopContracts.StoragesContracts; using HardwareShopContracts.StoragesContracts;
using HardwareShopContracts.ViewModels; using HardwareShopContracts.ViewModels;
@ -9,9 +11,15 @@ namespace HardwareShopBusinessLogic.BusinessLogics.Storekeeper
{ {
private readonly IComponentStorage _componentStorage; private readonly IComponentStorage _componentStorage;
public ReportStorekeeperLogic(IComponentStorage componentStorage) private readonly AbstractSaveToExcel _saveToExcel;
private readonly AbstractSaveToWord _saveToWord;
public ReportStorekeeperLogic(IComponentStorage componentStorage, AbstractSaveToExcel abstractSaveToExcel, AbstractSaveToWord abstractSaveToWord)
{ {
_componentStorage = componentStorage; _componentStorage = componentStorage;
_saveToExcel = abstractSaveToExcel;
_saveToWord = abstractSaveToWord;
} }
public List<ReportBuildGoodViewModel> GetBuildGood(List<GoodViewModel> goods) public List<ReportBuildGoodViewModel> GetBuildGood(List<GoodViewModel> goods)
{ {
@ -69,5 +77,25 @@ namespace HardwareShopBusinessLogic.BusinessLogics.Storekeeper
} }
return result; return result;
} }
public void SaveBuildGoodToExcelFile(ReportBindingModel model, List<GoodViewModel> goods)
{
_saveToExcel.CreateBuildGoodReport(new ExcelInfo
{
FileName = model.FileName,
Title = "Cписок сборок по выбранным товарам",
BuildGood = GetBuildGood(goods)
});
}
public void SaveBuildGoodToWordFile(ReportBindingModel model, List<GoodViewModel> goods)
{
_saveToWord.CreateBuildGoodReport(new WordInfo
{
FileName = model.FileName,
Title = "Cписок сборок по выбранным товарам",
BuildGood = GetBuildGood(goods)
});
}
} }
} }

View File

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

View File

@ -7,6 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DocumentFormat.OpenXml" Version="2.20.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
</ItemGroup> </ItemGroup>

View File

@ -0,0 +1,107 @@
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 = "C1"
});
/*uint rowIndex = 2;
foreach (var ss in info.BuildGood)
{
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "A",
RowIndex = rowIndex,
Text = ss.ShopName,
StyleInfo = ExcelStyleInfoType.Text
});
rowIndex++;
foreach (var (Sushi, Count) in ss.ListSushi)
{
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "B",
RowIndex = rowIndex,
Text = Sushi,
StyleInfo = ExcelStyleInfoType.TextWithBroder
});
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "C",
RowIndex = rowIndex,
Text = Count.ToString(),
StyleInfo = ExcelStyleInfoType.TextWithBroder
});
rowIndex++;
}
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "A",
RowIndex = rowIndex,
Text = "Итого",
StyleInfo = ExcelStyleInfoType.Text
});
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "C",
RowIndex = rowIndex,
Text = ss.TotalCount.ToString(),
StyleInfo = ExcelStyleInfoType.Text
});
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,74 @@
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 }),
("Дата открытия", new WordTextProperties { Size = "24", Bold = true })
}
});
/*foreach (var shop in info.BuildGood)
{
rows.Add(new WordRow
{
Rows = new List<(string, WordTextProperties)> {
(shop.ShopName, new WordTextProperties { Size = "24" }),
(shop.Address, new WordTextProperties { Size = "24" }),
(shop.DateOpening.ToShortDateString(), new WordTextProperties { Size = "24" })
}
});
}*/
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,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,13 @@
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();
}
}

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,13 @@
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();
}
}

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,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 = "3200" });
}
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,6 +1,7 @@
using HardwareShopContracts.BindingModels; using HardwareShopContracts.BindingModels;
using HardwareShopContracts.ViewModels; using HardwareShopContracts.ViewModels;
using HardwareShopDatabaseImplement.Models; using HardwareShopDatabaseImplement.Models;
using HardwareShopDatabaseImplement.Models.Storekeeper;
using HardwareShopDataModels.Enums; using HardwareShopDataModels.Enums;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -14,6 +15,7 @@ namespace HardwareShopStorekeeperApp.Controllers
{ {
_logger = logger; _logger = logger;
} }
public IActionResult CreateOrder() public IActionResult CreateOrder()
{ {
if (APIClient.User == null) if (APIClient.User == null)
@ -130,14 +132,110 @@ namespace HardwareShopStorekeeperApp.Controllers
goodModel.UserId = APIClient.User.Id; goodModel.UserId = APIClient.User.Id;
APIClient.PostRequest("api/good/creategood", goodModel); APIClient.PostRequest("api/good/creategood", goodModel);
} }
public IActionResult LinkBuilds() 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 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}");
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) if (APIClient.User == null)
{ {
return Redirect("~/Home/Enter"); return Redirect("~/Home/Enter");
} }
return View(); 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() public IActionResult CreateComponent()

View File

@ -16,7 +16,6 @@
@{ @{
<p> <p>
<a asp-action="CreateComponent" class="btn btn-primary mx-2">Создать комплектующее</a> <a asp-action="CreateComponent" class="btn btn-primary mx-2">Создать комплектующее</a>
<a asp-action="LinkBuilds" class="btn btn-primary mx-2">Привязка сборок</a>
</p> </p>
<table class="table"> <table class="table">
<thead> <thead>
@ -50,6 +49,12 @@
</td> </td>
<td> <td>
<div> <div>
<a asp-controller="Storekeeper"
asp-action="LinkBuilds"
asp-route-componentid="@item.Id"
class="btn btn-success">
<i class="fa fa-pen-to-square" aria-hidden="true"></i>
</a>
<button onclick="getComponent(@item.Id)" type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#updateModal"> <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> <i class="fa fa-pencil" aria-hidden="true"></i>
</button> </button>

View File

@ -10,15 +10,15 @@
<form method="post" class="d-flex flex-column align-items-center"> <form method="post" class="d-flex flex-column align-items-center">
<div class="col-sm-3"> <div class="col-sm-3">
<label class="form-label">Название</label> <label class="form-label">Название</label>
<input type="text" class="form-control" name="name"> <input type="text" class="form-control" name="name" required>
</div> </div>
<div class="col-sm-3"> <div class="col-sm-3">
<label class="form-label">Стоимость</label> <label class="form-label">Стоимость</label>
<input type="number" step="0.01" class="form-control" name="cost" min="0.01"> <input type="number" step="0.01" class="form-control" name="cost" min="0.01" required>
</div> </div>
<div class="col-sm-3"> <div class="col-sm-3">
<label class="form-label">Дата приобретения</label> <label class="form-label">Дата приобретения</label>
<input type="datetime-local" class="form-control" name="date"> <input type="datetime-local" class="form-control" name="date" required>
</div> </div>
<div class="col-sm-2 d-flex justify-content-evenly align-items-baseline"> <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> <button type="submit" class="btn btn-primary mt-3 px-4">Сохранить</button>

View File

@ -11,16 +11,15 @@
<div class="d-flex flex-column align-items-center"> <div class="d-flex flex-column align-items-center">
<div class="col-sm-3"> <div class="col-sm-3">
<label class="form-label">Название</label> <label class="form-label">Название</label>
<input type="text" class="form-control" name="name" id="name" required> <input type="text" class="form-control" name="name" id="name" >
</div> </div>
<div class="col-sm-3"> <div class="col-sm-3">
<label class="form-label">Цена</label> <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> <input type="number" step="0.01" class="form-control" name="price" id="price" readonly min="0.01" value="0" >
</div> </div>
<h1 class="display-6">Комплектующие</h1> <h1 class="display-6">Комплектующие</h1>
<div class="d-flex justify-content-center"> <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> <button type="button" class="btn btn-primary mx-2 mt-3" data-bs-toggle="modal" data-bs-target="#exampleModal">Добавить</button>
<button type="button" class="btn btn-primary mx-2 mt-3">Удалить</button>
</div> </div>
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
@ -28,6 +27,7 @@
<th scope="col">Комплектующее</th> <th scope="col">Комплектующее</th>
<th scope="col">Количество</th> <th scope="col">Количество</th>
<th scope="col">Сумма</th> <th scope="col">Сумма</th>
<th scope="col">Действие</th>
</tr> </tr>
</thead> </thead>
<tbody id="result"> <tbody id="result">
@ -49,7 +49,7 @@
<label class="form-label">Комплектующее</label> <label class="form-label">Комплектующее</label>
<select id="component" name="component" class="form-control" asp-items="@(new SelectList(@ViewBag.Components, "Id", "ComponentName"))"></select> <select id="component" name="component" class="form-control" asp-items="@(new SelectList(@ViewBag.Components, "Id", "ComponentName"))"></select>
<label class="form-label">Количество</label> <label class="form-label">Количество</label>
<input type="number" class="form-control" name="count" id="count" min="1" value="1" required> <input type="number" class="form-control" name="count" id="count" min="1" value="1" >
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
@ -65,7 +65,6 @@
let list = []; let list = [];
const name = document.getElementById("name"); const name = document.getElementById("name");
const submitComponentBtn = document.getElementById("savecomponent"); const submitComponentBtn = document.getElementById("savecomponent");
const editBtn = document.getElementById("editcomponent");
const saveBtn = document.getElementById("creategood"); const saveBtn = document.getElementById("creategood");
const countElem = document.getElementById("count"); const countElem = document.getElementById("count");
const resultTable = document.getElementById("result"); const resultTable = document.getElementById("result");
@ -75,6 +74,7 @@
console.log('try to add component') console.log('try to add component')
var count = $('#count').val(); var count = $('#count').val();
var component = $('#component').val(); var component = $('#component').val();
if (component && count)
$.ajax({ $.ajax({
method: "GET", method: "GET",
url: `/Storekeeper/GetComponent`, url: `/Storekeeper/GetComponent`,
@ -93,11 +93,17 @@
reloadTable() reloadTable()
countElem.value = '1' countElem.value = '1'
} }
}); }).fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
})
}) })
saveBtn.addEventListener("click", () => { saveBtn.addEventListener("click", () => {
console.log('try to add good') console.log('try to add good')
if (list.length == 0) {
alert('failed add good. components are empty')
return
}
let components = [] let components = []
let counts = [] let counts = []
list.forEach((x) => { list.forEach((x) => {
@ -113,18 +119,32 @@
"GoodComponentsComponents": components, "GoodComponentsCounts": counts }) "GoodComponentsComponents": components, "GoodComponentsCounts": counts })
} }
).done(() => window.location.href = '/Storekeeper/Goods') ).done(() => window.location.href = '/Storekeeper/Goods')
.fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
})
}) })
function reloadTable() { function reloadTable() {
resultTable.innerHTML = '' resultTable.innerHTML = ''
let price = 0; let price = 0;
let count = 0;
list.forEach((elem) => { list.forEach((elem) => {
console.log(elem); resultTable.innerHTML += `<tr><td>${elem.component.componentName}</td><td>${elem.count}</td><td>${Math.round(elem.component.cost * elem.count * 100) / 100}</td><td> \
resultTable.innerHTML += `<tr><td>${elem.component.componentName}</td><td>${elem.count}</td><td>${Math.round(elem.component.cost * elem.count * 100) / 100}</td></tr>` <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 price += elem.component.cost * elem.count
}) })
totalPrice.value = Math.round(price * 100) / 100 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> </script>
} }

View File

@ -10,7 +10,7 @@
<form method="post" class="d-flex flex-column align-items-center"> <form method="post" class="d-flex flex-column align-items-center">
<div class="col-sm-3"> <div class="col-sm-3">
<label class="form-label">Товар</label> <label class="form-label">Товар</label>
<select id="good" name="good" class="form-control" asp-items="@(new SelectList(@ViewBag.Goods, "Id", "GoodName"))"></select> <select id="good" name="good" class="form-control" asp-items="@(new SelectList(@ViewBag.Goods, "Id", "GoodName"))" required></select>
</div> </div>
<div class="col-sm-3"> <div class="col-sm-3">
<label class="form-label">Количество</label> <label class="form-label">Количество</label>
@ -18,7 +18,7 @@
</div> </div>
<div class="col-sm-3"> <div class="col-sm-3">
<label class="form-label">Сумма</label> <label class="form-label">Сумма</label>
<input type="number" step="0.01" class="form-control" name="sum" id="sum" value="0" readonly required> <input type="number" step="0.01" class="form-control" name="sum" id="sum" value="0" min="0,01" readonly required>
</div> </div>
<div class="col-sm-2 d-flex justify-content-evenly align-items-baseline"> <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> <button type="submit" class="btn btn-primary mt-3 px-4">Сохранить</button>
@ -46,6 +46,9 @@
success: function (result) { success: function (result) {
$("#sum").val(result); $("#sum").val(result);
} }
})
.fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
}); });
}; };
} }

View File

@ -14,8 +14,6 @@
<div class="text-center"> <div class="text-center">
<p> <p>
<a asp-action="CreateGood" class="btn btn-primary mx-2">Создать товар</a> <a asp-action="CreateGood" class="btn btn-primary mx-2">Создать товар</a>
<a asp-action="Goods" class="btn btn-primary mx-2">Изменить товар</a>
<a asp-action="Goods" class="btn btn-primary mx-2">Удалить товар</a>
</p> </p>
<table class="table"> <table class="table">
<thead> <thead>
@ -26,20 +24,49 @@
<th> <th>
Цена Цена
</th> </th>
<th>
Действия
</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@foreach (var item in Model) @foreach (var item in Model)
{ {
<tr> <tr>
<td> <td>
@Html.DisplayFor(modelItem => item.GoodName) @Html.DisplayFor(modelItem => item.GoodName)
</td> </td>
<td> <td>
@Html.DisplayFor(modelItem => item.Price) @Html.DisplayFor(modelItem => item.Price)
</td> </td>
</tr> <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> </tbody>
</table> </table>
</div> </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

@ -8,32 +8,28 @@
<h1 class="display-4">Привязка сборок</h1> <h1 class="display-4">Привязка сборок</h1>
</div> </div>
<form method="post" class="d-flex flex-column align-items-center"> <div class="d-flex flex-column align-items-center">
<div class="col-sm-3"> <div class="col-sm-3">
<label class="form-label">Комплектующее</label>
<select class="form-control" name="component"></select>
<div class="d-flex justify-content-center"> <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> <button type="button" class="btn btn-primary mx-2 mt-3" data-bs-toggle="modal" data-bs-target="#exampleModal">Привязать</button>
<button type="button" class="btn btn-primary mx-2 mt-3" data-bs-toggle="modal" data-bs-target="#exampleModal">Изменить</button>
<button type="button" class="btn btn-primary mx-2 mt-3">Отвязать</button>
</div> </div>
</div> </div>
<h1 class="display-6">Привязанные сборки</h1> <h1 class="display-6">Привязанные сборки</h1>
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th id="name" scope="col">Сборка</th> <th scope="col">Сборка</th>
<th id="price" scope="col">Количество</th> <th scope="col">Количество</th>
<th scope="col">Действие</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody id="result">
</tbody> </tbody>
</table> </table>
<div class="col-sm-2 d-flex justify-content-evenly align-items-baseline"> <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> <button type="button" class="btn btn-primary mt-3 px-4" id="linkbuilds">Сохранить</button>
</div> </div>
</form> </div>
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true"> <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
@ -44,14 +40,120 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<label class="form-label">Сборка</label> <label class="form-label">Сборка</label>
<select class="form-control" name="build"></select> <select id="build" name="build" class="form-control" asp-items="@(new SelectList(@ViewBag.Builds, "Id", "BuildName"))" required></select>
<label class="form-label">Количество</label> <label class="form-label">Количество</label>
<input type="number" class="form-control" name="count" min="1"> <input type="number" class="form-control" name="count" id="count" min="1" value="1" required>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="submit" class="btn btn-primary">Сохранить</button> <button type="button" class="btn btn-primary" data-bs-dismiss="modal" id="savebuild">Сохранить</button>
</div> </div>
</div> </div>
</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");
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 deleteBuild(id) {
list = list.filter(value => value.build.buildName != resultTable.rows[id].cells[0].innerText)
reloadTable()
}
function getComponentBuilds() {
console.log(componentid)
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) {
console.log(result)
if (result) {
result.forEach(elem => {
list.push({ build: elem.item1, count: elem.item2 })
})
reloadTable()
}
}
});
};
}
getComponentBuilds();
</script>
}

View File

@ -118,19 +118,25 @@
data: { id: order } data: { id: order }
} }
).done(() => window.location.href='/Storekeeper/Orders') ).done(() => window.location.href='/Storekeeper/Orders')
.fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
})
}) })
inwork.addEventListener("click", () => { inwork.addEventListener("click", () => {
console.log('try to delete order') console.log('try to update order status')
$.ajax( $.ajax(
{ {
url: `/Storekeeper/UpdateOrder`, url: `/Storekeeper/UpdateOrder`,
type: 'POST', type: 'POST',
data: { id : order, status : 1 } data: { id : order, status : 1 }
} }
).done(() => window.location.href='/Storekeeper/Orders') ).done((result) => window.location.href='/Storekeeper/Orders')
.fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
})
}) })
ready.addEventListener("click", () => { ready.addEventListener("click", () => {
console.log('try to delete order') console.log('try to update order status')
$.ajax( $.ajax(
{ {
url: `/Storekeeper/UpdateOrder`, url: `/Storekeeper/UpdateOrder`,
@ -138,9 +144,12 @@
data: { id : order, status : 2 } data: { id : order, status : 2 }
} }
).done(() => window.location.href='/Storekeeper/Orders') ).done(() => window.location.href='/Storekeeper/Orders')
.fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
})
}) })
done.addEventListener("click", () => { done.addEventListener("click", () => {
console.log('try to delete order') console.log('try to update order status')
$.ajax( $.ajax(
{ {
url: `/Storekeeper/UpdateOrder`, url: `/Storekeeper/UpdateOrder`,
@ -148,6 +157,9 @@
data: { id : order, status : 3 } data: { id : order, status : 3 }
} }
).done(() => window.location.href='/Storekeeper/Orders') ).done(() => window.location.href='/Storekeeper/Orders')
.fail(function(xhr, textStatus, errorThrown) {
alert(xhr.responseText);
})
}) })
}; };
} }

View File

@ -0,0 +1,179 @@
@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>
</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();
$.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.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,4 +1,5 @@
using HardwareShopDataModels.Models; using HardwareShopContracts.ViewModels;
using HardwareShopDataModels.Models;
namespace HardwareShopContracts.BindingModels namespace HardwareShopContracts.BindingModels
{ {
@ -19,5 +20,17 @@ namespace HardwareShopContracts.BindingModels
get; get;
set; set;
} = new(); } = new();
public List<BuildViewModel> ComponentBuildsBuilds
{
get;
set;
} = new();
public List<int> ComponentBuildsCounts
{
get;
set;
} = new();
} }
} }

View File

@ -19,5 +19,17 @@ namespace HardwareShopContracts.BusinessLogicsContracts
/// <param name="user"></param> /// <param name="user"></param>
/// <returns></returns> /// <returns></returns>
List<ReportComponentsViewModel> GetComponents(UserBindingModel user, ReportBindingModel model); List<ReportComponentsViewModel> GetComponents(UserBindingModel user, ReportBindingModel model);
/// <summary>
/// Сохранение списка сборок по выбранным товарам в файл-Word
/// </summary>
/// <param name="model"></param>
void SaveBuildGoodToWordFile(ReportBindingModel model, List<GoodViewModel> goods);
/// <summary>
/// Сохранение списка сборок по выбранным товарам в файл-Excel
/// </summary>
/// <param name="model"></param>
void SaveBuildGoodToExcelFile(ReportBindingModel model, List<GoodViewModel> goods);
} }
} }

View File

@ -10,7 +10,7 @@ namespace HardwareShopDatabaseImplement
{ {
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{ {
optionsBuilder.UseNpgsql("Host=localhost;Port=5432;Database=Computer_Hardware_Store4;Username=postgres;Password=1234"); optionsBuilder.UseNpgsql("Host=localhost;Port=5433;Database=Computer_Hardware_Store;Username=user;Password=12345");
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
} }

View File

@ -18,6 +18,13 @@ namespace HardwareShopDatabaseImplement.Implements.Storekeeper
.FirstOrDefault(rec => rec.Id == model.Id); .FirstOrDefault(rec => rec.Id == model.Id);
if (element != null) if (element != null)
{ {
var componentGoods = context.GoodsComponents.Where(x => x.ComponentId == element.Id).ToList();
componentGoods.ForEach(x =>
{
var good = context.Goods.FirstOrDefault(rec => rec.Id == x.GoodId);
if (good != null)
context.Goods.Remove(good);
});
context.Components.Remove(element); context.Components.Remove(element);
context.SaveChanges(); context.SaveChanges();
return element.GetViewModel; return element.GetViewModel;

View File

@ -85,12 +85,34 @@ namespace HardwareShopDatabaseImplement.Models.Storekeeper
var componentBuilds = context.ComponentsBuilds.Where(rec => rec.ComponentId == model.Id).ToList(); var componentBuilds = context.ComponentsBuilds.Where(rec => rec.ComponentId == model.Id).ToList();
if (componentBuilds != null && componentBuilds.Count > 0) if (componentBuilds != null && componentBuilds.Count > 0)
{ // удалили те, которых нет в модели { // удалили те, которых нет в модели
context.ComponentsBuilds.RemoveRange(componentBuilds.Where(rec => !model.ComponentBuilds.ContainsKey(rec.BuildId))); context.ComponentsBuilds.RemoveRange(componentBuilds.Where(rec =>
{
bool flag = !model.ComponentBuilds.ContainsKey(rec.BuildId);
if (flag)
{
var build = context.Builds.First(x => x.Id == rec.Build.Id);
if (build != null)
{
build.Price -= rec.Component.Cost * rec.Count;
}
context.SaveChanges();
}
return flag;
}));
context.SaveChanges(); context.SaveChanges();
componentBuilds = context.ComponentsBuilds.Where(rec => rec.ComponentId == model.Id).ToList(); componentBuilds = context.ComponentsBuilds.Where(rec => rec.ComponentId == model.Id).ToList();
// обновили количество у существующих записей // обновили количество у существующих записей
foreach (var updateBuild in componentBuilds) foreach (var updateBuild in componentBuilds)
{ {
var build = context.Builds.First(x => x.Id == updateBuild.Build.Id);
if (updateBuild.Count > model.ComponentBuilds[updateBuild.BuildId].Item2)
{
build.Price -= (updateBuild.Count - model.ComponentBuilds[updateBuild.BuildId].Item2) * updateBuild.Component.Cost;
}
else if (updateBuild.Count < model.ComponentBuilds[updateBuild.BuildId].Item2)
{
build.Price += (model.ComponentBuilds[updateBuild.BuildId].Item2 - updateBuild.Count) * updateBuild.Component.Cost;
}
updateBuild.Count = model.ComponentBuilds[updateBuild.BuildId].Item2; updateBuild.Count = model.ComponentBuilds[updateBuild.BuildId].Item2;
model.ComponentBuilds.Remove(updateBuild.BuildId); model.ComponentBuilds.Remove(updateBuild.BuildId);
} }

View File

@ -30,10 +30,12 @@ namespace HardwareShopRestApi.Controllers
} }
[HttpGet] [HttpGet]
public List<BuildViewModel>? GetBuilds(int userId) public List<BuildViewModel>? GetBuilds(int userId = 0)
{ {
try try
{ {
if (userId == 0)
return _buildLogic.ReadList(null);
return _buildLogic.ReadList(new BuildSearchModel return _buildLogic.ReadList(new BuildSearchModel
{ {
UserId = userId UserId = userId
@ -46,7 +48,6 @@ namespace HardwareShopRestApi.Controllers
} }
} }
[HttpGet] [HttpGet]
public BuildViewModel? GetBuild(int buildId) public BuildViewModel? GetBuild(int buildId)
{ {

View File

@ -2,6 +2,8 @@
using HardwareShopContracts.BusinessLogicsContracts; using HardwareShopContracts.BusinessLogicsContracts;
using HardwareShopContracts.SearchModels; using HardwareShopContracts.SearchModels;
using HardwareShopContracts.ViewModels; using HardwareShopContracts.ViewModels;
using HardwareShopDatabaseImplement.Models.Storekeeper;
using HardwareShopDataModels.Models;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace HardwareShopRestApi.Controllers namespace HardwareShopRestApi.Controllers
@ -48,6 +50,28 @@ namespace HardwareShopRestApi.Controllers
} }
} }
[HttpGet]
public List<Tuple<BuildViewModel, int>>? GetComponentBuilds(int id)
{
try
{
var component = _component.ReadElement(new() { Id = id });
if (component == null)
return null;
var tuple = component.ComponentBuilds.Select(x => Tuple.Create(new BuildViewModel
{
Id = x.Value.Item1.Id,
BuildName = x.Value.Item1.BuildName,
}, x.Value.Item2)).ToList();
return tuple;
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения сборок комплектующего");
throw;
}
}
[HttpPost] [HttpPost]
public void CreateComponent(ComponentBindingModel model) public void CreateComponent(ComponentBindingModel model)
{ {
@ -67,6 +91,11 @@ namespace HardwareShopRestApi.Controllers
{ {
try try
{ {
for (int i = 0; i < model.ComponentBuildsCounts.Count; i++)
{
model.ComponentBuilds.Add(model.ComponentBuildsBuilds[i].Id,
(model.ComponentBuildsBuilds[i] as IBuildModel, model.ComponentBuildsCounts[i]));
}
_component.Update(model); _component.Update(model);
} }
catch (Exception ex) catch (Exception ex)

View File

@ -62,6 +62,30 @@ namespace HardwareShopRestApi.Controllers
} }
} }
[HttpGet]
public Tuple<GoodViewModel, List<Tuple<ComponentViewModel, int>>>? GetGoodUpdate(int id)
{
try
{
var good = _good.ReadElement(new() { Id = id });
if (good == null)
return null;
var tuple = Tuple.Create(good,
good.GoodComponents.Select(x => Tuple.Create(new ComponentViewModel
{
Id = x.Value.Item1.Id,
ComponentName = x.Value.Item1.ComponentName,
Cost = x.Value.Item1.Cost
}, x.Value.Item2)).ToList());
return tuple;
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения товара");
throw;
}
}
[HttpPost] [HttpPost]
public void CreateGood(GoodBindingModel model) public void CreateGood(GoodBindingModel model)
{ {
@ -86,6 +110,11 @@ namespace HardwareShopRestApi.Controllers
{ {
try try
{ {
for (int i = 0; i < model.GoodComponentsCounts.Count; i++)
{
model.GoodComponents.Add(model.GoodComponentsComponents[i].Id,
(model.GoodComponentsComponents[i] as IComponentModel, model.GoodComponentsCounts[i]));
}
_good.Update(model); _good.Update(model);
} }
catch (Exception ex) catch (Exception ex)