build v0.10.54

This commit is contained in:
Frede Hundewadt 2022-08-05 13:03:19 +02:00
parent dda2965026
commit 2c68d27c2f
23 changed files with 530 additions and 333 deletions

View file

@ -17,35 +17,41 @@
@if (Inventory.Any())
{
<div class="list-group list-group-flush">
<div class="list-group-item px-3 bg-black text-white rounded-2 opacity-75">
<div class="row">
<div class="col fw-bold">
Produkt
</div>
<div class="col fw-bold">
Varenr.
</div>
<div class="col fw-bold">
Antal
</div>
</div>
</div>
<table class="table table-striped">
<thead>
<tr>
<th scope="col">
Produkt
</th>
<th scope="col">
Varenr.
</th>
<th scope="col" class="text-center">
Antal
</th>
<th scope="col" class="text-center">
Historik
</th>
</tr>
</thead>
<tbody>
@foreach (var product in Inventory)
{
<a class="list-group-item list-group-item-action" href="/companies/@CompanyId/h/p/@product.Sku">
<div class="row">
<div class="col">
@product.Description
</div>
<div class="col">
@product.Sku
</div>
<div class="col">
@product.Quantity
</div>
</div>
</a>
<tr>
<td class="align-middle">
@product.Description
</td>
<td class="align-middle">
@product.Sku
</td>
<td class="align-middle text-center">
@product.Quantity
</td>
<td class="align-middle">
<a class="btn btn-warning d-block" href="/companies/@CompanyId/h/p/@product.Sku">Linjer</a>
</td>
</tr>
}
</div>
</tbody>
</table>
}

View file

@ -17,7 +17,7 @@
<div class="input-group">
<input id="search-input" type="text" class="form-control" placeholder="Søg ..." aria-described-by="search-addon"
@bind-value="@_searchTerm" @bind-value:event="oninput" @onkeyup="OnSearchChanged"/>
@bind-value="@_searchTerm" @bind-value:event="oninput" @onkeyup="OnSearchChanged" />
<span class="input-group-text" id="search-addon"><i class="oi oi-delete" @onclick="ClearSearch"></i></span>
</div>

View file

@ -24,7 +24,7 @@ namespace Wonky.Client.Components
private Timer _timer = new();
private string _searchTerm { get; set; } = "";
[Parameter] public EventCallback<string> OnChanged { get; set; }
[Parameter] public string SavedSearch { get; set; } = string.Empty;
[Parameter] public string SavedSearch { get; set; } = "";
protected override void OnParametersSet()
{
@ -33,29 +33,32 @@ namespace Wonky.Client.Components
protected override void OnInitialized()
{
if (string.IsNullOrWhiteSpace(_searchTerm))
return;
OnSearchChanged();
if (string.IsNullOrWhiteSpace(_searchTerm)) return;
OnChanged.InvokeAsync(_searchTerm);
}
private void ClearSearch()
{
SavedSearch = string.Empty;
_searchTerm = string.Empty;
SavedSearch = "";
_searchTerm = "";
OnChanged.InvokeAsync(_searchTerm);
}
private void OnSearchChanged()
{
_timer = new Timer(750);
_timer.Enabled = false;
_timer.Dispose();
_timer = new Timer(500);
_timer.Elapsed += OnTimerElapsed;
_timer.AutoReset = false;
_timer.Enabled = true;
}
private void OnTimerElapsed(object? sender, ElapsedEventArgs e)
{
OnChanged.InvokeAsync(_searchTerm);
_timer.Elapsed -= OnTimerElapsed;
_timer.Enabled = false;
_timer.Dispose();
}

View file

@ -25,7 +25,7 @@ public class DraftItem
public decimal Price { get; set; }
public decimal Discount { get; set; }
public bool Sas { get; set; }
public decimal LineTotal => (Price - Price * Discount / 100) * (decimal) Quantity;
public decimal LineTotal => (Price - Price * Discount / 100) * Quantity;
}
public class Draft

View file

