SQL to create ControlTable
USE [PurchaseOrders]
GO
DROP TABLE IF EXISTS [dbo].[ControlTable]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[ControlTable](
[ControlTableID] [int] IDENTITY(1,1) NOT NULL,
[SmtpHost] [nvarchar](50) NULL,
[SmtpPort] [int] NULL,
[SmtpUserFriendlyName] [nvarchar](50) NULL,
[SmtpUserEmailAddress] [nvarchar](256) NULL,
[SmtpPass] [nvarchar](50) NULL,
[SENDGRID_APIKEY] [nvarchar](256) NULL,
[SenderEmail] [nvarchar](256) NULL,
[SenderEmailFriendlyName] [nvarchar](50) NULL,
[DefaultTaxID] [int] NULL,
[EmailSendMethod] [nvarchar](10) NULL
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[ControlTable] ON
GO
INSERT [dbo].[ControlTable] ([ControlTableID], [SmtpHost], [SmtpPort], [SmtpUserFriendlyName], [SmtpUserEmailAddress], [SmtpPass], [SENDGRID_APIKEY], [SenderEmail], [SenderEmailFriendlyName], [DefaultTaxID], [EmailSendMethod]) VALUES (1, N'smtp.host.com', 587, N'John Smith', N'johnsmith@hostname.com', N'johnsmithpassword', N'Sendgrid.api.key', N'johnsmith@hostname.com', N'John Smith', 1, N'SendGrid')
GO
SET IDENTITY_INSERT [dbo].[ControlTable] OFF
GO
SQL stored procedures for ControlTable
-------------- Stored Proc for SELECT (one)
DROP PROCEDURE IF EXISTS [dbo].[spControlTable_GetOne]
GO
CREATE PROCEDURE spControlTable_GetOne
-- Needs one parameter for primary key
AS
BEGIN
-- SQL Select for one table row
SELECT top(1) ControlTableID, SmtpHost, SmtpPort, SmtpUserFriendlyName, SmtpUserEmailAddress, SmtpPass, SENDGRID_APIKEY, SenderEmail, SenderEmailFriendlyName, DefaultTaxID, EmailSendMethod FROM ControlTable ORDER BY ControlTableID DESC
END
GO
-------------- Stored Proc for UPDATE
DROP PROCEDURE IF EXISTS [dbo].[spControlTable_Update]
GO
CREATE PROCEDURE spControlTable_Update
-- Parameters for Update stored procedure.
@ControlTableID int,
@SmtpHost nvarchar(50),
@SmtpPort int,
@SmtpUserFriendlyName nvarchar(50),
@SmtpUserEmailAddress nvarchar(50),
@SmtpPass nvarchar(50),
@SENDGRID_APIKEY nvarchar(255),
@SenderEmail nvarchar(50),
@SenderEmailFriendlyName nvarchar(50),
@DefaultTaxID int,
@EmailSendMethod nvarchar(10)
AS
BEGIN
-- SQL for Update stored procedure
UPDATE ControlTable SET SmtpHost = @SmtpHost, SmtpPort = @SmtpPort, SmtpUserFriendlyName = @SmtpUserFriendlyName, SmtpUserEmailAddress = @SmtpUserEmailAddress, SmtpPass = @SmtpPass, SENDGRID_APIKEY = @SENDGRID_APIKEY, SenderEmail = @SenderEmail, SenderEmailFriendlyName = @SenderEmailFriendlyName, DefaultTaxID = @DefaultTaxID, EmailSendMethod = @EmailSendMethod WHERE ControlTableID = @ControlTableID
END
GO
Model: ControlTable.cs
using System;
using System.ComponentModel.DataAnnotations;
// This is the model for one row in the database table. You may need to make some adjustments.
namespace BlazorPurchaseOrders.Data
{
public class ControlTable
{
[Required]
public int ControlTableID { get; set; }
[StringLength(50)]
public string SmtpHost { get; set; }
public int SmtpPort { get; set; }
[StringLength(50, ErrorMessage = "'Email' has a maximum length of 50 characters.")]
public string SmtpUserEmailAddress { get; set; }
[StringLength(50, ErrorMessage = "'Friendly Name' has a maximum length of 50 characters.")]
public string SmtpUserFriendlyName { get; set; }
[StringLength(50, ErrorMessage = "'Smtp Password' has a maximum length of 50 characters.")]
public string SmtpPass { get; set; }
[StringLength(255)]
public string SENDGRID_APIKEY { get; set; }
[StringLength(50, ErrorMessage = "'Email' has a maximum length of 50 characters.")]
public string SenderEmail { get; set; }
[StringLength(50, ErrorMessage = "'Friendly Name' has a maximum length of 50 characters.")]
public string SenderEmailFriendlyName { get; set; }
public int DefaultTaxID { get; set; }
[StringLength(10)]
public string EmailSendMethod { get; set; }
}
}
Service: ControlTableService.cs
using Dapper;
using Microsoft.Data.SqlClient;
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;
namespace BlazorPurchaseOrders.Data
{
public class ControlTableService : IControlTableService
{
// Database connection
private readonly SqlConnectionConfiguration _configuration;
public ControlTableService(SqlConnectionConfiguration configuration)
{
_configuration = configuration;
}
// Get the top(1) record from controltable (SQL Select)
public async Task<ControlTable> ControlTable_GetOne()
{
ControlTable controltable = new ();
using (var conn = new SqlConnection(_configuration.Value))
{
controltable = await conn.QueryFirstOrDefaultAsync<ControlTable>("spControlTable_GetOne", commandType: CommandType.StoredProcedure);
}
return controltable;
}
// Update one ControlTable row based on its ControlTableID (SQL Update)
public async Task<bool> ControlTableUpdate(ControlTable controltable)
{
using (var conn = new SqlConnection(_configuration.Value))
{
var parameters = new DynamicParameters();
parameters.Add("ControlTableID", controltable.ControlTableID, DbType.Int32);
parameters.Add("SmtpHost", controltable.SmtpHost, DbType.String);
parameters.Add("SmtpPort", controltable.SmtpPort, DbType.Int32);
parameters.Add("SmtpUserFriendlyName", controltable.SmtpUserFriendlyName, DbType.String);
parameters.Add("SmtpUserEmailAddress", controltable.SmtpUserEmailAddress, DbType.String);
parameters.Add("SmtpPass", controltable.SmtpPass, DbType.String);
parameters.Add("SENDGRID_APIKEY", controltable.SENDGRID_APIKEY, DbType.String);
parameters.Add("SenderEmail", controltable.SenderEmail, DbType.String);
parameters.Add("SenderEmailFriendlyName", controltable.SenderEmailFriendlyName, DbType.String);
parameters.Add("DefaultTaxID", controltable.DefaultTaxID, DbType.Int32);
parameters.Add("EmailSendMethod", controltable.EmailSendMethod, DbType.String);
await conn.ExecuteAsync("spControlTable_Update", parameters, commandType: CommandType.StoredProcedure);
}
return true;
}
}
}
Interface: IControlTableService.cs
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace BlazorPurchaseOrders.Data
{
// Each item below provides an interface to a method in ControlTableServices.cs
public interface IControlTableService
{
Task<ControlTable> ControlTable_GetOne();
Task<bool> ControlTableUpdate(ControlTable controltable);
}
}
@page "/controltable"
@using BlazorPurchaseOrders.Data
@inject IControlTableService ControlTableService
@inject ITaxService TaxService
@inject NavigationManager NavigationManager
<h3>Control Table</h3>
<hr />
<EditForm Model="@controltable" OnValidSubmit="@controltableSave">
<DataAnnotationsValidator />
<h5>SMTP Email Settings</h5>
<div class=flex-container>
<SfTextBox Enabled="true" Placeholder="SMTP Host (e.g. smtp.live.com)"
FloatLabelType="@FloatLabelType.Always"
@bind-Value="controltable.SmtpHost"></SfTextBox>
<SfNumericTextBox Enabled="true" Placeholder="SMTP Port No. (e.g. 587)" Width="50"
Format="N0"
ShowSpinButton="false"
FloatLabelType="@FloatLabelType.Always"
@bind-Value="controltable.SmtpPort"></SfNumericTextBox>
</div>
<div class=flex-container>
<SfTextBox Enabled="true" Placeholder="SMTP Email - Friendly Name (e.g. John Smith)"
FloatLabelType="@FloatLabelType.Always"
@bind-Value="controltable.SmtpUserFriendlyName"></SfTextBox>
<ValidationMessage For="@(() => controltable.SmtpUserFriendlyName)" />
<SfTextBox Enabled="true" Placeholder="SMTP Email Address (e.g. johnsmith@outlook.com)"
FloatLabelType="@FloatLabelType.Always"
@bind-Value="controltable.SmtpUserEmailAddress"></SfTextBox>
<ValidationMessage For="@(() => controltable.SmtpUserEmailAddress)" />
<SfTextBox Enabled="true" Placeholder="SMTP Email Password"
Type="InputType.Password"
FloatLabelType="@FloatLabelType.Always"
@bind-Value="controltable.SmtpPass"></SfTextBox>
<ValidationMessage For="@(() => controltable.SmtpPass)" />
</div>
<br />
<h5>SendGrid Email Settings</h5>
<div class=flex-container>
<SfTextBox Enabled="true" Placeholder="SendGrid API Key"
Type="InputType.Password"
FloatLabelType="@FloatLabelType.Always"
@bind-Value="controltable.SENDGRID_APIKEY"></SfTextBox>
<ValidationMessage For="@(() => controltable.SENDGRID_APIKEY)" />
</div>
<div class=flex-container>
<SfTextBox Enabled="true" Placeholder="SendGrid Email - Friendly Name"
FloatLabelType="@FloatLabelType.Always"
@bind-Value="controltable.SenderEmailFriendlyName"></SfTextBox>
<ValidationMessage For="@(() => controltable.SenderEmailFriendlyName)" />
<SfTextBox Enabled="true" Placeholder="SendGrid Email Address"
FloatLabelType="@FloatLabelType.Always"
@bind-Value="controltable.SenderEmail"></SfTextBox>
<ValidationMessage For="@(() => controltable.SenderEmail)" />
</div>
<br />
<h5>Select Email Method</h5>
<div class=flex-container>
<SfDropDownList TValue="string" TItem="EmailMethod" Placeholder="Select an Email Method" DataSource="@LocalData" @bind-Value="controltable.EmailSendMethod">
<DropDownListFieldSettings Value="EmailItem" Text="EmailItem"></DropDownListFieldSettings>
</SfDropDownList>
</div>
<br />
<h5>Default Tax Rate</h5>
<div class=flex-container>
<SfDropDownList DataSource="@taxrate"
TItem="Tax"
TValue="int"
Text="TaxID"
@bind-Value="controltable.DefaultTaxID"
FloatLabelType="@FloatLabelType.Auto"
Placeholder="Select a Default Tax Rate"
Enabled="true">
<DropDownListFieldSettings Text="TaxDescription" Value="TaxID"></DropDownListFieldSettings>
</SfDropDownList>
</div>
<br />
<div class=flex-box>
<button type="submit" class="e-btn e-normal e-primary">Save</button>
<button type="button" class="e-btn e-normal" @onclick="@Cancel">Cancel</button>
</div>
</EditForm>
<WarningPage @ref="Warning" WarningHeaderMessage="@WarningHeaderMessage" WarningContentMessage="@WarningContentMessage" />
<style>
.flex-container {
display: flex;
flex-direction: row; /* Causes tab to move along row and then onto following row */
justify-content: space-evenly; /* Equal space left and right margin and between elements */
margin: 10px; /* This appears to be vertical margin between rows */
column-gap: 10px; /* The gap betwen columns */
}
.flex-box {
display: flex;
justify-content: flex-end;
margin: 10px;
column-gap:10px;
}
</style>
@code {
ControlTable controltable = new();
IEnumerable<Tax> taxrate;
WarningPage Warning;
string WarningHeaderMessage = "";
string WarningContentMessage = "";
public class EmailMethod
{
public string EmailItem { get; set; }
}
List<EmailMethod> LocalData = new List<EmailMethod> {
new EmailMethod() { EmailItem= "SMTP" },
new EmailMethod() { EmailItem= "SendGrid" },
};
protected override async Task OnInitializedAsync()
{
//Get the only record - should be ID=1
controltable = await ControlTableService.ControlTable_GetOne();
taxrate = await TaxService.TaxList();
}
protected async Task controltableSave()
{
bool Success = await ControlTableService.ControlTableUpdate(controltable);
if (Success == false)
{
WarningHeaderMessage = "Warning!";
WarningContentMessage = "Control record was not saved.";
Warning.OpenDialog();
return;
}
else
{
NavigationManager.NavigateTo("/");
}
}
//Executes if user clicks the Cancel button.
void Cancel()
{
NavigationManager.NavigateTo("/");
}
}
<div class="top-row pl-4 navbar navbar-dark">
<a class="navbar-brand" href="">Blazor Purchase Orders</a>
<button class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Purchase Orders
</NavLink>
</li>
<AuthorizeView Roles="Admin, Manager">
<li class="nav-item px-3">
<NavLink class="nav-link" href="supplier">
<span class="oi oi-list-rich" aria-hidden="true"></span> Suppliers
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="product">
<span class="oi oi-list-rich" aria-hidden="true"></span> Products
</NavLink>
</li>
</AuthorizeView>
<AuthorizeView Roles="Admin">
<li class="nav-item px-3">
<NavLink class="nav-link" href="tax">
<span class="oi oi-list-rich" aria-hidden="true"></span> Tax Rates
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="controltable">
<span class="oi oi-list-rich" aria-hidden="true"></span> Control Table
</NavLink>
</li>
</AuthorizeView>
</ul>
</div>
@code {
private bool collapseNavMenu = true;
private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}
@page "/purchaseorder/{POHeaderGuid:guid}"
@using BlazorPurchaseOrders.Data
@using System.Data
@inject NavigationManager NavigationManager
@inject ISupplierService SupplierService
@inject IPOHeaderService POHeaderService
@inject IPOLineService POLineService
@inject IProductService ProductService
@inject ITaxService TaxService
@inject IPurchaseOrderService PurchaseOrderService
@inject IControlTableService ControlTableService
@using Microsoft.AspNetCore.Components.Authorization
@inject AuthenticationStateProvider AuthenticationStateProvider
@using System
@using System.Collections.Generic
<h3>@pagetitle</h3>
<EditForm Model="@orderaddedit" OnValidSubmit="@OrderSave">
<DataAnnotationsValidator />
<div class="grid-container">
<div class="grid-child left-column">
<SfDropDownList DataSource="@supplier"
TItem="Supplier"
TValue="int"
Text="SupplierID"
@bind-Value="orderaddedit.POHeaderSupplierID"
FloatLabelType="@FloatLabelType.Auto"
Placeholder="Select a Supplier"
Enabled="@supplierEnabled">
<DropDownListFieldSettings Text="SupplierName" Value="SupplierID"></DropDownListFieldSettings>
<DropDownListEvents TItem="Supplier" TValue="int" ValueChange="OnChangeSupplier"></DropDownListEvents>
</SfDropDownList>
<SfTextBox Enabled="true" Placeholder="Address"
FloatLabelType="@FloatLabelType.Always"
@bind-Value="orderaddedit.POHeaderSupplierAddress1"></SfTextBox>
<ValidationMessage For="@(() => orderaddedit.POHeaderSupplierAddress1)" />
<SfTextBox Enabled="true" Placeholder=""
FloatLabelType="@FloatLabelType.Never"
@bind-Value="orderaddedit.POHeaderSupplierAddress2"></SfTextBox>
<ValidationMessage For="@(() => orderaddedit.POHeaderSupplierAddress2)" />
<SfTextBox Enabled="true" Placeholder=""
FloatLabelType="@FloatLabelType.Never"
@bind-Value="orderaddedit.POHeaderSupplierAddress3"></SfTextBox>
<ValidationMessage For="@(() => orderaddedit.POHeaderSupplierAddress3)" />
<SfTextBox Enabled="true" Placeholder="Post Code"
FloatLabelType="@FloatLabelType.Never"
@bind-Value="orderaddedit.POHeaderSupplierPostCode"></SfTextBox>
<ValidationMessage For="@(() => orderaddedit.POHeaderSupplierPostCode)" />
<SfTextBox Enabled="true" Placeholder="Email"
FloatLabelType="@FloatLabelType.Auto"
@bind-Value="orderaddedit.POHeaderSupplierEmail"></SfTextBox>
<ValidationMessage For="@(() => orderaddedit.POHeaderSupplierEmail)" />
</div>
<div class="grid-child right-column">
<SfNumericTextBox Enabled="false" Placeholder="Order No"
FloatLabelType="@FloatLabelType.Always"
ShowSpinButton="false"
@bind-Value="orderaddedit.POHeaderOrderNumber"></SfNumericTextBox>
<SfDatePicker TValue="DateTime"
Placeholder='Order Date'
FloatLabelType="@FloatLabelType.Auto"
@bind-Value="orderaddedit.POHeaderOrderDate"></SfDatePicker>
<SfTextBox Enabled="false" Placeholder="Requested by"
FloatLabelType="@FloatLabelType.Always"
@bind-Value="orderaddedit.POHeaderRequestedBy"></SfTextBox>
</div>
</div>
<br />
<SfGrid @ref="OrderLinesGrid"
DataSource="@orderLines"
Toolbar="@Toolbaritems"
AllowResizing="true">
<GridColumns>
<GridColumn Field="@nameof(POLine.POLineProductCode)"
HeaderText="Product"
TextAlign="@TextAlign.Left"
Width="20">
</GridColumn>
<GridColumn Field="@nameof(POLine.POLineProductDescription)"
HeaderText="Description"
TextAlign="@TextAlign.Left"
Width="30">
</GridColumn>
<GridColumn Field="@nameof(POLine.POLineProductQuantity)"
HeaderText="Quantity"
TextAlign="@TextAlign.Right"
Format="n0"
Width="10">
</GridColumn>
<GridColumn Field="@nameof(POLine.POLineProductUnitPrice)"
HeaderText="Unit Price"
TextAlign="@TextAlign.Right"
Format="C2"
Width="10">
</GridColumn>
<GridColumn Field="@nameof(POLine.POLineNetPrice)"
HeaderText="Net Price"
TextAlign="@TextAlign.Right"
Format="C2"
Width="10">
</GridColumn>
<GridColumn Field="@nameof(POLine.POLineTaxRate)"
HeaderText="Tax Rate"
TextAlign="@TextAlign.Right"
Format="p2"
Width="10">
</GridColumn>
<GridColumn Field="@nameof(POLine.POLineTaxAmount)"
HeaderText="Tax"
TextAlign="@TextAlign.Right"
Format="C2"
Width="10">
</GridColumn>
<GridColumn Field="@nameof(POLine.POLineGrossPrice)"
HeaderText="Total"
TextAlign="@TextAlign.Right"
Format="C2"
Width="10">
</GridColumn>
</GridColumns>
<GridAggregates>
<GridAggregate>
<GridAggregateColumns>
<GridAggregateColumn Field=@nameof(POLine.POLineNetPrice) Type="AggregateType.Sum" Format="C2">
<FooterTemplate Context="NetContext">
@{
var aggregate = NetContext as AggregateTemplateContext;
<div>
<p>@aggregate.Sum</p>
</div>
}
</FooterTemplate>
</GridAggregateColumn>
<GridAggregateColumn Field=@nameof(POLine.POLineTaxAmount) Type="AggregateType.Sum" Format="C2">
<FooterTemplate Context="TaxContext">
@{
var aggregate = TaxContext as AggregateTemplateContext;
<div>
<p>@aggregate.Sum</p>
</div>
}
</FooterTemplate>
</GridAggregateColumn>
<GridAggregateColumn Field=@nameof(POLine.POLineGrossPrice) Type="AggregateType.Sum" Format="C2">
<FooterTemplate Context="GrossContext">
@{
var aggregate = GrossContext as AggregateTemplateContext;
<div>
<p>@aggregate.Sum</p>
</div>
}
</FooterTemplate>
</GridAggregateColumn>
</GridAggregateColumns>
</GridAggregate>
</GridAggregates>
<GridEvents RowSelected="RowSelectHandler" OnToolbarClick="ToolbarClickHandler" TValue="POLine"></GridEvents>
</SfGrid>
<br />
<div class="e-footer-content" style="text-align: right; width: 100%;">
<div class="button-container">
<button type="submit" class="e-btn e-normal e-primary">Save</button>
<button type="button" class="e-btn e-normal" @onclick="@Cancel">Cancel</button>
</div>
</div>
</EditForm>
<SfDialog @ref="DialogAddEditOrderLine" IsModal="true" Width="600px" ShowCloseIcon="true" Visible="false">
<DialogTemplates>
<Header> @dialogHeaderText </Header>
</DialogTemplates>
<EditForm Model="@addeditOrderLine" OnValidSubmit="@OrderLineSave">
<DataAnnotationsValidator />
<div class=flex-container>
<SfDropDownList DataSource="@product"
TItem="Product"
TValue="int"
Text="ProductID"
@bind-Value="addeditOrderLine.POLineProductID"
FloatLabelType="@FloatLabelType.Always"
Placeholder="Select a Product"
Enabled="true">
<DropDownListTemplates TItem="Product">
<HeaderTemplate>
<span class='head'>
<span class='productcode'>Code</span>
<span class='description'>Description</span>
</span>
</HeaderTemplate>
<ItemTemplate Context="contextName">
<span class='item'>
<span class='productcode'>@((contextName as Product).ProductCode)</span>
<span class='description'>@((contextName as Product).ProductDescription)</span>
</span>
</ItemTemplate>
</DropDownListTemplates>
<DropDownListFieldSettings Text="ProductCode" Value="ProductID"></DropDownListFieldSettings>
<DropDownListEvents TItem="Product" TValue="int" OnValueSelect="OnChangeProduct"></DropDownListEvents>
</SfDropDownList>
</div>
<div class=flex-container>
<SfTextBox Enabled="true" Placeholder="Product Description"
FloatLabelType="@FloatLabelType.Always"
@bind-Value="addeditOrderLine.POLineProductDescription"></SfTextBox>
</div>
<div class=flex-container>
<ValidationMessage For="@(() => addeditOrderLine.POLineProductDescription)" />
</div>
<div class=flex-container>
<SfNumericTextBox Enabled="true" Placeholder="Quantity"
FloatLabelType="@FloatLabelType.Always"
ShowSpinButton="false"
Format="n0"
Decimals="0"
ValidateDecimalOnType="true"
CssClass="e-style"
@bind-Value="addeditOrderLine.POLineProductQuantity"
@onfocusout='@POLineCalc'>
</SfNumericTextBox>
<SfNumericTextBox Enabled="true" Placeholder="Unit Price"
FloatLabelType="@FloatLabelType.Always"
ShowSpinButton="false"
Format="c2"
Decimals="2"
ValidateDecimalOnType="true"
CssClass="e-style"
@bind-Value="addeditOrderLine.POLineProductUnitPrice"
@onfocusout='@POLineCalc'>
</SfNumericTextBox>
<SfNumericTextBox Enabled="false" Placeholder="Net Price"
FloatLabelType="@FloatLabelType.Always"
ShowSpinButton="false"
Format="c2"
EnableRtl="true"
@bind-Value="addeditOrderLine.POLineNetPrice">
</SfNumericTextBox>
</div>
<div class=flex-container>
<SfDropDownList DataSource="@tax"
TItem="Tax"
TValue="int"
Text="TaxID"
@bind-Value="addeditOrderLine.POLineTaxID"
FloatLabelType="@FloatLabelType.Always"
Placeholder="Tax Rate"
Enabled="true">
<DropDownListFieldSettings Text="TaxDescription" Value="TaxID"></DropDownListFieldSettings>
<DropDownListEvents TItem="Tax" TValue="int" OnValueSelect="OnChangeTax"></DropDownListEvents>
</SfDropDownList>
<SfNumericTextBox Enabled="false" Placeholder="Tax Rate %"
FloatLabelType="@FloatLabelType.Always"
ShowSpinButton="false"
Format="p2"
EnableRtl="true"
@bind-Value="addeditOrderLine.POLineTaxRate">
</SfNumericTextBox>
<SfNumericTextBox Enabled="false" Placeholder="Tax Amount"
FloatLabelType="@FloatLabelType.Always"
ShowSpinButton="false"
Format="c2"
EnableRtl="true"
@bind-Value="addeditOrderLine.POLineTaxAmount">
</SfNumericTextBox>
</div>
<div class=flex-container>
<SfNumericTextBox Enabled="false" Placeholder="Total Price"
FloatLabelType="@FloatLabelType.Always"
ShowSpinButton="false"
Format="c2"
EnableRtl="true"
@bind-Value="addeditOrderLine.POLineGrossPrice">
</SfNumericTextBox>
</div>
<br />
<div class="e-footer-content">
<div class="button-container">
<button type="submit" class="e-btn e-normal e-primary">Save</button>
<button type="button" class="e-btn e-normal" @onclick="@CloseDialog">Cancel</button>
</div>
</div>
</EditForm>
</SfDialog>
<ConfirmPage @ref="ConfirmDeletion" ConfirmHeaderMessage="@ConfirmHeaderMessage" ConfirmContentMessage="@ConfirmContentMessage" ConfirmationChanged="ConfirmDelete" />
<WarningPage @ref="Warning" WarningHeaderMessage="@WarningHeaderMessage" WarningContentMessage="@WarningContentMessage" />
<ConfirmPage @ref="ConfirmSaveOrder" ConfirmHeaderMessage="@ConfirmHeaderMessage" ConfirmContentMessage="@ConfirmContentMessage" ConfirmationChanged="OrderSaveProcess" />
<style>
.grid-container {
display: grid;
max-width: 900px; /* Maximum width of the whole container - in this case both columns */
grid-template-columns: 1fr 1fr; /* Relative width of each column (1fr 1fr is equivalent to, say, 33fr 33fr */
grid-gap: 75px; /* size of the gap between columns */
}
.flex-container {
display: flex;
flex-direction: row; /* Causes tab to move along row and then onto following row */
justify-content: space-evenly; /* Equal space left and right margin and between elements */
margin: 10px; /* This appears to be vertical margin between rows */
column-gap: 10px; /* Tgap betwen columns */
}
.e-numeric.e-style .e-control.e-numerictextbox {
text-align: right;
padding: 0px 5px 0px 0px;
}
.head, .item {
display: table;
width: 100%;
margin: auto;
text-align: left;
}
.head {
height: 30px;
font-size: 15px;
font-weight: 600;
}
.productcode {
display: table-cell;
vertical-align: middle;
text-align: left;
width: 25%;
}
.description {
display: table-cell;
vertical-align: middle;
text-align: left;
width: 75%;
}
.head .productcode {
text-indent: 17px;
}
.head .description {
text-indent: 14px;
}
</style>
@code {
POHeader orderaddedit = new POHeader();
IEnumerable<Supplier> supplier;
IEnumerable<Product> product;
IEnumerable<Tax> tax;
IEnumerable<POLine> orderLinesByPOHeader;
string pagetitle = "";
string dialogHeaderText = "";
private string UserName;
SfGrid<POLine> OrderLinesGrid;
public List<POLine> orderLines = new List<POLine>();
private List<ItemModel> Toolbaritems = new List<ItemModel>();
private List<int> OrderLinesToBeDeleted = new List<int>();
SfDialog DialogAddEditOrderLine;
public POLine addeditOrderLine = new POLine();
WarningPage Warning;
string WarningHeaderMessage = "";
string WarningContentMessage = "";
ConfirmPage ConfirmDeletion;
ConfirmPage ConfirmSaveOrder;
string ConfirmHeaderMessage = "";
string ConfirmContentMessage = "";
public bool ConfirmationChanged { get; set; } = false;
public bool supplierEnabled { get; set; } = true;
private int tmpPOLineID { get; set; } = 0;
private int selectedPOLineID { get; set; } = 0;
private int POHeaderID { get; set; } = 0;
ControlTable controltable = new(); //NEW
private int defaultTaxID { get; set; } = 0; //NEW
private decimal defaultTaxRate { get; set; } = 0; //NEW
[Parameter]
public Guid POHeaderGuid { get; set; }
//Executes on page open, sets headings and gets data in the case of edit
protected override async Task OnInitializedAsync()
{
supplier = await SupplierService.SupplierList();
orderaddedit.POHeaderOrderDate = DateTime.Now;
tax = await TaxService.TaxList();
controltable = await ControlTableService.ControlTable_GetOne(); //NEW
defaultTaxID = controltable.DefaultTaxID; //NEW
defaultTaxRate = (decimal) tax.FirstOrDefault(x => x.TaxID == defaultTaxID).TaxRate; //NEW
if (POHeaderGuid == Guid.Empty)
{
pagetitle = "Add an Order";
//Get user if logged in and populate the 'Requested by' column
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
if (user.Identity.IsAuthenticated)
{
UserName = user.Identity.Name;
}
else
{
UserName = "The user is NOT authenticated.";
}
orderaddedit.POHeaderRequestedBy = UserName;
}
else
{
pagetitle = "Edit an Order";
orderaddedit = await POHeaderService.POHeader_GetOneByGuid(POHeaderGuid);
POHeaderID = orderaddedit.POHeaderID;
orderLinesByPOHeader = await POLineService.POLine_GetByPOHeader(POHeaderID);
orderLines = orderLinesByPOHeader.ToList(); //Convert from IEnumable to List
supplierEnabled = false;
}
Toolbaritems.Add(new ItemModel() { Text = "Add", TooltipText = "Add a new order line", PrefixIcon = "e-add" });
Toolbaritems.Add(new ItemModel() { Text = "Edit", TooltipText = "Edit selected order line", PrefixIcon = "e-edit" });
Toolbaritems.Add(new ItemModel() { Text = "Delete", TooltipText = "Delete selected order line", PrefixIcon = "e-delete" });
}
private void OnChangeSupplier(Syncfusion.Blazor.DropDowns.ChangeEventArgs<int, Supplier> args)
{
this.orderaddedit.POHeaderSupplierAddress1 = args.ItemData.SupplierAddress1;
this.orderaddedit.POHeaderSupplierAddress2 = args.ItemData.SupplierAddress2;
this.orderaddedit.POHeaderSupplierAddress3 = args.ItemData.SupplierAddress3;
this.orderaddedit.POHeaderSupplierPostCode = args.ItemData.SupplierPostCode;
this.orderaddedit.POHeaderSupplierEmail = args.ItemData.SupplierEmail;
}
// Executes OnValidSubmit of EditForm above
protected async Task OrderSave()
{
if (orderaddedit.POHeaderSupplierID == 0)
{
WarningHeaderMessage = "Warning!";
WarningContentMessage = "Please Select a Supplier before saving the order.";
Warning.OpenDialog();
return;
}
if (orderLines.Count == 0)
{
ConfirmHeaderMessage = "Confirm Save!";
ConfirmContentMessage = "There are no order lines. Please confirm order should be saved.";
ConfirmSaveOrder.OpenDialog();
}
else
{
await OrderSaveProcess(true);
}
}
//Executes if user clicks the Cancel button.
void Cancel()
{
NavigationManager.NavigateTo("/");
}
public async Task ToolbarClickHandler(Syncfusion.Blazor.Navigations.ClickEventArgs args)
{
//Refresh product to select only those products for this supplier (and products with null suppliers)
product = await ProductService.ProductListBySupplier(orderaddedit.POHeaderSupplierID);
if (args.Item.Text == "Add")
{
//Code for adding goes here
dialogHeaderText = "Add an Order Line";
//Check that a supplier has been selected from the drop-down list
if (orderaddedit.POHeaderSupplierID == 0)
{
WarningHeaderMessage = "Warning!";
WarningContentMessage = "Please Select a Supplier before adding order lines.";
Warning.OpenDialog();
}
else
{
addeditOrderLine = new POLine(); // Ensures a blank form when adding
addeditOrderLine.POLineNetPrice = 0;
addeditOrderLine.POLineTaxID = defaultTaxID; //NEW
addeditOrderLine.POLineTaxRate = defaultTaxRate; //NEW
addeditOrderLine.POLineProductID = 0;
await this.DialogAddEditOrderLine.Show();
}
}
if (args.Item.Text == "Edit")
{
dialogHeaderText = "Edit an Order Line";
//Check that an order line has been selected
if (selectedPOLineID == 0)
{
WarningHeaderMessage = "Warning!";
WarningContentMessage = "Please select an Order Line from the grid.";
Warning.OpenDialog();
}
else
{
//populate addeditOrderLine (temporary data set used for the editing process)
addeditOrderLine = orderLines.Where(x => x.POLineID == selectedPOLineID).FirstOrDefault();
StateHasChanged();
await this.DialogAddEditOrderLine.Show();
}
}
if (args.Item.Text == "Delete")
{
if (selectedPOLineID == 0)
{
WarningHeaderMessage = "Warning!";
WarningContentMessage = "Please select an Order Line from the grid.";
Warning.OpenDialog();
}
else
{
ConfirmHeaderMessage = "Confirm Deletion";
ConfirmContentMessage = "Please confirm that this order line should be deleted.";
ConfirmDeletion.OpenDialog();
}
}
}
private async Task OrderLineSave()
{
if (addeditOrderLine.POLineID == 0)
{
//Check that a product has been selected from the drop-down list
if (addeditOrderLine.POLineProductCode == null || addeditOrderLine.POLineProductCode == "")
{
WarningHeaderMessage = "Warning!";
WarningContentMessage = "Please select a Product.";
Warning.OpenDialog();
}
//And check that a tax rate has been selected from the drop-down list
else if (addeditOrderLine.POLineTaxID == 0)
{
WarningHeaderMessage = "Warning!";
WarningContentMessage = "Please select a Tax Rate.";
Warning.OpenDialog();
}
else
{
POLineCalc();
tmpPOLineID = tmpPOLineID - 1; //used to provide a temporary ID for POLineID
orderLines.Add(new POLine
{
POLineID = tmpPOLineID,
POLineHeaderID = 0,
POLineProductID = addeditOrderLine.POLineProductID,
POLineProductCode = addeditOrderLine.POLineProductCode,
POLineProductDescription = addeditOrderLine.POLineProductDescription,
POLineProductQuantity = addeditOrderLine.POLineProductQuantity,
POLineProductUnitPrice = addeditOrderLine.POLineProductUnitPrice,
POLineNetPrice = addeditOrderLine.POLineNetPrice,
POLineTaxRate = addeditOrderLine.POLineTaxRate,
POLineTaxAmount = addeditOrderLine.POLineTaxAmount,
POLineGrossPrice = addeditOrderLine.POLineGrossPrice,
POLineTaxID = addeditOrderLine.POLineTaxID
});
OrderLinesGrid.Refresh();
StateHasChanged(); //<----- THIS IS ABSOLUTELY ESSENTIAL
//addeditOrderLine = new POLine(); //<----- THIS gives errors (nulls)
addeditOrderLine.POLineProductID = 0;
addeditOrderLine.POLineProductCode = "";
addeditOrderLine.POLineProductDescription = "";
addeditOrderLine.POLineProductQuantity = 0;
addeditOrderLine.POLineProductUnitPrice = 0;
addeditOrderLine.POLineNetPrice = 0;
addeditOrderLine.POLineTaxAmount = 0;
addeditOrderLine.POLineGrossPrice = 0;
//We now have order lines, so prevent user from changing the supplier
supplierEnabled = false;
}
}
else
//An order line is being edited
//Check that a product has been selected from the drop-down list
if (addeditOrderLine.POLineProductCode == null || addeditOrderLine.POLineProductCode == "")
{
WarningHeaderMessage = "Warning!";
WarningContentMessage = "Please select a Product.";
Warning.OpenDialog();
}
//And check that a tax rate has been selected from the drop-down list
else if (addeditOrderLine.POLineTaxID == 0)
{
WarningHeaderMessage = "Warning!";
WarningContentMessage = "Please select a Tax Rate.";
Warning.OpenDialog();
}
else
{
OrderLinesGrid.Refresh();
StateHasChanged(); //<----- THIS IS ABSOLUTELY ESSENTIAL
await CloseDialog(); //No need to keep dialog open
}
}
private async Task CloseDialog()
{
await this.DialogAddEditOrderLine.Hide();
}
private void OnChangeProduct(Syncfusion.Blazor.DropDowns.SelectEventArgs<Product> args)
{
this.addeditOrderLine.POLineProductCode = args.ItemData.ProductCode;
this.addeditOrderLine.POLineProductDescription = args.ItemData.ProductDescription;
this.addeditOrderLine.POLineProductUnitPrice = args.ItemData.ProductUnitPrice;
POLineCalc();
}
private void OnChangeTax(Syncfusion.Blazor.DropDowns.SelectEventArgs<Tax> args)
{
// int testTaxId = args.ItemData.TaxID;
this.addeditOrderLine.POLineTaxRate = args.ItemData.TaxRate;
POLineCalc();
}
private void POLineCalc()
{
addeditOrderLine.POLineNetPrice = addeditOrderLine.POLineProductUnitPrice * addeditOrderLine.POLineProductQuantity;
addeditOrderLine.POLineTaxAmount = addeditOrderLine.POLineNetPrice.Value * addeditOrderLine.POLineTaxRate;
addeditOrderLine.POLineGrossPrice = addeditOrderLine.POLineNetPrice.Value * (1 + addeditOrderLine.POLineTaxRate);
}
public void RowSelectHandler(RowSelectEventArgs<POLine> args)
{
//{args.Data} returns the current selected records.
selectedPOLineID = args.Data.POLineID;
}
private void OrderLineDelete()
{
if (selectedPOLineID > 0)
{
//Order line has already been saved to the database, and was present at start of this order edit
//Add to list of orders to be deleted when order is saved
OrderLinesToBeDeleted.Add(selectedPOLineID);
}
//And then delete from orderLines
var itemToRemove = orderLines.Single(x => x.POLineID == selectedPOLineID);
orderLines.Remove(itemToRemove);
OrderLinesGrid.Refresh();
}
protected void ConfirmDelete(bool deleteConfirmed)
{
if (deleteConfirmed)
{
OrderLineDelete();
StateHasChanged();
selectedPOLineID = 0;
}
}
protected async Task OrderSaveProcess(bool saveConfirmed)
{
if (saveConfirmed)
{
if (POHeaderID == 0) //Save a new purchase order
{
DataTable orderLinesDataTable = new DataTable();
orderLinesDataTable.Columns.Add("POLineHeaderID", typeof(Int32));
orderLinesDataTable.Columns.Add("POLineProductID", typeof(Int32));
orderLinesDataTable.Columns.Add("POLineProductDescription", typeof(string));
orderLinesDataTable.Columns.Add("POLineProductQuantity", typeof(decimal));
orderLinesDataTable.Columns.Add("POLineProductUnitPrice", typeof(decimal));
orderLinesDataTable.Columns.Add("POLineTaxRate", typeof(decimal));
orderLinesDataTable.Columns.Add("POLineTaxID", typeof(decimal));
foreach (var individualPOLine in orderLines)
{
DataRow newRow = orderLinesDataTable.NewRow();
// Set values in the columns:
//newRow["POLineHeaderID"] = HeaderID;
newRow["POLineProductID"] = individualPOLine.POLineProductID;
newRow["POLineProductDescription"] = individualPOLine.POLineProductDescription;
newRow["POLineProductQuantity"] = individualPOLine.POLineProductQuantity;
newRow["POLineProductUnitPrice"] = individualPOLine.POLineProductUnitPrice;
newRow["POLineTaxRate"] = individualPOLine.POLineTaxRate;
newRow["POLineTaxID"] = individualPOLine.POLineTaxID;
// Add the row to the rows collection.
orderLinesDataTable.Rows.Add(newRow);
}
bool Success = await PurchaseOrderService.POInsert(
orderaddedit.POHeaderOrderDate,
orderaddedit.POHeaderSupplierID,
orderaddedit.POHeaderSupplierAddress1,
orderaddedit.POHeaderSupplierAddress2,
orderaddedit.POHeaderSupplierAddress3,
orderaddedit.POHeaderSupplierPostCode,
orderaddedit.POHeaderSupplierEmail,
orderaddedit.POHeaderRequestedBy,
orderLinesDataTable);
if (Success == false)
{
WarningHeaderMessage = "Warning!";
WarningContentMessage = "Order was not saved.";
Warning.OpenDialog();
return;
}
orderLinesDataTable.Clear();
NavigationManager.NavigateTo("/");
}
else
{
//Order is being edited
//POHeader
bool Success = await POHeaderService.POHeaderUpdate(orderaddedit);
//POLines
foreach (var individualPOLine in orderLines)
{
//If POLineHeaderID is positive it means it has been edited during the edit of this order
if (individualPOLine.POLineID > 0)
{
Success = await POLineService.POLineUpdate(individualPOLine);
}
else
//If POLineID is negative it means it has been added during the edit of this order
{
individualPOLine.POLineHeaderID = POHeaderID;
Success = await POLineService.POLineInsert(individualPOLine);
}
}
foreach (var individualPOLine in OrderLinesToBeDeleted)
{
Success = await POLineService.POLineDeleteOne(individualPOLine);
}
//Clear the list of POLines to be deleted
OrderLinesToBeDeleted.Clear();
NavigationManager.NavigateTo("/");
}
}
}
}
EmailSenderMailKit.cs
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;
using MimeKit.Text;
namespace BlazorPurchaseOrders.Data
{
public interface IEmailService
{
bool Send(string SmtpHost,
int SmtpPort,
string SmtpUserFriendlyName,
string SmtpUserEmailAddress,
string SmtpPass,
string to,
string subject,
string body);
}
public class EmailService : IEmailService
{
public bool Send(string SmtpHost,
int SmtpPort,
string SmtpUserFriendlyName,
string SmtpUserEmailAddress,
string SmtpPass,
string to,
string subject,
string body)
{
// create message
var email = new MimeMessage();
email.From.Add(new MailboxAddress(SmtpUserFriendlyName, SmtpUserEmailAddress));
email.To.Add(new MailboxAddress(to, to));
email.Subject = subject;
email.Body = new TextPart(TextFormat.Html) { Text = body };
try
{
// send email
using var smtp = new SmtpClient();
smtp.Connect(SmtpHost, SmtpPort, SecureSocketOptions.StartTls);
smtp.Authenticate(SmtpUserEmailAddress, SmtpPass);
smtp.Send(email);
smtp.Disconnect(true);
return true;
}
catch
{
return false;
}
}
}
}
EmailSenderSendGrid.cs
using System.Threading.Tasks;
using SendGrid;
using SendGrid.Helpers.Mail;
namespace BlazorPurchaseOrders.Data
{
public interface IEmailSender
{
Task<bool> SendEmail(string apiKey,
string FromFriendlyName,
string FromEmailAddress,
string toEmail,
string subject,
string body);
}
public class EmailSender : IEmailSender
{
public async Task<bool> SendEmail(string apiKey,
string FromFriendlyName,
string FromEmailAddress,
string toEmail,
string subject,
string body)
{
var client = new SendGridClient(apiKey);
var from = new EmailAddress(FromEmailAddress, FromFriendlyName);
var to = new EmailAddress(toEmail, toEmail);
var plainTextContent = body;
var htmlContent = body;
var msg = MailHelper.CreateSingleEmail(from, to, subject, plainTextContent, htmlContent);
var response = await client.SendEmailAsync(msg).ConfigureAwait(false);
return response.IsSuccessStatusCode;
}
}
}
Index.razor
@page "/"
@using BlazorPurchaseOrders.Data
@using Microsoft.Extensions.Configuration
@inject IPOHeaderService POHeaderService
@inject NavigationManager NavigationManager
@inject IJSRuntime IJS
@inject IEmailService EmailService
@inject IEmailSender EmailSender
@inject IConfiguration _config
@inject IControlTableService ControlTableService
@attribute [Microsoft.AspNetCore.Authorization.AllowAnonymous]
@inject AuthenticationStateProvider AuthenticationStateProvider
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css">
</head>
<div class="col-sm-12">
<AuthorizeView>
<NotAuthorized>
<h5>Please Log in</h5>
<h6>or</h6>
<h5>Register to use Blazor Purchase Orders</h5>
</NotAuthorized>
<Authorized>
<h3>Purchase Orders</h3>
<br />
<SfGrid DataSource="@poheader"
Toolbar="Toolbaritems">
<GridEvents RowSelected="RowSelectHandler" OnToolbarClick="ToolbarClickHandler" TValue="POHeader"></GridEvents>
<GridColumns>
<GridColumn Field="@nameof(POHeader.POHeaderOrderNumber)"
HeaderText="No"
TextAlign="@TextAlign.Left"
Width="10">
</GridColumn>
<GridColumn Field="@nameof(POHeader.POHeaderOrderDate)"
HeaderText="Date"
Format="d"
Type="ColumnType.Date"
TextAlign="@TextAlign.Center"
Width="15">
</GridColumn>
<GridColumn Field="@nameof(POHeader.SupplierName)"
HeaderText="Supplier"
TextAlign="@TextAlign.Left"
Width="40">
</GridColumn>
<GridColumn Field="@nameof(POHeader.TotalOrderValue)"
HeaderText="Value"
TextAlign="@TextAlign.Right"
Format="C2"
Width="20">
</GridColumn>
<GridColumn Field="@nameof(POHeader.POHeaderRequestedBy)"
HeaderText="Requested by"
TextAlign="@TextAlign.Left"
Width="40">
</GridColumn>
</GridColumns>
</SfGrid>
<WarningPage @ref="Warning" WarningHeaderMessage="@WarningHeaderMessage" WarningContentMessage="@WarningContentMessage" />
<ConfirmPage @ref="ConfirmOrderDelete" ConfirmHeaderMessage="@ConfirmHeaderMessage" ConfirmContentMessage="@ConfirmContentMessage" ConfirmationChanged="ConfirmOrderArchive" />
</Authorized>
</AuthorizeView>
</div>
<div class="col-lg-12 control-section toast-default-section">
<SfToast @ref="ToastObj" Timeout="4000">
<ToastPosition X="Right" Y="Top"></ToastPosition>
</SfToast>
</div>
<style>
@@font-face {
font-family: 'Toast_icons';
src: url(data:application/x-font-ttf;charset=utf-8;base64,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) format('truetype');
font-weight: normal;
font-style: normal;
}
#toast_types button {
margin: 5px;
min-width: 160px;
max-width: 160px;
}
.toast-icons {
font-family: 'Toast_icons' !important;
speak: none;
font-size: 55px;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
#toast_type .e-toast-icon.e-icons {
height: auto;
font-size: 30px;
}
.toast-icons.e-success::before {
content: "\e701";
}
.toast-icons.e-error::before {
content: "\e700";
}
.toast-icons.e-info::before {
content: "\e704";
}
.toast-icons.e-warning::before {
content: "\e703";
}
#toast_types {
text-align: center;
}
</style>
@code {
// Create an empty list, named poheader, of empty POHeader objects.
IEnumerable<POHeader> poheader;
POHeader orderHeader = new POHeader();
private List<ItemModel> Toolbaritems = new List<ItemModel>();
//int POHeaderID = 0;
private int selectedPOHeaderID { get; set; } = 0;
private Guid selectedPOHeaderGuid { get; set; }
private string selectedPOHeaderSupplierEmail { get; set; } = "";
private int selectedPOHeaderOrderNumber { get; set; } = 0;
WarningPage Warning;
string WarningHeaderMessage = "";
string WarningContentMessage = "";
ConfirmPage ConfirmOrderDelete;
string ConfirmHeaderMessage = "";
string ConfirmContentMessage = "";
public bool ConfirmationChanged { get; set; } = false;
private string emailbody = "";
[CascadingParameter]
private Task<AuthenticationState> authenticationStateTask { get; set; }
SfToast ToastObj;
string ServerLocation = "";
ControlTable controltable = new(); //NEW
private string SmtpHost { get; set; } = ""; //NEW
private int SmtpPort { get; set; } = 0; //NEW
private string SmtpUserFriendlyName { get; set; } = ""; //NEW
private string SmtpUserEmailAddress { get; set; } = ""; //NEW
private string SmtpPass { get; set; } = ""; //NEW
private string SENDGRID_APIKEY { get; set; } = ""; //NEW
private string SenderEmail { get; set; } = ""; //NEW
private string SenderEmailFriendlyName { get; set; } = ""; //NEW
private string EmailSendMethod { get; set; } = ""; //NEW
private List<ToastModel> Toast = new List<ToastModel>
{
new ToastModel{ Title = "Warning!", Content="There was a problem with your network connection.", CssClass="e-toast-warning", Icon="e-warning toast-icons" },
new ToastModel{ Title = "Success!", Content="Your message has been sent successfully.", CssClass="e-toast-success", Icon="e-success toast-icons" },
new ToastModel{ Title = "Error!", Content="A problem has been occurred while submitting your email", CssClass="e-toast-danger", Icon="e-error toast-icons" },
new ToastModel{ Title = "Information!", Content="Please read the comments carefully.", CssClass="e-toast-info", Icon="e-info toast-icons" }
};
protected override async Task OnInitializedAsync()
{
//Populate the list of orders objects from the Purchase Order Header table.
await GetOrderList();
Toolbaritems.Add(new ItemModel() { Text = "Add", TooltipText = "Add a new order", PrefixIcon = "e-add" });
Toolbaritems.Add(new ItemModel() { Text = "Edit", TooltipText = "Edit selected order", PrefixIcon = "e-edit" });
Toolbaritems.Add(new ItemModel() { Text = "Delete", TooltipText = "Delete selected order", PrefixIcon = "e-delete" });
Toolbaritems.Add(new ItemModel() { Text = "Preview", TooltipText = "Preview selected order", PrefixIcon = "e-print" });
Toolbaritems.Add(new ItemModel() { Text = "Email", TooltipText = "Email selected order", PrefixIcon = "bi-envelope" });
ServerLocation = _config.GetValue<string>("ApplicationSettings:PreviewOrderLinkRoute");
controltable = await ControlTableService.ControlTable_GetOne(); //NEW
SmtpHost = controltable.SmtpHost; //NEW
SmtpPort = controltable.SmtpPort; //NEW
SmtpUserFriendlyName = controltable.SmtpUserFriendlyName; //NEW
SmtpUserEmailAddress = controltable.SmtpUserEmailAddress; //NEW
SmtpPass = controltable.SmtpPass; //NEW
SENDGRID_APIKEY = controltable.SENDGRID_APIKEY; //NEW
SenderEmail = controltable.SenderEmail; //NEW
SenderEmailFriendlyName = controltable.SenderEmailFriendlyName; //NEW
EmailSendMethod = controltable.EmailSendMethod; //NEW
}
public async Task ToolbarClickHandler(Syncfusion.Blazor.Navigations.ClickEventArgs args)
{
if (args.Item.Text == "Add")
{
//Code for adding goes here
//POHeaderID = 0;
NavigationManager.NavigateTo($"/purchaseorder/{Guid.Empty}");
}
if (args.Item.Text == "Edit")
{
//Code for editing - Check that an Order has been selected from the grid
if (selectedPOHeaderID == 0)
{
WarningHeaderMessage = "Warning!";
WarningContentMessage = "Please select an Order from the grid.";
Warning.OpenDialog();
}
else
{
NavigationManager.NavigateTo($"/purchaseorder/{selectedPOHeaderGuid}");
}
}
if (args.Item.Text == "Delete")
{
//Code for deleting
if (selectedPOHeaderID == 0) //Check that an order has been selected
{
WarningHeaderMessage = "Warning!";
WarningContentMessage = "Please select an Order from the grid.";
Warning.OpenDialog();
}
else
{
//Populate orderHeader using selectedPOHeaderID
orderHeader = await POHeaderService.POHeader_GetOne(selectedPOHeaderID);
ConfirmHeaderMessage = "Confirm Deletion";
ConfirmContentMessage = "Please confirm that this order should be deleted.";
ConfirmOrderDelete.OpenDialog();
}
}
if (args.Item.Text == "Preview")
{
//Code for editing - Check that an Order has been selected from the grid
if (selectedPOHeaderID == 0)
{
WarningHeaderMessage = "Warning!";
WarningContentMessage = "Please select an Order from the grid.";
Warning.OpenDialog();
}
else
{
await IJS.InvokeAsync<object>("open", new object[] { "/previeworder/" + selectedPOHeaderGuid + "", "_blank" });
}
}
if (args.Item.Text == "Email")
{
//Code for editing - Check that an Order has been selected from the grid
if (selectedPOHeaderID == 0)
{
WarningHeaderMessage = "Warning!";
WarningContentMessage = "Please select an Order from the grid.";
Warning.OpenDialog();
return;
}
if(selectedPOHeaderSupplierEmail==null || selectedPOHeaderSupplierEmail == "")
{
WarningHeaderMessage = "Warning!";
WarningContentMessage = "Supplier Email Address is missing.";
Warning.OpenDialog();
return;
}
//Create body of email using html
emailbody = "<p>Dear Supplier</p>";
emailbody = emailbody + "<p>Please click one of the following links (or copy and paste the link into your browser) to view our ";
emailbody = emailbody + "order number " + selectedPOHeaderOrderNumber + " </p>";
//without the anchor tag
emailbody = emailbody + "<p>" + ServerLocation + "/previeworder/"
+ selectedPOHeaderGuid + "</p>";
//with the anchor tag
emailbody = emailbody + "<p><a href= "" + ServerLocation + "/previeworder/"
+ selectedPOHeaderGuid + "">" + ServerLocation + "/previeworder/"
+ selectedPOHeaderGuid + "<a></p>";
emailbody = emailbody + "<p>Kind regards</p>";
emailbody = emailbody + "<p>BlazorCode</p>";
bool emailSent = false;
switch (EmailSendMethod)
{
case "SMTP":
emailSent = EmailService.Send(SmtpHost,
SmtpPort,
SmtpUserFriendlyName,
SmtpUserEmailAddress,
SmtpPass,
selectedPOHeaderSupplierEmail,
"Purchase Order",
emailbody);
break;
case "SendGrid":
emailSent = await EmailSender.SendEmail(SENDGRID_APIKEY,
SenderEmailFriendlyName,
SenderEmail,
selectedPOHeaderSupplierEmail,
"Purchase Order",
emailbody);
break;
default:
WarningHeaderMessage = "Warning!";
WarningContentMessage = "No Email Method has been setup in Control Table.";
Warning.OpenDialog();
break;
}
if (emailSent)
{
await this.ToastObj.Show(Toast[1]);
}
else
{
await this.ToastObj.Show(Toast[2]);
}
}
}
public void RowSelectHandler(RowSelectEventArgs<POHeader> args)
{
//{args.Data} returns the current selected records.
selectedPOHeaderID = args.Data.POHeaderID;
selectedPOHeaderGuid = args.Data.POHeaderGuid;
selectedPOHeaderSupplierEmail = args.Data.POHeaderSupplierEmail;
selectedPOHeaderOrderNumber = args.Data.POHeaderOrderNumber;
}
protected async Task ConfirmOrderArchive(bool archiveConfirmed)
{
if (archiveConfirmed)
{
orderHeader.POHeaderIsArchived = true;
bool Success = await POHeaderService.POHeaderUpdate(orderHeader);
await GetOrderList();
StateHasChanged();
}
}
protected async Task GetOrderList()
{
var user = (await authenticationStateTask).User;
if (user.IsInRole("Admin") || user.IsInRole("Manager"))
{
poheader = await POHeaderService.POHeaderList(null); //leave user name blank
}
else
{
poheader = await POHeaderService.POHeaderList(user.Identity.Name); //pass user name
}
}
}