misc cosmetic changes

office customer list
office customer list added isHidden = 1 to custom page filter
office create order added salesRep name
draft - added property to use for enabling disabling submit button
B2B page work
This commit is contained in:
Frede Hundewadt 2023-12-06 12:53:24 +01:00
parent a03d23192a
commit eb518b3429
19 changed files with 302 additions and 126 deletions

View file

@ -44,6 +44,8 @@
} }
<div class="card-body mx-0"> <div class="card-body mx-0">
<table class="m-0 table table-sm table-striped table-light"> <table class="m-0 table table-sm table-striped table-light">
<thead></thead>
<tbody>
<tr> <tr>
<th scope="row" class="text-sm-start align-middle">Konto</th> <th scope="row" class="text-sm-start align-middle">Konto</th>
<td class="text-sm-start align-middle">@company.Account</td> <td class="text-sm-start align-middle">@company.Account</td>
@ -64,7 +66,7 @@
</tr> </tr>
<tr> <tr>
<th scope="row" class="text-sm-start align-middle"> <th scope="row" class="text-sm-start align-middle">
Sælgernr. Sælger Nr.
</th> </th>
<td colspan="3" class="text-sm-start align-middle"> <td colspan="3" class="text-sm-start align-middle">
@company.SalesRep @company.SalesRep
@ -72,9 +74,10 @@
</tr> </tr>
<tr> <tr>
<th scope="row" class="text-sm-start align-middle">Sidst besøgt</th> <th scope="row" class="text-sm-start align-middle">Sidst besøgt</th>
<td colspan="3" class="text-sm-start align-middle"> <td class="text-sm-start align-middle">
@(Mapper.MapVisitState(company.LastVisit) == "the-draw" ? "?" : company.LastVisit) @(Mapper.MapVisitState(company.LastVisit) == "the-draw" ? "?" : company.LastVisit)
</td> </td>
<td colspan="2" class="text-sm-end fw-bold align-middle">@(company.IsHidden == 1 ? "Skjult af sælger." : "")</td>
</tr> </tr>
<tr> <tr>
<th scope="row" class="text-sm-start align-middle">Næste besøg</th> <th scope="row" class="text-sm-start align-middle">Næste besøg</th>
@ -86,12 +89,13 @@
<button class="btn btn-sm btn-secondary w-100" @onclick="() => RequestBusinessCentralData(company.CompanyId, true)"><i class="bi-arrow-repeat"></i> BC</button> <button class="btn btn-sm btn-secondary w-100" @onclick="() => RequestBusinessCentralData(company.CompanyId, true)"><i class="bi-arrow-repeat"></i> BC</button>
</td> </td>
</tr> </tr>
</tbody>
</table> </table>
</div> </div>
<div class="card-footer"> <div class="card-footer">
<div class="row"> <div class="row">
<div class="px-1 col-sm-3"> <div class="px-1 col-sm-3">
<button class="btn btn-sm btn-danger w-100" @onclick="@(() => ShowInvoiceList(company.CompanyId))"> Salg</button> <button class="btn btn-sm btn-danger w-100" @onclick="@(() => ShowInvoiceList(company.CompanyId))"> Salg</button>
</div> </div>
<div class="px-1 col-sm-3"> <div class="px-1 col-sm-3">
<button class="btn btn-sm btn-warning w-100" @onclick="@(() => ShowActivityList(company.CompanyId))">Aktivitet</button> <button class="btn btn-sm btn-warning w-100" @onclick="@(() => ShowActivityList(company.CompanyId))">Aktivitet</button>

View file

@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace Wonky.Client.Extensions;
public static class JsInteropExtensions
{
public static ValueTask SetFocusAsync(this IJSRuntime jsRuntime, ElementReference elementReference)
{
return jsRuntime.InvokeVoidAsync("FormFocusElement", elementReference);
}
}

View file

