Merge branch 'office-functionality---phone-order' into dev-v6

This commit is contained in:
Frede Hundewadt 2023-02-01 09:49:28 +01:00
commit 35cf79beba
207 changed files with 2608 additions and 1940 deletions

View file

@ -2,9 +2,12 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Affero/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Affero/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Bestilling/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Bestilling/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Bes_00F8g/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Bes_00F8g/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Danmark/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=fejl/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=fejl/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=kontrolleres/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=kontrolleres/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Norge/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=opst_00E5et/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=opst_00E5et/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Sverige/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Tilbud/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Tilbud/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Venligst/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Venligst/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Virk/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> <s:Boolean x:Key="/Default/UserDictionary/Words/=Virk/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View file

@ -8,7 +8,7 @@
@code { @code {
[Inject] public UserProfileService ProfileService { get; set; } [Inject] private UserProfileService ProfileService { get; set; }
private int KmMorning { get; set; } private int KmMorning { get; set; }
private UserPref Prefs { get; set; } = new(); private UserPref Prefs { get; set; } = new();

View file

@ -18,7 +18,7 @@
@if (ActivityList.Any()) @if (ActivityList.Any())
{ {
<table class="table table-bordered d-print-table table-striped"> <table class="table table-sm table-bordered d-print-table table-striped">
<thead> <thead>
<tr class="bg-dark text-white opacity-75 border-bottom"> <tr class="bg-dark text-white opacity-75 border-bottom">
<th scope="col">Kunde</th> <th scope="col">Kunde</th>
@ -38,7 +38,7 @@
@foreach (var activity in ActivityList) @foreach (var activity in ActivityList)
{ {
<tr> <tr>
<td class="align-middle"><a class="btn btn-outline-info text-black d-block" href="/customers/@activity.Company.CompanyId/orders/@activity.ActivityId">@activity.Company.Name</a></td> <td class="align-middle"><a href="/advisor/customers/@activity.Company.CompanyId/orders/@activity.ActivityId">@activity.Company.Name</a></td>
<td class="align-middle">@activity.Company.City</td> <td class="align-middle">@activity.Company.City</td>
<td class="align-middle">@activity.Demo</td> <td class="align-middle">@activity.Demo</td>
<td class="align-middle">@activity.Sales</td> <td class="align-middle">@activity.Sales</td>

View file

@ -18,9 +18,9 @@ using Wonky.Entity.Views;
namespace Wonky.Client.Components; namespace Wonky.Client.Components;
public partial class ActivityListComponent public partial class AdvisorActivityListComponent
{ {
[Parameter] public List<ReportItemView> ActivityList { get; set; } = new(); [Parameter] public List<ReportItemView> ActivityList { get; set; } = new();
[Inject] public NavigationManager Navigator { get; set; } [Inject] private NavigationManager Navigator { get; set; }
} }

View file

@ -58,7 +58,7 @@
@company.City @company.City
</td> </td>
<td class="align-middle"> <td class="align-middle">
<ActivityButton CompanyId="@company.CompanyId" ActionLink="/customers/$ID$/activities/new" <ActivityButton CompanyId="@company.CompanyId" ActionLink="/advisor/customers/$ID$/activities/new"
ButtonText="Besøg" ButtonType="primary" Enabled="@company.ValidVat"/> ButtonText="Besøg" ButtonType="primary" Enabled="@company.ValidVat"/>
</td> </td>
</tr> </tr>

View file

@ -27,13 +27,13 @@ using Wonky.Entity.Views;
namespace Wonky.Client.Components namespace Wonky.Client.Components
{ {
public partial class AdvisorCompanyTableComponent public partial class AdvisorCustomerListComponent
{ {
[Parameter] public List<CompanyDto> CompanyList { get; set; } = new(); [Parameter] public List<CompanyDto> CompanyList { get; set; } = new();
[Parameter] public EventCallback<string> OnDelete { get; set; } [Parameter] public EventCallback<string> OnDelete { get; set; }
[Parameter] public EventCallback<string> OnSelect { get; set; } [Parameter] public EventCallback<string> OnSelect { get; set; }
[Inject] public NavigationManager Navigator { get; set; } [Inject] private NavigationManager Navigator { get; set; }
[Inject] public IJSRuntime Js { get; set; } [Inject] private IJSRuntime Js { get; set; }
private Lazy<IJSObjectReference> BsTooltip = new(); private Lazy<IJSObjectReference> BsTooltip = new();
@ -43,7 +43,7 @@ namespace Wonky.Client.Components
private void ViewCustomer(string companyId) private void ViewCustomer(string companyId)
{ {
Navigator.NavigateTo($"/customers/{companyId}"); Navigator.NavigateTo($"/advisor/customers/{companyId}");
} }
private void CallInformationModal(string info) private void CallInformationModal(string info)

View file

@ -0,0 +1,93 @@
@using Wonky.Entity.Views
@using Wonky.Entity.DTO
@*
// 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]
//
*@
<table class="table table-sm table-bordered table-striped">
<thead>
<tr class="bg-dark text-white opacity-75 border-bottom">
<th></th>
<th class="text-center" colspan="2" scope="col">Dagens Demo @(ReportData.NewDemoCount + ReportData.RecallDemoCount)</th>
<th class="text-center border-end" colspan="2" scope="col">Dagens Resultat</th>
<th class="text-center" colspan="4" scope="col">Måneds Resultat</th>
</tr>
</thead>
<tbody>
<tr class="bg-dark bg-opacity-50 border-bottom">
<td></td>
<th class="text-end text-white" scope="col">Besøg</th>
<th class="text-end text-white" scope="col">Demo</th>
<th class="text-end text-white" scope="col">Salg</th>
<th class="text-end text-white border-end" scope="col">Beløb</th>
<th class="text-end text-white" scope="col">Besøg</th>
<th class="text-end text-white" scope="col">Demo</th>
<th class="text-end text-white" scope="col">Salg</th>
<th class="text-end text-white" scope="col">Beløb</th>
</tr>
<tr>
<th scope="row">N</th>
<td class="text-end">@ReportData.NewVisitCount</td>
<td class="text-end">@ReportData.NewDemoCount</td>
<td class="text-end">@ReportData.NewSaleCount</td>
<td class="text-end border-end">@ReportData.NewTurnover</td>
<td class="text-end">@ReportData.NewVisitCountMonth</td>
<td class="text-end">@ReportData.NewDemoCountMonth</td>
<td class="text-end">@ReportData.NewSaleCountMonth</td>
<td class="text-end">@ReportData.NewTurnoverMonth</td>
</tr>
<tr>
<th scope="row">R</th>
<td class="text-end">@ReportData.RecallVisitCount</td>
<td class="text-end">@ReportData.RecallDemoCount</td>
<td class="text-end">@ReportData.RecallSaleCount</td>
<td class="text-end border-end">@ReportData.RecallTurnover</td>
<td class="text-end">@ReportData.RecallVisitCountMonth</td>
<td class="text-end">@ReportData.RecallDemoCountMonth</td>
<td class="text-end">@ReportData.RecallSaleCountMonth</td>
<td class="text-end">@ReportData.RecallTurnoverMonth</td>
</tr>
<tr>
<th scope="row">SAS</th>
<td class="bg-light"></td>
<td class="bg-light"></td>
<td class="text-end">@ReportData.SasCount</td>
<td class="text-end border-end">@ReportData.SasTurnover</td>
<td class="bg-light"></td>
<td class="bg-light"></td>
<td class="text-end">@ReportData.SasCountMonth</td>
<td class="text-end">@ReportData.SasTurnoverMonth</td>
</tr>
<tr>
<th scope="row">TOTAL</th>
<td class="text-end">@ReportData.TotalVisitCount</td>
<td class="text-end">@ReportData.TotalDemoCount</td>
<td class="text-end">@ReportData.TotalSaleCount</td>
<td class="text-end border-end">@ReportData.TotalTurnover</td>
<td class="text-end">@ReportData.TotalVisitCountMonth</td>
<td class="text-end">@ReportData.TotalDemoCountMonth</td>
<td class="text-end">@ReportData.TotalSaleCountMonth</td>
<td class="text-end">@ReportData.TotalTurnoverMonth</td>
</tr>
</tbody>
</table>
@code{
[Parameter]
public ReportFiguresDto ReportData { get; set; } = new();
}

View file

@ -22,8 +22,8 @@ namespace Wonky.Client.Components;
public partial class CatalogGroupComponent public partial class CatalogGroupComponent
{ {
[Inject] public ILocalStorageService Storage { get; set; } [Inject] private ILocalStorageService Storage { get; set; }
[Inject] public UserProfileService ProfileService { get; set; } [Inject] private UserProfileService ProfileService { get; set; }
[Parameter] public EventCallback<string> OnChanged { get; set; } [Parameter] public EventCallback<string> OnChanged { get; set; }
private Dictionary<string, string> Items { get; set; } = new(); private Dictionary<string, string> Items { get; set; } = new();
private UserPref Prefs = new(); private UserPref Prefs = new();

View file

@ -24,5 +24,5 @@ namespace Wonky.Client.Components;
public partial class CatalogListComponent public partial class CatalogListComponent
{ {
[Parameter] public List<SalesItemView> ItemList { get; set; } = new(); [Parameter] public List<SalesItemView> ItemList { get; set; } = new();
[Inject] public IToastService ToastService { get; set; } [Inject] private IToastService ToastService { get; set; }
} }

View file

@ -17,7 +17,7 @@
<select class="form-select bg-warning text-bg-warning" @bind-value="@SearchCol" @bind-value:event="oninput" @onchange="OnSelectChanged"> <select class="form-select bg-warning text-bg-warning" @bind-value="@SearchCol" @bind-value:event="oninput" @onchange="OnSelectChanged">
<option value="-1" selected disabled>SØGNING</option> <option value="-1" selected disabled>SØGNING</option>
<option value="name">Søg Navn</option> <option value="name">Navn</option>
<option value="sku">Søg Nummer</option> <option value="sku">Nummer</option>
<option value="shortName">Søg Forkort.</option> <option value="shortName">Forkort.</option>
</select> </select>

View file

@ -24,7 +24,7 @@ public partial class CatalogSearchComponent : IDisposable
/// <summary> /// <summary>
/// User preference service /// User preference service
/// </summary> /// </summary>
[Inject] public UserProfileService ProfileService { get; set; } [Inject] private UserProfileService ProfileService { get; set; }
/// <summary> /// <summary>
/// OnChanged event callback /// OnChanged event callback

View file

@ -17,6 +17,6 @@
<select class="form-select bg-success text-bg-success" @bind-value="@SortCol" @bind-value:event="oninput" @onchange="OnSelectChanged"> <select class="form-select bg-success text-bg-success" @bind-value="@SortCol" @bind-value:event="oninput" @onchange="OnSelectChanged">
<option value="-1" selected disabled>SORTERING</option> <option value="-1" selected disabled>SORTERING</option>
<option value="name">Navn sort</option> <option value="name">Navn</option>
<option value="sku">Varenr sort</option> <option value="sku">Varenr</option>
</select> </select>

View file

@ -24,7 +24,7 @@ public partial class CatalogSortComponent : IDisposable
/// <summary> /// <summary>
/// User preference service /// User preference service
/// </summary> /// </summary>
[Inject] public UserProfileService ProfileService { get; set; } [Inject] private UserProfileService ProfileService { get; set; }
/// <summary> /// <summary>
/// OnChanged callback function /// OnChanged callback function

View file

@ -27,7 +27,7 @@ namespace Wonky.Client.Components;
public partial class CustomerInventoryListComponent public partial class CustomerInventoryListComponent
{ {
[Inject] public ILocalStorageService Storage { get; set; } [Inject] private ILocalStorageService Storage { get; set; }
// Parameters // Parameters
[Parameter] public List<ProductInventoryView> Inventory { get; set; } = new(); [Parameter] public List<ProductInventoryView> Inventory { get; set; } = new();
[Parameter] public string CompanyId { get; set; } = ""; [Parameter] public string CompanyId { get; set; } = "";

View file

@ -37,7 +37,7 @@
{ {
<div class="row"> <div class="row">
<div class="col-md-1"></div> <div class="col-md-1"></div>
<div class="col-md-1"><i class="bi-pencil"></i></div> <div class="col-md-1"><i class="bi-card-text"></i></div>
<div class="col-md-10 fw-bold">@invoice.OrderNote</div> <div class="col-md-10 fw-bold">@invoice.OrderNote</div>
</div> </div>
} }

View file

@ -28,7 +28,7 @@ public partial class CustomerProductCheckListComponent
{ {
[Parameter] public List<ProductInventoryView> ProductList { get; set; } = new(); [Parameter] public List<ProductInventoryView> ProductList { get; set; } = new();
[Parameter] public string CompanyId { get; set; } = ""; [Parameter] public string CompanyId { get; set; } = "";
[Inject] public ILocalStorageService Storage { get; set; } [Inject] private ILocalStorageService Storage { get; set; }
// private variables // private variables
private bool Descending { get; set; } private bool Descending { get; set; }

View file

@ -17,9 +17,9 @@
<select class="form-select bg-warning text-bg-warning" @bind-value="@SearchCol" @bind-value:event="oninput" @onchange="OnSelectionChanged"> <select class="form-select bg-warning text-bg-warning" @bind-value="@SearchCol" @bind-value:event="oninput" @onchange="OnSelectionChanged">
<option value="-1" disabled>SØGNING</option> <option value="-1" disabled>SØGNING</option>
<option value="name">Søg Navn</option> <option value="name">Navn</option>
<option value="city">Søg By</option> <option value="city">Bynavn</option>
<option value="zip">Søg Post</option> <option value="zip">Postnr</option>
<option value="account">Søg Konto</option> <option value="account">Konto</option>
<option value="phone">Søg Tlf.</option> <option value="phone">Telefon</option>
</select> </select>

View file

@ -22,10 +22,10 @@ using Wonky.Client.Services;
namespace Wonky.Client.Components; namespace Wonky.Client.Components;
public partial class CompanySearchColumnComponent : IDisposable public partial class CustomerSearchColumnComponent : IDisposable
{ {
[Inject] public ILocalStorageService Storage { get; set; } [Inject] private ILocalStorageService Storage { get; set; }
[Inject] public UserProfileService ProfileService { get; set; } [Inject] private UserProfileService ProfileService { get; set; }
[Parameter] public EventCallback<string> OnChanged { get; set; } [Parameter] public EventCallback<string> OnChanged { get; set; }
private Dictionary<string, string> Items { get; set; } = new(); private Dictionary<string, string> Items { get; set; } = new();
private UserPref Prefs { get; set; } = new(); private UserPref Prefs { get; set; } = new();

View file

@ -20,12 +20,12 @@ using Timer = System.Timers.Timer;
namespace Wonky.Client.Components namespace Wonky.Client.Components
{ {
public partial class CompanySearchPhraseComponent public partial class CustomerSearchPhraseComponent
{ {
private Timer InputTimer { get; set; } = new(); private Timer InputTimer { get; set; } = new();
private string SearchTerm { get; set; } = ""; private string SearchTerm { get; set; } = "";
private UserPref Prefs { get; set; } = new (); private UserPref Prefs { get; set; } = new ();
[Inject] public UserProfileService ProfileService { get; set; } [Inject] private UserProfileService ProfileService { get; set; }
[Parameter] public EventCallback<string> OnChanged { get; set; } [Parameter] public EventCallback<string> OnChanged { get; set; }
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()

View file

@ -17,6 +17,6 @@
<select class="form-select bg-success text-bg-success" @bind-value="@SortCol" @bind-value:event="oninput" @onchange="OnSelectionChanged"> <select class="form-select bg-success text-bg-success" @bind-value="@SortCol" @bind-value:event="oninput" @onchange="OnSelectionChanged">
<option value="-1" selected disabled>SORTERING</option> <option value="-1" selected disabled>SORTERING</option>
<option value="name">Navne sort.</option> <option value="name">Firma</option>
<option value="city">By sort.</option> <option value="city">Bynavn</option>
</select> </select>

View file

@ -21,10 +21,10 @@ using Wonky.Client.Services;
namespace Wonky.Client.Components namespace Wonky.Client.Components
{ {
public partial class CompanySortComponent : IDisposable public partial class CustomerSortComponent : IDisposable
{ {
[Inject] public ILocalStorageService Storage { get; set; } [Inject] private ILocalStorageService Storage { get; set; }
[Inject] public UserProfileService ProfileService { get; set; } [Inject] private UserProfileService ProfileService { get; set; }
[Parameter] public EventCallback<string> OnChanged { get; set; } [Parameter] public EventCallback<string> OnChanged { get; set; }
private Dictionary<string, string> Items { get; set; } = new(); private Dictionary<string, string> Items { get; set; } = new();
private UserPref Prefs = new(); private UserPref Prefs = new();

View file

@ -0,0 +1,75 @@
@*
// 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]
//
*@
<div class="list-group">
<div class="list-group-item">
<div class="row">
<div class="col">
<h4>Dato</h4>
</div>
<div class="col">
<h4>Demo</h4>
</div>
<div class="col">
<h4>Salg</h4>
</div>
<div class="col">
<h4>Ordre Note</h4>
</div>
<div class="col">
<h4>Ordre Note</h4>
</div>
</div>
</div>
@if (Activities.Any())
{
@foreach (var activity in Activities)
{
<div class="list-group-item list-group-item-action" style="cursor: pointer" @onclick="() => ShowVisitOverlay(activity.ActivityId)">
<div class="row">
<div class="col">
@activity.OrderDate
</div>
<div class="col">
@activity.Demo
</div>
<div class="col">
@activity.Sales
</div>
<div class="col">
@activity.OfficeNote
</div>
<div class="col">
@activity.CrmNote
</div>
</div>
</div>
}
}
else
{
<div class="list-group-item">
<div class="row">
<div class="col">
Ingen data
</div>
</div>
</div>
}
</div>
<CustomerActivityViewModalOverlay ReportItem="Activity" @ref="ActivityViewOverlay"/>

View file

@ -0,0 +1,41 @@
// 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 System.Globalization;
using System.Runtime.InteropServices;
using Blazored.LocalStorage;
using Microsoft.AspNetCore.Components;
using Wonky.Client.HttpInterfaces;
using Wonky.Client.Models;
using Wonky.Client.Shared;
using Wonky.Entity.DTO;
using Wonky.Entity.Views;
namespace Wonky.Client.Components;
public partial class CustomerVisitListComponent
{
[Parameter] public List<ReportItemView> Activities { get; set; } = new();
private CustomerActivityViewModalOverlay ActivityViewOverlay { get; set; } = new();
private ReportItemView Activity { get; set; } = new();
private void ShowVisitOverlay(string activityId)
{
Activity = Activities.First(x => x.ActivityId == activityId);
ActivityViewOverlay.Show();
}
}

View file

@ -0,0 +1,47 @@
<h3>Sælger</h3>
<table class="table">
<thead>
<tr>
<th>Symbol</th>
<th>Betydning</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<i class="bi-calendar" style="font-size:1.3rem"></i>
</td>
<td>ToDo liste</td>
</tr>
<tr>
<td>
<i class="bi-file-spreadsheet" style="font-size:1.3rem"></i>
</td>
<td>Pris katalog</td>
</tr>
<tr>
<td>
<i class="bi-building" style="font-size:1.3rem"></i>
</td>
<td>Firmaer/Kunder</td>
</tr>
<tr>
<td>
<i class="bi-calculator" style="font-size:1.3rem"></i>
</td>
<td>Aftaler/Tilbud</td>
</tr>
<tr>
<td>
<i class="bi-activity" style="font-size:1.3rem"></i>
</td>
<td>Aktivitet</td>
</tr>
<tr>
<td>
<i class="bi-file-earmark-spreadsheet" style="font-size:1.3rem"></i>
</td>
<td>Dagsrapporter</td>
</tr>
</tbody>
</table>

View file

@ -1,4 +1,6 @@
.pictogram {
max-width: 30px;
}
.color-code { .color-code {
max-width: 40px; max-width: 30px;
} }

View file

@ -1,4 +1,6 @@
.pictogram {
max-width: 30px;
}
.color-code { .color-code {
max-width: 40px; max-width: 30px;
} }

View file

@ -0,0 +1,41 @@
<h3>Fælles</h3>
<table class="table">
<thead>
<tr>
<th>Symbol</th>
<th>Betydning</th>
</tr>
<tr>
<td><i class="bi-cloud-arrow-up" style="font-size:1.3rem"></i></td>
<td>Gem data</td>
</tr>
<tr>
<td><i class="bi-search" style="font-size:1.3rem"></i></td>
<td>Søg</td>
</tr>
<tr>
<td><i class="bi-plus" style="font-size:1.3rem"></i></td>
<td>Opret</td>
</tr>
<tr>
<td><i class="bi-card-text" style="font-size:1.3rem"></i></td>
<td>Notat</td>
</tr>
<tr>
<td><i class="bi-printer" style="font-size:1.3rem"></i></td>
<td>Udskrivning</td>
</tr>
<tr>
<td><i class="bi-sliders" style="font-size:1.3rem"></i></td>
<td>Indstillinger</td>
</tr>
<tr>
<td><i class="bi-lock" style="font-size:1.3rem"></i></td>
<td>Log af</td>
</tr>
<tr>
<td><i class="bi-question" style="font-size:1.3rem"></i></td>
<td>Hjælp/Info</td>
</tr>
</thead>
</table>

View file

@ -0,0 +1,17 @@
<h3>Kontor</h3>
<table class="table">
<thead>
<tr>
<th>Symbol</th>
<th>Betydning</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<i class="bi-people" style="font-size:1.3rem"></i>
</td>
<td>Brugere</td>
</tr>
</tbody>
</table>

View file

@ -1,5 +1,5 @@
<h3>Aktivitet Oversigt</h3> <h3>Bestilling Status</h3>
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
@ -18,6 +18,10 @@
<td><i class="bi-file-earmark" style="font-size:1.3rem"></i></td> <td><i class="bi-file-earmark" style="font-size:1.3rem"></i></td>
<td>Ubehandlet</td> <td>Ubehandlet</td>
</tr> </tr>
<tr>
<td><i class="bi-hand-thumbs-up" style="font-size:1.3rem"></i></td>
<td>Accepteret</td>
</tr>
<tr> <tr>
<td><i class="bi-file-earmark-check" style="font-size:1.3rem"></i></td> <td><i class="bi-file-earmark-check" style="font-size:1.3rem"></i></td>
<td>Plukket</td> <td>Plukket</td>
@ -28,7 +32,7 @@
</tr> </tr>
<tr> <tr>
<td><i class="bi-truck" style="font-size:1.3rem"></i></td> <td><i class="bi-truck" style="font-size:1.3rem"></i></td>
<td>Afhentet</td> <td>Leveret</td>
</tr> </tr>
</thead> </thead>
</table> </table>

View file

@ -0,0 +1,17 @@
<h3>Lager</h3>
<table class="table">
<thead>
<tr>
<th>Symbol</th>
<th>Betydning</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<i class="bi-box" style="font-size:1.3rem"></i>
</td>
<td>Pakning / Forsendelse</td>
</tr>
</tbody>
</table>

View file

@ -18,8 +18,6 @@
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@using Wonky.Client.Components @using Wonky.Client.Components
<PageTitle>Innotec Rådgiver</PageTitle>
<AuthorizeView Roles="Advisor"> <AuthorizeView Roles="Advisor">
<div class="bg-dark text-white rounded-2 mb-2 py-2"> <div class="bg-dark text-white rounded-2 mb-2 py-2">
<WorkDateComponent /> <WorkDateComponent />

View file

@ -32,7 +32,7 @@ using Wonky.Entity.Views;
namespace Wonky.Client.Components; namespace Wonky.Client.Components;
public partial class LandingComponentAdvisor public partial class LandingComponentAdvisor
{ {
[Inject] public UserProfileService ProfileService { get; set; } [Inject] private UserProfileService ProfileService { get; set; }
private readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions private readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions
{ {

View file

@ -24,15 +24,17 @@
<div class="card-header">Danmark</div> <div class="card-header">Danmark</div>
<div class="card-body"> <div class="card-body">
<div class="list-group list-group-flush"> <div class="list-group list-group-flush">
<AuthorizeView Roles="Admin"> <AuthorizeView Roles="Admin,Office">
<a class="list-group-item list-group-item-action list-group-item-warning" href="/office/users/advisors/dk"> <a class="list-group-item list-group-item-action list-group-item-warning" href="/office/users/advisors/dk">
<i class="bi-activity"></i> Sælgere <i class="bi-activity"></i> Sælgere
</a> </a>
</AuthorizeView> </AuthorizeView>
@*
<a class="list-group-item list-group-item-action list-group-item-success" href="/office/customers/dk"> <a class="list-group-item list-group-item-action list-group-item-success" href="/office/customers/dk">
<i class="bi-phone"></i> Tlf.Ordre <i class="bi-building"></i> Tlf.Ordre
</a> </a>
<a class="list-group-item list-group-item-action list-group-item-info" href="/office/catalog/dk"> *@
<a class="list-group-item list-group-item-action list-group-item-info" href="/catalog/dk">
<i class="bi-file-spreadsheet"></i> Priser <i class="bi-file-spreadsheet"></i> Priser
</a> </a>
</div> </div>
@ -45,15 +47,17 @@
<div class="card-header">Norge</div> <div class="card-header">Norge</div>
<div class="card-body"> <div class="card-body">
<div class="list-group"> <div class="list-group">
<AuthorizeView Roles="Admin"> <AuthorizeView Roles="Admin,Office">
<a class="list-group-item list-group-item-action list-group-item-warning" href="/office/users/advisors/no"> <a class="list-group-item list-group-item-action list-group-item-warning" href="/office/users/advisors/no">
<i class="bi-activity"></i> Sælgere <i class="bi-activity"></i> Sælgere
</a> </a>
</AuthorizeView> </AuthorizeView>
@*
<a class="list-group-item list-group-item-action list-group-item-success" href="/office/customers/no"> <a class="list-group-item list-group-item-action list-group-item-success" href="/office/customers/no">
<i class="bi-phone"></i> Tlf.Ordre <i class="bi-phone"></i> Tlf.Ordre
</a> </a>
<a class="list-group-item list-group-item-action list-group-item-info" href="/office/catalog/no"> *@
<a class="list-group-item list-group-item-action list-group-item-info" href="/catalog/no">
<i class="bi-file-spreadsheet"></i> Priser <i class="bi-file-spreadsheet"></i> Priser
</a> </a>
</div> </div>
@ -66,15 +70,17 @@
<div class="card-header">Sverige</div> <div class="card-header">Sverige</div>
<div class="card-body"> <div class="card-body">
<div class="list-group"> <div class="list-group">
<AuthorizeView Roles="Admin"> <AuthorizeView Roles="Admin,Office">
<a class="list-group-item list-group-item-action list-group-item-warning" href="/office/users/advisors/se"> <a class="list-group-item list-group-item-action list-group-item-warning" href="/office/users/advisors/se">
<i class="bi-activity"></i> Sælgere <i class="bi-activity"></i> Sælgere
</a> </a>
</AuthorizeView> </AuthorizeView>
@*
<a class="list-group-item list-group-item-action list-group-item-success" href="/office/customers/se"> <a class="list-group-item list-group-item-action list-group-item-success" href="/office/customers/se">
<i class="bi-phone"></i> Tlf.Ordre <i class="bi-phone"></i> Tlf.Ordre
</a> </a>
<a class="list-group-item list-group-item-action list-group-item-info" href="/office/catalog/se"> *@
<a class="list-group-item list-group-item-action list-group-item-info" href="/catalog/se">
<i class="bi-file-spreadsheet"></i> Priser <i class="bi-file-spreadsheet"></i> Priser
</a> </a>
</div> </div>

View file

@ -30,9 +30,9 @@ using Wonky.Entity.Models;
using Wonky.Entity.Views; using Wonky.Entity.Views;
namespace Wonky.Client.Components; namespace Wonky.Client.Components;
public partial class LandingComponentAdmin public partial class LandingComponentOffice
{ {
[Inject] public UserProfileService ProfileService { get; set; } [Inject] private UserProfileService ProfileService { get; set; }
private readonly JsonSerializerOptions _options = new JsonSerializerOptions private readonly JsonSerializerOptions _options = new JsonSerializerOptions
{ {

View file

@ -0,0 +1,92 @@
@*
// 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.Entity.Views
@* Report activities *@
<table class="table table-striped">
<thead>
<tr class="bg-black opacity-75 text-white">
<th scope="col">Kunde</th>
<th scope="col">Bynavn</th>
<th scope="col">Demo</th>
<th scope="col">Salg</th>
<th scope="col">Note</th>
<th scope="col" class="text-end">sas</th>
<th scope="col" class="text-end">Beløb</th>
<th scope="col" class="text-center">
<i style="font-size:1.3em;" class="bi-phone"></i>
</th>
<th scope="col" class="text-center">
<i class="bi-lightning"></i>
</th>
<th scope="col" class="text-center">
<i class="bi-calculator"></i>
</th>
<th scope="col" class="text-center">
<i class="bi-truck"></i>
</th>
</tr>
</thead>
<tbody>
@foreach (var activity in Activities)
{
<tr>
<td class="text-sm-start">@activity.Company.Name</td>
<td class="text-sm-start">@activity.Company.City</td>
<td class="text-sm-start">@activity.Demo</td>
<td class="text-sm-start">@activity.Sales</td>
<td class="text-sm-start">@activity.OfficeNote</td>
<td class="text-end">@($"{activity.SasAmount:N2}")</td>
<td class="text-center">@(activity.StatusTypeEnum.Contains("Quote") ? $"{0:N2}" : $"{activity.OrderAmount:N2}")</td>
<td class="text-center">
@if (activity.OurRef.Contains("T:"))
{
<i style="font-size:1.5em;" class="bi-phone"></i>
}
</td>
<td class="text-center">
@if (activity.Express)
{
<i style="font-size:1.5em;" class="bi-lightning"></i>
}
</td>
<td class="text-center">
@if (activity.StatusTypeEnum == "Quote")
{
<i style="font-size:1.5em;" class="bi-calculator"></i>
}
</td>
<td class="text-center">
<ProcessStateComponent StateClass="@activity.ProcessStatusEnum"/>
</td>
</tr>
}
<tr>
<td class="bg-black opacity-75" colspan="5"></td>
<td class="text-end">Total</td>
<td class="text-end">@Activities.Where(x => x.StatusTypeEnum != "Quote").Sum(x => x.OrderAmount)</td>
<td class="bg-black opacity-75" colspan="4"></td>
</tr>
</tbody>
</table>
@code {
[Parameter]
public List<ReportItemView> Activities { get; set; } = new();
}

View file

@ -32,7 +32,11 @@
<a class="btn btn-success d-block" href="/office/users/advisors/@user.CountryCode.ToLower()/@user.UserId/customers">Kunder</a> <a class="btn btn-success d-block" href="/office/users/advisors/@user.CountryCode.ToLower()/@user.UserId/customers">Kunder</a>
</div> </div>
<div class="col-sm-2"> <div class="col-sm-2">
<a class="btn btn-info d-block" href="/office/users/advisors/@user.CountryCode.ToLower()/@user.UserId/view">Rediger</a> <AuthorizeView Roles="Admin">
<Authorized>
<a class="btn btn-info d-block" href="/office/users/advisors/@user.CountryCode.ToLower()/@user.UserId/view">Rediger</a>
</Authorized>
</AuthorizeView>
</div> </div>
</div> </div>
</div> </div>

View file

@ -19,7 +19,7 @@ using Wonky.Entity.Views;
namespace Wonky.Client.Components; namespace Wonky.Client.Components;
public partial class CountrySalesRepListComponent public partial class OfficeCountryAdvisorListComponent
{ {
[Parameter] public List<UserListAdminView> UserList { get; set; } = new(); [Parameter] public List<UserListAdminView> UserList { get; set; } = new();
} }

View file

@ -20,6 +20,7 @@
@if (CompanyList.Any()) @if (CompanyList.Any())
{ {
@*
<div class="list-group list-group-flush"> <div class="list-group list-group-flush">
<div class="list-group-item px-3 bg-black text-white opacity-75"> <div class="list-group-item px-3 bg-black text-white opacity-75">
<div class="row"> <div class="row">
@ -40,9 +41,40 @@
</div> </div>
</div> </div>
</div> </div>
*@
<div class="row d-flex g-3">
@foreach (var company in CompanyList) @foreach (var company in CompanyList)
{ {
<a class=" list-group-item list-group-item-action" href="/office/customers/@CountryCode/@company.CompanyId/view"> <div class="col-sm-6">
<div class="card">
<div class="card-body">
<h5 class="card-title">
@company.Name
</h5>
<div class="row">
<div class="col-sm-3 col-md-3 fw-bold">Konto</div>
<div class="col-sm-3 col-dm-3">@company.Account</div>
<div class="col-sm-3 col-md-3 fw-bold">CVR / ORG</div>
<div class="col-sm-3 col-md-3">@company.VatNumber</div>
</div>
<div class="row">
<div class="col-sm-3 col-md-3 fw-bold">Telefon</div>
<div class="col-sm-3 col-md-3">@company.Phone</div>
</div>
<div class="row">
<div class="col-sm-3 col-md-3 fw-bold">Adresse</div>
<div class="col-sm-9 col-md-9">@company.Address1 @(string.IsNullOrWhiteSpace(company.Address2) ? "" : ",") @company.Address2</div>
</div>
<div class="row">
<div class="col-sm-3 col-md-3 fw-bold">Post By</div>
<div class="col-sm-9 col-md-9">@company.CountryCode.ToUpper()-@company.ZipCode @company.City</div>
</div>
</div>
</div>
</div>
@*
<a class=" list-group-item list-group-item-action" href="/office/customers/@CountryCode/@company.CompanyId/order">
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col-sm-1"> <div class="col-sm-1">
@company.SalesRep @company.SalesRep
@ -61,6 +93,7 @@
</div> </div>
</div> </div>
</a> </a>
*@
} }
</div> </div>
} }

View file

@ -24,7 +24,7 @@ using Wonky.Entity.Views;
namespace Wonky.Client.Components namespace Wonky.Client.Components
{ {
public partial class CountryCustomerListComponent public partial class OfficeCountryCustomerListComponent
{ {
[Parameter] public List<CompanyDto> CompanyList { get; set; } = new(); [Parameter] public List<CompanyDto> CompanyList { get; set; } = new();
[Parameter] public string CountryCode { get; set; } = ""; [Parameter] public string CountryCode { get; set; } = "";

View file

@ -18,7 +18,7 @@ using Wonky.Entity.Views;
namespace Wonky.Client.Components; namespace Wonky.Client.Components;
public partial class CountryUserListComponent public partial class OfficeCountryUserListComponent
{ {
[Parameter] public List<UserListAdminView> UserList { get; set; } = new(); [Parameter] public List<UserListAdminView> UserList { get; set; } = new();
} }

View file

@ -1,3 +1,4 @@
@using Wonky.Client.Helpers
@* @*
// Copyright (C) 2022 FCS Frede's Computer Services. // Copyright (C) 2022 FCS Frede's Computer Services.
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
@ -15,9 +16,10 @@
// //
*@ *@
@using Wonky.Client.Helpers
@if (ActivityList.Any()) @if (ActivityList.Any())
{ {
<table class="table table-bordered d-print-table table-striped"> <table class="table table-sm table-bordered d-print-table table-striped">
<thead> <thead>
<tr class="bg-dark text-white opacity-75 border-bottom"> <tr class="bg-dark text-white opacity-75 border-bottom">
<th scope="col">Kunde</th> <th scope="col">Kunde</th>
@ -27,10 +29,10 @@
<th scope="col">Note</th> <th scope="col">Note</th>
<th class="text-end" scope="col">sas</th> <th class="text-end" scope="col">sas</th>
<th class="text-end" scope="col">Beløb</th> <th class="text-end" scope="col">Beløb</th>
<th class="text-center" scope="col"><i class="oi oi-phone"></i></th> <th class="text-center" scope="col"><i class="bi-phone"></i></th>
<th class="text-center" scope="col"><i class="oi oi-flash"></i></th> <th class="text-center" scope="col"><i class="bi-lightning"></i></th>
<th class="text-center" scope="col"><i class="oi oi-calculator"></i></th> <th class="text-center" scope="col"><i class="bi-calculator"></i></th>
<th class="text-center" scope="col"><i class="bi bi-truck"></i></th> <th class="text-center" scope="col"><i class="bi-truck"></i></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -59,7 +61,7 @@
<td class="align-middle state"> <td class="align-middle state">
@if (activity.Lines.Any() && activity.StatusTypeEnum == "Order") @if (activity.Lines.Any() && activity.StatusTypeEnum == "Order")
{ {
<ProcessStateComponent StateClass="@GetProcessStatus(activity.ProcessStatusEnum)"/> <ProcessStateComponent StateClass="@Utils.GetProcessStatus(activity.ProcessStatusEnum)"/>
} }
</td> </td>
</tr> </tr>

View file

@ -13,12 +13,18 @@
// along with this program. If not, see [https://www.gnu.org/licenses/agpl-3.0.en.html] // along with this program. If not, see [https://www.gnu.org/licenses/agpl-3.0.en.html]
// //
using Wonky.Entity.Views;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Wonky.Entity.Views;
namespace Wonky.Client.Components; namespace Wonky.Client.Components;
public partial class ReportActivityLedgerComponent public partial class OfficeReportActivityListComponent
{ {
[Parameter] public ReportData ReportData { get; set; } = new(); [Parameter] public List<ReportItemView> ActivityList { get; set; } = new();
[Inject] private NavigationManager Navigator { get; set; }
private void ShowOrder(string companyId, string orderId)
{
Navigator.NavigateTo($"/office/customers/{companyId}/orders/{orderId}");
}
} }

View file

@ -18,7 +18,7 @@ using Wonky.Entity.Views;
namespace Wonky.Client.Components; namespace Wonky.Client.Components;
public partial class ReportListOfficeComponent public partial class OfficeReportListComponent
{ {
[Parameter] public List<SalesReportListView> ReportList { get; set; } = new(); [Parameter] public List<SalesReportListView> ReportList { get; set; } = new();
[Parameter] public string UserId { get; set; } = ""; [Parameter] public string UserId { get; set; } = "";

View file

@ -23,18 +23,20 @@ namespace Wonky.Client.Components
{ {
public partial class PageSizeComponent : IDisposable public partial class PageSizeComponent : IDisposable
{ {
[Inject] public ILocalStorageService Storage { get; set; } [Inject] private ILocalStorageService Storage { get; set; }
[Inject] public UserProfileService ProfileService { get; set; } [Inject] private UserProfileService ProfileService { get; set; }
[Parameter] public EventCallback<string> OnChanged { get; set; } [Parameter] public EventCallback<string> OnChanged { get; set; }
private Dictionary<string, string> Items { get; set; } = new(); private Dictionary<string, string> Items { get; set; } = new();
private UserPref Prefs = new(); private UserPref Prefs = new();
private string PageSize { get; set; } = ""; private string PageSize { get; set; } = "";
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
ProfileService.OnChange += ProfileServiceOnOnChange; ProfileService.OnChange += ProfileServiceOnOnChange;
Prefs = await ProfileService.GetPreferences(); Prefs = await ProfileService.GetPreferences();
PageSize = Prefs.PageSize; PageSize = Prefs.PageSize;
} }
private async Task OnSelectChanged(ChangeEventArgs e) private async Task OnSelectChanged(ChangeEventArgs e)
{ {
var val = e.Value.ToString(); var val = e.Value.ToString();
@ -42,11 +44,13 @@ namespace Wonky.Client.Components
await OnChanged.InvokeAsync(val); await OnChanged.InvokeAsync(val);
await ProfileService.SetPageSize(val); await ProfileService.SetPageSize(val);
} }
private void ProfileServiceOnOnChange(UserPref newUserPref) private void ProfileServiceOnOnChange(UserPref newUserPref)
{ {
Prefs = newUserPref; Prefs = newUserPref;
StateHasChanged(); StateHasChanged();
} }
public void Dispose() public void Dispose()
{ {
ProfileService.OnChange -= ProfileServiceOnOnChange; ProfileService.OnChange -= ProfileServiceOnOnChange;

View file

@ -30,6 +30,7 @@
"the-bad" => "file-earmark-check", "the-bad" => "file-earmark-check",
"the-ugly" => "box2-fill", "the-ugly" => "box2-fill",
"the-dead" => "truck", "the-dead" => "truck",
"accepted" => "hand-thumbs-up",
_ => "question-square" _ => "question-square"
}; };
} }

View file

@ -64,20 +64,20 @@
</div> </div>
<div class="col-sm-2 text-center"> <div class="col-sm-2 text-center">
<a class="btn btn-outline-dark d-block" style="font-family:monospace;font-size: 14px;" <a class="btn btn-outline-dark d-block" style="font-family:monospace;font-size: 14px;"
href="/customers/@quote.Company.CompanyId/quotes/@quote.ActivityId">@quote.ESalesNumber</a> href="/advisor/customers/@quote.Company.CompanyId/quotes/@quote.ActivityId">@quote.ESalesNumber</a>
</div> </div>
@if (!string.IsNullOrWhiteSpace(quote.OfficeNote)) @if (!string.IsNullOrWhiteSpace(quote.OfficeNote))
{ {
<div class="col-sm-2 text-end">Note</div> <div class="col-sm-2 text-end">Kontor <i class="bi-card-text"></i></div>
<div class="col-sm-10"> <div class="col-sm-10">
<i class="bi-pencil"></i> @quote.OfficeNote @quote.OfficeNote
</div> </div>
} }
@if (!string.IsNullOrWhiteSpace(quote.CrmNote)) @if (!string.IsNullOrWhiteSpace(quote.CrmNote))
{ {
<div class="col-sm-2 text-end">CRM note</div> <div class="col-sm-2 text-end">CRM <i class="bi-card-text"></i></div>
<div class="col-sm-10"> <div class="col-sm-10">
<i class="bi-pencil"></i> @quote.CrmNote @quote.CrmNote
</div> </div>
} }
</div> </div>

View file

@ -8,11 +8,11 @@ public partial class QuoteListComponent
{ {
[Parameter] [Parameter]
public List<ReportItemView> Quotes { get; set; } = new(); public List<ReportItemView> Quotes { get; set; } = new();
[Parameter] public EventCallback<QuoteCallbackArgs> OnChangedCallback { get; set; } [Parameter] public EventCallback<QCallbackArgs> OnChangedCallback { get; set; }
private async Task SetQuote(string eSalesNumber, QStatus status) private async Task SetQuote(string eSalesNumber, QStatus status)
{ {
var args = new QuoteCallbackArgs() var args = new QCallbackArgs()
{ {
ESalesNumber = eSalesNumber, ESalesNumber = eSalesNumber,
Status = status Status = status

View file

@ -1,3 +1,4 @@
@using Wonky.Entity.Views
@* @*
// Copyright (C) 2022 FCS Frede's Computer Services. // Copyright (C) 2022 FCS Frede's Computer Services.
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
@ -84,4 +85,8 @@
</tbody> </tbody>
</table> </table>
</div> </div>
@code{
[Parameter] public ReportData ReportData { get; set; } = new();
}

View file

@ -1,43 +0,0 @@
// 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.Entity.Views;
namespace Wonky.Client.Components;
public partial class ReportActivityTableOfficeComponent
{
[Parameter] public List<ReportItemView> ActivityList { get; set; } = new();
[Inject] public NavigationManager Navigator { get; set; }
private static string GetProcessStatus(string processStatus)
{
return processStatus.ToLower() switch
{
"express" => "the-fast",
"none" => "the-good",
"picked" => "the-bad",
"packed" => "the-ugly",
"shipped" => "the-dead",
_ => "the-draw"
};
}
private void ShowOrder(string companyId, string orderId)
{
Navigator.NavigateTo($"office/customers/{companyId}/orders/{orderId}");
}
}

View file

@ -22,7 +22,7 @@
<thead> <thead>
<tr> <tr>
<th class="p-0" colspan="4"> <th class="p-0" colspan="4">
<div class="bg-light text-dark border border-1 rounded-3 pt-3 mb-2"> <div class="alert d-print-block border border-1 border-dark pt-3 mb-2">
<h2 class="fw-bold text-center">@ReportItem.Company.Name</h2> <h2 class="fw-bold text-center">@ReportItem.Company.Name</h2>
@if (ReportItem.Express) @if (ReportItem.Express)
{ {
@ -134,10 +134,10 @@
</table> </table>
@if (!string.IsNullOrWhiteSpace(@ReportItem.OfficeNote)) @if (!string.IsNullOrWhiteSpace(@ReportItem.OfficeNote))
{ {
<div class="alert alert-dark d-print-block"> <div class="alert d-print-block border border-1 border-dark">
<h4 class="text-center"> <p class="text-center h4">
@ReportItem.OfficeNote @ReportItem.OfficeNote
</h4> </p>
</div> </div>
} }
</div> </div>

View file

@ -47,7 +47,7 @@
</label> </label>
</td> </td>
<td class="align-middle"> <td class="align-middle">
<a class="btn btn-light border-dark" href="/tasks/@task.TaskItemId"> <a class="btn btn-light border-dark" href="/advisor/tasks/@task.TaskItemId">
<i class="oi oi-calendar"></i> <i class="oi oi-calendar"></i>
</a> </a>
</td> </td>
@ -64,7 +64,7 @@
<td class="align-middle"> <td class="align-middle">
@if (task.TaskTypeEnum is "Recall") @if (task.TaskTypeEnum is "Recall")
{ {
<a class="btn btn-light border-dark pe-3 me-2" href="/customers/@task.ReferenceId"> <a class="btn btn-light border-dark pe-3 me-2" href="/advisor/customers/@task.ReferenceId">
<i class="oi oi-pencil"></i> <i class="oi oi-pencil"></i>
</a> </a>
} }
@ -81,7 +81,7 @@
} }
</tbody> </tbody>
</table> </table>
<ConfirmationModal BodyMessage="Handlingen kan ikke gøres om. Vil du slette opgaven?" OnOkClicked="DeleteTask" @ref="_confirmationModal"/> <ConfirmationModal BodyMessage="Handlingen kan ikke gøres om. Vil du slette opgaven?" OnOkClicked="DeleteTask" OnCancelClicked="OnCancelCallback" @ref="_confirmationModal"/>
} }
else else
{ {

View file

@ -61,6 +61,10 @@ namespace Wonky.Client.Components
_confirmationModal.Show(); _confirmationModal.Show();
} }
private void OnCancelCallback()
{
_confirmationModal.Hide();
}
/// <summary> /// <summary>
/// Delete task call back /// Delete task call back
/// </summary> /// </summary>

View file

@ -19,9 +19,9 @@
<AuthorizeView> <AuthorizeView>
<Authorized> <Authorized>
<div class="d-print-none"> <div class="d-print-none">
<a class="btn btn-outline-light" href="logout" title="Log af" ><i class="bi-lock"></i></a> <a class="btn btn-outline-light" href="/logout" title="Log af" ><i class="bi-lock"></i></a>
<a class="btn btn-outline-light" href="info" title="Information"><i class="bi-question"></i></a> <a class="btn btn-outline-light" href="/info" title="Information"><i class="bi-question"></i></a>
<a class="btn btn-outline-light" href="preferences" title="Indstillinger"><i class="bi-sliders"></i></a> <a class="btn btn-outline-light" href="/preferences" title="Indstillinger"><i class="bi-sliders"></i></a>
</div> </div>
</Authorized> </Authorized>
</AuthorizeView> </AuthorizeView>

View file

@ -21,8 +21,8 @@
<EditForm EditContext="WorkDateContext"> <EditForm EditContext="WorkDateContext">
<div class="container-fluid"> <div class="container-fluid">
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col-sm-7 work-date"> <div class="col-sm-7 fw-bold">
@SelectedDate.ToLongDateString() d. @(SelectedDate.Day)/@(SelectedDate.Month)
</div> </div>
<div class="col-sm-5"> <div class="col-sm-5">
<InputDate class="form-control calendar" @bind-Value="SelectedDate" @oninput="OnDateChanged"/> <InputDate class="form-control calendar" @bind-Value="SelectedDate" @oninput="OnDateChanged"/>

View file

@ -14,7 +14,7 @@
</div> </div>
@foreach (var workplace in Workplaces) @foreach (var workplace in Workplaces)
{ {
<a class="list-group-item list-group-item-action" href="/customers/@CompanyId/workplaces/@workplace.WorkplaceId"> <a class="list-group-item list-group-item-action" href="/advisor/customers/@CompanyId/workplaces/@workplace.WorkplaceId">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
@workplace.Name @workplace.Name

View file

@ -18,9 +18,27 @@ using System.Net.Mail;
using Wonky.Entity.DTO; using Wonky.Entity.DTO;
namespace Wonky.Client.Helpers; namespace Wonky.Client.Helpers;
/// <summary>
/// Utilities
/// </summary>
public static class Utils public static class Utils
{ {
/// <summary>
/// return Country Name from countryCode
/// </summary>
/// <param name="countryCode"></param>
/// <returns></returns>
public static string CountryName(string countryCode)
{
return countryCode.ToLower() switch
{
"dk" => "Danmark",
"no" => "Norge",
"se" => "Sverige",
_ => ""
};
}
/// <summary> /// <summary>
/// Helper to parse querystring /// Helper to parse querystring
/// </summary> /// </summary>
@ -128,7 +146,8 @@ public static class Utils
"picked" => "the-bad", "picked" => "the-bad",
"packed" => "the-ugly", "packed" => "the-ugly",
"shipped" => "the-dead", "shipped" => "the-dead",
_ => "question-square" "accepted" => "accepted",
_ => "question"
}; };
} }
} }

View file

@ -14,6 +14,9 @@
// //
using System.Globalization; using System.Globalization;
using System.Reflection.Metadata.Ecma335;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
namespace Wonky.Client.Helpers; namespace Wonky.Client.Helpers;
@ -29,11 +32,11 @@ public class VatUtils
/// <returns></returns> /// <returns></returns>
public static bool ValidateFormat(string countryCode, string vatNumber) public static bool ValidateFormat(string countryCode, string vatNumber)
{ {
if (string.IsNullOrWhiteSpace(vatNumber) || string.IsNullOrWhiteSpace(countryCode) || !IsDigitsOnly(vatNumber)) if (string.IsNullOrWhiteSpace(vatNumber) || string.IsNullOrWhiteSpace(countryCode))
return false; return false;
var sanitisedVat = SanitizeVatNumber(vatNumber); var sanitisedVat = SanitizeVatNumber(vatNumber);
return countryCode.ToUpperInvariant() switch return countryCode.ToUpperInvariant() switch
{ {
"DK" => ValidateFormatDk(sanitisedVat), "DK" => ValidateFormatDk(sanitisedVat),
@ -43,6 +46,19 @@ public class VatUtils
}; };
} }
/// <summary>
/// Sanitize Vat remove everything but digits
/// </summary>
/// <param name="vatNumber"></param>
/// <returns></returns>
public static string SanitizeVatNumber(string vatNumber)
{
if (string.IsNullOrWhiteSpace(vatNumber))
return "";
var regexObj = new Regex(@"[^\d]");
return regexObj.Replace(vatNumber, "");
}
/// <summary> /// <summary>
/// Validate string is only numbers /// Validate string is only numbers
/// </summary> /// </summary>
@ -96,6 +112,34 @@ public class VatUtils
/// <param name="vatNumber"></param> /// <param name="vatNumber"></param>
/// <returns></returns> /// <returns></returns>
private static bool ValidateFormatSe(string vatNumber) private static bool ValidateFormatSe(string vatNumber)
{
var vatToCheck = vatNumber;
if (long.Parse(vatToCheck) == 0)
return false;
switch (vatToCheck.Length)
{
// if less than 10 chars validate as SSI
case 6:
return ValidateFormatSeExt(vatToCheck);
case < 10:
return false;
case > 10:
vatNumber = vatNumber[..10];
break;
}
// calculate check digit
var c10 = C10(vatToCheck);
// return comparison
return $"{vatToCheck[..9]}{c10}" == vatNumber;
}
/// <summary>
/// Calculate check digit for swedish org number
/// </summary>
/// <param name="orgNumber"></param>
/// <returns></returns>
private static int C10(string orgNumber)
{ {
// https://wiki.scn.sap.com/wiki/display/CRM/Sweden // https://wiki.scn.sap.com/wiki/display/CRM/Sweden
// 12 digits 0 to 9 // 12 digits 0 to 9
@ -104,23 +148,57 @@ public class VatUtils
// Si = int(Ci/5) + (Ci*2)MOD10) // Si = int(Ci/5) + (Ci*2)MOD10)
// https://www.skatteverket.se/skatter/mervardesskattmoms/momsregistreringsnummer.4.18e1b10334ebe8bc80002649.html // https://www.skatteverket.se/skatter/mervardesskattmoms/momsregistreringsnummer.4.18e1b10334ebe8bc80002649.html
// C11 C12 == 01 (De två sista siffrorna är alltid 01) // C11 C12 == 01 (De två sista siffrorna är alltid 01)
var vatToCheck = vatNumber;
if (vatToCheck.Length < 10 || long.Parse(vatToCheck) == 0)
return false;
var r = new[] { 0, 2, 4, 6, 8 } var r = new[] { 0, 2, 4, 6, 8 }
.Sum(m => (int)char.GetNumericValue(vatToCheck[m]) / 5 + .Sum(m => (int)char.GetNumericValue(orgNumber[m]) / 5 +
(int)char.GetNumericValue(vatToCheck[m]) * 2 % 10); (int)char.GetNumericValue(orgNumber[m]) * 2 % 10);
var c1 = new[] { 1, 3, 5, 7 }.Sum(m => (int)char.GetNumericValue(vatToCheck[m])); var c1 = new[] { 1, 3, 5, 7 }.Sum(m => (int)char.GetNumericValue(orgNumber[m]));
var c10 = (10 - (r + c1) % 10) % 10; var c10 = (10 - (r + c1) % 10) % 10;
if (vatToCheck.Length == 10) return c10;
{ // end check digit calculation
return $"{vatToCheck[..9]}{c10}" == vatNumber;
}
return $"{vatToCheck[..9]}{c10}01" == vatNumber;
} }
/// <summary>
///
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private static bool ValidateFormatSeExt(string data)
{
// Swedish personally held companies uses SSN number
// a relaxed validation is required as only first 6 digits is supplied
// birthday format e.g. 991231 yyMMdd
var y = int.Parse(data[..2]);
var m = int.Parse(data[2..4]);
var d = int.Parse(data[4..6]);
// this calculation is only valid within 21st century
var leap = y % 4 == 0; // 2000 was a leap year;
// day
if(d is < 1 or > 31)
return false;
// month
switch (m)
{
// feb
case 2:
{
if (leap)
return d <= 29;
return d <= 28;
}
// apr, jun, sep, nov
case 4 or 6 or 9 or 11:
return d <= 30;
// jan, mar, may, july, aug, oct, dec
case 1 or 3 or 5 or 7 or 8 or 10 or 12:
return true;
// does not exist
default:
return false;
}
}
/// <summary> /// <summary>
/// Modulus11 validator /// Modulus11 validator
/// </summary> /// </summary>
@ -139,19 +217,4 @@ public class VatUtils
return sum % 11 == 0; return sum % 11 == 0;
} }
/// <summary>
/// Sanitize Vat number to it's raw numbers
/// </summary>
/// <param name="vatNumber"></param>
/// <returns></returns>
private static string SanitizeVatNumber(string vatNumber)
{
return vatNumber.Replace(" ", "")
.Replace("-", "")
.Replace("DK", "")
.Replace("NO", "")
.Replace("SE", "")
.Replace("MVA", "");
}
} }

View file

@ -15,6 +15,7 @@
using System.Net; using System.Net;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using Blazored.LocalStorage; using Blazored.LocalStorage;
using Blazored.Toast.Services; using Blazored.Toast.Services;
@ -31,7 +32,7 @@ namespace Wonky.Client.HttpInterceptors
private readonly NavigationManager _navigation; private readonly NavigationManager _navigation;
private readonly IToastService _toast; private readonly IToastService _toast;
private readonly RefreshTokenService _refreshTokenService; private readonly RefreshTokenService _refreshTokenService;
private ILogger<HttpInterceptorService> _logger; private readonly ILogger<HttpInterceptorService> _logger;
private readonly ILocalStorageService _storage; private readonly ILocalStorageService _storage;
private readonly IAuthenticationService _authenticationService; private readonly IAuthenticationService _authenticationService;
@ -86,35 +87,37 @@ namespace Wonky.Client.HttpInterceptors
if (e.Response == null || e.Response.IsSuccessStatusCode) if (e.Response == null || e.Response.IsSuccessStatusCode)
return; return;
var message = "En fejl er opstået"; var message = $"En fejl er opstået \n {JsonSerializer.Serialize(e)}";
var currDoc = _navigation.ToBaseRelativePath(_navigation.Uri); var currDoc = _navigation.ToBaseRelativePath(_navigation.Uri);
if (currDoc.Contains("login/")) if (currDoc.Contains("login/"))
currDoc = ""; currDoc = "";
switch (e.Response.StatusCode) switch (e.Response.StatusCode)
{ {
case HttpStatusCode.NotFound: case HttpStatusCode.NotFound:
_logger.LogDebug("NotFound <= {}", currDoc); _logger.LogDebug("NotFound <= {}", currDoc);
break; break;
case HttpStatusCode.BadRequest: case HttpStatusCode.BadRequest:
_logger.LogDebug("BadRequest <= {}", currDoc); _logger.LogDebug("BadRequest <= {}", currDoc);
_logger.LogDebug("{}", message);
break; break;
case HttpStatusCode.Unauthorized: case HttpStatusCode.Unauthorized:
_logger.LogDebug("Unauthorized <= {}", currDoc);
_logger.LogDebug("{}", message);
_authenticationService.Logout(); _authenticationService.Logout();
_navigation.NavigateTo($"/login/{currDoc}"); _navigation.NavigateTo($"/login/{currDoc}");
message = "Venligst login ..."; _toast.ShowInfo("Venligst Login. Tak.");
_toast.ShowInfo(message);
break; break;
case HttpStatusCode.Conflict: case HttpStatusCode.Conflict:
_logger.LogDebug("Conflict <= {}", currDoc); _logger.LogDebug("Conflict <= {}", currDoc);
_logger.LogDebug("{}", message);
break; break;
case HttpStatusCode.InternalServerError: case HttpStatusCode.InternalServerError:
// message = "Der er interne problemer på serveren ...";
// _toast.ShowError(message);
_logger.LogDebug("InternalServerError <= {}", currDoc); _logger.LogDebug("InternalServerError <= {}", currDoc);
_logger.LogDebug("{}", message);
break; break;
default: default:
_toast.ShowError(message); _logger.LogDebug("{}", message);
break; break;
} }
// throw new HttpResponseException(message); // throw new HttpResponseException(message);

View file

@ -1,68 +0,0 @@
// 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.Features;
using Wonky.Entity.Requests;
using Wonky.Entity.Views;
namespace Wonky.Client.HttpInterfaces;
/// <summary>
/// Interface Catalog Http repository
/// </summary>
public interface IAdvisorCatalogRepository
{
/// <summary>
/// Get a paged sales item list
/// </summary>
/// <param name="pagingParameters"></param>
/// <returns></returns>
Task<PagingResponse<SalesItemView>> GetSalesItemsPaged(CatalogPaging pagingParameters);
/// <summary>
/// Get sales item by id
/// </summary>
/// <param name="salesItemId"></param>
/// <returns></returns>
Task<SalesItemView> GetSalesItemId(string salesItemId);
/// <summary>
/// Overload Get sales item by sku and country code
/// </summary>
/// <param name="sku"></param>
/// <param name="countryCode"></param>
/// <returns></returns>
Task<SalesItemView> GetSalesItemSku(string countryCode, string sku);
/// <summary>
/// Get sales item by variant id
/// </summary>
/// <param name="variantId"></param>
/// <returns></returns>
Task<SalesItemView> GetSalesVariantId(string variantId);
/// <summary>
/// Complete catalog for print
/// </summary>
/// <returns></returns>
Task<List<SalesItemView>> GetPriceList();
/// <summary>
/// Complete catalog for print country
/// </summary>
/// <param name="countryCode"></param>
/// <returns></returns>
Task<List<SalesItemView>> GetPriceList(string countryCode);
}

View file

@ -66,5 +66,5 @@ public interface IAdvisorCustomerHistoryRepository
/// <param name="companyId"></param> /// <param name="companyId"></param>
/// <param name="syncDate"></param> /// <param name="syncDate"></param>
/// <returns></returns> /// <returns></returns>
Task<string> ErpInvoiceToCrmRpc(string companyId, string syncDate); Task<string> InvoiceErpToCrmRpc(string companyId, string syncDate);
} }

View file

@ -21,7 +21,7 @@ namespace Wonky.Client.HttpInterfaces;
/// <summary> /// <summary>
/// Interface for handling Customer Workplaces (chemical document service) /// Interface for handling Customer Workplaces (chemical document service)
/// </summary> /// </summary>
public interface IWorkplaceRepository public interface IAdvisorWorkplaceRepository
{ {
/// <summary> /// <summary>
/// Get Workplaces for given customer id /// Get Workplaces for given customer id

View file

@ -28,9 +28,9 @@ public interface ICountryCatalogRepository
/// Get a paged sales item list /// Get a paged sales item list
/// </summary> /// </summary>
/// <param name="countryCode"></param> /// <param name="countryCode"></param>
/// <param name="paging"></param> /// <param name="pager"></param>
/// <returns></returns> /// <returns></returns>
Task<PagingResponse<SalesItemView>> GetSalesItemsPaged(string countryCode, CatalogPaging paging); Task<PagingResponse<SalesItemView>> GetSalesItemsPaged(string countryCode, CatalogPager pager);
/// <summary> /// <summary>
/// Get sales item by id /// Get sales item by id

View file

@ -66,5 +66,5 @@ public interface ICountryCustomerHistoryRepository
/// <param name="companyId"></param> /// <param name="companyId"></param>
/// <param name="syncDate"></param> /// <param name="syncDate"></param>
/// <returns></returns> /// <returns></returns>
Task<string> ErpInvoiceToCrmRpc(string countryCode, string companyId, string syncDate); Task<string> InvoiceErpToCrmRpc(string countryCode, string companyId, string syncDate);
} }

View file

@ -13,6 +13,7 @@
// along with this program. If not, see [https://www.gnu.org/licenses/agpl-3.0.en.html] // along with this program. If not, see [https://www.gnu.org/licenses/agpl-3.0.en.html]
// //
using Wonky.Entity.DTO;
using Wonky.Entity.Views; using Wonky.Entity.Views;
namespace Wonky.Client.HttpInterfaces; namespace Wonky.Client.HttpInterfaces;
@ -20,7 +21,7 @@ namespace Wonky.Client.HttpInterfaces;
/// <summary> /// <summary>
/// Interface for processing orders in warehouse /// Interface for processing orders in warehouse
/// </summary> /// </summary>
public interface IWarehouseRepository public interface IOrderProcessRepository
{ {
/// <summary> /// <summary>
/// Get warehouse order list by date /// Get warehouse order list by date
@ -47,7 +48,7 @@ public interface IWarehouseRepository
/// <summary> /// <summary>
/// Update Order status setting new process status /// Update Order status setting new process status
/// </summary> /// </summary>
/// <param name="process"></param> /// <param name="processState"></param>
/// <returns></returns> /// <returns></returns>
Task UpdateWarehouseOrderStatus(WarehouseProcess process); Task UpdateWarehouseOrderStatus(OrderProcessState processState);
} }

View file

@ -36,7 +36,7 @@ public class AdvisorActivityRepository : IAdvisorActivityRepository
}; };
private readonly NavigationManager _navigation; private readonly NavigationManager _navigation;
private ILogger<AdvisorActivityRepository> _logger; private readonly ILogger<AdvisorActivityRepository> _logger;
private readonly HttpClient _client; private readonly HttpClient _client;
private readonly ApiConfig _api; private readonly ApiConfig _api;
@ -166,7 +166,7 @@ public class AdvisorActivityRepository : IAdvisorActivityRepository
{ {
Code = 404, Code = 404,
IsSuccess = false, IsSuccess = false,
Message = "Uventet svare fra server", Message = "Uventet svar fra server",
Id = "" Id = ""
}; };
} }

View file

@ -1,187 +0,0 @@
// 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 System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading.Tasks;
using Wonky.Client.Features;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Options;
using Wonky.Client.HttpInterfaces;
using Wonky.Entity.Configuration;
using Wonky.Entity.DTO;
using Wonky.Entity.Requests;
using Wonky.Entity.Views;
namespace Wonky.Client.HttpRepository;
public class AdvisorCatalogRepository : IAdvisorCatalogRepository
{
private readonly JsonSerializerOptions _options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
private readonly NavigationManager _navigation;
private ILogger<AdvisorCatalogRepository> _logger;
private readonly HttpClient _client;
private readonly ApiConfig _apiConfig;
public AdvisorCatalogRepository(HttpClient client,
ILogger<AdvisorCatalogRepository> logger,
NavigationManager navigation, IOptions<ApiConfig> configuration)
{
_client = client;
_logger = logger;
_navigation = navigation;
_apiConfig = configuration.Value;
}
/// <summary>
/// Get a paged sales item list
/// </summary>
/// <param name="pagingParameters"></param>
/// <returns></returns>
public async Task<PagingResponse<SalesItemView>> GetSalesItemsPaged(CatalogPaging pagingParameters)
{
var queryString = new Dictionary<string, string>
{
["pageNumber"] = pagingParameters.PageNumber.ToString(),
["pageSize"] = pagingParameters.PageSize.ToString(),
["orderBy"] = pagingParameters.OrderBy,
["searchColumn"] = pagingParameters.SearchColumn,
["searchTerm"] = pagingParameters.SearchTerm,
["selectGroup"] = pagingParameters.SelectGroup == "0" ? "" : pagingParameters.SelectGroup,
};
var response = await _client
.GetAsync(QueryHelpers.AddQueryString($"{_apiConfig.Catalog}/page", queryString));
if (!response.IsSuccessStatusCode)
{
return new PagingResponse<SalesItemView>
{
Items = new List<SalesItemView>(),
MetaData = new MetaData()
};
}
var content = await response.Content.ReadAsStringAsync();
var pagingResponse = new PagingResponse<SalesItemView>
{
Items = JsonSerializer.Deserialize<List<SalesItemView>>(content, _options),
MetaData = JsonSerializer.Deserialize<MetaData>(
response.Headers.GetValues("X-Pagination").First(), _options)
};
return pagingResponse;
}
/// <summary>
/// Get a paged sales item list for country
/// </summary>
/// <param name="pagingParameters"></param>
/// <param name="countryCode"></param>
/// <returns></returns>
public async Task<PagingResponse<SalesItemView>> GetSalesItemsPaged(CatalogPaging pagingParameters, string countryCode)
{
var queryString = new Dictionary<string, string>
{
["pageNumber"] = pagingParameters.PageNumber.ToString(),
["pageSize"] = pagingParameters.PageSize.ToString(),
["orderBy"] = pagingParameters.OrderBy,
["searchColumn"] = pagingParameters.SearchColumn,
["searchTerm"] = pagingParameters.SearchTerm,
["selectGroup"] = pagingParameters.SelectGroup == "0" ? "" : pagingParameters.SelectGroup,
};
var response = await _client
.GetAsync(QueryHelpers.AddQueryString($"{_apiConfig.Catalog}/page", queryString));
if (!response.IsSuccessStatusCode)
{
return new PagingResponse<SalesItemView>
{
Items = new List<SalesItemView>(),
MetaData = new MetaData()
};
}
var content = await response.Content.ReadAsStringAsync();
var pagingResponse = new PagingResponse<SalesItemView>
{
Items = JsonSerializer.Deserialize<List<SalesItemView>>(content, _options),
MetaData = JsonSerializer.Deserialize<MetaData>(
response.Headers.GetValues("X-Pagination").First(), _options)
};
return pagingResponse;
}
/// <summary>
/// Get sales item by id
/// </summary>
/// <param name="salesItemId"></param>
/// <returns></returns>
public async Task<SalesItemView> GetSalesItemId(string salesItemId)
{
var salesItem = await _client
.GetFromJsonAsync<SalesItemView>($"{_apiConfig.Catalog}/{salesItemId}");
return salesItem ?? new SalesItemView();
}
/// <summary>
/// Overload Get sales item by sku and country code
/// </summary>
/// <param name="sku"></param>
/// <param name="countryCode"></param>
/// <returns></returns>
public async Task<SalesItemView> GetSalesItemSku(string countryCode, string sku)
{
var salesItem = await _client.GetFromJsonAsync<SalesItemView>($"{_apiConfig.Catalog}/sku/{countryCode}/{sku}");
return salesItem ?? new SalesItemView();
}
/// <summary>
/// Get sales item by variant id
/// </summary>
/// <param name="variantId"></param>
/// <returns></returns>
public async Task<SalesItemView> GetSalesVariantId(string variantId)
{
var salesItem = await _client
.GetFromJsonAsync<SalesItemView>($"{_apiConfig.Catalog}/variant/{variantId}");
return salesItem ?? new SalesItemView();
}
/// <summary>
/// Complete catalog for print
/// </summary>
/// <returns></returns>
public async Task<List<SalesItemView>> GetPriceList()
{
return await _client.GetFromJsonAsync<List<SalesItemView>>($"{_apiConfig.Catalog}", _options);
}
/// <summary>
/// Complete catalog for print country
/// </summary>
/// <param name="countryCode"></param>
/// <returns></returns>
public async Task<List<SalesItemView>> GetPriceList(string countryCode)
{
return await _client.GetFromJsonAsync<List<SalesItemView>>($"{_apiConfig.Catalog}/{countryCode}", _options);
}
}

View file

@ -128,9 +128,9 @@ public class AdvisorCustomerHistoryRepository : IAdvisorCustomerHistoryRepositor
/// <param name="companyId"></param> /// <param name="companyId"></param>
/// <param name="syncDate"></param> /// <param name="syncDate"></param>
/// <returns></returns> /// <returns></returns>
public async Task<string> ErpInvoiceToCrmRpc(string companyId, string syncDate) public async Task<string> InvoiceErpToCrmRpc(string companyId, string syncDate)
{ {
var x = await _client.GetAsync($"{_api.CrmCustomers}/{companyId}/{_api.CrmRpcSyncExt}/{syncDate}"); var x = await _client.GetAsync($"{_api.SyncRpc}/companies/{companyId}/{_api.SyncRpcInvoiceExt}/{syncDate}");
if (!x.IsSuccessStatusCode) if (!x.IsSuccessStatusCode)
return string.Empty; return string.Empty;
var content = await x.Content.ReadAsStringAsync(); var content = await x.Content.ReadAsStringAsync();

View file

@ -189,6 +189,8 @@ public class AdvisorCustomerRepository : IAdvisorCustomerRepository
}; };
var response = await _client.PutAsJsonAsync($"{_conf.CrmCustomers}/{companyId}/vat", model, _options); var response = await _client.PutAsJsonAsync($"{_conf.CrmCustomers}/{companyId}/vat", model, _options);
var content = await response.Content.ReadAsStringAsync(); var content = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<CompanyDto>(content); return response.IsSuccessStatusCode
? JsonSerializer.Deserialize<CompanyDto>(content)
: new CompanyDto{Name = "ERROR", VatNumber = vatNumber, CrmNotes = $"FEJL: {content}"};
} }
} }

View file

@ -35,7 +35,7 @@ public class AdvisorReportRepository : IAdvisorReportRepository
private readonly NavigationManager _navigation; private readonly NavigationManager _navigation;
private ILogger<AdvisorReportRepository> _logger; private ILogger<AdvisorReportRepository> _logger;
private readonly HttpClient _client; private readonly HttpClient _client;
private readonly ApiConfig _apiConfig; private readonly ApiConfig _api;
public AdvisorReportRepository(HttpClient client, public AdvisorReportRepository(HttpClient client,
ILogger<AdvisorReportRepository> logger, ILogger<AdvisorReportRepository> logger,
@ -44,7 +44,7 @@ public class AdvisorReportRepository : IAdvisorReportRepository
_client = client; _client = client;
_logger = logger; _logger = logger;
_navigation = navigation; _navigation = navigation;
_apiConfig = configuration.Value; _api = configuration.Value;
} }
/// <summary> /// <summary>
@ -55,7 +55,7 @@ public class AdvisorReportRepository : IAdvisorReportRepository
public async Task<bool> ReportExist(string workDate) public async Task<bool> ReportExist(string workDate)
{ {
var result = await _client.GetFromJsonAsync<SalesReportClosedView>( var result = await _client.GetFromJsonAsync<SalesReportClosedView>(
$"{_apiConfig.CrmReports}/exist/{workDate}"); $"{_api.CrmReports}/exist/{workDate}");
return result.ReportClosed; return result.ReportClosed;
} }
@ -65,7 +65,7 @@ public class AdvisorReportRepository : IAdvisorReportRepository
/// <returns></returns> /// <returns></returns>
public async Task<List<SalesReportListView>> GetReports() public async Task<List<SalesReportListView>> GetReports()
{ {
var result = await _client.GetAsync($"{_apiConfig.CrmReports}"); var result = await _client.GetAsync($"{_api.CrmReports}");
if (!result.IsSuccessStatusCode) if (!result.IsSuccessStatusCode)
return new List<SalesReportListView>(); return new List<SalesReportListView>();
return await result.Content.ReadFromJsonAsync<List<SalesReportListView>>(); return await result.Content.ReadFromJsonAsync<List<SalesReportListView>>();
@ -78,7 +78,7 @@ public class AdvisorReportRepository : IAdvisorReportRepository
/// <returns></returns> /// <returns></returns>
public async Task<ReportView> GetReport(string workDate) public async Task<ReportView> GetReport(string workDate)
{ {
var result = await _client.GetFromJsonAsync<ReportView>($"{_apiConfig.CrmReports}/{workDate}"); var result = await _client.GetFromJsonAsync<ReportView>($"{_api.CrmReports}/{workDate}");
return result ?? new ReportView(); return result ?? new ReportView();
} }
@ -90,7 +90,7 @@ public class AdvisorReportRepository : IAdvisorReportRepository
public async Task<ReportInitDto> InitializeReportData(string workDate) public async Task<ReportInitDto> InitializeReportData(string workDate)
{ {
var initData = await _client var initData = await _client
.GetFromJsonAsync<ReportInitDto>($"{_apiConfig.CrmReports}/init/{workDate}"); .GetFromJsonAsync<ReportInitDto>($"{_api.CrmReports}/init/{workDate}");
return initData ?? new ReportInitDto(); return initData ?? new ReportInitDto();
} }
@ -103,7 +103,7 @@ public class AdvisorReportRepository : IAdvisorReportRepository
public async Task<ApiResponseView> CreateReport(string workDate, ReportDto reportDto) public async Task<ApiResponseView> CreateReport(string workDate, ReportDto reportDto)
{ {
var response = await _client var response = await _client
.PostAsJsonAsync($"{_apiConfig.CrmReports}/{workDate}", reportDto, _options); .PostAsJsonAsync($"{_api.CrmReports}/{workDate}", reportDto, _options);
if (!response.IsSuccessStatusCode) if (!response.IsSuccessStatusCode)
return new ApiResponseView return new ApiResponseView
{ {

View file

@ -24,7 +24,7 @@ using Wonky.Entity.Views;
namespace Wonky.Client.HttpRepository; namespace Wonky.Client.HttpRepository;
public class WorkplaceRepository : IWorkplaceRepository public class AdvisorWorkplaceRepository : IAdvisorWorkplaceRepository
{ {
private readonly JsonSerializerOptions? _options = new JsonSerializerOptions private readonly JsonSerializerOptions? _options = new JsonSerializerOptions
{ {
@ -32,12 +32,12 @@ public class WorkplaceRepository : IWorkplaceRepository
}; };
private readonly NavigationManager _navigation; private readonly NavigationManager _navigation;
private ILogger<WorkplaceRepository> _logger; private ILogger<AdvisorWorkplaceRepository> _logger;
private readonly HttpClient _client; private readonly HttpClient _client;
private readonly ApiConfig _api; private readonly ApiConfig _api;
public WorkplaceRepository(HttpClient client, public AdvisorWorkplaceRepository(HttpClient client,
ILogger<WorkplaceRepository> logger, ILogger<AdvisorWorkplaceRepository> logger,
NavigationManager navigation, NavigationManager navigation,
IOptions<ApiConfig> configuration) IOptions<ApiConfig> configuration)
{ {

View file

@ -41,7 +41,7 @@ public class CountryCatalogRepository : ICountryCatalogRepository
private readonly NavigationManager _navigation; private readonly NavigationManager _navigation;
private ILogger<CountryCatalogRepository> _logger; private ILogger<CountryCatalogRepository> _logger;
private readonly HttpClient _client; private readonly HttpClient _client;
private readonly ApiConfig _apiConfig; private readonly ApiConfig _api;
public CountryCatalogRepository(HttpClient client, public CountryCatalogRepository(HttpClient client,
ILogger<CountryCatalogRepository> logger, ILogger<CountryCatalogRepository> logger,
@ -50,28 +50,28 @@ public class CountryCatalogRepository : ICountryCatalogRepository
_client = client; _client = client;
_logger = logger; _logger = logger;
_navigation = navigation; _navigation = navigation;
_apiConfig = configuration.Value; _api = configuration.Value;
} }
/// <summary> /// <summary>
/// Get a paged sales item list /// Get a paged sales item list
/// </summary> /// </summary>
/// <param name="countryCode"></param> /// <param name="countryCode"></param>
/// <param name="paging"></param> /// <param name="pager"></param>
/// <returns></returns> /// <returns></returns>
public async Task<PagingResponse<SalesItemView>> GetSalesItemsPaged(string countryCode, CatalogPaging paging) public async Task<PagingResponse<SalesItemView>> GetSalesItemsPaged(string countryCode, CatalogPager pager)
{ {
var queryString = new Dictionary<string, string> var queryString = new Dictionary<string, string>
{ {
["pageNumber"] = paging.PageNumber.ToString(), ["pageNumber"] = pager.PageNumber.ToString(),
["pageSize"] = paging.PageSize.ToString(), ["pageSize"] = pager.PageSize.ToString(),
["orderBy"] = paging.OrderBy, ["orderBy"] = pager.OrderBy,
["searchColumn"] = paging.SearchColumn, ["searchColumn"] = pager.SearchColumn,
["searchTerm"] = paging.SearchTerm, ["searchTerm"] = pager.SearchTerm,
["selectGroup"] = paging.SelectGroup == "0" ? "" : paging.SelectGroup ["selectGroup"] = pager.SelectGroup == "0" ? "" : pager.SelectGroup
}; };
var response = await _client var response = await _client
.GetAsync(QueryHelpers.AddQueryString($"{_apiConfig.Catalog}/country/{countryCode}/page", queryString)); .GetAsync(QueryHelpers.AddQueryString($"{_api.Catalog}/{countryCode}/page", queryString));
if (!response.IsSuccessStatusCode) if (!response.IsSuccessStatusCode)
{ {
@ -102,7 +102,7 @@ public class CountryCatalogRepository : ICountryCatalogRepository
public async Task<SalesItemView> GetSalesItemId(string countryCode, string salesItemId) public async Task<SalesItemView> GetSalesItemId(string countryCode, string salesItemId)
{ {
var salesItem = await _client var salesItem = await _client
.GetFromJsonAsync<SalesItemView>($"{_apiConfig.Catalog}/country/{countryCode}/{salesItemId}"); .GetFromJsonAsync<SalesItemView>($"{_api.Catalog}/{countryCode}/{salesItemId}");
return salesItem ?? new SalesItemView(); return salesItem ?? new SalesItemView();
} }
@ -114,7 +114,7 @@ public class CountryCatalogRepository : ICountryCatalogRepository
/// <returns></returns> /// <returns></returns>
public async Task<SalesItemView> GetSalesItemSku(string countryCode, string sku) public async Task<SalesItemView> GetSalesItemSku(string countryCode, string sku)
{ {
var salesItem = await _client.GetFromJsonAsync<SalesItemView>($"{_apiConfig.Catalog}/country/{countryCode}/sku/{sku}"); var salesItem = await _client.GetFromJsonAsync<SalesItemView>($"{_api.Catalog}/{countryCode}/sku/{sku}");
return salesItem ?? new SalesItemView(); return salesItem ?? new SalesItemView();
} }
@ -127,7 +127,7 @@ public class CountryCatalogRepository : ICountryCatalogRepository
public async Task<SalesItemView> GetSalesVariantId(string countryCode, string variantId) public async Task<SalesItemView> GetSalesVariantId(string countryCode, string variantId)
{ {
var salesItem = await _client var salesItem = await _client
.GetFromJsonAsync<SalesItemView>($"{_apiConfig.Catalog}/country/{countryCode}/variant/{variantId}"); .GetFromJsonAsync<SalesItemView>($"{_api.Catalog}/{countryCode}/variant/{variantId}");
return salesItem ?? new SalesItemView(); return salesItem ?? new SalesItemView();
} }
@ -138,6 +138,6 @@ public class CountryCatalogRepository : ICountryCatalogRepository
/// <returns></returns> /// <returns></returns>
public async Task<List<SalesItemView>> GetPriceList(string countryCode) public async Task<List<SalesItemView>> GetPriceList(string countryCode)
{ {
return await _client.GetFromJsonAsync<List<SalesItemView>>($"{_apiConfig.Catalog}/country/{countryCode}", _options); return await _client.GetFromJsonAsync<List<SalesItemView>>($"{_api.Catalog}/{countryCode}", _options);
} }
} }

View file

@ -134,9 +134,9 @@ public class CountryCustomerHistoryRepository : ICountryCustomerHistoryRepositor
/// <param name="companyId"></param> /// <param name="companyId"></param>
/// <param name="syncDate"></param> /// <param name="syncDate"></param>
/// <returns></returns> /// <returns></returns>
public async Task<string> ErpInvoiceToCrmRpc(string countryCode, string companyId, string syncDate) public async Task<string> InvoiceErpToCrmRpc(string countryCode, string companyId, string syncDate)
{ {
var x = await _client.GetAsync($"{_api.OfficeCustomers}/{countryCode}/{companyId}/{_api.CrmRpcSyncExt}/{syncDate}"); var x = await _client.GetAsync($"{_api.OfficeCustomers}/{countryCode}/{companyId}/{_api.SyncRpcInvoiceExt}/{syncDate}");
if (!x.IsSuccessStatusCode) if (!x.IsSuccessStatusCode)
return string.Empty; return string.Empty;
var content = await x.Content.ReadAsStringAsync(); var content = await x.Content.ReadAsStringAsync();

View file

@ -145,5 +145,4 @@ public class CountryCustomerRepository : ICountryCustomerRepository
Console.WriteLine(content); Console.WriteLine(content);
return response.IsSuccessStatusCode; return response.IsSuccessStatusCode;
} }
} }

View file

@ -55,7 +55,7 @@ public class CountryReportRepository : ICountryReportRepository
public async Task<bool> ReportExist(string salesRepId, string workDate) public async Task<bool> ReportExist(string salesRepId, string workDate)
{ {
var result = await _client.GetFromJsonAsync<SalesReportClosedView>( var result = await _client.GetFromJsonAsync<SalesReportClosedView>(
$"{_apiConfig.OfficeReports}/exist/{workDate}"); $"{_apiConfig.OfficeReports}/{salesRepId}/{workDate}/exist");
return result.ReportClosed; return result.ReportClosed;
} }
/// <summary> /// <summary>

View file

@ -19,11 +19,12 @@ using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Wonky.Client.HttpInterfaces; using Wonky.Client.HttpInterfaces;
using Wonky.Entity.Configuration; using Wonky.Entity.Configuration;
using Wonky.Entity.DTO;
using Wonky.Entity.Views; using Wonky.Entity.Views;
namespace Wonky.Client.HttpRepository; namespace Wonky.Client.HttpRepository;
public class WarehouseRepository : IWarehouseRepository public class OrderProcessRepository : IOrderProcessRepository
{ {
private readonly JsonSerializerOptions? _options = new JsonSerializerOptions private readonly JsonSerializerOptions? _options = new JsonSerializerOptions
{ {
@ -31,12 +32,12 @@ public class WarehouseRepository : IWarehouseRepository
}; };
private readonly NavigationManager _navigation; private readonly NavigationManager _navigation;
private ILogger<WarehouseRepository> _logger; private ILogger<OrderProcessRepository> _logger;
private readonly HttpClient _client; private readonly HttpClient _client;
private readonly ApiConfig _api; private readonly ApiConfig _api;
public WarehouseRepository(HttpClient client, public OrderProcessRepository(HttpClient client,
ILogger<WarehouseRepository> logger, ILogger<OrderProcessRepository> logger,
NavigationManager navigation, NavigationManager navigation,
IOptions<ApiConfig> configuration) IOptions<ApiConfig> configuration)
{ {
@ -81,11 +82,11 @@ public class WarehouseRepository : IWarehouseRepository
/// <summary> /// <summary>
/// Update Order status setting new process status /// Update Order status setting new process status
/// </summary> /// </summary>
/// <param name="process"></param> /// <param name="processState"></param>
/// <returns></returns> /// <returns></returns>
public async Task UpdateWarehouseOrderStatus(WarehouseProcess process) public async Task UpdateWarehouseOrderStatus(OrderProcessState processState)
{ {
_logger.LogDebug("process => {}", JsonSerializer.Serialize(process, _options)); _logger.LogDebug("process => {}", JsonSerializer.Serialize(processState, _options));
await _client.PutAsJsonAsync($"{_api.Warehouse}/{process.OrderId}", process, _options); await _client.PutAsJsonAsync($"{_api.Warehouse}/{processState.OrderId}", processState, _options);
} }
} }

View file

@ -7,5 +7,6 @@ public enum PStatus
Packed, Packed,
Shipped, Shipped,
All, All,
Express Express,
Accepted
} }

View file

@ -1,6 +1,6 @@
namespace Wonky.Client.Models; namespace Wonky.Client.Models;
public class QuoteCallbackArgs public class QCallbackArgs
{ {
/// <summary> /// <summary>
/// ESalesNumber /// ESalesNumber

View file

@ -18,21 +18,23 @@
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@using Wonky.Client.Components @using Wonky.Client.Components
@attribute [Authorize(Roles = "Advisor")] @attribute [Authorize(Roles = "Advisor")]
@page "/customers/{CompanyId}/activities/new" @page "/advisor/customers/{CompanyId}/activities/new"
<PageTitle>Ny aktivitet - @Company.Name</PageTitle>
<div class="row bg-dark text-white rounded-2 mb-2 py-2 align-items-center"> <div class="row bg-dark text-white rounded-2 mb-2 py-2 align-items-center">
<div class="col"> <div class="col">
<WorkDateComponent OnChangedCallback="WorkDateComponentCallback"/> <WorkDateComponent OnChangedCallback="WorkDateComponentCallback" />
</div> </div>
</div> </div>
@if (!string.IsNullOrWhiteSpace(_company.Blocked)) @if (!string.IsNullOrWhiteSpace(Company.Blocked))
{ {
<div class="alert alert-danger"> <div class="alert alert-danger">
<h4>Ring til kontoret. Denne konto er spærret med kode '@_company.Blocked'</h4> <h4>Ring til kontoret. Denne konto er spærret med kode '@Company.Blocked'</h4>
</div> </div>
} }
<div class="row bg-light border-1 border-dark rounded-3 p-3"> <div class="row mb-2 bg-dark text-white rounded-3 p-3">
<div class="col"> <div class="col">
<h3>@Activity.Name - @Activity.Account</h3> <h3>@Activity.Name - @Activity.Account</h3>
</div> </div>
@ -52,9 +54,9 @@ else
<EditForm EditContext="ActivityContext"> <EditForm EditContext="ActivityContext">
<DataAnnotationsValidator/> <DataAnnotationsValidator/>
<div class="row mb-1"> <div class="row mb-3 g-2">
<label for="activityType" class="col-md-2 col-form-label">Ordre Type</label> <label for="activityType" class="col-sm-2 col-form-label-sm">Ordre Type</label>
<div class="col-md-4"> <div class="col-sm-4">
<InputSelect id="activityType" class="form-select bg-primary text-bg-primary" @bind-Value="@Activity.ActivityTypeEnum"> <InputSelect id="activityType" class="form-select bg-primary text-bg-primary" @bind-Value="@Activity.ActivityTypeEnum">
<option value="">&rarr; TAG MIG &larr;</option> <option value="">&rarr; TAG MIG &larr;</option>
<option value="onSite">Besøg</option> <option value="onSite">Besøg</option>
@ -63,11 +65,11 @@ else
<ValidationMessage For="@(() => Activity.ActivityTypeEnum)"></ValidationMessage> <ValidationMessage For="@(() => Activity.ActivityTypeEnum)"></ValidationMessage>
</div> </div>
<label for="statusType" class="col-md-2 col-form-label">Status</label> <label for="statusType" class="col-sm-2 col-form-label-sm">Status</label>
<div class="col-md-4"> <div class="col-sm-4">
<InputSelect id="statusType" class="form-select bg-primary text-bg-primary" @bind-Value="@Activity.ActivityStatusEnum"> <InputSelect id="statusType" class="form-select bg-primary text-bg-primary" @bind-Value="@Activity.ActivityStatusEnum">
<option value="noSale">Ingen salg</option> <option value="noSale">Ingen salg</option>
@if (!string.IsNullOrEmpty(Activity.VatNumber) && !string.IsNullOrWhiteSpace(Activity.Address1) && _company.HasFolded == 0) @if (!string.IsNullOrEmpty(Activity.VatNumber) && !string.IsNullOrWhiteSpace(Activity.Address1) && Company.HasFolded == 0)
{ {
@if (DraftProvider.Draft.DraftType == "order") @if (DraftProvider.Draft.DraftType == "order")
{ {
@ -97,66 +99,78 @@ else
</div> </div>
} }
</div> </div>
</div>
<div class="row mb-1"> <label for="demo" class="col-sm-2 col-form-label-sm">Demo</label>
<label for="demo" class="col-md-2 col-form-label">Demo</label> <div class="col-sm-4">
<div class="col-md-4">
<InputText id="demo" class="form-control" @bind-Value="Activity.Demo"/> <InputText id="demo" class="form-control" @bind-Value="Activity.Demo"/>
<ValidationMessage For="@(() => Activity.Demo)"></ValidationMessage> <ValidationMessage For="@(() => Activity.Demo)"></ValidationMessage>
</div> </div>
<label for="email" class="col-md-2 col-form-label">Epost</label> <label for="email" class="col-sm-2 col-form-label-sm">Epost</label>
<div class="col-md-4"> <div class="col-sm-4">
<InputText id="email" class="form-control" @bind-Value="Activity.Email"/> <InputText id="email" class="form-control" @bind-Value="Activity.Email"/>
<ValidationMessage For="@(() => Activity.Email)"></ValidationMessage> <ValidationMessage For="@(() => Activity.Email)"></ValidationMessage>
</div> </div>
</div>
<div class="row mb-1"> <label for="referenceNumber" class="col-sm-2 col-form-label-sm">Rekvisition</label>
<label for="referenceNumber" class="col-md-2 col-form-label">Rekvisition</label> <div class="col-sm-4">
<div class="col-md-4">
<InputText id="referenceNumber" class="form-control" @bind-Value="Activity.ReferenceNumber"/> <InputText id="referenceNumber" class="form-control" @bind-Value="Activity.ReferenceNumber"/>
<ValidationMessage For="@(() => Activity.ReferenceNumber)"></ValidationMessage> <ValidationMessage For="@(() => Activity.ReferenceNumber)"></ValidationMessage>
</div> </div>
<label for="yourRef" class="col-md-2 col-form-label">Indkøber</label> <label for="yourRef" class="col-sm-2 col-form-label-sm">Indkøber</label>
<div class="col-md-4"> <div class="col-sm-4">
<InputText id="yourRef" class="form-control" @bind-Value="Activity.YourRef"/> <InputText id="yourRef" class="form-control" @bind-Value="Activity.YourRef"/>
<ValidationMessage For="@(() => Activity.YourRef)"></ValidationMessage> <ValidationMessage For="@(() => Activity.YourRef)"></ValidationMessage>
</div> </div>
</div>
<div class="row mb-1"> <label for="attention" class="col-sm-2 col-form-label-sm">Att.</label>
<label for="orderMessage" class="col-md-2 col-form-label">Note /Kontor</label> <div class="col-sm-4">
<div class="col-md-4">
<InputTextArea id="orderMessage" class="form-control" @bind-Value="Activity.OrderMessage"/>
<ValidationMessage For="@(() => Activity.OrderMessage)"></ValidationMessage>
</div>
<label for="crmNote" class="col-md-2 col-form-label">Note /Selv</label>
<div class="col-md-4">
<InputTextArea id="crmNote" class="form-control" @bind-Value="Activity.CrmNote"/>
<ValidationMessage For="@(() => Activity.CrmNote)"></ValidationMessage>
</div>
</div>
<div class="row mb-2">
<label for="attention" class="col-md-2 col-form-label">Att.</label>
<div class="col-md-4">
<InputText id="attention" class="form-control" @bind-Value="Activity.Attention"/> <InputText id="attention" class="form-control" @bind-Value="Activity.Attention"/>
<ValidationMessage For="@(() => Activity.Attention)"></ValidationMessage> <ValidationMessage For="@(() => Activity.Attention)"></ValidationMessage>
</div> </div>
<label for="phone" class="col-md-2 col-form-label">Tlf.</label> <label for="phone" class="col-sm-2 col-form-label-sm">Tlf.</label>
<div class="col-md-4"> <div class="col-sm-4">
<InputText id="phone" class="form-control" @bind-Value="Activity.Phone"/> <InputText id="phone" class="form-control" @bind-Value="Activity.Phone"/>
<ValidationMessage For="@(() => Activity.Phone)"></ValidationMessage> <ValidationMessage For="@(() => Activity.Phone)"></ValidationMessage>
</div> </div>
</div>
<label for="orderMessage" class="col-sm-2 col-form-label-sm">Note /Kontor</label>
<div class="col-sm-4">
<InputTextArea id="orderMessage" class="form-control" @bind-Value="Activity.OrderMessage"/>
<ValidationMessage For="@(() => Activity.OrderMessage)"></ValidationMessage>
</div>
<label for="crmNote" class="col-sm-2 col-form-label-sm">Note /Selv</label>
<div class="col-sm-4">
<InputTextArea id="crmNote" class="form-control" @bind-Value="Activity.CrmNote"/>
<ValidationMessage For="@(() => Activity.CrmNote)"></ValidationMessage>
</div>
<div class="col-sm-6"></div>
<label for="vatNumber" class="col-sm-2 col-form-label-sm">Cvr/Org nr.</label>
<div class="col-sm-4">
<InputText id="vatNumber" class="form-control" @bind-Value="Activity.VatNumber" />
<ValidationMessage For="@(() => Activity.VatNumber)" />
</div>
</div>
<div class="row g-2 mb-3">
<div class="col-sm-3 d-grid mx-auto">
<button class="btn btn-danger" disabled="@string.IsNullOrWhiteSpace(Activity.ActivityTypeEnum)" @onclick="ShowInvoiceOverlay">Faktura</button>
</div>
<div class="col-sm-3 d-grid mx-auto">
<button class="btn btn-warning" disabled="@string.IsNullOrWhiteSpace(Activity.ActivityTypeEnum)" @onclick="ShowVisitOverlay">Tidl. besøg</button>
@* <button class="btn btn-warning" disabled @onclick="ShowVisitOverlay">Tidl. besøg</button> *@
</div>
<div class="col-sm-3 d-grid mx-auto">
<button class="btn btn-success" disabled="@string.IsNullOrWhiteSpace(Activity.ActivityTypeEnum)" @onclick="ShowInventoryOverlay">Produkter</button>
</div>
</div>
<div id="this-draft" style="@(Activity.ActivityStatusEnum is "order" or "quote" ? "display: block" : "display:none")"> <div id="this-draft" style="@(Activity.ActivityStatusEnum is "order" or "quote" ? "display: block" : "display:none")">
@* Order lines -----------------------------------------------------*@ @* Draft lines in draft -----------------------------------------------------*@
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<table class="sticky-top table table-hover table-striped table-bordered"> <table class="sticky-top table table-hover table-striped table-bordered">
@ -207,7 +221,7 @@ else
<td class="align-middle text-black text-end fw-bold">@($"{DraftProvider.Draft.Total:N2}")</td> <td class="align-middle text-black text-end fw-bold">@($"{DraftProvider.Draft.Total:N2}")</td>
<td></td> <td></td>
<td class="align-middle text-end"> <td class="align-middle text-end">
<button class="btn btn-primary" type="button" @onclick="CallPriceListModal"> <button class="btn btn-primary" type="button" @onclick="ShowPriceListOverlay">
<i class="bi-plus"></i> Ny linje <i class="bi-plus"></i> Ny linje
</button> </button>
</td> </td>
@ -216,7 +230,7 @@ else
</table> </table>
</div> </div>
</div> </div>
@* draft line ----------------------------------------------------- *@ @* Create Draft line ----------------------------------------------------- *@
<div class="row"> <div class="row">
<div class="col"> <div class="col">
@if (!string.IsNullOrWhiteSpace(SelectedItem.Name) && ShowItem) @if (!string.IsNullOrWhiteSpace(SelectedItem.Name) && ShowItem)
@ -227,9 +241,9 @@ else
<th scope="col" colspan="6">Kladdelinje</th> <th scope="col" colspan="6">Kladdelinje</th>
</tr> </tr>
<tr> <tr>
<th scope="col">Antal</th> <th style="min-width:100px;" scope="col">Antal</th>
<th scope="col">Pris</th> <th style="min-width:200px;" scope="col">Pris</th>
<th scope="col">Rabat</th> <th style="min-width:100px;" scope="col">Rabat</th>
<th class="align-content-center justify-content-center" scope="col">SAS</th> <th class="align-content-center justify-content-center" scope="col">SAS</th>
<th scope="col">Varenr.</th> <th scope="col">Varenr.</th>
<th scope="col"></th> <th scope="col"></th>
@ -237,18 +251,18 @@ else
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td class="align-middle"> <td class="align-middle" style="min-width:100px;">
<input type="number" class="form-control" @bind-value="@Quantity"/> <input type="number" class="form-control" @bind-value="@Quantity"/>
</td> </td>
<td class="align-middle"> <td class="align-middle" style="min-width:200px;">
<div class="input-group"> <div class="input-group">
<input type="number" class="form-control" @bind-value="@Price"/> <input type="number" class="form-control" @bind-value="@Price"/>
<button class="btn btn-warning" type="button" @onclick="CallPriceHistoryModal"> <button class="btn btn-warning" type="button" @onclick="ShowPriceHistoryOverlay">
<i class="bi-list-ul"></i> <i class="bi-list-ul"></i>
</button> </button>
</div> </div>
</td> </td>
<td class="align-middle"> <td class="align-middle" style="min-width:100px;">
<input type="number" class="form-control" @bind-value="@Discount"/> <input type="number" class="form-control" @bind-value="@Discount"/>
</td> </td>
<td class="align-middle align-content-center justify-content-center"> <td class="align-middle align-content-center justify-content-center">
@ -256,7 +270,7 @@ else
</td> </td>
<td class="align-middle">@SelectedItem.Sku</td> <td class="align-middle">@SelectedItem.Sku</td>
<td class="align-middle"> <td class="align-middle">
<button type="button" class="btn btn-primary text-nowrap d-block" @onclick="@(() => AddItem(SelectedItem))">BESTIL @SelectedItem.Name</button> <button type="button" class="btn btn-primary d-block text-sm-center" @onclick="@(() => AddItem(SelectedItem))">@Quantity stk. @SelectedItem.Name</button>
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -278,31 +292,31 @@ else
aria-labelledby="deliveryHeader" data-bs-parent="#crmActivity"> aria-labelledby="deliveryHeader" data-bs-parent="#crmActivity">
<div class="accordion-body"> <div class="accordion-body">
<div class="row mb-1"> <div class="row mb-1">
<label for="dlvName" class="col-md-2 col-form-label">Lev. Navn</label> <label for="dlvName" class="col-sm-2 col-form-label-sm">Lev. Navn</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="dlvName" class="form-control" @bind-Value="Activity.DlvName"/> <InputText id="dlvName" class="form-control" @bind-Value="Activity.DlvName"/>
</div> </div>
</div> </div>
<div class="row mb-1"> <div class="row mb-1">
<label for="dlvAddress1" class="col-md-2 col-form-label">Lev. Adresse</label> <label for="dlvAddress1" class="col-sm-2 col-form-label-sm">Lev. Adresse</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="dlvAddress1" class="form-control" @bind-Value="Activity.DlvAddress1"/> <InputText id="dlvAddress1" class="form-control" @bind-Value="Activity.DlvAddress1"/>
</div> </div>
</div> </div>
<div class="row mb-1"> <div class="row mb-1">
<label for="dlvAddress2" class="col-md-2 col-form-label">Lev. Adresse</label> <label for="dlvAddress2" class="col-sm-2 col-form-label-sm">Lev. Adresse</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="dlvAddress2" class="form-control" @bind-Value="Activity.DlvAddress2"/> <InputText id="dlvAddress2" class="form-control" @bind-Value="Activity.DlvAddress2"/>
</div> </div>
</div> </div>
<div class="row mb-1"> <div class="row mb-1">
<label for="dlvZipCode" class="col-md-2 col-form-label">Lev. Postnr</label> <label for="dlvZipCode" class="col-sm-2 col-form-label-sm">Lev. Postnr</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="dlvZipCode" class="form-control" @bind-Value="Activity.DlvZipCode"/> <InputText id="dlvZipCode" class="form-control" @bind-Value="Activity.DlvZipCode"/>
</div> </div>
</div> </div>
<div class="row mb-1"> <div class="row mb-1">
<label for="dlvCity" class="col-md-2 col-form-label">Lev. Bynavn</label> <label for="dlvCity" class="col-sm-2 col-form-label-sm">Lev. Bynavn</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="dlvCity" class="form-control" @bind-Value="Activity.DlvCity"/> <InputText id="dlvCity" class="form-control" @bind-Value="Activity.DlvCity"/>
</div> </div>
@ -314,16 +328,31 @@ else
</EditForm> </EditForm>
<div class="row mt-5 mb-2"> <div class="row mt-5 mb-2">
<div class="col-sm-6"> <div class="col-sm-6">
<a class="btn btn-warning" href="/customers/@_company.CompanyId">Kundekort <i class="bi-arrow-left"></i></a> <a class="btn btn-warning" href="/advisor/customers/@Company.CompanyId">Kundekort <i class="bi-arrow-left"></i></a>
</div> </div>
<div class="col-sm-4 text-end"> <div class="col-sm-4 text-end">
<button type="button" class="btn btn-warning" @onclick="CallConfirmProductCheckModel" disabled="@(PoFormInvalid || Working)"><i class="bi-save-fill"></i> @ButtonText</button> <button type="button" class="btn btn-warning" @onclick="CallConfirmCheckOverlay" disabled="@(PoFormInvalid || Working)"><i class="bi-cloud-arrow-up"></i> @ButtonText</button>
</div> </div>
</div> </div>
} }
<ConfirmWorkDateModal BodyMessage="@PromptDateConfirm" OnOkClicked="WorkDateConfirmCallback" @ref="ConfirmWorkDateModal"/> <ConfirmWorkDateModalOverlay BodyMessage="@PromptDateConfirm"
<PriceListModal OnSelected="PriceListCallback" @ref="PriceListModal"/> OnOkClicked="WorkDateConfirmCallback" @ref="WorkDateOverlay"/>
<ProductHistoryModal CompanyId="@CompanyId" ItemSku="@SelectedItem.Sku" @ref="HistoryModal"/>
<ProductPriceHistoryModal OnSelected="PriceHistoryCallback" CompanyId="@CompanyId" Sku="@SelectedItem.Sku" @ref="PriceHistoryModal"/> <PriceCatalogModalOverlay CountryCode="@Company.CountryCode.ToLower()"
<ConfirmProductCheckModal BodyMessage="" CompanyId="@CompanyId" Products="CheckList" OnOkClicked="ConfirmProductCheckCallback" @ref="ConfirmProductCheckModal" /> OnSelected="PriceListCallback" @ref="CatalogOverlay"/>
<ProductHistoryModalOverlay CompanyId="@CompanyId" ItemSku="@SelectedItem.Sku" @ref="ProductOverlay"/>
<ProductPriceHistoryModal CompanyId="@CompanyId" Sku="@SelectedItem.Sku"
OnSelected="PriceHistoryCallback" @ref="PriceOverlay"/>
<ConfirmProductCheckModalOverlay BodyMessage="" CompanyId="@CompanyId" Products="CheckList"
OnOkClicked="ConfirmProductCheckCallback" @ref="ProductCheckOverlay" />
<CustomerInvoiceListModalOverlay CustomerInvoices="CompanyInvoices" @ref="InvoiceListOverlay" />
<CustomerInventoryListModalOverlay CompanyName="@Company.Name" CompanyId="@CompanyId" CountryCode="@Company.CountryCode"
OnInventorySelected="OnInventoryCallback" Inventory="Inventory" @ref="InventoryListOverlay" />
<CustomerActivityListModalOverlay Activities="Activities" CompanyName="@Company.Name" @ref="ActivityListOverlay" />

View file

@ -30,29 +30,30 @@ using Wonky.Entity.Views;
namespace Wonky.Client.Pages; namespace Wonky.Client.Pages;
public partial class AdvisorCreateActivityPage : IDisposable public partial class AdvisorActivityCreatePage : IDisposable
{ {
// Parameters
[CascadingParameter] DraftStateProvider DraftProvider { get; set; }
[Parameter] public string CompanyId { get; set; }
// Services // Services
[Inject] public ILogger<AdvisorCreateActivityPage> Logger { get; set; } [Inject] private ILogger<AdvisorActivityCreatePage> Logger { get; set; }
[Inject] public HttpInterceptorService Interceptor { get; set; } [Inject] private HttpInterceptorService Interceptor { get; set; }
[Inject] public UserProfileService Profiles { get; set; } [Inject] private UserProfileService Profiles { get; set; }
[Inject] public IToastService Toast { get; set; } [Inject] private IToastService Toaster { get; set; }
[Inject] public NavigationManager Navigator { get; set; } [Inject] private NavigationManager Navigator { get; set; }
[Inject] public ILocalStorageService Storage { get; set; } [Inject] private ILocalStorageService Storage { get; set; }
[Inject] public IAdvisorCatalogRepository AdvisorCatalogCrm { get; set; } [Inject] private ICountryCatalogRepository CatalogRepo { get; set; }
[Inject] public IAdvisorCustomerRepository CompanyRepo { get; set; } [Inject] private IAdvisorCustomerRepository CompanyRepo { get; set; }
[Inject] public IAdvisorActivityRepository AdvisorActivityRepo { get; set; } [Inject] private IAdvisorActivityRepository ActivityRepo { get; set; }
[Inject] public IAdvisorReportRepository AdvisorReportRepo { get; set; } [Inject] private IAdvisorReportRepository ReportRepo { get; set; }
[Inject] public IAdvisorCustomerHistoryRepository HistoryRepo { get; set; } [Inject] private IAdvisorCustomerHistoryRepository HistoryRepo { get; set; }
[CascadingParameter] private DraftStateProvider DraftProvider { get; set; } = new();
[Parameter] public string CompanyId { get; set; }
// variables // variables
private readonly JsonSerializerOptions _options = new() {PropertyNameCaseInsensitive = true}; private readonly JsonSerializerOptions _options = new() {PropertyNameCaseInsensitive = true};
private SalesItemView SelectedItem { get; set; } = new(); private SalesItemView SelectedItem { get; set; } = new();
private UserPref UserPrefs { get; set; } = new(); private UserPref UserPrefs { get; set; } = new();
private ActivityDto Activity { get; set; } = new(); private ActivityDto Activity { get; set; } = new();
private CompanyDto _company = new(); private CompanyDto Company = new();
private EditContext? ActivityContext { get; set; } private EditContext? ActivityContext { get; set; }
private bool PoFormInvalid { get; set; } = true; private bool PoFormInvalid { get; set; } = true;
private bool ShowItem { get; set; } private bool ShowItem { get; set; }
@ -64,21 +65,30 @@ public partial class AdvisorCreateActivityPage : IDisposable
private bool InvalidActivity { get; set; } = true; private bool InvalidActivity { get; set; } = true;
private bool ReportClosed { get; set; } private bool ReportClosed { get; set; }
private bool Working { get; set; } = true; private bool Working { get; set; } = true;
private UserInfoView ThisUserInfo { get; set; } = new(); private UserInfoView SalesRep { get; set; } = new();
private DateTime SelectedDate { get; set; } private DateTime SelectedDate { get; set; }
private string OldPhone { get; set; } = ""; private string OldPhone { get; set; } = "";
private string PromptDateConfirm { get; set; } = ""; private string PromptDateConfirm { get; set; } = "";
// MODAL DIALOGS
private PriceListModal PriceListModal { get; set; } = new(); // OVERLAY PAGES
private ProductHistoryModal HistoryModal { get; set; } = new(); private PriceCatalogModalOverlay CatalogOverlay { get; set; } = new();
private ProductPriceHistoryModal PriceHistoryModal { get; set; } = new(); private ProductHistoryModalOverlay ProductOverlay { get; set; } = new();
private ConfirmWorkDateModal ConfirmWorkDateModal { get; set; } = new(); private ProductPriceHistoryModal PriceOverlay { get; set; } = new();
private ConfirmProductCheckModal ConfirmProductCheckModal { get; set; } = new(); private ConfirmWorkDateModalOverlay WorkDateOverlay { get; set; } = new();
private ConfirmProductCheckModalOverlay ProductCheckOverlay { get; set; } = new();
private CustomerInvoiceListModalOverlay InvoiceListOverlay { get; set; } = new();
private CustomerInventoryListModalOverlay InventoryListOverlay { get; set; } = new();
private CustomerActivityListModalOverlay ActivityListOverlay { get; set; } = new();
private List<ProductInventoryView> Inventory { get; set; } = new();
private List<ProductInventoryView> CheckList { get; set; } = new(); private List<ProductInventoryView> CheckList { get; set; } = new();
private InvoiceListView CompanyInvoices { get; set; } = new();
private List<ReportItemView> Activities { get; set; } = new();
private string ButtonText { get; set; } = "Gem besøg"; private string ButtonText { get; set; } = "Gem besøg";
private bool OrgWarning { get; set; }
/// <summary> /// <summary>
/// Page initialization /// Page initialization
/// </summary> /// </summary>
@ -93,54 +103,55 @@ public partial class AdvisorCreateActivityPage : IDisposable
// User Preferences // User Preferences
UserPrefs = await Profiles.GetPreferences(); UserPrefs = await Profiles.GetPreferences();
// User Info // User Info
ThisUserInfo = await Storage.GetItemAsync<UserInfoView>("_xu"); SalesRep = await Storage.GetItemAsync<UserInfoView>("_xu");
// Fetch Customer from http // Fetch Customer from http
_company = await CompanyRepo.GetCompanyById(CompanyId); Company = await CompanyRepo.GetCompanyById(CompanyId);
if (_company.HasFolded == 1) if (Company.HasFolded == 1)
// Company has shutdown activities // Company has shutdown activities
Activity.OrderMessage = "BEMÆRK: CVR nummer er ophørt."; Activity.OrderMessage = "BEMÆRK: CVR nummer er ophørt.";
// variable to validate if customer needs phone number update // variable to validate if customer needs phone number update
OldPhone = _company.Phone; OldPhone = Company.Phone;
if (string.IsNullOrWhiteSpace(_company.Phone) if (string.IsNullOrWhiteSpace(Company.Phone)
&& !string.IsNullOrWhiteSpace(_company.Account) && !string.IsNullOrWhiteSpace(Company.Account)
&& _company.Account != "NY" && _company.Account.Length > 7) && Company.Account != "NY" && Company.Account.Length > 7)
{ {
_company.Phone = _company.Account[..8]; Company.Phone = Company.Account[..8];
} }
// Populate base activity information // Populate base activity information
Activity.BcId = _company.BcId; Activity.BcId = Company.BcId;
Activity.ActivityStatusEnum = "noSale"; Activity.ActivityStatusEnum = "noSale";
Activity.VisitTypeEnum = _company.Account is "" or "NY" ? "new" : "recall"; Activity.VisitTypeEnum = Company.Account is "" or "NY" ? "new" : "recall";
Activity.CompanyId = _company.CompanyId; Activity.CompanyId = Company.CompanyId;
Activity.SalesRepId = ThisUserInfo.Id; Activity.SalesRepId = SalesRep.Id;
Activity.SalesRep = ThisUserInfo.Advisor; Activity.SalesRep = SalesRep.Advisor;
Activity.CountryCode = ThisUserInfo.CountryCode; Activity.CountryCode = SalesRep.CountryCode;
Activity.Account = _company.Account; Activity.Account = Company.Account;
Activity.VatNumber = _company.VatNumber; Activity.VatNumber = Company.VatNumber;
Activity.Email = _company.Email; Activity.Email = Company.Email;
Activity.Phone = _company.Phone; Activity.Phone = Company.Phone;
Activity.Mobile = _company.Mobile; Activity.Mobile = Company.Mobile;
Activity.Name = _company.Name; Activity.Name = Company.Name;
Activity.Address1 = _company.Address1; Activity.Address1 = Company.Address1;
Activity.Address2 = _company.Address2; Activity.Address2 = Company.Address2;
Activity.ZipCode = _company.ZipCode; Activity.ZipCode = Company.ZipCode;
Activity.City = _company.City; Activity.City = Company.City;
Activity.DlvName = _company.Name; Activity.DlvName = Company.Name;
Activity.DlvAddress1 = _company.Address1; Activity.DlvAddress1 = Company.Address1;
Activity.DlvAddress2 = _company.Address2; Activity.DlvAddress2 = Company.Address2;
Activity.DlvZipCode = _company.ZipCode; Activity.DlvZipCode = Company.ZipCode;
Activity.DlvCity = _company.City; Activity.DlvCity = Company.City;
// Initialize date variable // Initialize date variable
SelectedDate = string.IsNullOrWhiteSpace(UserPrefs.WorkDate) ? DateTime.Now : DateTime.Parse(UserPrefs.WorkDate); SelectedDate = string.IsNullOrWhiteSpace(UserPrefs.WorkDate) ? DateTime.Now : DateTime.Parse(UserPrefs.WorkDate);
// raise flag if report is closed // raise flag if report is closed
ReportClosed = await AdvisorReportRepo.ReportExist($"{SelectedDate:yyyy-MM-dd}"); ReportClosed = await ReportRepo.ReportExist($"{SelectedDate:yyyy-MM-dd}");
// Ask for confirmation of date // Ask for confirmation of date
Logger.LogDebug("Preferences.DateConfirmed => {}", UserPrefs.DateConfirmed); Logger.LogDebug("Preferences.DateConfirmed => {}", UserPrefs.DateConfirmed);
if (!UserPrefs.DateConfirmed) if (!UserPrefs.DateConfirmed)
{ {
PromptDateConfirm = $"Aktiviteter oprettes med dato {SelectedDate.ToShortDateString()}. Er dette OK?"; PromptDateConfirm = $"Aktiviteter oprettes med dato {SelectedDate.ToShortDateString()}. Er dette OK?";
ConfirmWorkDateModal.Show(); WorkDateOverlay.Show();
} }
// Lines may already have been added from the company inventory page // Lines may already have been added from the company inventory page
if (DraftProvider.Draft.DraftType == "order") if (DraftProvider.Draft.DraftType == "order")
@ -151,14 +162,90 @@ public partial class AdvisorCreateActivityPage : IDisposable
PoFormInvalid = false; PoFormInvalid = false;
} }
Working = false; Working = false;
//StateHasChanged();
} }
private async Task CallConfirmProductCheckModel() private async Task ShowVisitOverlay()
{
Logger.LogDebug("ShowInventoryOverlay - wait for visits");
ActivityListOverlay.Show();
Activities = await ActivityRepo.GetCustomerActivities(CompanyId);
await Task.Delay(500);
}
private async Task ShowInventoryOverlay()
{
Logger.LogDebug("ShowInventoryOverlay - wait for inventory");
InventoryListOverlay.Show();
Inventory = await HistoryRepo.FetchInventory(CompanyId);
await Task.Delay(500);
}
private async Task OnInventoryCallback(DraftItem item)
{
Activity.ActivityStatusEnum = "order";
DraftProvider.Draft.DraftType = "order";
DraftProvider.Draft.Items.Add(item);
StateHasChanged();
}
private async Task ShowInvoiceOverlay()
{
Logger.LogDebug("ShowInvoiceOverlay - wait for invoices");
InvoiceListOverlay.Show();
CompanyInvoices = await FetchCompanyInvoices();
await Task.Delay(500);
}
private async Task<InvoiceListView> FetchCompanyInvoices()
{
// fetch from storage
var storage = await Storage.GetItemAsStringAsync($"{CompanyId}-invoices");
await Task.Delay(500);
var iDate = await Storage.GetItemAsStringAsync($"{CompanyId}-iDate");
// if we have a list and iDate was today return the list
if (!string.IsNullOrWhiteSpace(storage) && (!string.IsNullOrWhiteSpace(iDate) && DateTime.Parse(iDate.Replace("\"", "")) >= DateTime.Now))
{
Logger.LogDebug("fetching invoices from storage");
Logger.LogDebug("storage contains <= {}", storage);
return JsonSerializer.Deserialize<InvoiceListView>(storage);
}
Logger.LogDebug("pulling invoices from backend");
// pull invoices
var companyInvoices = await HistoryRepo.FetchInvoiceList(CompanyId);
// send invoices to storage
await Storage.SetItemAsync($"{CompanyId}-invoices", companyInvoices);
await Storage.SetItemAsync($"{CompanyId}-iDate", $"{DateTime.Now:yyyy-MM-dd}");
Logger.LogDebug(" --> return invoices from backend");
Working = false;
Logger.LogDebug("backend contains <= {}", JsonSerializer.Serialize(companyInvoices));
return companyInvoices;
}
private void ShowOrgWarning()
{
if (OrgWarning)
return;
OrgWarning = true;
if (Company.CountryCode.ToLower() == "se" && VatUtils.SanitizeVatNumber(Activity.VatNumber).Length < 10 && Activity.ActivityStatusEnum == "order")
{
Toaster.ShowWarning("Org nummer er ufuldstændig. Skal opdateres før bestilling kan sendes. ", "ADVARSEL");
}
}
private async Task CallConfirmCheckOverlay()
{ {
// check if new account // check if new account
if (string.IsNullOrWhiteSpace(_company.Account) if (string.IsNullOrWhiteSpace(Company.Account)
|| _company.Account.ToLower() == "ny" || Company.Account.ToLower() == "ny"
|| Activity.ActivityStatusEnum.ToLower() == "quote") || Activity.ActivityStatusEnum.ToLower() == "quote")
{ {
// proceed to create activity - as there is no product check to be done // proceed to create activity - as there is no product check to be done
@ -172,23 +259,26 @@ public partial class AdvisorCreateActivityPage : IDisposable
Logger.LogDebug("pStorage => {}", pStorage); Logger.LogDebug("pStorage => {}", pStorage);
// fetch pDate from storage // fetch pDate from storage
var pDate = await Storage.GetItemAsync<string>($"{CompanyId}-pDate"); var pDate = await Storage.GetItemAsync<string>($"{CompanyId}-pDate");
if (string.IsNullOrWhiteSpace(pDate))
pDate = $"{DateTime.Now.AddDays(-1):yyyy-MM-dd}";
Logger.LogDebug("pDate => {}", pDate); Logger.LogDebug("pDate => {}", pDate);
// check if product data is valid and updated today // check if product data is valid and updated today
if (string.IsNullOrWhiteSpace(pStorage) || pDate.Replace("\"", "") != $"{DateTime.Now:yyyy-MM-dd}") if (string.IsNullOrWhiteSpace(pStorage) || pDate.Replace("\"", "") != $"{DateTime.Now:yyyy-MM-dd}")
{ {
Working = true; Working = true;
// pop a message // pop a message
Toast.ShowError("Produkt gennemgang mangler. Vent mens produkt oversigt indlæses. Gå ikke væk fra siden!", "Produkt check ..."); Toaster.ShowError("Produkt gennemgang mangler. Vent mens produkt oversigt indlæses. Gå ikke væk fra siden!", "Produkt check ...");
// product inventory has not been updated // product inventory has not been updated
// send rpc call to sync ERP to CRM // send rpc call to sync ERP to CRM
Toast.ShowInfo("Vent mens data synkroniseres ...", "ERP til CRM ..."); Toaster.ShowInfo("Vent mens data synkroniseres ...", "ERP til CRM ...");
var ts = await HistoryRepo.ErpInvoiceToCrmRpc(CompanyId, _company.HistorySync); var ts = await HistoryRepo.InvoiceErpToCrmRpc(CompanyId, Company.HistorySync);
while (string.IsNullOrWhiteSpace(ts)) while (string.IsNullOrWhiteSpace(ts))
await Task.Delay(500); await Task.Delay(500);
// save pDate // save pDate
await Storage.SetItemAsync($"{CompanyId}-pDate", ts); await Storage.SetItemAsync($"{CompanyId}-pDate", ts);
// request products from backend // request products from backend
Toast.ShowInfo("Vent mens produkt oversigt hentes", "CRM produkt liste"); Toaster.ShowInfo("Vent mens produkt oversigt hentes", "CRM produkt liste");
CheckList = await HistoryRepo.FetchInventory(CompanyId); CheckList = await HistoryRepo.FetchInventory(CompanyId);
if(CheckList.Any()) if(CheckList.Any())
@ -206,11 +296,12 @@ public partial class AdvisorCreateActivityPage : IDisposable
} }
// Show CheckList modal // Show CheckList modal
ConfirmProductCheckModal.Show(); ProductCheckOverlay.Show();
} }
private async Task ConfirmProductCheckCallback() private async Task ConfirmProductCheckCallback()
{ {
ConfirmProductCheckModal.Hide(); ProductCheckOverlay.Hide();
await CreateActivity(); await CreateActivity();
foreach (var item in CheckList) foreach (var item in CheckList)
{ {
@ -226,7 +317,7 @@ public partial class AdvisorCreateActivityPage : IDisposable
private async Task WorkDateConfirmCallback() private async Task WorkDateConfirmCallback()
{ {
await Profiles.SetDateConfirmed(true); await Profiles.SetDateConfirmed(true);
ConfirmWorkDateModal.Hide(); WorkDateOverlay.Hide();
StateHasChanged(); StateHasChanged();
} }
@ -236,7 +327,7 @@ public partial class AdvisorCreateActivityPage : IDisposable
/// <param name="workDate"></param> /// <param name="workDate"></param>
private async Task WorkDateComponentCallback(string workDate) private async Task WorkDateComponentCallback(string workDate)
{ {
ReportClosed = await AdvisorReportRepo.ReportExist(workDate); ReportClosed = await ReportRepo.ReportExist(workDate);
SelectedDate = DateTime.Parse(workDate); SelectedDate = DateTime.Parse(workDate);
Activity.ActivityDate = workDate; Activity.ActivityDate = workDate;
} }
@ -244,9 +335,9 @@ public partial class AdvisorCreateActivityPage : IDisposable
/// <summary> /// <summary>
/// Show Price list modal /// Show Price list modal
/// </summary> /// </summary>
private void CallPriceListModal() private void ShowPriceListOverlay()
{ {
PriceListModal.Show(); CatalogOverlay.Show();
} }
/// <summary> /// <summary>
@ -258,7 +349,7 @@ public partial class AdvisorCreateActivityPage : IDisposable
// get selected item // get selected item
if (string.IsNullOrWhiteSpace(sku.ItemId)) if (string.IsNullOrWhiteSpace(sku.ItemId))
return; return;
SelectedItem = await AdvisorCatalogCrm.GetSalesItemId(sku.ItemId); SelectedItem = await CatalogRepo.GetSalesItemId(SalesRep.CountryCode.ToLower(), sku.ItemId);
ShowItem = true; ShowItem = true;
Price = sku.Rate; Price = sku.Rate;
Quantity = sku.Quantity; Quantity = sku.Quantity;
@ -268,10 +359,10 @@ public partial class AdvisorCreateActivityPage : IDisposable
/// <summary> /// <summary>
/// Show Price History modal /// Show Price History modal
/// </summary> /// </summary>
private void CallPriceHistoryModal() private void ShowPriceHistoryOverlay()
{ {
if(ShowItem) if(ShowItem)
PriceHistoryModal.Show(); PriceOverlay.Show();
} }
/// <summary> /// <summary>
@ -294,26 +385,35 @@ public partial class AdvisorCreateActivityPage : IDisposable
// avoid duplication // avoid duplication
if (Working) if (Working)
return; return;
// validate customer address1 - this is a required input // validate customer address1
// - this is a required input
if (string.IsNullOrWhiteSpace(Activity.Address1)) if (string.IsNullOrWhiteSpace(Activity.Address1))
{ {
Toast.ShowError("Kunde adresse er ufuldstændig."); Toaster.ShowError("Kunde adresse er ufuldstændig.");
return; return;
} }
// // validate org number
// - this is a required input
// - must validate according to country rules.
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 // don't accept order with no lines
case "order" when DraftProvider.Draft.Items.Count == 0: case "order" when DraftProvider.Draft.Items.Count == 0:
Toast.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 // phone number is required if first time customer
case "order" when _company.Account is "NY" or "" && string.IsNullOrWhiteSpace(Activity.Phone): case "order" when Company.Account is "NY" or "" && string.IsNullOrWhiteSpace(Activity.Phone):
Toast.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 // verify email address is a valid address
case "quote" when !Utils.IsValidEmail(Activity.Email): case "quote" when !Utils.IsValidEmail(Activity.Email):
Toast.ShowError("Ved tilbud skal en gyldig email adresse angives."); Toaster.ShowError("Ved tilbud skal en gyldig email adresse angives.");
return; return;
} }
// raise working flag // raise working flag
@ -324,15 +424,16 @@ public partial class AdvisorCreateActivityPage : IDisposable
// check if phone number need to be updated // check if phone number need to be updated
if (OldPhone != Activity.Phone) if (OldPhone != Activity.Phone)
{ {
_company.Phone = Activity.Phone; Company.Phone = Activity.Phone;
await CompanyRepo.UpdateErpData(_company.CompanyId, _company); Activity.OrderMessage = $"Telefonnr. opdateret.\n{Activity.OrderMessage}";
await CompanyRepo.UpdateErpData(Company.CompanyId, Company);
} }
// begin assembling activity // begin assembling activity
Activity.ActivityDate = $"{SelectedDate:yyyy-MM-dd}"; Activity.ActivityDate = $"{SelectedDate:yyyy-MM-dd}";
Activity.OurRef = Activity.ActivityTypeEnum switch Activity.OurRef = Activity.ActivityTypeEnum switch
{ {
"phone" => $"T:{ThisUserInfo.FullName.Split(" ")[0]}", "phone" => $"T:{SalesRep.FullName.Split(" ")[0]}",
"onSite" => $"B:{ThisUserInfo.FullName.Split(" ")[0]}", "onSite" => $"B:{SalesRep.FullName.Split(" ")[0]}",
_ => "" _ => ""
}; };
if (Activity.Express) if (Activity.Express)
@ -361,22 +462,22 @@ public partial class AdvisorCreateActivityPage : IDisposable
// debug logging // debug logging
Logger.LogDebug("CrmNewActivityPage => \n {}", JsonSerializer.Serialize(Activity)); Logger.LogDebug("CrmNewActivityPage => \n {}", JsonSerializer.Serialize(Activity));
// post to api // post to api
var result = await AdvisorActivityRepo.CreateActivity(Activity); var result = await ActivityRepo.CreateActivity(Activity);
// debug logging // debug logging
Logger.LogDebug("ApiResponseView => \n {}", JsonSerializer.Serialize(result)); Logger.LogDebug("ApiResponseView => \n {}", JsonSerializer.Serialize(result));
// show result message // show result message
if (result.IsSuccess) if (result.IsSuccess)
{ {
Toast.ShowSuccess($"{result.Message}", Toaster.ShowSuccess($"{result.Message}",
DraftProvider.Draft.Items.Count == 0 ? "Besøg er oprettet" : "Bestilling/Tilbud er oprettet"); DraftProvider.Draft.Items.Count == 0 ? "Besøg er oprettet" : "Bestilling/Tilbud er oprettet");
await DeleteDraft(); await DeleteDraft();
Navigator.NavigateTo($"/customers"); Navigator.NavigateTo($"/advisor/customers");
return; return;
} }
// lower working flag // lower working flag
Working = false; Working = false;
// show error message // show error message
Toast.ShowError(result.Message, "ORDRE FEJL"); Toaster.ShowError(result.Message, "ORDRE FEJL");
} }
/// <summary> /// <summary>
@ -446,12 +547,11 @@ public partial class AdvisorCreateActivityPage : IDisposable
DraftProvider.Draft.Items = new List<DraftItem>(); DraftProvider.Draft.Items = new List<DraftItem>();
} }
ButtonText = Activity.ActivityStatusEnum switch ButtonText = Activity.ActivityStatusEnum.ToLower() switch
{ {
"noSale" => "Gem Besøg", "nosale" => "Gem Besøg",
"order" => "Gem Bestilling", "order" => "Send Bestilling",
"quote" => "Gem Tilbud", "quote" => "Send Tilbud"
_ => ButtonText
}; };
// InvalidCanvas = InvalidActivityType; // InvalidCanvas = InvalidActivityType;
@ -477,10 +577,20 @@ public partial class AdvisorCreateActivityPage : IDisposable
{ {
if (string.IsNullOrEmpty(Activity.ActivityTypeEnum) && !ReportClosed) if (string.IsNullOrEmpty(Activity.ActivityTypeEnum) && !ReportClosed)
{ {
Toast.ShowWarning("Aktivitet type kan ikke være tom"); Toaster.ShowWarning("Aktivitet type kan ikke være tom");
PoFormInvalid = true; PoFormInvalid = true;
return; return;
} }
if (Activity.ActivityStatusEnum.ToLower() is "order" or "quote"
&& Company.CountryCode.ToLower() == "se"
&& VatUtils.SanitizeVatNumber(Activity.VatNumber).Length < 10)
{
ShowOrgWarning();
PoFormInvalid = true;
return;
}
PoFormInvalid = false; PoFormInvalid = false;
ActivityContext.OnFieldChanged -= HandleFieldChanged; ActivityContext.OnFieldChanged -= HandleFieldChanged;
ActivityContext.OnValidationStateChanged -= ValidationChanged; ActivityContext.OnValidationStateChanged -= ValidationChanged;

View file

@ -18,8 +18,8 @@
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@using Wonky.Client.Components @using Wonky.Client.Components
@attribute [Authorize(Roles = "Advisor")] @attribute [Authorize(Roles = "Advisor")]
@page "/activity-today" @page "/advisor/activity-today"
<PageTitle>Aktiviteter for @($"{SelectedDate:yyyy-MM-dd}")</PageTitle>
<div class="row bg-dark text-white rounded-2 mb-2 py-2 align-items-center"> <div class="row bg-dark text-white rounded-2 mb-2 py-2 align-items-center">
<div class="col-sm-7"> <div class="col-sm-7">
<WorkDateComponent OnChangedCallback="GetActivities"/> <WorkDateComponent OnChangedCallback="GetActivities"/>
@ -30,17 +30,17 @@
<div class="col-sm-2 text-end"> <div class="col-sm-2 text-end">
@if (ReportExist) @if (ReportExist)
{ {
<a class="btn btn-info" href="/sales-reports/view/@($"{SelectedDate:yyyy-MM-dd}")">Rapport <i style="font-size: 1.2em;" class="bi-info-lg"></i></a> <a class="btn btn-info" href="/advisor/reports/view/@($"{SelectedDate:yyyy-MM-dd}")">Rapport <i style="font-size: 1.2em;" class="bi-info-lg"></i></a>
} }
else else
{ {
<a class="btn btn-primary" href="/sales-reports/new">Rapport <i style="font-size:1.2em;" class="bi-plus-lg"></i></a> <a class="btn btn-primary" href="/advisor/reports/new">Rapport <i style="font-size:1.2em;" class="bi-plus-lg"></i></a>
} }
</div> </div>
</div> </div>
@if (ReportStatusView.ReportItems.Any()) @if (ReportStatusView.ReportItems.Any())
{ {
<ActivityListComponent ActivityList="ReportStatusView.ReportItems"/> <AdvisorActivityListComponent ActivityList="ReportStatusView.ReportItems"/>
} }
@if (Working) @if (Working)
{ {

View file

@ -25,15 +25,15 @@ using Wonky.Entity.Views;
namespace Wonky.Client.Pages; namespace Wonky.Client.Pages;
public partial class AdvisorTodayActivityListPage : IDisposable public partial class AdvisorActivityTodayListPage : IDisposable
{ {
[Inject] public UserProfileService UserProfileService { get; set; } [Inject] private UserProfileService UserProfileService { get; set; }
[Inject] public ILogger<AdvisorTodayActivityListPage> Logger { get; set; } [Inject] private ILogger<AdvisorActivityTodayListPage> Logger { get; set; }
[Inject] public HttpInterceptorService Interceptor { get; set; } [Inject] private HttpInterceptorService Interceptor { get; set; }
[Inject] public NavigationManager Navigator { get; set; } [Inject] private NavigationManager Navigator { get; set; }
[Inject] public IAdvisorActivityRepository AdvisorActivityRepo { get; set; } [Inject] private IAdvisorActivityRepository AdvisorActivityRepo { get; set; }
[Inject] public IAdvisorReportRepository AdvisorReportRepo { get; set; } [Inject] private IAdvisorReportRepository AdvisorReportRepo { get; set; }
[Inject] public IToastService Toaster { get; set; } [Inject] private IToastService Toaster { get; set; }
private ReportStatusView? ReportStatusView { get; set; } = new(); private ReportStatusView? ReportStatusView { get; set; } = new();
private UserPref UserPref { get; set; } = new(); private UserPref UserPref { get; set; } = new();
private DateTime SelectedDate { get; set; } private DateTime SelectedDate { get; set; }

View file

@ -19,9 +19,10 @@
@using Wonky.Client.Components @using Wonky.Client.Components
@attribute [Authorize(Roles = "Admin,Advisor,Warehouse")] @attribute [Authorize(Roles = "Admin,Advisor,Warehouse")]
@page "/customers/{CompanyId}/orders/{OrderId}" @page "/advisor/customers/{CompanyId}/orders/{OrderId}"
@page "/customers/{CompanyId}/quotes/{OrderId}" @page "/advisor/customers/{CompanyId}/quotes/{OrderId}"
<PageTitle>@ReportItem.Company.Name @ReportItem.OrderDate</PageTitle>
@* <ReportItemComponent ReportItem="@_item" /> *@ @* <ReportItemComponent ReportItem="@_item" /> *@
<table class="table table-sm table-striped d-print-table"> <table class="table table-sm table-striped d-print-table">
@ -129,7 +130,7 @@
</tbody> </tbody>
</table> </table>
@* Office Note *@ @* Office Note *@
@if (ReportItem.ProcessStatusEnum == "None" && !ReportItem.Express && AllowOfficeNoteUpdate()) @if (ReportItem is { ProcessStatusEnum: "None",Express: false } && AllowOfficeNoteUpdate())
{ {
<div class="alert border border-1 border-primary"> <div class="alert border border-1 border-primary">
<EditForm EditContext="NoteContext"> <EditForm EditContext="NoteContext">

View file

@ -29,15 +29,15 @@ using Wonky.Entity.Views;
namespace Wonky.Client.Pages; namespace Wonky.Client.Pages;
public partial class AdvisorViewActivityPage : IDisposable public partial class AdvisorActivityViewPage : IDisposable
{ {
[Parameter] public string CompanyId { get; set; } = ""; [Parameter] public string CompanyId { get; set; } = "";
[Parameter] public string OrderId { get; set; } = ""; [Parameter] public string OrderId { get; set; } = "";
[Inject] public HttpInterceptorService Interceptor { get; set; } [Inject] private HttpInterceptorService Interceptor { get; set; }
[Inject] public IAdvisorActivityRepository AdvisorActivityRepo { get; set; } [Inject] private IAdvisorActivityRepository AdvisorActivityRepo { get; set; }
[Inject] public ILogger<AdvisorViewActivityPage> Logger { get; set; } [Inject] private ILogger<AdvisorActivityViewPage> Logger { get; set; }
[Inject] public IToastService Toaster { get; set; } [Inject] private IToastService Toaster { get; set; }
[Inject] public NavigationManager Navigator { get; set; } [Inject] private NavigationManager Navigator { get; set; }
private ReportItemView ReportItem { get; set; } = new(); private ReportItemView ReportItem { get; set; } = new();
private ActivityOfficeNote Note { get; set; } = new(); private ActivityOfficeNote Note { get; set; } = new();
private EditContext NoteContext { get; set; } private EditContext NoteContext { get; set; }
@ -70,7 +70,7 @@ public partial class AdvisorViewActivityPage : IDisposable
Logger.LogDebug("OfficeNote => \n {}", JsonSerializer.Serialize(Note)); Logger.LogDebug("OfficeNote => \n {}", JsonSerializer.Serialize(Note));
await AdvisorActivityRepo.UpdateOfficeNote(Note); await AdvisorActivityRepo.UpdateOfficeNote(Note);
Toaster.ShowInfo($"{ReportItem.ESalesNumber} - notat opdateret"); Toaster.ShowInfo($"{ReportItem.ESalesNumber} - notat opdateret");
Navigator.NavigateTo("/activity-today"); Navigator.NavigateTo("/advisor/activity-today");
} }
private bool AllowOfficeNoteUpdate() private bool AllowOfficeNoteUpdate()

View file

@ -1,52 +0,0 @@
@*
// 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 Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "Advisor")]
@page "/price-catalog"
<div class="sticky-top bg-dark rounded-2 px-3">
<div class="row g-3 mb-3">
<div class="col-sm-2">
<CatalogGroupComponent OnChanged="SetGroupCol"/>
</div>
<div class="col-sm-2">
<CatalogSearchComponent OnChanged="SetSearchCol"/>
</div>
<div class="col-sm-4">
<CatalogSearchPhraseComponent OnChanged="SetSearchPhrase"/>
</div>
<div class="col-sm-2">
<CatalogSortComponent OnChanged="SetSortCol"/>
</div>
<div class="col-sm-2">
<PageSizeComponent OnChanged="SetPageSize"/>
</div>
<div class="col-sm-10">
<PaginationComponent MetaData="MetaInfo" Spread="2" SelectedPage="SetSelectedPage"/>
</div>
<div class="col-sm-2 text-end">
<a class="btn btn-secondary" href="/print/catalog"><i class="bi-printer"></i> Udskriv</a>
</div>
</div>
</div>
<CatalogListComponent ItemList="Items"/>
@if (Working)
{
<WorkingThreeDots />
}

View file

@ -1,119 +0,0 @@
// 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 System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Blazored.LocalStorage;
using Wonky.Client.HttpInterceptors;
using Wonky.Client.HttpRepository;
using Microsoft.AspNetCore.Components;
using Wonky.Client.Components;
using Wonky.Client.HttpInterfaces;
using Wonky.Client.Services;
using Wonky.Entity.DTO;
using Wonky.Entity.Requests;
using Wonky.Entity.Views;
namespace Wonky.Client.Pages;
public partial class AdvisorCatalogPage : IDisposable
{
[Inject] public ILocalStorageService Storage { get; set; }
[Inject] public IAdvisorCatalogRepository ItemRepo { get; set; }
[Inject] public HttpInterceptorService Interceptor { get; set; }
[Inject] public UserProfileService ProfileService { get; set; }
private List<SalesItemView> Items { get; set; } = new();
private MetaData MetaInfo { get; set; } = new();
private CatalogPaging _page = new();
private UserPref Prefs = new();
private UserInfoView UserInfo { get; set; } = new();
private bool Working { get; set; } = true;
protected override async Task OnInitializedAsync()
{
Prefs = await ProfileService.GetPreferences();
UserInfo = await Storage.GetItemAsync<UserInfoView>("_xu");
_page.OrderBy = Prefs.ItemSort;
_page.SearchColumn = Prefs.ItemSearch;
_page.PageSize = Convert.ToInt32(Prefs.PageSize);
Interceptor.RegisterEvent();
Interceptor.RegisterBeforeSendEvent();
await FetchSalesItems();
}
private async Task SetSearchPhrase(string searchTerm)
{
Items = new List<SalesItemView>();
_page.PageNumber = 1;
_page.SearchTerm = searchTerm;
await FetchSalesItems();
}
private async Task SetPageSize(string pageSize)
{
Items = new List<SalesItemView>();
_page.PageSize = Convert.ToInt32(pageSize);
_page.PageNumber = 1;
await FetchSalesItems();
}
private async Task SetSearchCol(string columnName)
{
Items = new List<SalesItemView>();
_page.PageNumber = 1;
_page.SearchColumn = columnName;
await FetchSalesItems();
}
private async Task SetSortCol(string orderBy)
{
Items = new List<SalesItemView>();
_page.OrderBy = orderBy;
await FetchSalesItems();
}
private async Task SetSelectedPage(int page)
{
Items = new List<SalesItemView>();
_page.PageNumber = page;
await FetchSalesItems();
}
private async Task SetGroupCol(string groupFilter)
{
Items = new List<SalesItemView>();
_page.PageNumber = 1;
_page.SelectGroup = groupFilter;
await FetchSalesItems();
}
private async Task FetchSalesItems()
{
Working = true;
var pagingResponse = await ItemRepo.GetSalesItemsPaged(_page);
Working = false;
Items = pagingResponse.Items!;
MetaInfo = pagingResponse.MetaData;
}
public void Dispose() => Interceptor.DisposeEvent();
}

View file

@ -18,7 +18,9 @@
@using Wonky.Client.Components @using Wonky.Client.Components
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "Advisor")] @attribute [Authorize(Roles = "Advisor")]
@page "/customers/{CompanyId}/activities" @page "/advisor/customers/{CompanyId}/activities"
<PageTitle>Rådgiver Aktiviteter for @Company.Name</PageTitle>
@if (!string.IsNullOrWhiteSpace(Company.Name)) @if (!string.IsNullOrWhiteSpace(Company.Name))
{ {
@ -27,70 +29,14 @@
<h4 class="pt-1">@Company.Name</h4> <h4 class="pt-1">@Company.Name</h4>
</div> </div>
<div class="col-sm-3 align-content-end"> <div class="col-sm-3 align-content-end">
<a class="btn btn-primary d-block" href="/customers/@Company.CompanyId"><i class="bi-arrow-right"></i> Kundekort</a> <a class="btn btn-primary d-block" href="/advisor/customers/@Company.CompanyId"><i class="bi-arrow-right"></i> Kundekort</a>
</div> </div>
<div class="col-sm-3 align-content-end"> <div class="col-sm-3 align-content-end">
<a class="btn btn-primary d-block" href="/customers/@Company.CompanyId/activities/new"><i class="bi-arrow-right"></i> Besøg</a> <a class="btn btn-primary d-block" href="/advisor/customers/@Company.CompanyId/activities/new"><i class="bi-arrow-right"></i> Besøg</a>
</div> </div>
</div> </div>
<div class="list-group"> <CustomerVisitListComponent Activities="Activities" />
<div class="list-group-item">
<div class="row">
<div class="col">
<h4>Dato</h4>
</div>
<div class="col">
<h4>Demo</h4>
</div>
<div class="col">
<h4>Salg</h4>
</div>
<div class="col">
<h4>Note /Kontor</h4>
</div>
<div class="col">
<h4>Note /Selv</h4>
</div>
</div>
</div>
@if (Activities.Any())
{
@foreach (var activity in Activities)
{
<div class="list-group-item">
<div class="row">
<div class="col">
@activity.OrderDate
</div>
<div class="col">
@activity.Demo
</div>
<div class="col">
@activity.Sales
</div>
<div class="col">
@activity.OfficeNote
</div>
<div class="col">
@activity.CrmNote
</div>
</div>
</div>
}
}
else
{
<div class="list-group-item">
<div class="row">
<div class="col">
Ingen data
</div>
</div>
</div>
}
</div>
} }
@if (Working) @if (Working)
{ {

View file

@ -28,9 +28,9 @@ namespace Wonky.Client.Pages;
public partial class AdvisorCustomerActivityListPage : IDisposable public partial class AdvisorCustomerActivityListPage : IDisposable
{ {
[Parameter] public string CompanyId { get; set; } = ""; [Parameter] public string CompanyId { get; set; } = "";
[Inject] public HttpInterceptorService _interceptor { get; set; } [Inject] private HttpInterceptorService _interceptor { get; set; }
[Inject] public IAdvisorActivityRepository AdvisorActivityRepo { get; set; } [Inject] private IAdvisorActivityRepository AdvisorActivityRepo { get; set; }
[Inject] public IAdvisorCustomerRepository CompanyRepo { get; set; } [Inject] private IAdvisorCustomerRepository CompanyRepo { get; set; }
private List<ReportItemView> Activities { get; set; } = new(); private List<ReportItemView> Activities { get; set; } = new();
private CompanyDto Company { get; set; } = new(); private CompanyDto Company { get; set; } = new();
private bool Working { get; set; } = true; private bool Working { get; set; } = true;

View file

@ -15,12 +15,13 @@
// //
*@ *@
@page "/customers/new" @page "/advisor/customers/new"
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@using Wonky.Client.Components @using Wonky.Client.Components
@using System.Xml @using System.Xml
@attribute [Authorize(Roles = "Advisor")] @attribute [Authorize(Roles = "Advisor")]
<PageTitle>Rådgiver Opret Kunde</PageTitle>
<h2>Opret kunde</h2> <h2>Opret kunde</h2>
<EditForm EditContext="CompanyContext" OnValidSubmit="SubmitCompanyForm"> <EditForm EditContext="CompanyContext" OnValidSubmit="SubmitCompanyForm">
@ -30,7 +31,20 @@
<div class="row g-2"> <div class="row g-2">
@* vat lookup *@ @* vat lookup *@
<div class="col-sm-12 text-end"> <div class="col-sm-12 text-end">
<button type="button" class="btn btn-primary" @onclick="CallVatLookupModal">CVR opslag</button> <div class="d-grid mx-auto">
@switch (Company.CountryCode)
{
case "dk":
<button type="button" class="btn btn-info" @onclick="CallVatLookupModal"><i class="bi-search"></i> Firma søgning</button>
break;
case "no":
<a class="btn btn-info" href="https://brreg.no/" target="_blank"><i class="bi-search"></i> Firma søgning</a>
break;
case "se":
<a class="btn btn-info" href="https://www.allabolag.se/@(string.IsNullOrWhiteSpace(Company.Name) ? "" : "what/@Company.Name")" target="_blank"><i class="bi-search"></i> Firma søgning</a>
break;
}
</div>
</div> </div>
@* entity name *@ @* entity name *@
<label for="name" class="col-sm-1 col-form-label-sm">Navn</label> <label for="name" class="col-sm-1 col-form-label-sm">Navn</label>
@ -69,7 +83,7 @@
<ValidationMessage For="@(() => Company.City)"></ValidationMessage> <ValidationMessage For="@(() => Company.City)"></ValidationMessage>
</div> </div>
@* entity vat number *@ @* entity vat number *@
<label for="vatNumber" class="col-sm-1 col-form-label-sm">Moms Nr</label> <label for="vatNumber" class="col-sm-1 col-form-label-sm">Cvr/Org Nr.</label>
<div class="col-sm-3"> <div class="col-sm-3">
<div class="input-group"> <div class="input-group">
<span class="input-group-text"> <span class="input-group-text">
@ -99,7 +113,7 @@
</div> </div>
</div> </div>
<hr class="mb-3"/> <hr class="mb-3"/>
<div class="row g-2"> <div class="row g-2 mb-3">
<label for="note" class="col-sm-1 col-form-label-sm">OBS</label> <label for="note" class="col-sm-1 col-form-label-sm">OBS</label>
<div class="col-sm-5"> <div class="col-sm-5">
<InputText id="note" class="form-control" @bind-Value="Company.Note"/> <InputText id="note" class="form-control" @bind-Value="Company.Note"/>
@ -124,7 +138,7 @@
</div> </div>
</div> </div>
<div class="row mb-3"> <div class="row mb-3">
<div class="col-sm-12 text-end"> <div class="col-sm-12 d-grid mx-auto">
<button type="submit" class="btn btn-success" disabled="@FormInvalid">Opret</button> <button type="submit" class="btn btn-success" disabled="@FormInvalid">Opret</button>
</div> </div>
</div> </div>

View file

@ -37,15 +37,15 @@ using Wonky.Entity.Views;
namespace Wonky.Client.Pages namespace Wonky.Client.Pages
{ {
public partial class AdvisorCreateCustomerPage : IDisposable public partial class AdvisorCustomerCreatePage : IDisposable
{ {
[Inject] public IToastService Toaster { get; set; } [Inject] private IToastService Toaster { get; set; }
[Inject] public ILogger<AdvisorCreateCustomerPage> Logger { get; set; } [Inject] private ILogger<AdvisorCustomerCreatePage> Logger { get; set; }
[Inject] public ILocalStorageService Storage { get; set; } [Inject] private ILocalStorageService Storage { get; set; }
[Inject] public NavigationManager Navigator { get; set; } [Inject] private NavigationManager Navigator { get; set; }
[Inject] public IAdvisorCustomerRepository CompanyRepo { get; set; } [Inject] private IAdvisorCustomerRepository CompanyRepo { get; set; }
[Inject] public HttpInterceptorService Interceptor { get; set; } [Inject] private HttpInterceptorService Interceptor { get; set; }
[Inject] public VatInfoLookupService VatService { get; set; } [Inject] private VatInfoLookupService VatService { get; set; }
private EditContext CompanyContext { get; set; } private EditContext CompanyContext { get; set; }
private CompanyDto Company { get; set; } = new(); private CompanyDto Company { get; set; } = new();
private VirkRegInfo CompanyRegInfo { get; set; } = new(); private VirkRegInfo CompanyRegInfo { get; set; } = new();
@ -136,7 +136,7 @@ namespace Wonky.Client.Pages
/// <param name="regInfo"></param> /// <param name="regInfo"></param>
private void SelectCompanyCallback(VirkRegInfo regInfo) private void SelectCompanyCallback(VirkRegInfo regInfo)
{ {
Logger.LogDebug($"CrmCompanyView => SelectCompanyCallback => {JsonSerializer.Serialize(regInfo)}"); Logger.LogDebug("CrmCompanyView => SelectCompanyCallback => {}", JsonSerializer.Serialize(regInfo));
// this can be removed in favor of the new data returned from updating the VatNumber // this can be removed in favor of the new data returned from updating the VatNumber
RegState = regInfo.States[0].State.ToLower() == "normal" ? "the-good" : "the-dead"; RegState = regInfo.States[0].State.ToLower() == "normal" ? "the-good" : "the-dead";
@ -177,7 +177,7 @@ namespace Wonky.Client.Pages
if (!string.IsNullOrWhiteSpace(newId)) if (!string.IsNullOrWhiteSpace(newId))
{ {
Toaster.ShowSuccess($"'{Company.Name}' er oprettet i CRM."); Toaster.ShowSuccess($"'{Company.Name}' er oprettet i CRM.");
Navigator.NavigateTo($"/customers/{newId}"); Navigator.NavigateTo($"/advisor/customers/{newId}");
} }
else else
{ {

View file

@ -17,24 +17,24 @@
@using Wonky.Client.Components @using Wonky.Client.Components
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@page "/customers/{CompanyId}/h/i" @page "/advisor/customers/{CompanyId}/h/i"
@attribute [Authorize(Roles = "Advisor")] @attribute [Authorize(Roles = "Advisor")]
<PageTitle>Produkt oversigt for @Company.Name</PageTitle>
<div class="row pt-2 pb-1 rounded-2 bg-dark text-white"> <div class="row pt-2 pb-1 rounded-2 bg-dark text-white">
<div class="col-sm-6"> <div class="col-sm-6">
<h4 class="pt-1">@Company.Name</h4> <h4 class="pt-1">@Company.Name</h4>
</div> </div>
<div class="col-sm-3 align-content-end"> <div class="col-sm-3 align-content-end">
<a class="btn btn-primary d-block" href="/customers/@CompanyId"><i class="bi-arrow-left"></i> Kundekort</a> <a class="btn btn-primary d-block" href="/advisor/customers/@CompanyId"><i class="bi-arrow-right"></i> Kundekort</a>
</div> </div>
<div class="col-sm-3 align-content-end"> <div class="col-sm-3 align-content-end">
<a class="btn btn-primary d-block" href="/customers/@CompanyId/activities/new"><i class="bi-arrow-right"></i> Nyt Besøg</a> <a class="btn btn-primary d-block" href="/advisor/customers/@CompanyId/activities/new"><i class="bi-arrow-right"></i> Nyt Besøg</a>
</div> </div>
</div> </div>
<CustomerInventoryListComponent OnReorderSelected="OnReorderCallback" CompanyId="@CompanyId" Inventory="@Inventory"/> <CustomerInventoryListComponent OnReorderSelected="OnReorderCallback" CompanyId="@CompanyId" Inventory="@Inventory"/>
<InventoryReorderModal OnSelected="@OnSelectedItem" CompanyId="@CompanyId" SalesItem="@SalesItem" @ref="ReorderModal"/> <CustomerInventoryReorderModalOverlay OnSelected="@OnSelectedItem" CompanyId="@CompanyId" SalesItem="@SalesItem" @ref="CustomerInventoryReorderOverlay"/>
@if (Working) @if (Working)
{ {

View file

@ -28,13 +28,13 @@ namespace Wonky.Client.Pages;
public partial class AdvisorCustomerInventoryListPage : IDisposable public partial class AdvisorCustomerInventoryListPage : IDisposable
{ {
[Inject] public IAdvisorCustomerHistoryRepository HistoryRepo { get; set; } [Inject] private IAdvisorCustomerHistoryRepository CustomerHistory { get; set; }
[Inject] public IAdvisorCustomerRepository CompanyRepo { get; set; } [Inject] private IAdvisorCustomerRepository Customers { get; set; }
[Inject] public HttpInterceptorService Interceptor { get; set; } [Inject] private HttpInterceptorService Interceptor { get; set; }
[Inject] public IToastService Toaster { get; set; } [Inject] private IToastService Toaster { get; set; }
[Inject] public ILogger<AdvisorCustomerInventoryListPage> Logger { get; set; } [Inject] private ILogger<AdvisorCustomerInventoryListPage> Logger { get; set; }
[Inject] public ILocalStorageService Storage { get; set; } [Inject] private ILocalStorageService Storage { get; set; }
[Inject] public IAdvisorCatalogRepository AdvisorCatalogRepo { get; set; } [Inject] private ICountryCatalogRepository Catalog { get; set; }
[CascadingParameter] public DraftStateProvider DraftStateProvider { get; set; } = new(); [CascadingParameter] public DraftStateProvider DraftStateProvider { get; set; } = new();
[Parameter] public string CompanyId { get; set; } = ""; [Parameter] public string CompanyId { get; set; } = "";
@ -44,7 +44,7 @@ public partial class AdvisorCustomerInventoryListPage : IDisposable
private CompanyDto Company { get; set; } = new(); private CompanyDto Company { get; set; } = new();
private bool Working { get; set; } = true; private bool Working { get; set; } = true;
private SalesItemView SalesItem { get; set; } = new(); private SalesItemView SalesItem { get; set; } = new();
private InventoryReorderModal ReorderModal { get; set; } = new(); private CustomerInventoryReorderModalOverlay CustomerInventoryReorderOverlay { get; set; } = new();
private List<ProductInventoryView> Inventory { get; set; } = new(); private List<ProductInventoryView> Inventory { get; set; } = new();
@ -53,7 +53,7 @@ public partial class AdvisorCustomerInventoryListPage : IDisposable
Interceptor.RegisterEvent(); Interceptor.RegisterEvent();
Interceptor.RegisterBeforeSendEvent(); Interceptor.RegisterBeforeSendEvent();
Company = await CompanyRepo.GetCompanyById(CompanyId); Company = await Customers.GetCompanyById(CompanyId);
// fetch product inventory // fetch product inventory
await FetchProductInventory(); await FetchProductInventory();
@ -64,8 +64,8 @@ public partial class AdvisorCustomerInventoryListPage : IDisposable
private async Task OnReorderCallback(string sku) private async Task OnReorderCallback(string sku)
{ {
// fetch item from http repo // fetch item from http repo
SalesItem = await AdvisorCatalogRepo.GetSalesItemSku(Company.CountryCode.ToLower(), sku); SalesItem = await Catalog.GetSalesItemSku(Company.CountryCode.ToLower(), sku);
ReorderModal.Show(); CustomerInventoryReorderOverlay.Show();
} }
private async Task OnSelectedItem(DraftItem draftItem) private async Task OnSelectedItem(DraftItem draftItem)
@ -92,7 +92,7 @@ public partial class AdvisorCustomerInventoryListPage : IDisposable
} }
Logger.LogDebug("pulling products from backend"); Logger.LogDebug("pulling products from backend");
// fetch product history // fetch product history
Inventory = await HistoryRepo.FetchInventory(CompanyId); Inventory = await CustomerHistory.FetchInventory(CompanyId);
// default sort order by description // default sort order by description
if (Inventory.Any()) if (Inventory.Any())
Inventory = Inventory.OrderBy(x => x.Description).ToList(); Inventory = Inventory.OrderBy(x => x.Description).ToList();

View file

@ -16,9 +16,9 @@
*@ *@
@using Wonky.Client.Components @using Wonky.Client.Components
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@page "/customers/{CompanyId}/invoices" @page "/advisor/customers/{CompanyId}/invoices"
@attribute [Authorize(Roles = "Advisor")] @attribute [Authorize(Roles = "Advisor")]
<PageTitle>Faktura Oversigt for @Company.Name</PageTitle>
@if (!string.IsNullOrWhiteSpace(Company.Name)) @if (!string.IsNullOrWhiteSpace(Company.Name))
{ {
<div class="row pt-2 pb-1 rounded-2 bg-dark text-white"> <div class="row pt-2 pb-1 rounded-2 bg-dark text-white">
@ -26,14 +26,14 @@
<h4 class="pt-1">@Company.Name</h4> <h4 class="pt-1">@Company.Name</h4>
</div> </div>
<div class="col-sm-3 align-content-end"> <div class="col-sm-3 align-content-end">
<a class="btn btn-primary d-block" href="/customers/@Company.CompanyId"><i class="bi-arrow-right"></i> Kundekort</a> <a class="btn btn-primary d-block" href="/advisor/customers/@Company.CompanyId"><i class="bi-arrow-right"></i> Kundekort</a>
</div> </div>
<div class="col-sm-3 align-content-end"> <div class="col-sm-3 align-content-end">
<a class="btn btn-primary d-block" href="/customers/@Company.CompanyId/activities/new"><i class="bi-arrow-right"></i> Besøg</a> <a class="btn btn-primary d-block" href="/advisor/customers/@Company.CompanyId/activities/new"><i class="bi-arrow-right"></i> Besøg</a>
</div> </div>
</div> </div>
<CustomerInvoiceListComponent OnShowInvoice="CallInvoiceModal" CompanyId="@_companyId" InvoiceList="@CompanyInvoices.Invoices"/> <CustomerInvoiceListComponent OnShowInvoice="CallInvoiceModal" CompanyId="@_companyId" InvoiceList="@CompanyInvoices.Invoices"/>
<InvoiceViewModal CompanyId="@_companyId" InvoiceId="@InvoiceId" @ref="InvoiceView" /> <CustomerInvoiceViewModalOverlay CompanyId="@_companyId" InvoiceId="@InvoiceId" @ref="CustomerInvoiceView" />
} }
@if (Working) @if (Working)

View file

@ -15,17 +15,17 @@ namespace Wonky.Client.Pages;
public partial class AdvisorCustomerInvoiceListPage : IDisposable public partial class AdvisorCustomerInvoiceListPage : IDisposable
{ {
[Parameter] public string CompanyId { get; set; } = ""; [Parameter] public string CompanyId { get; set; } = "";
[Inject] public IAdvisorCustomerRepository CompanyRepo { get; set; } [Inject] private IAdvisorCustomerRepository CompanyRepo { get; set; }
[Inject] public HttpInterceptorService Interceptor { get; set; } [Inject] private HttpInterceptorService Interceptor { get; set; }
[Inject] public IAdvisorCustomerHistoryRepository HistoryRepo { get; set; } [Inject] private IAdvisorCustomerHistoryRepository HistoryRepo { get; set; }
[Inject] public IToastService Toaster { get; set; } [Inject] private IToastService Toaster { get; set; }
[Inject] public ILocalStorageService Storage { get; set; } [Inject] private ILocalStorageService Storage { get; set; }
[Inject] public ILogger<AdvisorCustomerInvoiceListPage> Logger { get; set; } [Inject] private ILogger<AdvisorCustomerInvoiceListPage> Logger { get; set; }
private InvoiceListView CompanyInvoices { get; set; } = new(); private InvoiceListView CompanyInvoices { get; set; } = new();
private CompanyDto Company { get; set; } = new(); private CompanyDto Company { get; set; } = new();
private bool Working { get; set; } private CustomerInvoiceViewModalOverlay CustomerInvoiceView { get; set; } = new();
private InvoiceViewModal InvoiceView { get; set; } = new();
private string InvoiceId { get; set; } = ""; private string InvoiceId { get; set; } = "";
private bool Working { get; set; }
private bool AllSet { get; set; } private bool AllSet { get; set; }
private string _companyId = ""; private string _companyId = "";
@ -63,7 +63,7 @@ public partial class AdvisorCustomerInvoiceListPage : IDisposable
private void CallInvoiceModal(string invoiceId) private void CallInvoiceModal(string invoiceId)
{ {
InvoiceId = invoiceId; InvoiceId = invoiceId;
InvoiceView.Show(); CustomerInvoiceView.Show();
} }
private async Task<InvoiceListView> FetchCompanyInvoices() private async Task<InvoiceListView> FetchCompanyInvoices()
@ -80,13 +80,18 @@ public partial class AdvisorCustomerInvoiceListPage : IDisposable
} }
Logger.LogDebug("pulling invoices from backend"); Logger.LogDebug("pulling invoices from backend");
// pull invoices // pull invoices
var invoices = await HistoryRepo.FetchInvoiceList(_companyId); var companyInvoices = await HistoryRepo.FetchInvoiceList(_companyId);
if (companyInvoices.Invoices.Any())
companyInvoices.Invoices = companyInvoices.Invoices
.OrderByDescending(x => x.DocumentDate)
.ToList();
// send invoices to storage // send invoices to storage
await Storage.SetItemAsync($"{_companyId}-invoices", invoices); await Storage.SetItemAsync($"{_companyId}-invoices", companyInvoices);
await Storage.SetItemAsync($"{_companyId}-iDate", $"{DateTime.Now:yyyy-MM-dd}"); await Storage.SetItemAsync($"{_companyId}-iDate", $"{DateTime.Now:yyyy-MM-dd}");
Logger.LogDebug("return invoices from backend"); Logger.LogDebug("return invoices from backend");
Working = false; Working = false;
return invoices; return companyInvoices;
} }
public void Dispose() public void Dispose()

View file

@ -17,39 +17,46 @@
@using Wonky.Client.Components @using Wonky.Client.Components
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@page "/customers" @page "/advisor/customers"
@attribute [Authorize(Roles = "Advisor")] @attribute [Authorize(Roles = "Advisor")]
<PageTitle>Kunde oversigt</PageTitle>
<div class="sticky-top bg-dark text-light rounded-2 px-3"> <div class="sticky-top bg-dark text-light rounded-2 px-3">
<div class="row g-3"> <div class="row g-3">
<div class="col-sm-2"> <div class="col-sm-2">
<CompanySearchColumnComponent OnChanged="SetSearchCol" /> <CustomerSearchColumnComponent OnChanged="SetSearchCol" />
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<CompanySearchPhraseComponent OnChanged="SetSearchPhrase" /> <CustomerSearchPhraseComponent OnChanged="SetSearchPhrase" />
</div> </div>
<div class="col-sm-2"> <div class="col-sm-2">
<CompanySortComponent OnChanged="SetSortCol" /> <CustomerSortComponent OnChanged="SetSortCol" />
</div> </div>
<div class="col-sm-2"> <div class="col-sm-2">
<PageSizeComponent OnChanged="SetPageSize" /> <PageSizeComponent OnChanged="SetPageSize" />
</div> </div>
<div class="col-sm-2"> <div class="col-sm-2">
<button type button class="btn btn-warning @(@IncludeFolded ? "active" : "")"
data-bs-toggle="button" aria-pressed="@IncludeFolded" @onclick="OnFoldedClick">
@ButtonFoldedText
</button>
@*
<div class="form-check"> <div class="form-check">
<input type="checkbox" id="folded" class="form-check-input" checked="@IncludeFolded" @onclick="OnFoldedClick" > <input type="checkbox" id="folded" class="form-check-input" checked="@IncludeFolded" @onclick="OnFoldedClick" >
<label for="folded" class="form-check-label">Ophørte</label> <label for="folded" class="form-check-label">Ophørte</label>
</div> </div>
*@
</div> </div>
<div class="col-sm-8"> <div class="col-sm-8">
<PaginationComponent MetaData="PageData" Spread="2" SelectedPage="SelectedPage"/> <PaginationComponent MetaData="PageData" Spread="2" SelectedPage="SelectedPage"/>
</div> </div>
<div class="col-sm-2 text-end"> <div class="col-sm-2 text-end">
<a class="btn btn-success text-nowrap" href="/customers/new">Opret kunde <i class="bi-plus"></i></a> <a class="btn btn-success text-nowrap" href="/advisor/customers/new">Opret kunde <i class="bi-plus"></i></a>
</div> </div>
</div> </div>
</div> </div>
<AdvisorCompanyTableComponent CompanyList="Companies" OnDelete="DeleteCompany" /> <AdvisorCustomerListComponent CompanyList="Companies" OnDelete="DeleteCompany" />
@if (Working) @if (Working)
{ {

View file

@ -30,11 +30,11 @@ namespace Wonky.Client.Pages
{ {
public partial class AdvisorCustomerListPage : IDisposable public partial class AdvisorCustomerListPage : IDisposable
{ {
[Inject] public ILocalStorageService Storage { get; set; } [Inject] private ILocalStorageService Storage { get; set; }
[Inject] public UserProfileService ProfileService { get; set; } [Inject] private UserProfileService ProfileService { get; set; }
[Inject] public IAdvisorCustomerRepository CompanyRepo { get; set; } [Inject] private IAdvisorCustomerRepository CompanyRepo { get; set; }
[Inject] public HttpInterceptorService Interceptor { get; set; } [Inject] private HttpInterceptorService Interceptor { get; set; }
[Inject] public NavigationManager Navigator { get; set; } [Inject] private NavigationManager Navigator { get; set; }
private List<CompanyDto> Companies { get; set; } = new(); private List<CompanyDto> Companies { get; set; } = new();
private UserPref Prefs { get; set; } = new(); private UserPref Prefs { get; set; } = new();
private UserInfoView UserInfo { get; set; } = new(); private UserInfoView UserInfo { get; set; } = new();
@ -43,6 +43,7 @@ namespace Wonky.Client.Pages
private bool Working { get; set; } = true; private bool Working { get; set; } = true;
private MetaData PageData { get; set; } = new(); private MetaData PageData { get; set; } = new();
private CustomerPaging Paging { get; set; } = new(); private CustomerPaging Paging { get; set; } = new();
private string ButtonFoldedText { get; set; } = "Vis Ophørte";
protected override void OnParametersSet() protected override void OnParametersSet()
{ {
@ -66,24 +67,26 @@ namespace Wonky.Client.Pages
Paging.SearchTerm = SavedSearch; Paging.SearchTerm = SavedSearch;
// get companies // get companies
await FetchCompanies(); await FetchCustomers();
Working = false; Working = false;
} }
private async Task OnFoldedClick() private async Task OnFoldedClick()
{ {
Working = true;
IncludeFolded = !IncludeFolded; IncludeFolded = !IncludeFolded;
ButtonFoldedText = IncludeFolded ? "Vis Aktive" : "Vis Ophørte";
Companies = new List<CompanyDto>(); Companies = new List<CompanyDto>();
Paging.PageNumber = 1; Paging.PageNumber = 1;
Paging.HasFolded = IncludeFolded ? 1 : 0; Paging.HasFolded = IncludeFolded ? 1 : 0;
await FetchCompanies(); await FetchCustomers();
} }
private async Task SelectedPage(int page) private async Task SelectedPage(int page)
{ {
Companies = new List<CompanyDto>(); Companies = new List<CompanyDto>();
Paging.PageNumber = page; Paging.PageNumber = page;
await FetchCompanies(); await FetchCustomers();
} }
private async Task SetSearchCol(string searchColumn) private async Task SetSearchCol(string searchColumn)
@ -91,14 +94,14 @@ namespace Wonky.Client.Pages
Companies = new List<CompanyDto>(); Companies = new List<CompanyDto>();
Paging.SearchColumn = searchColumn; Paging.SearchColumn = searchColumn;
Paging.PageNumber = 1; Paging.PageNumber = 1;
await FetchCompanies(); await FetchCustomers();
} }
private async Task SetPageSize(string pageSize) private async Task SetPageSize(string pageSize)
{ {
Companies = new List<CompanyDto>(); Companies = new List<CompanyDto>();
Paging.PageSize = Convert.ToInt32(pageSize); Paging.PageSize = Convert.ToInt32(pageSize);
Paging.PageNumber = 1; Paging.PageNumber = 1;
await FetchCompanies(); await FetchCustomers();
} }
private async Task SetSearchPhrase(string searchTerm) private async Task SetSearchPhrase(string searchTerm)
@ -106,14 +109,14 @@ namespace Wonky.Client.Pages
Companies = new List<CompanyDto>(); Companies = new List<CompanyDto>();
Paging.PageNumber = 1; Paging.PageNumber = 1;
Paging.SearchTerm = searchTerm; Paging.SearchTerm = searchTerm;
await FetchCompanies(); await FetchCustomers();
} }
private async Task SetSortCol(string orderBy) private async Task SetSortCol(string orderBy)
{ {
Companies = new List<CompanyDto>(); Companies = new List<CompanyDto>();
Paging.OrderBy = orderBy; Paging.OrderBy = orderBy;
await FetchCompanies(); await FetchCustomers();
} }
/// <summary> /// <summary>
@ -126,10 +129,10 @@ namespace Wonky.Client.Pages
await CompanyRepo.DeleteCompany(companyId); await CompanyRepo.DeleteCompany(companyId);
if (Paging.PageNumber > 1 && Companies.Count == 1) if (Paging.PageNumber > 1 && Companies.Count == 1)
Paging.PageNumber--; Paging.PageNumber--;
await FetchCompanies(); await FetchCustomers();
} }
private async Task FetchCompanies() private async Task FetchCustomers()
{ {
Working = true; Working = true;
var pageRes = await CompanyRepo.GetCompanies(Paging); var pageRes = await CompanyRepo.GetCompanies(Paging);

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