@ -15,30 +15,38 @@
//
*@
@page "/companies/{companyId}/activities/new"
@page "/companies/{CompanyId}/activities/new"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "Advisor")]
@using Wonky.Client.Components
<PriceListModal OnSelectedItem="SelectSku" @ref="_priceList"/>
<div class="card">
<div class="card-header">
<div class="row align-items-center">
<div class="col">
<h3 class="workDate">@_workDate.ToLongDateString()</h3>
</div>
<div class="col">
<WorkDateComponent SelectedDate="@_selectedDate" OnChanged="SetWorkDate"></WorkDateComponent>
</div>
<PriceListModal OnSelected="SelectSku" @ref="_priceListModal"/>
<ProductHistoryModal CompanyId="@CompanyId" ItemSku="@_selectedItem.Sku" @ref="_historyModal"/>
<div class="row align-items-center bg-dark text-white rounded-3 p-3">
<div class="col-9">
<h3 class="workDate">@_workDate.ToLongDateString()</h3>
</div>
<div class="col-3">
<WorkDateComponent SelectedDate="@_selectedDate" OnChanged="SetWorkDate"></WorkDateComponent>
</div>
</div>
<div class="card-body">
<EditForm EditContext="_editContext">
<DataAnnotationsValidator/>
<div class="card-header">
@_draft.Name - @_draft.Account
<div class="row bg-light border-1 border-dark rounded-3 p-3">
<div class="col">
<h3>@_draft.Name - @_draft.Account</h3>
</div>
</div>
<div class="card-body">
@if (_reportClosdd)
{
<h5>Der kan ikke oprettes besøg når der findes rapport for @_workDate.ToShortDateString()</h5>
}
else
{
<EditForm EditContext="_editContext">
<DataAnnotationsValidator/>
<div class="row mb-1">
<label for="activityType" class="col-md-2 col-form-label">Ordre Type</label>
<div class="col-md-4">
@ -48,6 +56,13 @@
<option value="phone">Telefon</option>
</InputSelect>
<ValidationMessage For="@(() => _draft.ActivityTypeEnum)"></ValidationMessage>
@if (_draft.ActivityTypeEnum == "phone")
{
<div class="form-check">
<InputCheckbox id="express" class="form-check-input" @bind-Value="@_draft.Express"/>
<label class="form-check-label" for="express">Express</label>
</div>
}
</div>
<label for="statusType" class="col-md-2 col-form-label">Status</label>
@ -120,187 +135,189 @@
</div>
</div>
</div>
<div class="accordion" id="crmActivity">
@* Order lines *@
<div class="accordion-item" style="@(_draft.ActivityStatusEnum is "order" or "quote" ? "display: block" : "display:none")">
<h2 class="accordion-header" id="catalogHeader">
<button class="accordion-button collapsed bg-light" type="button"
data-bs-toggle="collapse" data-bs-target="#catalogBody"
aria-expanded="false" aria-controls="catalogBody">
Bestilling
</button>
</h2>
<div id="catalogBody" class="accordion-collapse collapse" aria-labelledby="catalogHeader" data-bs-parent="#crmActivity">
<div class="accordion-body">
<div class="row">
<div class="col">
@* Order draft lines *@
<table class="sticky-top table table-hover table-striped table-bordered">
<thead>
<tr class="bg-dark text-white">
<th scope="col" colspan="6">
Ordrekladde <span class="mx-2 draft-expires-msg">Global kladde (udløber efter @(DraftStateProvider.Draft.TimeToLiveInSeconds / 60)m inaktivitet)</span>
</th>
<th scope="col" class="text-end">
<button type="button" class="btn btn-danger btn-sm" @onclick="@DeleteDraft" disabled="@(DraftStateProvider.Draft.Items.Count == 0)">Slet kladde</button>
</th>
</tr>
<tr class="bg-dark opacity-75 text-white">
<th scope="col">Navn</th>
<th scope="col" class="text-nowrap">Varenr</th>
<th scope="col" class="text-end">Antal</th>
<th scope="col" class="text-end">Enhedspris</th>
<th scope="col" class="text-end">Linjesum</th>
<th scope="col" class="text-end">SAS</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
@if (DraftStateProvider != null && DraftStateProvider.Draft.Items.Count > 0)
{
@foreach (var cartItem in DraftStateProvider.Draft.Items)
{
<tr>
<td class="align-middle">@cartItem.Item.Name</td>
<td class="align-middle">@cartItem.Item.Sku</td>
<td class="align-middle text-end">@cartItem.Quantity</td>
<td class="align-middle text-end">@cartItem.Price</td>
<td class="align-middle text-end">@cartItem.LineTotal</td>
<td class="align-middle text-center">
<input type="checkbox" checked="@cartItem.Sas" disabled/>
</td>
<td class="align-middle text-end">
<button type="button" class="btn btn-danger" @onclick="@(() => RemoveItem(cartItem))">Slet Linje</button>
</td>
</tr>
}
}
<tr>
<td colspan="3"></td>
<td class="align-middle text-black text-end fw-bold">Total</td>
<td class="align-middle text-black text-end fw-bold">@DraftStateProvider.Draft.Total</td>
<td></td>
<td class="align-middle text-end">
<button class="btn btn-primary" type="button" @onclick="CallPriceListModal">
<i class="oi oi-plus"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="col">
@* draft line *@
@if (_selectedItem != null && ShowItem)
{
<table id="draft-line" class="table table-bordered">
<div class="accordion" id="crmActivity">
@* Order lines *@
<div class="accordion-item" style="@(_draft.ActivityStatusEnum is "order" or "quote" ? "display: block" : "display:none")">
<h2 class="accordion-header" id="catalogHeader">
<button class="accordion-button collapsed bg-light" type="button"
data-bs-toggle="collapse" data-bs-target="#catalogBody"
aria-expanded="false" aria-controls="catalogBody">
Bestilling
</button>
</h2>
<div id="catalogBody" class="accordion-collapse collapse" aria-labelledby="catalogHeader" data-bs-parent="#crmActivity">
<div class="accordion-body">
<div class="row">
<div class="col">
@* Order draft lines *@
<table class="sticky-top table table-hover table-striped table-bordered">
<thead>
<tr class="bg-dark text-white">
<th scope="col" colspan="6">Kladdelinje</th>
<th scope="col" colspan="6">
Ordrekladde <span class="mx-2 draft-expires-msg">Global kladde (udløber efter @(DraftStateProvider.Draft.TimeToLiveInSeconds / 60)m inaktivitet)</span>
</th>
<th scope="col" class="text-end">
<button type="button" class="btn btn-danger btn-sm" @onclick="@DeleteDraft" disabled="@(DraftStateProvider.Draft.Items.Count == 0)">Slet kladde</button>
</th>
</tr>
<tr>
<th scope="col">Antal</th>
<th scope="col">Pris</th>
<th scope="col">Rabat</th>
<th class="align-content-center justify-content-center" scope="col">SAS</th>
<th scope="col">Varenr.</th>
<tr class="bg-dark opacity-75 text-white">
<th scope="col">Navn</th>
<th scope="col" class="text-nowrap">Varenr</th>
<th scope="col" class="text-end">Antal</th>
<th scope="col" class="text-end">Enhedspris</th>
<th scope="col" class="text-end">Linjesum</th>
<th scope="col" class="text-end">SAS</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
@if (DraftStateProvider.Draft.Items.Count > 0)
{
@foreach (var cartItem in DraftStateProvider.Draft.Items)
{
<tr>
<td class="align-middle">@cartItem.Item.Name</td>
<td class="align-middle">@cartItem.Item.Sku</td>
<td class="align-middle text-end">@cartItem.Quantity</td>
<td class="align-middle text-end">@cartItem.Price</td>
<td class="align-middle text-end">@cartItem.LineTotal</td>
<td class="align-middle text-center">
<input type="checkbox" checked="@cartItem.Sas" disabled/>
</td>
<td class="align-middle text-end">
<button type="button" class="btn btn-danger" @onclick="@(() => RemoveItem(cartItem))">Slet Linje</button>
</td>
</tr>
}
}
<tr>
<td class="align-middle">
<input type="number" class="form-control" @bind-value="@Quantity"/>
</td>
<td class="align-middle">
<input type="number" class="form-control" @bind-value="@Price"/>
</td>
<td class="align-middle">
<input type="number" class="form-control" @bind-value="@Discount"/>
</td>
<td class="align-middle align-content-center justify-content-center">
<input type="checkbox" class="form-check" @bind-value="@Sas"/>
</td>
<td class="align-middle">@_selectedItem.Sku</td>
<td class="align-middle">
<button type="button" class="btn btn-warning text-nowrap d-block" @onclick="@(() => AddItem(_selectedItem))">bestil @Quantity stk @_selectedItem.Name</button>
<td colspan="3"></td>
<td class="align-middle text-black text-end fw-bold">Total</td>
<td class="align-middle text-black text-end fw-bold">@DraftStateProvider.Draft.Total</td>
<td></td>
<td class="align-middle text-end">
<button class="btn btn-primary" type="button" @onclick="CallPriceListModal" >
<i class="oi oi-plus"></i>
</button>
</td>
</tr>
</tbody>
</table>
}
</div>
</div>
<div class="row">
<div class="col">
@* draft line *@
@if (!string.IsNullOrWhiteSpace(_selectedItem.Name) && ShowItem)
{
<table id="draft-line" class="table table-bordered">
<thead>
<tr class="bg-dark text-white">
<th scope="col" colspan="6">Kladdelinje</th>
</tr>
<tr>
<th scope="col">Antal</th>
<th scope="col">Pris</th>
<th scope="col">Rabat</th>
<th class="align-content-center justify-content-center" scope="col">SAS</th>
<th scope="col">Varenr.</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr>
<td class="align-middle">
<input type="number" class="form-control" @bind-value="@Quantity"/>
</td>
<td class="align-middle">
<div class="input-group">
<input type="number" class="form-control" @bind-value="@Price"/>
<button class="btn btn-info btn-sm" type="button" @onclick="CallHistoryModal">
<i class="oi oi-list"></i>
</button>
</div>
</td>
<td class="align-middle">
<input type="number" class="form-control" @bind-value="@Discount"/>
</td>
<td class="align-middle align-content-center justify-content-center">
<input type="checkbox" class="form-check" @bind-value="@Sas"/>
</td>
<td class="align-middle">@_selectedItem.Sku</td>
<td class="align-middle">
<button type="button" class="btn btn-warning text-nowrap d-block" @onclick="@(() => AddItem(_selectedItem))">bestil @Quantity stk @_selectedItem.Name</button>
</td>
</tr>
</tbody>
</table>
}
</div>
</div>
</div>
</div>
</div>
@* Delivery address *@
<div class="accordion-item" style="@(_draft.ActivityStatusEnum == "order" ? "display: block" : "display:none")">
<h2 class="accordion-header" id="deliveryHeader">
<button class="accordion-button collapsed bg-light" type="button" data-bs-toggle="collapse" data-bs-target="#deliveryBody" aria-expanded="false" aria-controls="deliveryBody">
Leveringsadresse
</button>
</h2>
<div id="deliveryBody" class="accordion-collapse collapse"
aria-labelledby="deliveryHeader" data-bs-parent="#crmActivity">
<div class="accordion-body">
<div class="row mb-1">
<label for="dlvName" class="col-md-2 col-form-label">Lev. Navn</label>
<div class="col-md-10">
<InputText id="dlvName" class="form-control" @bind-Value="_draft.DlvName"/>
</div>
</div>
<div class="row mb-1">
<label for="dlvAddress1" class="col-md-2 col-form-label">Lev. Adresse</label>
<div class="col-md-10">
<InputText id="dlvAddress1" class="form-control" @bind-Value="_draft.DlvAddress1"/>
</div>
</div>
<div class="row mb-1">
<label for="dlvAddress2" class="col-md-2 col-form-label">Lev. Adresse</label>
<div class="col-md-10">
<InputText id="dlvAddress2" class="form-control" @bind-Value="_draft.DlvAddress2"/>
</div>
</div>
<div class="row mb-1">
<label for="dlvZipCode" class="col-md-2 col-form-label">Lev. Postnr</label>
<div class="col-md-10">
<InputText id="dlvZipCode" class="form-control" @bind-Value="_draft.DlvZipCode"/>
</div>
</div>
<div class="row mb-1">
<label for="dlvCity" class="col-md-2 col-form-label">Lev. Bynavn</label>
<div class="col-md-10">
<InputText id="dlvCity" class="form-control" @bind-Value="_draft.DlvCity"/>
</div>
</div>
</div>
</div>
</div>
</div>
@* Delivery address *@
<div class="accordion-item" style="@(_draft.ActivityStatusEnum == "order" ? "display: block" : "display:none")">
<h2 class="accordion-header" id="deliveryHeader">
<button class="accordion-button collapsed bg-light" type="button" data-bs-toggle="collapse" data-bs-target="#deliveryBody" aria-expanded="false" aria-controls="deliveryBody">
Leveringsadresse
</button>
</h2>
<div id="deliveryBody" class="accordion-collapse collapse"
aria-labelledby="deliveryHeader" data-bs-parent="#crmActivity">
<div class="accordion-body">
<div class="row mb-1">
<label for="dlvName" class="col-md-2 col-form-label">Lev. Navn</label>
<div class="col-md-10">
<InputText id="dlvName" class="form-control" @bind-Value="_draft.DlvName"/>
</div>
</div>
<div class="row mb-1">
<label for="dlvAddress1" class="col-md-2 col-form-label">Lev. Adresse</label>
<div class="col-md-10">
<InputText id="dlvAddress1" class="form-control" @bind-Value="_draft.DlvAddress1"/>
</div>
</div>
<div class="row mb-1">
<label for="dlvAddress2" class="col-md-2 col-form-label">Lev. Adresse</label>
<div class="col-md-10">
<InputText id="dlvAddress2" class="form-control" @bind-Value="_draft.DlvAddress2"/>
</div>
</div>
<div class="row mb-1">
<label for="dlvZipCode" class="col-md-2 col-form-label">Lev. Postnr</label>
<div class="col-md-10">
<InputText id="dlvZipCode" class="form-control" @bind-Value="_draft.DlvZipCode"/>
</div>
</div>
<div class="row mb-1">
<label for="dlvCity" class="col-md-2 col-form-label">Lev. Bynavn</label>
<div class="col-md-10">
<InputText id="dlvCity" class="form-control" @bind-Value="_draft.DlvCity"/>
</div>
</div>
</div>
</EditForm>
@if (HideButtons)
{
<LoaderThreeDots/>
}
else
{
<div class="row mt-2 mb-2">
<div class="col">
<a class="btn btn-info" href="/companies">Til Oversigt</a>
</div>
<div class="col">
<a class="btn btn-warning" href="/companies/@_company.CompanyId">Annuller</a>
</div>
<div class="col">
<button type="button" class="btn btn-primary" @onclick="CreateActivity" disabled="@_poFormInvalid">Opret besøg</button>
</div>
</div>
</div>
</EditForm>
</div>
<div class="card-footer">
@if (HideButtons)
{
<LoaderThreeDots/>
}
else
{
<div class="row mt-2 mb-2">
<div class="col">
<a class="btn btn-info" href="/companies">Til Oversigt</a>
</div>
<div class="col">
<a class="btn btn-warning" href="/companies/@_company.CompanyId">Annuller</a>
</div>
<div class="col">
<button type="button" class="btn btn-primary" @onclick="CreateActivity" disabled="@_poFormInvalid">Opret besøg</button>
</div>
</div>
}
</div>
</div>
}
}