@ -22,12 +22,13 @@ public class Draft
{ {
public string DraftId { get; set; } = ""; public string DraftId { get; set; } = "";
public string DraftType { get; set; } = "noSale"; public string DraftType { get; set; } = "noSale";
public bool Invalid => Items.Count == 0;
public List<DraftItem> Items { get; set; } = new (); public List<DraftItem> Items { get; set; } = new ();
public decimal Total public decimal Total
{ {
get get
{ {
return Items.Sum(item => item.LineTotal); return Items.Sum(item => item.LineSum);
} }
} }
public DateTime LastAccessed { get; set; } public DateTime LastAccessed { get; set; }

View file

@ -22,10 +22,10 @@ namespace Wonky.Client.Models;
/// </summary> /// </summary>
public class DraftItem public class DraftItem
{ {
public int Quantity { get; set; }
public SalesItemView Item { get; set; }
public decimal Price { get; set; }
public decimal Discount { get; set; } public decimal Discount { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
public decimal LineSum => (Price - Price * Discount / 100) * Quantity;
public bool Sas { get; set; } public bool Sas { get; set; }
public decimal LineTotal => (Price - Price * Discount / 100) * Quantity; public SalesItemView Item { get; set; } = new();
} }

View file

@ -19,7 +19,7 @@
<div class="modal-dialog modal-dialog-scrollable modal-fullscreen"> <div class="modal-dialog modal-dialog-scrollable modal-fullscreen">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h3 class="modal-title">Faktura @Invoice.DocumentNumber</h3> <h3 class="modal-title">Salgsinfo @Invoice.DocumentNumber</h3>
<button type="button" class="btn btn-danger" @onclick="@Hide" data-bs-dismiss="modal" aria-label="Luk"><i class="bi-x-lg"></i></button> <button type="button" class="btn btn-danger" @onclick="@Hide" data-bs-dismiss="modal" aria-label="Luk"><i class="bi-x-lg"></i></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@ -98,7 +98,7 @@
} }
else else
{ {
<span>Ingen data</span> <div class="alert alert-info">Ingen data</div>
} }
</div> </div>
</div> </div>

View file

@ -287,7 +287,7 @@ else
<td class="align-middle text-end">@cartItem.Quantity</td> <td class="align-middle text-end">@cartItem.Quantity</td>
<td class="align-middle text-end">@($"{cartItem.Price:N2}")</td> <td class="align-middle text-end">@($"{cartItem.Price:N2}")</td>
<td class="align-middle text-end">@($"{cartItem.Discount:N2}")</td> <td class="align-middle text-end">@($"{cartItem.Discount:N2}")</td>
<td class="align-middle text-end">@($"{cartItem.LineTotal:N2}")</td> <td class="align-middle text-end">@($"{cartItem.LineSum:N2}")</td>
<td class="align-middle text-center"> <td class="align-middle text-center">
<input type="checkbox" checked="@cartItem.Sas" disabled/> <input type="checkbox" checked="@cartItem.Sas" disabled/>
</td> </td>

View file

@ -474,7 +474,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
{ {
Price = item.Price, Price = item.Price,
Discount = item.Discount, Discount = item.Discount,
LineAmount = item.LineTotal, LineAmount = item.LineSum,
Qty = item.Quantity, Qty = item.Quantity,
Sku = item.Item.Sku, Sku = item.Item.Sku,
Text = item.Item.Name, Text = item.Item.Name,

View file

@ -123,7 +123,7 @@
<a class="btn btn-primary" href="@Workplace.ShortUrl" target="_blank"><i class="bi-list-ul"></i> Vis QR-koder</a> <a class="btn btn-primary" href="@Workplace.ShortUrl" target="_blank"><i class="bi-list-ul"></i> Vis QR-koder</a>
</div> </div>
<div class="col-sm-3 text-end"> <div class="col-sm-3 text-end">
<a class="btn btn-primary" href="/advisor/customers/@CompanyId/workplaces/@WorkplaceId/documents/new"><i class="bi-pencil-square"></i> Revision</a> <a class="btn btn-primary" href="/advisor/customers/@CompanyId/workplaces/@WorkplaceId/documents/new"><i class="bi-pencil-square"></i> Tilføj Produkt(er)</a>
</div> </div>
<div class="col-sm-12 col-md-6"> <div class="col-sm-12 col-md-6">
<div class="input-group"> <div class="input-group">
@ -174,7 +174,7 @@
@docView.DocumentDate @docView.DocumentDate
</div> </div>
<div class="col-sm-2"> <div class="col-sm-2">
<img class="img-fluid img-thumbnail" src="@(docView.PictureLink)?height=100" <img class="img-fluid img-thumbnail" src="@(docView.PictureLink)?height=100" alt="produkt-billede"
onerror="this.onerror=null;this.src='@(_config.AssetUrl)/images/no-image.png?height=100'"/> onerror="this.onerror=null;this.src='@(_config.AssetUrl)/images/no-image.png?height=100'"/>
</div> </div>
</div> </div>
@ -182,6 +182,10 @@
</li> </li>
} }
} }
else
{
<div class="alert alert-info"><h3>Ingen dokumenter oprettet.</h3></div>
}
</ol> </ol>
<div class="row mb-5"> <div class="row mb-5">

View file

@ -16,3 +16,59 @@
@attribute [Authorize(Roles = "EShop")] @attribute [Authorize(Roles = "EShop")]
@page "/b2b/{countryCode}/{companyId}/order/new" @page "/b2b/{countryCode}/{companyId}/order/new"
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
<div class="card">
<div class="card-header">
Bestilling
</div>
<div class="card-body">
<div class="card-text">
<div class="row">
<div class="col h5">
@_businessInfo.Name
</div>
<div class="col">
<i class="bi-card-heading"></i> @_businessInfo.Account
</div>
<div class="col">
<i class="bi-phone"></i> @_businessInfo.Phone
</div>
</div>
</div>
<div class="card-text">
@Activity.DlvAddress1, @Activity.DlvAddress2
</div>
<div class="card-text">
@CountryCode.ToUpper()-@Activity.DlvZipCode @Activity.DlvCity
</div>
</div>
<div class="card-body">
<div class="list-group-item">
<div class="row">
<div class="col">
Vare No.
</div>
<div class="col">
Beskrivelse
</div>
<div class="col">
Antal
</div>
</div>
@foreach (var item in DraftProvider.Draft.Items)
{
<div class="row">
<div class="col">
@item.Item.Sku
</div>
<div class="col">
@item.Item.Name
</div>
<div class="col">
@item.Quantity
</div>
</div>
}
</div>
</div>
</div>

View file

@ -1,5 +1,6 @@
using System.Diagnostics; using System.Diagnostics;
using System.Text.Json; using System.Text.Json;
using Blazored.Toast.Services;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms; using Microsoft.AspNetCore.Components.Forms;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -25,6 +26,7 @@ public partial class BusinessOrderViewPage
[Inject] public ICountryPriceCatalogRepository PriceCatalog { get; set; } [Inject] public ICountryPriceCatalogRepository PriceCatalog { get; set; }
[Inject] public IOptions<ApiConfig> Config { get; set; } [Inject] public IOptions<ApiConfig> Config { get; set; }
[Inject] public IUserInfoService UserInfoService { get; set; } [Inject] public IUserInfoService UserInfoService { get; set; }
[Inject] public IToastService Toaster { get; set; }
// ############################################################## // ##############################################################
[CascadingParameter] private DraftStateProvider DraftProvider { get; set; } = new(); [CascadingParameter] private DraftStateProvider DraftProvider { get; set; } = new();
@ -34,12 +36,7 @@ public partial class BusinessOrderViewPage
// ############################################################## // ##############################################################
private UserManagerEditView UserInfo { get; set; } = new(); private UserManagerEditView UserInfo { get; set; } = new();
private B2BBusinessInfo _businessInfo = new(); private B2BBusinessInfo _businessInfo = new();
private B2BAdvisorInfo _advisorInfo = new();
private List<ProductHistoryView> _productHistory = new();
private List<ProductInventoryItemView> _productInventory = new();
private ApiConfig _config = new(); private ApiConfig _config = new();
private B2BProductPriceHistoryOverlay PriceHistoryOverlay { get; set; }
private ItemSelect _selectedItem = new();
private ActivityDto Activity { get; set; } = new(); private ActivityDto Activity { get; set; } = new();
private DateTime SelectedDate { get; set; } private DateTime SelectedDate { get; set; }
private bool Working { get; set; } = true; private bool Working { get; set; } = true;
@ -52,6 +49,7 @@ public partial class BusinessOrderViewPage
// get info for logged in user // get info for logged in user
UserInfo = await UserInfoService.GetUserInfo(); UserInfo = await UserInfoService.GetUserInfo();
_businessInfo = await B2BRepo.GetBusinessInfo(CompanyId);
SelectedDate = DateTime.Now; SelectedDate = DateTime.Now;
var today = $"{SelectedDate:yyyy-MM-dd}"; var today = $"{SelectedDate:yyyy-MM-dd}";
@ -62,34 +60,147 @@ public partial class BusinessOrderViewPage
Activity.ActivityStatusEnum = "order"; Activity.ActivityStatusEnum = "order";
Activity.Express = true; Activity.Express = true;
Activity.OurRef = $"WEB:{UserInfo.FirstName}"; Activity.OurRef = $"WEB:{UserInfo.FirstName}";
// get sales rep info
// SalesRep = await UserRepo.GetUserInfo(Company.SalesRepId);
// Logger.LogDebug("OfficeOrderCreate => SalesRep => {}", JsonSerializer.Serialize(SalesRep));
// assign salesRep and countryCode to activity // assign salesRep and countryCode to activity
// Activity.SalesRep = _advisorInfo.SalesRep; Activity.SalesRep = _businessInfo.SalesRep;
// Activity.CountryCode = SalesRep.CountryCode; Activity.CountryCode = _businessInfo.CountryCode;
// Activity.SalesRepId = Company.SalesRepId; Activity.SalesRepId = _businessInfo.AdvisorId;
// assign customer info into activity properties // assign customer info into activity properties
// Activity.Account = Company.Account; Activity.Account = _businessInfo.Account;
// Activity.VatNumber = Company.VatNumber; Activity.VatNumber = _businessInfo.VatNumber;
// Activity.Email = Company.Email; Activity.Email = _businessInfo.Email;
// Activity.Phone = Company.Phone; Activity.Phone = _businessInfo.Phone;
// Activity.Mobile = Company.Mobile; Activity.Mobile = _businessInfo.Mobile;
// Activity.Name = Company.Name; Activity.Name = _businessInfo.Name;
// Activity.Address1 = Company.Address1; Activity.Address1 = _businessInfo.Address1;
// Activity.Address2 = Company.Address2; Activity.Address2 = _businessInfo.Address2;
// Activity.ZipCode = Company.ZipCode; Activity.ZipCode = _businessInfo.ZipCode;
// Activity.City = Company.City; Activity.City = _businessInfo.City;
// Activity.DlvName = Company.Name; Activity.DlvName = _businessInfo.Name;
// Activity.DlvAddress1 = Company.Address1; Activity.DlvAddress1 = _businessInfo.Address1;
// Activity.DlvAddress2 = Company.Address2; Activity.DlvAddress2 = _businessInfo.Address2;
// Activity.DlvZipCode = Company.ZipCode; Activity.DlvZipCode = _businessInfo.ZipCode;
// Activity.DlvCity = Company.City; Activity.DlvCity = _businessInfo.City;
// Activity.BcId = Company.BcId; Activity.BcId = _businessInfo.BcId;
// Activity.CompanyId = Company.CompanyId; Activity.CompanyId = _businessInfo.CompanyId;
// assign activity properties // assign activity properties
// var lineNo = 0;
// foreach (var item in DraftProvider.Draft.Items)
// {
// lineNo += 1;
// Activity.Lines.Add(new ActivityLineDto()
// {
// Discount = item.Discount,
// Price = item.Price,
// Qty = item.Quantity,
// LineAmount = item.LineSum,
// LineNumber = lineNo,
// ShortName = item.Item.ShortName,
// Sku = item.Item.Sku,
// Text = item.Item.Name,
// Location = item.Item.Location,
// Sas = item.Sas
// });
// }
Working = false; Working = false;
}
private async Task CreateActivity()
{
// avoid duplication
if (Working)
return;
// validate customer address1
// - this is a required input
// --- exclude for office orders
// if (string.IsNullOrWhiteSpace(Activity.Address1))
// {
// Toaster.ShowError("Kunde adresse er ufuldstændig.");
// return;
// }
// validate org number
// - this is a required input
// - must validate according to country rules.
// --- exclude for office orders
// if (!VatUtils.ValidateFormat(Company.CountryCode, Activity.VatNumber))
// {
// Toaster.ShowError("Firma registreringsnummer er ikke korrekt.");
// return;
// }
// // validate input according to status
// switch (Activity.ActivityStatusEnum)
// {
// // don't accept order with no lines
// case "order" when DraftProvider.Draft.Items.Count == 0:
// Toaster.ShowError("Ved bestilling skal der være en eller flere linjer i kladden.");
// return;
// // phone number is required if first time customer
// case "order" when Company.Account.StartsWith("NY") && string.IsNullOrWhiteSpace(Activity.Phone):
// Toaster.ShowError("Ved bestilling til ny kunde skal telefon nummer angives.");
// return;
// // verify email address is a valid address
// // --- exclude for office orders
// // case "quote" when !Utils.IsValidEmail(Activity.Email):
// // Toaster.ShowError("Ved tilbud skal en gyldig email adresse angives.");
// // return;
// }
// raise working flag
Working = true;
// reset selected item
// SelectedItem = new SalesItemView();
// begin assembling activity
Activity.ActivityDate = $"{SelectedDate:yyyy-MM-dd}";
if (Activity.Express)
Activity.OurRef = $"E{Activity.OurRef}";
// begin lines
Activity.Lines = new List<ActivityLineDto>();
var lineNo = 0;
if (DraftProvider.Draft.Items.Count != 0)
{
var lines = DraftProvider.Draft.Items.Select(item => new ActivityLineDto
{
// sales properties
Discount = item.Discount,
Price = item.Price,
Qty = item.Quantity,
LineAmount = item.LineSum,
LineNumber = ++lineNo,
Sas = item.Sas,
// item properties
Location = item.Item.Location,
ShortName = item.Item.ShortName,
Sku = item.Item.Sku,
Text = item.Item.Name,
})
.ToList();
Activity.Lines = lines;
}
// // debug logging
// Logger.LogDebug("CrmNewActivityPage => \n {}", JsonSerializer.Serialize(Activity));
// // post to api
// var result = await CountryActivityRepo.PostPhoneOrder(Company.CompanyId, Activity);
// // debug logging
// Logger.LogDebug("ApiResponseView => \n {}", JsonSerializer.Serialize(result));
// // show result message
// if (result.IsSuccess)
// {
// Toaster.ShowSuccess($"{result.Message}");
// await DeleteDraft();
// Navigator.NavigateTo($"/office/customers/{CompanyId}/orders/{result.Id}");
// Working = false;
// return;
// }
// lower working flag
Working = false;
// show error message
// Toaster.ShowError(result.Message);
} }
} }