View file

@ -46,8 +46,9 @@ public partial class ActivityNewVisitPage : IDisposable
[Inject] private IActivityHttpRepository _activityRepo { get; set; }
[Inject] private IReportHttpRepository _reportRepo { get; set; }
// variables
private PriceListModal _priceList { get; set; }
private readonly JsonSerializerOptions? _options = new JsonSerializerOptions{PropertyNameCaseInsensitive = true};
private PriceListModal _priceListModal { get; set; }
private ProductHistoryModal _historyModal { get; set; }
private SalesItemView _selectedItem { get; set; } = new();
private Preferences _prefs { get; set; } = new();
private ActivityDto _draft { get; set; } = new();
@ -64,11 +65,13 @@ public partial class ActivityNewVisitPage : IDisposable
private bool InvalidStatusType = true;
private bool InvalidActivity = true;
private bool InvalidCanvas = true;
private bool _reportClosdd { get; set; } = false;
private bool NoHistory = true;
private bool _reportClosdd { get; set; }
private UserInfoView _ux { get; set; } = new();
private DateTime _workDate { get; set; } = DateTime.Now;
private string _selectedDate { get; set; } = "";
private string _phone { get; set; } = "";
protected override async Task OnParametersSetAsync()
{
@ -80,9 +83,6 @@ public partial class ActivityNewVisitPage : IDisposable
// raise flag if report is closed
_reportClosdd = await _reportRepo.ReportExist(_selectedDate);
if(_reportClosdd)
_navigator.NavigateTo($"/sales-reports/view/{_selectedDate}");
}
protected override async Task OnInitializedAsync()
@ -112,6 +112,7 @@ public partial class ActivityNewVisitPage : IDisposable
_draft.CompanyId = _company.CompanyId;
_draft.BcId = _company.BcId;
_draft.SalesRepId = _ux.Id;
_draft.SalesRep = _ux.Advisor;
_draft.ActivityStatusEnum = "noSale";
_draft.VisitTypeEnum = _company.Account is "" or "NY" ? "new" : "recall";
@ -126,7 +127,7 @@ public partial class ActivityNewVisitPage : IDisposable
_draft.Name = _company.Name;
_draft.Address1 = _company.Address1;
_draft.Address2 = _company.Address2;
_draft.Address2 = _company.Address2;
_draft.ZipCode = _company.ZipCode;
_draft.City = _company.City;
@ -139,21 +140,38 @@ public partial class ActivityNewVisitPage : IDisposable
}
private void CallPriceListModal()
{
_priceList.Show();
}
_priceListModal.Show();
}
private async Task SelectSku(SelectedSku sku)
{
ShowItem = true;
Console.WriteLine($"XTextPriceList => sku: {JsonSerializer.Serialize(sku)}");
// fetch selected item
_selectedItem = await _itemRepo.GetSalesItem(sku.ItemId);
ShowItem = true;
Price = sku.Rate;
Quantity = sku.Quantity;
StateHasChanged();
}
private void SetWorkDate(string workDate)
private void CallHistoryModal()
{
_historyModal.Show();
}
// private void SelectPrice(decimal price)
// {
// Price = price.ToString("N2");
// StateHasChanged();
// }
private async Task SetWorkDate(string workDate)
{
_selectedDate = workDate;
_workDate = DateTime.Parse(_selectedDate);
_draft.ActivityDate = _selectedDate;
_reportClosdd = await _reportRepo.ReportExist(_selectedDate);
}
private async Task CreateActivity()
@ -164,18 +182,22 @@ public partial class ActivityNewVisitPage : IDisposable
return;
}
if (_draft.ActivityStatusEnum == "order" && DraftStateProvider.Draft.Items.Count == 0)
if (_draft.ActivityStatusEnum == "order")
{
_toast.ShowError("Ved bestilling skal der angives et eller flere varenumre.");
return;
}
if (DraftStateProvider.Draft.Items.Count == 0)
{
_toast.ShowError("Ved bestilling skal der angives et eller flere varenumre.");
return;
}
if (_draft.ActivityStatusEnum == "order" && string.IsNullOrWhiteSpace(_draft.Phone))
{
_toast.ShowError("Ved bestilling til ny kunde skal telefon nummer angives.");
return;
if (string.IsNullOrWhiteSpace(_draft.Phone))
{
_toast.ShowError("Ved bestilling til ny kunde skal telefon nummer angives.");
return;
}
}
HideButtons = true;
_draft.ActivityDate = _prefs.WorkDate;
_draft.OurRef = _draft.ActivityTypeEnum switch
@ -184,29 +206,34 @@ public partial class ActivityNewVisitPage : IDisposable
"onSite" => $"B:{_ux.FullName.Split(" ")[0]}",
_ => ""
};
_draft.Lines = new List<ActivityLineDto>();
var ln = 0;
var lines = DraftStateProvider.Draft.Items.Select(item => new ActivityLineDto
{
Price = item.Price,
Discount = item.Discount,
LineAmount = item.LineTotal,
Qty = item.Quantity,
Sku = item.Item.Sku,
Text = item.Item.Name,
ShortName = item.Item.ShortName,
LineNumber = ++ln,
Sas = item.Sas
})
.ToList();
_draft.Lines = lines;
if (DraftStateProvider.Draft.Items.Count != 0)
{
var lines = DraftStateProvider.Draft.Items.Select(item => new ActivityLineDto
{
Price = item.Price,
Discount = item.Discount,
LineAmount = item.LineTotal,
Qty = item.Quantity,
Sku = item.Item.Sku,
Text = item.Item.Name,
ShortName = item.Item.ShortName,
LineNumber = ++ln,
Sas = item.Sas
})
.ToList();
_draft.Lines = lines;
}
if (_phone != _draft.Phone)
{
_company.Phone = _draft.Phone;
await _companyRepo.UpdateCompany(_company.CompanyId, _company);
_toast.ShowInfo("Kunde telefon nummer er opdateret.");
}
// post to api
var result = await _activityRepo.CreateActivity(_draft);
// show result message
_toast.ShowSuccess($"{result.Message}.");
await DraftStateProvider.DeleteDraftAsync();
_navigator.NavigateTo($"/companies");
@ -271,8 +298,11 @@ public partial class ActivityNewVisitPage : IDisposable
}
private void ValidationChanged(object sender, ValidationStateChangedEventArgs e)
{
if (string.IsNullOrEmpty(_draft.ActivityTypeEnum))
if (string.IsNullOrEmpty(_draft.ActivityTypeEnum) && !_reportClosdd)
{
_toast.ShowWarning("Aktivitet type kan ikke være tom");
return;
}
_poFormInvalid = false;

View file

@ -182,13 +182,7 @@
<InputNumber id="interval" class="form-control" @bind-Value="_company.Interval"/>
<ValidationMessage For="@(() => _company.Interval)"></ValidationMessage>
</td>
<th scope="row">
Historik
</th>
<td>
<label>@_company.HistorySync</label>
<button class="ms-2 btn btn-light border-dark" type="button" @onclick="@RefreshHistory" disabled="@_working">@_btnUpdateText</button>
</td>
<td colspan="2"></td>
</tr>
</tbody>
</table>
@ -198,15 +192,12 @@
<div class="col">
<a class="btn btn-light border-dark d-block" href="/companies">Oversigt</a>
</div>
<div class="col">
<a class="btn btn-light border-dark d-block" href="/companies/@_company.CompanyId/h/i">Produktkøb</a>
</div>
<div class="col">
<a class="btn btn-light border-dark d-block" href="/companies/@_company.CompanyId/h/p">Varelinjer</a>
</div>
<div class="col">
<a class="btn btn-light border-dark d-block" href="/companies/@_company.CompanyId/workplaces">Kemi</a>
</div>
<div class="col">
<a class="btn btn-light border-dark d-block" href="/companies/@_company.CompanyId/h/i">Produktkøb</a>
</div>
<div class="col">
<button type="submit" class="btn btn-light border-dark d-block">Gem</button>
</div>

View file

@ -69,18 +69,7 @@ public partial class CustomerViewPage : IDisposable
{
PropertyNameCaseInsensitive = true
};
private async Task RefreshHistory()
{
_working = true;
_btnUpdateText = "vent venligst ...";
_toast.ShowInfo("Vent mens data opdateres ...");
_toast.ShowInfo("Der kan gå nogle minuutter inden data er klar ...");
var ts = await _historyRepo.UpdateProductHistory(_company.CompanyId, $"{DateTime.Parse(_company.HistorySync):yyyy-MM-dd}");;
_company.HistorySync = ts.Replace("\"", "");
_btnUpdateText = "Opdater";
_working = false;
}
protected override async Task OnInitializedAsync()
{
var ux = await _storage.GetItemAsync<UserInfoView>("_xu");

View file

@ -31,7 +31,7 @@
<ItemSearchComponent OnChanged="SetSearchCol"/>
</div>
<div class="col">
<SearchPhraseComponent OnChanged="SetSearchPhrase"/>
<SearchPhraseComponent SavedSearch="@_searchTerm" OnChanged="SetSearchPhrase"/>
</div>
<div class="col">
<ItemSortComponent OnChanged="SetSortCol"/>

View file

@ -38,6 +38,7 @@ public partial class ItemCatalogPage : IDisposable
private MetaData? _metaData { get; set; } = new();
private CatalogPagingParams _paging = new();
private Preferences _preferences = new();
private string _searchTerm { get; set; } = "";
protected override async Task OnInitializedAsync()
{
@ -58,6 +59,15 @@ public partial class ItemCatalogPage : IDisposable
_metaData = pagingResponse.MetaData;
}
private async Task SetSearchPhrase(string searchTerm)
{
_searchTerm = searchTerm;
_items = new List<SalesItemView>();
_paging.PageNumber = 1;
_paging.SearchTerm = searchTerm;
await GetSalesItems();
}
private async Task SetPageSize(string pageSize)
{
_items = new List<SalesItemView>();
@ -73,14 +83,7 @@ public partial class ItemCatalogPage : IDisposable
_paging.SearchColumn = columnName;
await GetSalesItems();
}
private async Task SetSearchPhrase(string searchTerm)
{
_items = new List<SalesItemView>();
_paging.PageNumber = 1;
_paging.SearchTerm = searchTerm;
await GetSalesItems();
}
private async Task SetSortCol(string orderBy)
{
_items = new List<SalesItemView>();

View file

@ -27,7 +27,7 @@
<h3>@_company.Name</h3>
</div>
<div class="col align-content-end">
<a class="btn btn-primary" href="/companies/@_company.CompanyId">Tilbage</a>
<a class="btn btn-primary" href="/companies/@_company.CompanyId/h/i">Produkter</a>
</div>
</div>
</div>

View file

@ -37,6 +37,7 @@ public partial class ProductHistoryItemPage : IDisposable
_interceptor.RegisterBeforeSendEvent();
_company = await _companyRepo.GetCompanyById(CompanyId);
_lines = await _historyRepo.FetchHistory(CompanyId, Sku);
_lines = _lines.OrderByDescending(x => x.DeliveryDate).ToList();
}
public void Dispose()

View file

@ -27,7 +27,7 @@
<h3>@_company.Name</h3>
</div>
<div class="col align-content-end">
<a class="btn btn-primary" href="/companies/@_company.CompanyId">Tilbage</a>
<a class="btn btn-primary" href="/companies/@CompanyId">Kundekort</a>
</div>
</div>
</div>

View file

@ -23,11 +23,15 @@
<div class="card">
<div class="card-header">
<div class="row">
<div class="col">
<div class="col-6">
<h3>@_company.Name</h3>
</div>
<div class="col align-content-end">
<a class="btn btn-primary" href="/companies/@_company.CompanyId">Tilbage</a>
<div class="col-3 align-content-end">
<a class="btn btn-primary" href="/companies/@_company.CompanyId">Kundekort</a>
</div>
<div class="col-3">
<button class="btn btn-warning" @onclick="RefreshHistory" disabled="@_working">@_btnUpdateText</button>
<label class="fw-bold ms-2">@_company.HistorySync</label>
</div>
</div>
</div>

View file

@ -14,6 +14,7 @@
//
using Blazored.Toast.Services;
using Microsoft.AspNetCore.Components;
using Wonky.Client.HttpInterceptors;
using Wonky.Client.HttpRepository;
@ -29,9 +30,11 @@ public partial class ProductInventoryPage : IDisposable
[Inject] private IHistoryHttpRepository _historyRepo { get; set; }
[Inject] private ICompanyHttpRepository _companyRepo { get; set; }
[Inject] private HttpInterceptorService _interceptor { get; set; }
[Inject] private IToastService _toast { get; set; }
private CompanyDto _company { get; set; } = new();
private List<ProductInventoryView> _inventory { get; set; } = new();
private string _btnUpdateText { get; set; } = "check";
private bool _working { get; set; }
protected override async Task OnInitializedAsync()
{
@ -40,8 +43,22 @@ public partial class ProductInventoryPage : IDisposable
_interceptor.RegisterBeforeSendEvent();
_company = await _companyRepo.GetCompanyById(CompanyId);
_inventory = await _historyRepo.FetchInventory(CompanyId);
_inventory = _inventory.OrderBy(x => x.Description).ToList();
}
private async Task RefreshHistory()
{
_working = true;
_btnUpdateText = "vent venligst ...";
_toast.ShowInfo("Vent mens data checkes ...");
var ts = await _historyRepo.UpdateProductHistory(_company.CompanyId, $"{DateTime.Parse(_company.HistorySync):yyyy-MM-dd}");;
_company.HistorySync = ts.Replace("\"", "");
_btnUpdateText = "check";
_inventory = await _historyRepo.FetchInventory(CompanyId);
_inventory = _inventory.OrderBy(x => x.Description).ToList();
_working = false;
}
public void Dispose()
{
_interceptor.DisposeEvent();

View file

@ -22,16 +22,15 @@ namespace Wonky.Client.Shared;
public partial class DraftStateProvider
{
[Parameter] public RenderFragment ChildContent { get; set; }
public Draft Draft { get; set; }
[Parameter] public string Account { get; set; } = "MyCart";
[Inject] public ILocalStorageService LocalStorageService { get; set; }
public Draft Draft { get; set; } = new();
private bool _hasLoaded;
protected override async Task OnParametersSetAsync()
{
Draft = await LocalStorageService.GetItemAsync<Draft>(Account)
;
Draft = await LocalStorageService.GetItemAsync<Draft>(Account);
if (Draft == null || Draft.Items.Count == 0)
{
@ -50,14 +49,12 @@ public partial class DraftStateProvider
public async Task SaveChangesAsync()
{
await LocalStorageService.SetItemAsync(Account, Draft)
;
await LocalStorageService.SetItemAsync(Account, Draft);
}
public async Task DeleteDraftAsync()
{
Draft.Items.Clear();
await LocalStorageService.SetItemAsync(Account, Draft)
;
await LocalStorageService.SetItemAsync(Account, Draft);
}
}

View file

@ -44,9 +44,9 @@
</NavLink>
</div>
</NotAuthorized>
</AuthorizeView>
</AuthorizeView>
<AuthorizeView Roles="Admin,Warehouse,Office">
<AuthorizeView Roles="Warehouse">
<div class="nav-item px-3">
<NavLink class="nav-link ps-2" href="/warehouse/orders">
<span class="oi oi-box" aria-hidden="true"></span> Forsendelse

View file

@ -17,7 +17,7 @@
@using Wonky.Client.Components
@using Wonky.Client.Helpers
<div class="modal" tabindex="-1" role="dialog" style="display:@_modalDisplay">
<div class="modal-dialog modal-lg modal-xl">
<div class="modal-dialog modal-dialog-scrollable modal-lg modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Pris Katalog</h5>
@ -30,7 +30,7 @@
<ItemSearchComponent OnChanged="SetSearchCol"/>
</div>
<div class="col">
<SearchPhraseComponent OnChanged="SetSearchPhrase"/>
<SearchPhraseComponent SavedSearch="@_searchTerm" OnChanged="SetSearchPhrase"/>
</div>
@* <div class="col"> *@
@* <ItemSortComponent OnChanged="SetSortCol"/> *@
@ -65,7 +65,7 @@
<div class="text-sm-end me-1">@rate.Quantity</div>
<div class="text-sm-end me-1">@rate.Rate</div>
<div>
<a class="btn btn-primary btn-sm" @onclick="@(() => SelectItem(item.ItemId, rate.Quantity, rate.Rate))">
<a class="btn btn-primary btn-sm" data-bs-dismiss="modal" @onclick="@(() => SelectItem(item.ItemId, rate.Quantity, rate.Rate))">
<i class="oi oi-plus"></i>
</a>
</div>

View file

@ -28,13 +28,14 @@ public partial class PriceListModal : IDisposable
private string _modalDisplay = "";
private bool _showBackdrop;
private List<SalesItemView> _items { get; set; } = new();
[Parameter] public EventCallback<SelectedSku> OnSelectedItem { get; set; }
[Parameter] public EventCallback<SelectedSku> OnSelected { get; set; }
[Inject] private ISalesItemHttpRepository _itemRepo { get; set; }
[Inject] private HttpInterceptorService _interceptor { get; set; }
[Inject] private UserPreferenceService _preferenceService { get; set; }
private MetaData? _metaData { get; set; } = new();
private CatalogPagingParams _paging = new();
private Preferences _preferences = new();
private string _searchTerm { get; set; } = "";
protected override async Task OnInitializedAsync()
{
@ -46,33 +47,28 @@ public partial class PriceListModal : IDisposable
await GetSalesItems();
}
public void Show()
{
_modalDisplay = "block;";
_showBackdrop = true;
StateHasChanged();
}
private void SelectItem(string itemId, string quantity, string rate)
{
OnSelectedItem.InvokeAsync(new SelectedSku { Quantity = quantity, Rate = rate, ItemId = itemId });
OnSelected.InvokeAsync(new SelectedSku { Quantity = quantity, Rate = rate, ItemId = itemId });
Hide();
}
private void Hide()
private async Task SetSearchPhrase(string searchTerm)
{
_modalDisplay = "none;";
_showBackdrop = false;
StateHasChanged();
_searchTerm = searchTerm;
_items = new List<SalesItemView>();
_paging.PageNumber = 1;
_paging.SearchTerm = searchTerm;
await GetSalesItems();
}
private async Task GetSalesItems()
{
var pagingResponse = await _itemRepo.GetSalesItemsPaged(_paging);
_items = pagingResponse.Items!;
_metaData = pagingResponse.MetaData;
}
private async Task SetPageSize(string pageSize)
{
_items = new List<SalesItemView>();
@ -88,13 +84,6 @@ public partial class PriceListModal : IDisposable
_paging.SearchColumn = columnName;
await GetSalesItems();
}
private async Task SetSearchPhrase(string searchTerm)
{
_items = new List<SalesItemView>();
_paging.PageNumber = 1;
_paging.SearchTerm = searchTerm;
await GetSalesItems();
}
private async Task SetSortCol(string orderBy)
{
@ -103,5 +92,19 @@ public partial class PriceListModal : IDisposable
await GetSalesItems();
}
public void Show()
{
_modalDisplay = "block;";
_showBackdrop = true;
StateHasChanged();
}
private void Hide()
{
_modalDisplay = "none;";
_showBackdrop = false;
StateHasChanged();
}
public void Dispose() => _interceptor.DisposeEvent();
}

View file

@ -0,0 +1,68 @@
@*
// Copyright (C) 2022 FCS Frede's Computer Services.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see [https://www.gnu.org/licenses/agpl-3.0.en.html]
//
*@
@using Wonky.Client.Components
@using Wonky.Client.Helpers
<div class="modal" tabindex="-1" role="dialog" style="display:@_modalDisplay">
<div class="modal-dialog modal-dialog-scrollable modal-lg modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">@ProductName</h5>
<button type="button" class="btn-close" @onclick="Hide" data-bs-dismiss="modal" aria-label="Luk"></button>
</div>
<div class="modal-body">
@if (!string.IsNullOrWhiteSpace(ProductName))
{
<table class="table table-striped">
<thead>
<tr>
<th scope="col">Dato</th>
<th scope="col">Antal</th>
<th scope="col">Rabat</th>
<th scope="col">Pris</th>
@* <th scope="col"></th> *@
</tr>
</thead>
<tbody>
@foreach (var entry in History)
{
<tr>
<td>@entry.DeliveryDate</td>
<td>@entry.Quantity</td>
<td>@entry.Discount</td>
<td>@entry.Price</td>
@*
<td>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" @onclick="() => SelectPrice(entry.Price)">Overfør</button>
</td>
*@
</tr>
}
</tbody>
</table>
}
else
{
<h3>Ingen data</h3>
}
</div>
</div>
</div>
</div>
@if (_showBackdrop)
{
<div class="modal-backdrop fade show"></div>
}

View file

@ -0,0 +1,65 @@
// Copyright (C) 2022 FCS Frede's Computer Services.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see [https://www.gnu.org/licenses/agpl-3.0.en.html]
//
using Microsoft.AspNetCore.Components;
using Wonky.Client.HttpInterceptors;
using Wonky.Client.HttpRepository;
using Wonky.Client.Models;
using Wonky.Client.Services;
using Wonky.Entity.Requests;
using Wonky.Entity.Views;
namespace Wonky.Client.Shared;
public partial class ProductHistoryModal
{
// [Parameter] public EventCallback<decimal> OnSelected { get; set; }
[Parameter] public string CompanyId { get; set; } = "";
[Parameter] public string ItemSku { get; set; } = "";
[Inject] public IHistoryHttpRepository _historyRepo { get; set; }
private List<ProductHistoryView> History { get; set; }
private string ProductName { get; set; } = "";
private string _modalDisplay = "";
private bool _showBackdrop;
protected override async Task OnParametersSetAsync()
{
History = await _historyRepo.FetchHistory(CompanyId, ItemSku);
if (History.Any())
{
ProductName = History[0].Description;
}
}
// private void SelectPrice(decimal price)
// {
// OnSelected.InvokeAsync(price);
// Hide();
// }
public void Show()
{
_modalDisplay = "block;";
_showBackdrop = true;
StateHasChanged();
}
private void Hide()
{
_modalDisplay = "none;";
_showBackdrop = false;
StateHasChanged();
}
}

View file

@ -1,13 +1,13 @@
{
"appInfo": {
"name": "Wonky Client",
"version": "0.10.27",
"version": "0.10.54",
"rc": true,
"sandBox": false,
"image": "grumpy-coder.png"
},
"apiConfig": {
"innoBaseUrl": "https://production.innotec.dk",
"innoBaseUrl": "https://app.innotec.dk",
"glsTrackUrl": "https://www.gls-group.eu/276-I-PORTAL-WEB/content/GLS/DK01/DA/5004.htm?txtAction=71000&txtRefNo=",
"glsId": "",
"serviceVirk": "api/v2/services/virk",

View file

@ -113,7 +113,10 @@ namespace Wonky.Entity.DTO
/// </summary>
[Required(ErrorMessage = "Vælg aktivitetstype")]
public string ActivityTypeEnum { get; set; } = "";
/// <summary>
/// Flag express order
/// </summary>
public bool Express { get; set; }
/// <summary>
/// Activity status enum as string
/// </summary>