View file

@ -61,6 +61,7 @@ public partial class OfficeAdvisorCustomerPagedListPage : IDisposable
Paging.OrderBy = UserPreference.CompanySort; Paging.OrderBy = UserPreference.CompanySort;
Paging.SearchColumn = UserPreference.CompanySearch; Paging.SearchColumn = UserPreference.CompanySearch;
Paging.PageSize = Convert.ToInt32(UserPreference.PageSize); Paging.PageSize = Convert.ToInt32(UserPreference.PageSize);
Paging.IsHidden = 1;
Paging.HasFolded = IncludeFolded ? 1 : 0; Paging.HasFolded = IncludeFolded ? 1 : 0;
// load saved search // load saved search

View file

@ -67,6 +67,7 @@ public partial class OfficeCustomerCountryPagedListPage : IDisposable
Paging.OrderBy = Preference.CompanySort; Paging.OrderBy = Preference.CompanySort;
Paging.SearchColumn = Preference.CompanySearch; Paging.SearchColumn = Preference.CompanySearch;
Paging.PageSize = Convert.ToInt32(Preference.PageSize); Paging.PageSize = Convert.ToInt32(Preference.PageSize);
Paging.IsHidden = 1;
Paging.HasFolded = ShowFolded ? 1 : 0; Paging.HasFolded = ShowFolded ? 1 : 0;
// load saved search // load saved search

View file

@ -20,9 +20,9 @@
@page "/office/customers/{CountryCode}/{CompanyId}/order" @page "/office/customers/{CountryCode}/{CompanyId}/order"
<PageTitle>Telefon Ordre - @Company.Name - @Company.Account</PageTitle> <PageTitle>@Company.Name - @Company.Account - Telefon Bestilling</PageTitle>
<div class="row bg-light border-2 border-dark rounded-3 p-3"> <div class="row mb-3">
<div class="col-sm-8"> <div class="col-sm-8">
<h3>@Company.Name - @Company.Account</h3> <h3>@Company.Name - @Company.Account</h3>
</div> </div>
@ -36,6 +36,8 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-sm-2">Sælger</div>
<div class="col-sm-10">@SalesRep.FirstName @SalesRep.LastName</div>
</div> </div>
<EditForm EditContext="ActivityContext"> <EditForm EditContext="ActivityContext">
@ -76,6 +78,7 @@
<ValidationMessage For="@(() => Activity.DlvCity)"></ValidationMessage> <ValidationMessage For="@(() => Activity.DlvCity)"></ValidationMessage>
</div> </div>
<hr style="height: 2px;" />
<label for="yourRef" class="col-sm-2 col-md-2 col-form-label">Indkøber</label> <label for="yourRef" class="col-sm-2 col-md-2 col-form-label">Indkøber</label>
<div class="col-sm-10 col-md-4"> <div class="col-sm-10 col-md-4">
<InputText id="yourRef" class="form-control" <InputText id="yourRef" class="form-control"
@ -165,7 +168,7 @@
<td class="align-middle text-end">@cartItem.Quantity</td> <td class="align-middle text-end">@cartItem.Quantity</td>
<td class="align-middle text-end">@($"{cartItem.Price:N2}")</td> <td class="align-middle text-end">@($"{cartItem.Price:N2}")</td>
<td class="align-middle text-end">@($"{cartItem.Discount:N2}")</td> <td class="align-middle text-end">@($"{cartItem.Discount:N2}")</td>
<td class="align-middle text-end">@($"{cartItem.LineTotal:N2}")</td> <td class="align-middle text-end">@($"{cartItem.LineSum:N2}")</td>
<td class="align-middle text-center"> <td class="align-middle text-center">
<input type="checkbox" checked="@cartItem.Sas" disabled/> <input type="checkbox" checked="@cartItem.Sas" disabled/>
</td> </td>

View file

@ -19,6 +19,8 @@ using System.Text.Json;
using Blazored.Toast.Services; using Blazored.Toast.Services;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms; using Microsoft.AspNetCore.Components.Forms;
using Microsoft.JSInterop;
using Wonky.Client.Extensions;
using Wonky.Client.Helpers; using Wonky.Client.Helpers;
using Wonky.Client.HttpInterceptors; using Wonky.Client.HttpInterceptors;
using Wonky.Client.HttpRepository; using Wonky.Client.HttpRepository;
@ -47,6 +49,7 @@ public partial class OfficeCustomerOrderCreatePage : IDisposable
[Inject] public ICountryUserInfoRepository UserRepo { get; set; } [Inject] public ICountryUserInfoRepository UserRepo { get; set; }
[Inject] public NavigationManager Navigator { get; set; } [Inject] public NavigationManager Navigator { get; set; }
[Inject] public IUserInfoService UserInfoService { get; set; } [Inject] public IUserInfoService UserInfoService { get; set; }
[Inject] public IJSRuntime JsRuntime { get; set; }
// ############################################################# // #############################################################
// parameters // parameters
@ -88,56 +91,53 @@ public partial class OfficeCustomerOrderCreatePage : IDisposable
private List<ProductInventoryItemView> CompanyInventory { get; set; } = new(); private List<ProductInventoryItemView> CompanyInventory { get; set; } = new();
private InvoiceListView CompanyInvoices { get; set; } = new(); private InvoiceListView CompanyInvoices { get; set; } = new();
private List<ReportItemView> CompanyActivities { get; set; } = new(); private List<ReportItemView> CompanyActivities { get; set; } = new();
private readonly ElementReference _yourRef = new ("yourRef");
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JsRuntime.SetFocusAsync(_yourRef);
}
}
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
Interceptor.RegisterEvent(); Interceptor.RegisterEvent();
Interceptor.RegisterBeforeSendEvent(); Interceptor.RegisterBeforeSendEvent();
// get info for logged in user
UserInfo = await UserInfoService.GetUserInfo(); UserInfo = await UserInfoService.GetUserInfo();
// setup edit context
ActivityContext = new EditContext(Activity); ActivityContext = new EditContext(Activity);
ActivityContext.OnFieldChanged += HandleFieldChanged; ActivityContext.OnFieldChanged += HandleFieldChanged;
ActivityContext.OnValidationStateChanged += ValidationChanged; ActivityContext.OnValidationStateChanged += ValidationChanged;
// fetch customer
Company = await CustomerRepo.GetByCustomerId(CountryCode, CompanyId); Company = await CustomerRepo.GetByCustomerId(CountryCode, CompanyId);
Logger.LogDebug("OfficeOrderCreate => Customer => {}", JsonSerializer.Serialize(Company));
// construct today's workdate
// Initialize date variable
SelectedDate = DateTime.Now; SelectedDate = DateTime.Now;
var today = $"{SelectedDate:yyyy-MM-dd}"; var today = $"{SelectedDate:yyyy-MM-dd}";
// assign activity properties
Activity.ActivityVisitEnum = "office"; Activity.ActivityVisitEnum = "office";
Activity.ActivityTypeEnum = "phone"; Activity.ActivityTypeEnum = "phone";
Activity.ActivityStatusEnum = "order"; Activity.ActivityStatusEnum = "order";
Activity.Express = true; Activity.Express = true;
Activity.OurRef = $"INNOTEC:{UserInfo.FirstName}"; Activity.OurRef = $"INNOTEC:{UserInfo.FirstName}";
Activity.ActivityDate = today; Activity.ActivityDate = today;
// initiate a sync to ensure up-to-date product history
if (Company.HistorySync != today) if (Company.HistorySync != today)
{ {
Company.HistorySync = await HistoryRepo.RequestErpToCrmSync(CompanyId, Company.HistorySync, false); Company.HistorySync = await HistoryRepo.RequestErpToCrmSync(CompanyId, Company.HistorySync, false);
Logger.LogDebug("OfficeOrderCreate => RequestErpToCrmSync <= {}", Company.HistorySync);
} }
// fetch invoices
CompanyInvoices = await HistoryRepo.GetInvoiceList(CountryCode, CompanyId); CompanyInvoices = await HistoryRepo.GetInvoiceList(CountryCode, CompanyId);
Logger.LogDebug("OfficeOrderCreate => Invoices => {}", JsonSerializer.Serialize(CompanyInvoices));
// fetch activities
CompanyActivities = await CountryActivityRepo.GetActivityList(CompanyId); CompanyActivities = await CountryActivityRepo.GetActivityList(CompanyId);
Logger.LogDebug("OfficeOrderCreate => Activities => {}", JsonSerializer.Serialize(CompanyActivities));
// fetch inventory
CompanyInventory = await HistoryRepo.GetInventory(CountryCode, CompanyId); CompanyInventory = await HistoryRepo.GetInventory(CountryCode, CompanyId);
CompanyInventory = CompanyInventory.OrderBy(x => x.Description).ToList(); CompanyInventory = CompanyInventory.OrderBy(x => x.Description).ToList();
Logger.LogDebug("OfficeOrderCreate => Inventory => {}", JsonSerializer.Serialize(CompanyInventory));
// get sales rep info
SalesRep = await UserRepo.GetUserInfo(Company.SalesRepId); SalesRep = await UserRepo.GetUserInfo(Company.SalesRepId);
Logger.LogDebug("OfficeOrderCreate => SalesRep => {}", JsonSerializer.Serialize(SalesRep));
// assign salesRep and countryCode to activity
Activity.SalesRep = SalesRep.SalesRep; Activity.SalesRep = SalesRep.SalesRep;
Activity.CountryCode = SalesRep.CountryCode; Activity.CountryCode = SalesRep.CountryCode;
Activity.SalesRepId = Company.SalesRepId; Activity.SalesRepId = Company.SalesRepId;
// assign customer info into activity properties
Activity.Account = Company.Account; Activity.Account = Company.Account;
Activity.VatNumber = Company.VatNumber; Activity.VatNumber = Company.VatNumber;
Activity.Email = Company.Email; Activity.Email = Company.Email;
@ -156,6 +156,9 @@ public partial class OfficeCustomerOrderCreatePage : IDisposable
Activity.BcId = Company.BcId; Activity.BcId = Company.BcId;
Activity.CompanyId = Company.CompanyId; Activity.CompanyId = Company.CompanyId;
DraftProvider.Draft.DraftType = "order";
PoFormInvalid = DraftProvider.Draft.Invalid;
Working = false; Working = false;
} }
@ -163,22 +166,23 @@ public partial class OfficeCustomerOrderCreatePage : IDisposable
private async Task DeleteDraft() private async Task DeleteDraft()
{ {
await DraftProvider.DeleteDraftAsync(); await DraftProvider.DeleteDraftAsync();
PoFormInvalid = DraftProvider.Draft.Invalid;
} }
private async Task RemoveItem(DraftItem item) private async Task RemoveItem(DraftItem item)
{ {
// remove item
DraftProvider.Draft.Items.Remove(item); DraftProvider.Draft.Items.Remove(item);
// save the remaining draft
await DraftProvider.SaveChangesAsync(); await DraftProvider.SaveChangesAsync();
PoFormInvalid = DraftProvider.Draft.Invalid;
} }
private async Task AddItem(SalesItemView salesItem) private async Task AddItem(SalesItemView salesItem)
{ {
ShowItem = false; ShowItem = false;
// create a new cart item
var item = new DraftItem var item = new DraftItem
{ {
Item = salesItem, Item = salesItem,
@ -187,30 +191,30 @@ public partial class OfficeCustomerOrderCreatePage : IDisposable
Discount = Convert.ToDecimal(Discount, CultureInfo.InvariantCulture), Discount = Convert.ToDecimal(Discount, CultureInfo.InvariantCulture),
Sas = Sas Sas = Sas
}; };
// reset internals to initial state
Sas = false; Sas = false;
Quantity = "1"; Quantity = "1";
Price = "0"; Price = "0";
Discount = "0"; Discount = "0";
// add it to the cart
SelectedItem = new SalesItemView();
DraftProvider.Draft.Items.Add(item); DraftProvider.Draft.Items.Add(item);
if(Activity.ActivityStatusEnum != "quote") PoFormInvalid = DraftProvider.Draft.Invalid;
Activity.ActivityStatusEnum = "order";
// save the item using the CartStateProvider's save method
await DraftProvider.SaveChangesAsync(); await DraftProvider.SaveChangesAsync();
} }
private async Task PriceListCallback(ItemSelect selectedItem) private async Task PriceListCallback(ItemSelect selectedItem)
{ {
// get selected item SelectedItem = new SalesItemView();
if (string.IsNullOrWhiteSpace(selectedItem.ItemNo)) if (string.IsNullOrWhiteSpace(selectedItem.ItemNo))
return; return;
SelectedItem = await PriceCatalog.GetSalesItemSku(CountryCode, selectedItem.ItemNo); SelectedItem = await PriceCatalog.GetSalesItemSku(CountryCode, selectedItem.ItemNo);
ShowItem = true; ShowItem = true;
Price = selectedItem.Rate; Price = selectedItem.Rate;
Quantity = selectedItem.Quantity; Quantity = selectedItem.Quantity;
StateHasChanged();
} }
@ -240,71 +244,40 @@ public partial class OfficeCustomerOrderCreatePage : IDisposable
private async Task CreateActivity() private async Task CreateActivity()
{ {
// avoid duplication
if (Working) if (Working)
return; return;
// validate customer address1
// - this is a required input
// --- exclude for office orders
// if (string.IsNullOrWhiteSpace(Activity.Address1))
// {
// Toaster.ShowError("Kunde adresse er ufuldstændig.");
// return;
// }
// validate org number
// - this is a required input
// - must validate according to country rules.
// --- exclude for office orders
// if (!VatUtils.ValidateFormat(Company.CountryCode, Activity.VatNumber))
// {
// Toaster.ShowError("Firma registreringsnummer er ikke korrekt.");
// return;
// }
// validate input according to status
switch (Activity.ActivityStatusEnum) switch (Activity.ActivityStatusEnum)
{ {
// don't accept order with no lines case "order" when DraftProvider.Draft.Invalid:
case "order" when DraftProvider.Draft.Items.Count == 0:
Toaster.ShowError("Ved bestilling skal der være en eller flere linjer i kladden."); Toaster.ShowError("Ved bestilling skal der være en eller flere linjer i kladden.");
return; return;
// phone number is required if first time customer
case "order" when Company.Account.StartsWith("NY") && string.IsNullOrWhiteSpace(Activity.Phone): case "order" when Company.Account.StartsWith("NY") && string.IsNullOrWhiteSpace(Activity.Phone):
Toaster.ShowError("Ved bestilling til ny kunde skal telefon nummer angives."); Toaster.ShowError("Ved bestilling til ny kunde skal telefon nummer angives.");
return; return;
// verify email address is a valid address
// --- exclude for office orders
// case "quote" when !Utils.IsValidEmail(Activity.Email):
// Toaster.ShowError("Ved tilbud skal en gyldig email adresse angives.");
// return;
} }
// raise working flag
Working = true; Working = true;
// reset selected item
SelectedItem = new SalesItemView();
// begin assembling activity
Activity.ActivityDate = $"{SelectedDate:yyyy-MM-dd}"; Activity.ActivityDate = $"{SelectedDate:yyyy-MM-dd}";
if (Activity.Express) if (Activity.Express)
Activity.OurRef = $"E{Activity.OurRef}"; Activity.OurRef = $"E{Activity.OurRef}";
// begin lines
Activity.Lines = new List<ActivityLineDto>(); Activity.Lines = new List<ActivityLineDto>();
var ln = 0; var ln = 0;
if (DraftProvider.Draft.Items.Count != 0) if (DraftProvider.Draft.Items.Count != 0)
{ {
var lines = DraftProvider.Draft.Items.Select(item => new ActivityLineDto var lines = DraftProvider.Draft.Items.Select(item => new ActivityLineDto
{ {
// sales properties
Discount = item.Discount, Discount = item.Discount,
Price = item.Price, Price = item.Price,
Qty = item.Quantity, Qty = item.Quantity,
LineAmount = item.LineTotal, LineAmount = item.LineSum,
LineNumber = ++ln, LineNumber = ++ln,
Sas = item.Sas, Sas = item.Sas,
// item properties
Location = item.Item.Location, Location = item.Item.Location,
ShortName = item.Item.ShortName, ShortName = item.Item.ShortName,
Sku = item.Item.Sku, Sku = item.Item.Sku,
@ -314,13 +287,8 @@ public partial class OfficeCustomerOrderCreatePage : IDisposable
Activity.Lines = lines; Activity.Lines = lines;
} }
// debug logging
Logger.LogDebug("CrmNewActivityPage => \n {}", JsonSerializer.Serialize(Activity));
// post to api
var result = await CountryActivityRepo.PostPhoneOrder(Company.CompanyId, Activity); var result = await CountryActivityRepo.PostPhoneOrder(Company.CompanyId, Activity);
// debug logging
Logger.LogDebug("ApiResponseView => \n {}", JsonSerializer.Serialize(result));
// show result message
if (result.IsSuccess) if (result.IsSuccess)
{ {
Toaster.ShowSuccess($"{result.Message}"); Toaster.ShowSuccess($"{result.Message}");
@ -329,9 +297,9 @@ public partial class OfficeCustomerOrderCreatePage : IDisposable
Working = false; Working = false;
return; return;
} }
// lower working flag
Working = false; Working = false;
// show error message
Toaster.ShowError(result.Message); Toaster.ShowError(result.Message);
} }

View file

@ -1,7 +1,7 @@
{ {
"appInfo": { "appInfo": {
"name": "Wonky Online", "name": "Wonky Online",
"version": "315.0", "version": "316.0",
"rc": false, "rc": false,
"sandBox": true, "sandBox": true,
"image": "grumpy-coder.png", "image": "grumpy-coder.png",

View file

@ -29,7 +29,8 @@
</div> </div>
</div> </div>
<script src="/bootstrap/js/bootstrap.bundle.js"></script> <script src="/bootstrap/js/bootstrap.bundle.js"></script>
<script src="/_framework/blazor.webassembly.js"></script>
<script type="module" src="/scripts/print-invoke.js"></script> <script type="module" src="/scripts/print-invoke.js"></script>
<script src="/scripts/focus-element.js"></script>
<script src="/_framework/blazor.webassembly.js"></script>
</body> </body>
</html> </html>

View file

@ -0,0 +1,5 @@
function FormFocusElement(element) {
if (element instanceof HTMLElement) {
element.focus();
}
}

View file

@ -2,7 +2,10 @@ namespace Wonky.Entity.DTO;
public class B2BAdvisorInfo public class B2BAdvisorInfo
{ {
public string AdvisorId { get; set; } = "";
public string CountryCode { get; set; } = "";
public string Email { get; set; } = "";
public string Name { get; set; } = ""; public string Name { get; set; } = "";
public string Phone { get; set; } = ""; public string Phone { get; set; } = "";
public string Email { get; set; } = ""; public string SalesRep { get; set; } = "";
} }

View file

@ -5,10 +5,16 @@ public class B2BBusinessInfo
public string Account { get; set; } = ""; public string Account { get; set; } = "";
public string Address1 { get; set; } = ""; public string Address1 { get; set; } = "";
public string Address2 { get; set; } = ""; public string Address2 { get; set; } = "";
public string AdvisorId { get; set; } = "";
public string BcId { get; set; } = "";
public string City { get; set; } = ""; public string City { get; set; } = "";
public string CompanyId { get; set; } = ""; public string CompanyId { get; set; } = "";
public string CountryCode { get; set; } = "";
public string Email { get; set; } = "";
public string Mobile { get; set; } = "";
public string Name { get; set; } = ""; public string Name { get; set; } = "";
public string ZipCode { get; set; } = "";
public string Phone { get; set; } = ""; public string Phone { get; set; } = "";
public string Email { get; set; } = ""; public string SalesRep { get; set; } = "";
public string VatNumber { get; set; } = "";
public string ZipCode { get; set; } = "";
} }