FEAT: KANVAS customer

This commit is contained in:
Frede Hundewadt 2023-04-06 13:22:02 +02:00
parent 0a96c9c640
commit 7572127425
7 changed files with 477 additions and 350 deletions

View file

@ -37,7 +37,7 @@
} }
<div class="row mb-2 bg-dark text-white 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> <span class="h3">@Activity.Name</span> <span>(@Activity.Account)</span>
</div> </div>
</div> </div>
@ -58,9 +58,16 @@ else
<label for="activityType" class="col-sm-2 col-form-label-sm">Ordre Type</label> <label for="activityType" class="col-sm-2 col-form-label-sm">Ordre Type</label>
<div class="col-sm-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> @if (Kanvas)
<option value="onSite">Besøg</option> {
<option value="phone">Telefon</option> <option value="canvas" selected>Kanvas</option>
}
else
{
<option value="">&rarr; TAG MIG &larr;</option>
<option value="onSite">Besøg</option>
<option value="phone">Telefon</option>
}
</InputSelect> </InputSelect>
<ValidationMessage For="@(() => Activity.ActivityTypeEnum)"></ValidationMessage> <ValidationMessage For="@(() => Activity.ActivityTypeEnum)"></ValidationMessage>
</div> </div>
@ -68,25 +75,32 @@ else
<label for="statusType" class="col-sm-2 col-form-label-sm">Status</label> <label for="statusType" class="col-sm-2 col-form-label-sm">Status</label>
<div class="col-sm-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> @if (Kanvas)
@if (!string.IsNullOrEmpty(Activity.VatNumber) && !string.IsNullOrWhiteSpace(Activity.Address1) && Company.HasFolded == 0)
{ {
@if (DraftProvider.Draft.DraftType == "order") <option value="canvas" selected>Kanvas</option>
}
else
{
<option value="noSale">Ingen salg</option>
@if (!string.IsNullOrEmpty(Activity.VatNumber) && !string.IsNullOrWhiteSpace(Activity.Address1) && Company.HasFolded == 0)
{ {
<option selected value="order">Bestilling</option> @if (DraftProvider.Draft.DraftType == "order")
} {
else <option selected value="order">Bestilling</option>
{ }
<option value="order">Bestilling</option> else
} {
<option value="order">Bestilling</option>
}
@if (DraftProvider.Draft.DraftType == "offer") @if (DraftProvider.Draft.DraftType == "offer")
{ {
<option selected value="quote">Tilbud</option> <option selected value="quote">Tilbud</option>
} }
else else
{ {
<option value="quote">Tilbud</option> <option value="quote">Tilbud</option>
}
} }
} }
</InputSelect> </InputSelect>
@ -156,6 +170,8 @@ else
</div> </div>
</div> </div>
@if (!Kanvas)
{
<div class="row g-2 mb-3"> <div class="row g-2 mb-3">
<div class="col-sm-3 d-grid mx-auto"> <div class="col-sm-3 d-grid mx-auto">
@* @*
@ -348,7 +364,9 @@ else
</div> </div>
</div> </div>
</div> </div>
}
</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="/advisor/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>

View file

@ -96,6 +96,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
private List<ProductInventoryView> CheckList { get; set; } = new(); private List<ProductInventoryView> CheckList { get; set; } = new();
private InvoiceListView CompanyInvoices { get; set; } = new(); private InvoiceListView CompanyInvoices { get; set; } = new();
private List<ReportItemView> Activities { get; set; } = new(); private List<ReportItemView> Activities { get; set; } = new();
private bool Kanvas { get; set; }
/// <summary> /// <summary>
/// Page initialization /// Page initialization
@ -114,23 +115,35 @@ public partial class AdvisorActivityCreatePage : IDisposable
SalesRep = await UserInfo.GetUserInfo(); SalesRep = await UserInfo.GetUserInfo();
// Fetch Customer from http // Fetch Customer from http
Company = await CompanyRepo.GetCompanyById(CompanyId); Company = await CompanyRepo.GetCompanyById(CompanyId);
if (Company.HasFolded == 1) if (Company.Account.StartsWith("KANVAS"))
// Company has shut down
Activity.OrderMessage = "BEMÆRK: CVR nummer er ophørt.";
// variable to validate if customer needs phone number update
OldPhone = Company.Phone;
if (string.IsNullOrWhiteSpace(Company.Phone)
&& !string.IsNullOrWhiteSpace(Company.Account)
&& Company.Account != "NY" && Company.Account.Length > 7)
{ {
Company.Phone = Company.Account[..8]; Kanvas = true;
Activity.ActivityStatusEnum = "canvas";
Activity.ActivityTypeEnum = "canvas";
Activity.ActivityVisitEnum = "new";
PoFormInvalid = false;
}
else
{
if (Company.HasFolded == 1)
// Company has shut down
Activity.OrderMessage = "BEMÆRK: CVR nummer er ophørt.";
// variable to validate if customer needs phone number update
OldPhone = Company.Phone;
if (string.IsNullOrWhiteSpace(Company.Phone)
&& !string.IsNullOrWhiteSpace(Company.Account)
&& Company.Account != "NY" && Company.Account.Length > 7)
{
Company.Phone = Company.Account[..8];
}
Activity.ActivityStatusEnum = "noSale";
Activity.ActivityVisitEnum = Company.Account is "" or "NY" ? "new" : "recall";
} }
// Populate base activity information // Populate base activity information
Activity.BcId = Company.BcId; Activity.BcId = Company.BcId;
Activity.ActivityStatusEnum = "noSale";
Activity.VisitTypeEnum = Company.Account is "" or "NY" ? "new" : "recall";
Activity.CompanyId = Company.CompanyId; Activity.CompanyId = Company.CompanyId;
Activity.SalesRepId = SalesRep.UserId; Activity.SalesRepId = SalesRep.UserId;
Activity.SalesRep = SalesRep.SalesRep; Activity.SalesRep = SalesRep.SalesRep;
@ -169,7 +182,9 @@ public partial class AdvisorActivityCreatePage : IDisposable
if (DraftProvider.Draft.DraftType == "order") if (DraftProvider.Draft.DraftType == "order")
{ {
// set dropdown selection accordingly // set dropdown selection accordingly
Activity.ActivityTypeEnum = "onSite"; if(Activity.ActivityTypeEnum != "phone")
Activity.ActivityTypeEnum = "onSite";
Activity.ActivityStatusEnum = "order"; Activity.ActivityStatusEnum = "order";
PoFormInvalid = false; PoFormInvalid = false;
} }
@ -177,91 +192,50 @@ public partial class AdvisorActivityCreatePage : IDisposable
Working = false; Working = false;
} }
// #############################################################
// overlays
private async Task ShowVisitOverlay() private async Task ShowVisitOverlay()
{ {
Logger.LogDebug("ShowInventoryOverlay - wait for visits"); Logger.LogDebug("ShowInventoryOverlay - wait for visits");
ActivityListOverlay.Show(); ActivityListOverlay.Show();
Activities = await ActivityRepo.GetCustomerActivities(CompanyId); Activities = await ActivityRepo.GetCustomerActivities(CompanyId);
await Task.Delay(500); await Task.Delay(500);
} }
private async Task ShowInventoryOverlay() private async Task ShowInventoryOverlay()
{ {
Logger.LogDebug("ShowInventoryOverlay - wait for inventory"); Logger.LogDebug("ShowInventoryOverlay - wait for inventory");
InventoryListOverlay.Show(); InventoryListOverlay.Show();
Inventory = await HistoryRepo.FetchInventory(CompanyId); Inventory = await HistoryRepo.FetchInventory(CompanyId);
Inventory = Inventory.OrderBy(x => x.Description).ToList(); Inventory = Inventory.OrderBy(x => x.Description).ToList();
await Task.Delay(500); 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() private async Task ShowInvoiceOverlay()
{ {
Logger.LogDebug("ShowInvoiceOverlay - wait for invoices"); Logger.LogDebug("ShowInvoiceOverlay - wait for invoices");
InvoiceListOverlay.Show(); InvoiceListOverlay.Show();
CompanyInvoices = await FetchCompanyInvoices(); CompanyInvoices = await FetchCompanyInvoices();
await Task.Delay(500); await Task.Delay(500);
} }
private async Task<InvoiceListView> FetchCompanyInvoices()
private void ShowPriceListOverlay()
{ {
// fetch from storage CatalogOverlay.Show();
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" && Utils.StringToDigits(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() 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"
|| Activity.ActivityStatusEnum.ToLower() == "canvas")
{ {
// 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
await CreateActivity(); await CreateActivity();
@ -316,38 +290,16 @@ public partial class AdvisorActivityCreatePage : IDisposable
ConfirmationCheckOverlay.Show(); ConfirmationCheckOverlay.Show();
} }
private async Task ConfirmProductCheckCallback()
{
ConfirmationCheckOverlay.Hide();
await CreateActivity();
foreach (var item in CheckList)
{
item.Check = false;
}
await Storage.SetItemAsync($"{CompanyId}-products", CheckList); private void ShowPriceHistoryOverlay()
{
if (ShowItem)
PriceOverlay.Show();
} }
private async Task WorkDateConfirmCallback()
{
await PreferenceService.SetDateConfirmed(true);
Activity.ActivityDate = $"{SelectedDate:yyyy-MM-dd}";
ConfirmWorkDate.Hide();
StateHasChanged();
}
private async Task WorkDateComponentCallback(string workDate)
{
ReportClosed = await ReportRepo.ReportExist(workDate);
SelectedDate = DateTime.Parse(workDate);
Activity.ActivityDate = workDate;
}
private void ShowPriceListOverlay()
{
CatalogOverlay.Show();
}
// #############################################################
// callbacks
private async Task PriceListCallback(SelectedSku sku) private async Task PriceListCallback(SelectedSku sku)
{ {
// get selected item // get selected item
@ -360,11 +312,6 @@ public partial class AdvisorActivityCreatePage : IDisposable
StateHasChanged(); StateHasChanged();
} }
private void ShowPriceHistoryOverlay()
{
if (ShowItem)
PriceOverlay.Show();
}
private void PriceHistoryCallback(decimal price) private void PriceHistoryCallback(decimal price)
{ {
@ -374,26 +321,99 @@ public partial class AdvisorActivityCreatePage : IDisposable
StateHasChanged(); StateHasChanged();
} }
private void OnInventoryCallback(DraftItem item)
{
Activity.ActivityStatusEnum = "order";
DraftProvider.Draft.DraftType = "order";
DraftProvider.Draft.Items.Add(item);
StateHasChanged();
}
private async Task ConfirmProductCheckCallback()
{
ConfirmationCheckOverlay.Hide();
await CreateActivity();
foreach (var item in CheckList)
{
item.Check = false;
}
await Storage.SetItemAsync($"{CompanyId}-products", CheckList);
}
private async Task WorkDateConfirmCallback()
{
await PreferenceService.SetDateConfirmed(true);
Activity.ActivityDate = $"{SelectedDate:yyyy-MM-dd}";
ConfirmWorkDate.Hide();
StateHasChanged();
}
private async Task WorkDateComponentCallback(string workDate)
{
ReportClosed = await ReportRepo.ReportExist(workDate);
SelectedDate = DateTime.Parse(workDate);
Activity.ActivityDate = workDate;
}
// ################################################################################################
// fetch invoices for customer
private async Task<InvoiceListView> FetchCompanyInvoices()
{
// no need to do for kanvas entry
if (Kanvas) return new InvoiceListView();
// 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;
}
// ##################################################################################################
// create activity
private async Task CreateActivity() private async Task CreateActivity()
{ {
// avoid duplication // avoid duplication
if (Working) if (Working)
return; return;
// validate customer address1
// - this is a required input
if (string.IsNullOrWhiteSpace(Activity.Address1))
{
Toaster.ShowError("Kunde adresse er ufuldstændig.");
return;
}
// validate org number Logger.LogDebug("view kanvas activity => {}", JsonSerializer.Serialize(Activity));
// - this is a required input switch (Kanvas)
// - must validate according to country rules.
if (!VatUtils.ValidateFormat(Company.CountryCode, Activity.VatNumber))
{ {
Toaster.ShowError("Firma registreringsnummer er ikke korrekt."); // validate customer address1
return; // - this is a required input
case false when string.IsNullOrWhiteSpace(Activity.Address1):
Toaster.ShowError("Kunde adresse er ufuldstændig.");
return;
// validate org number
// - this is a required input
// - must validate according to country rules.
case false when !VatUtils.ValidateFormat(Company.CountryCode, Activity.VatNumber):
Toaster.ShowError("Firma registreringsnummer er ikke korrekt.");
return;
} }
// validate input according to status // validate input according to status
@ -419,7 +439,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
// reset selected item // reset selected item
SelectedItem = new SalesItemView(); SelectedItem = new SalesItemView();
// check if phone number need to be updated // check if phone number need to be updated
if (OldPhone != Activity.Phone) if (!Kanvas && OldPhone != Activity.Phone)
{ {
Company.Phone = Activity.Phone; Company.Phone = Activity.Phone;
Activity.OrderMessage = $"BEMÆRK: {Activity.Phone}\n{Activity.OrderMessage}"; Activity.OrderMessage = $"BEMÆRK: {Activity.Phone}\n{Activity.OrderMessage}";
@ -482,12 +502,29 @@ public partial class AdvisorActivityCreatePage : IDisposable
Toaster.ShowError(result.Message, "ORDRE FEJL"); Toaster.ShowError(result.Message, "ORDRE FEJL");
} }
private void ShowOrgWarning()
{
if (Kanvas) return;
if (OrgWarning) return;
OrgWarning = true;
if (Company.CountryCode.ToLower() == "se" && Utils.StringToDigits(Activity.VatNumber).Length < 10 &&
Activity.ActivityStatusEnum == "order")
{
Toaster.ShowWarning("Org nummer er ufuldstændig. Skal opdateres før bestilling kan sendes. ", "ADVARSEL");
}
}
// ###############################################################################
// draft functions
private async Task DeleteDraft() private async Task DeleteDraft()
{ {
await DraftProvider.DeleteDraftAsync(); await DraftProvider.DeleteDraftAsync();
Activity.ActivityStatusEnum = "noSale"; Activity.ActivityStatusEnum = "noSale";
} }
private async Task AddItem(SalesItemView salesItem) private async Task AddItem(SalesItemView salesItem)
{ {
ShowItem = false; ShowItem = false;
@ -513,6 +550,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
await DraftProvider.SaveChangesAsync(); await DraftProvider.SaveChangesAsync();
} }
private async Task RemoveItem(DraftItem item) private async Task RemoveItem(DraftItem item)
{ {
// remove item // remove item
@ -523,6 +561,9 @@ public partial class AdvisorActivityCreatePage : IDisposable
Activity.ActivityStatusEnum = "noSale"; Activity.ActivityStatusEnum = "noSale";
} }
// ##################################################################################################
// form validation
private void HandleFieldChanged(object sender, FieldChangedEventArgs e) private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
{ {
Logger.LogDebug("ActivityNewPage => HandleFieldChanged => ActivityStatusEnum <= '{}'", Logger.LogDebug("ActivityNewPage => HandleFieldChanged => ActivityStatusEnum <= '{}'",
@ -556,6 +597,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
StateHasChanged(); StateHasChanged();
} }
private void ValidationChanged(object sender, ValidationStateChangedEventArgs e) private void ValidationChanged(object sender, ValidationStateChangedEventArgs e)
{ {
if (string.IsNullOrEmpty(Activity.ActivityTypeEnum) && !ReportClosed) if (string.IsNullOrEmpty(Activity.ActivityTypeEnum) && !ReportClosed)
@ -582,6 +624,9 @@ public partial class AdvisorActivityCreatePage : IDisposable
ActivityContext.OnValidationStateChanged += ValidationChanged; ActivityContext.OnValidationStateChanged += ValidationChanged;
} }
// #########################################################################################################
// dispose
public void Dispose() public void Dispose()
{ {
Interceptor.DisposeEvent(); Interceptor.DisposeEvent();

View file

@ -29,7 +29,9 @@
</div> </div>
} }
<div class="row pt-2 mb-2 rounded rounded-2 bg-dark text-white"> <div class="row pt-2 mb-2 rounded rounded-2 bg-dark text-white">
<h3>@Company.Name @(string.IsNullOrWhiteSpace(Company.Account) ? "" : "- ")@Company.Account</h3> <div class="col-sm-12">
<span class="h3">@Company.Name</span> <span>(@Company.Account)</span>
</div>
</div> </div>
// erp context // erp context
<EditForm EditContext="ErpContext"> <EditForm EditContext="ErpContext">
@ -89,65 +91,81 @@
<InputText id="email" class="form-control" @bind-Value="Company.Email" readonly="@(ErpEditDisabled)"/> <InputText id="email" class="form-control" @bind-Value="Company.Email" readonly="@(ErpEditDisabled)"/>
<ValidationMessage For="@(() => Company.Email)"></ValidationMessage> <ValidationMessage For="@(() => Company.Email)"></ValidationMessage>
</div> </div>
<div class="col-sm-4">@* ---- placeholder --- *@</div>
@* Force enable visit *@ @if (!Kanvas)
<div class="col-sm-3 d-grid mx-auto"> {
<button type="button" class="btn btn-primary d-block" disabled="@(Company.HasFolded == 0 || Company.Name == "ERROR")" @onclick="ForceActivity">Aktiver besøg</button> <div class="col-sm-4">@* ---- placeholder --- *@</div>
</div>
@* Enable edit/save *@ @* Enable edit/save *@
<div class="col-sm-2 d-grid mx-auto"> <div class="col-sm-2 d-grid mx-auto">
<button type="button" class="btn btn-edit" @onclick="ToggleErpEdit"><i class="bi-pencil"></i> STAM data</button> <button type="button" class="btn btn-edit" @onclick="ToggleErpEdit"><i class="bi-pencil"></i> STAM data</button>
</div> </div>
<div class="col-sm-3 d-grid mx-auto"> @* Force enable visit *@
<button type="button" class="btn btn-danger d-block" onclick="@UpdateErpData" disabled="@(Working || Company.Name == "ERROR" || ErpEditDisabled)"><i class="bi-cloud-arrow-up"></i> STAM data </button> <div class="col-sm-3 d-grid mx-auto">
</div> <button type="button" class="btn btn-primary d-block" @onclick="ForceActivity">Aktiver besøg</button>
@* vat number*@ </div>
<label for="vatNumber" class="col-sm-1 col-form-label-sm">CVR/Org nr.</label> @* Save erp data *@
<div class="col-sm-3"> <div class="col-sm-3 d-grid mx-auto">
<div class="input-group"> <button type="button" class="btn btn-danger d-block" onclick="@UpdateErpData" disabled="@(Working || Company.Name == "ERROR" || ErpEditDisabled)"><i class="bi-cloud-arrow-up"></i> STAM data </button>
<span class="input-group-text"> </div>
<DisplayStateComponent StateClass="@VatState"/> @* vat number*@
</span> <label for="vatNumber" class="col-sm-1 col-form-label-sm">CVR/Org nr.</label>
<InputText id="vatNumber" class="form-control" @bind-Value="Company.VatNumber" readonly="@(VatEditDisabled)"/> <div class="col-sm-3">
<ValidationMessage For="@(() => Company.VatNumber)"></ValidationMessage> <div class="input-group">
<span class="input-group-text">
<DisplayStateComponent StateClass="@VatState"/>
</span>
<InputText id="vatNumber" class="form-control" @bind-Value="Company.VatNumber" readonly="@(VatEditDisabled)"/>
<ValidationMessage For="@(() => Company.VatNumber)"></ValidationMessage>
</div>
</div>
@* Enable edit/save vatnumber *@
<div class="col-sm-2 d-grid mx-auto">
<button type="button" class="btn btn-edit" @onclick="ToggleVatEdit"><i class="bi-pencil"></i> Moms/Org Nr.</button>
</div> </div>
</div>
@* vat lookup *@ @* vat lookup *@
<div class="col-sm-3 d-grid mx-auto"> <div class="col-sm-3 d-grid mx-auto">
@switch (CountryCode) @switch (CountryCode)
{ {
case "dk": case "dk":
<button type="button" class="btn btn-info" @onclick="OpenVatLookupModal"><i class="bi-search"></i> CVR</button> <button type="button" class="btn btn-info" @onclick="OpenVatLookupModal" disabled="@(VatEditDisabled)"><i class="bi-search"></i> CVR</button>
break; break;
case "no": case "no":
<a class="btn btn-info" href="https://brreg.no/" target="_blank"><i class="bi-search"></i> brreg.no</a> <a class="btn btn-info" href="https://brreg.no/" target="_blank"><i class="bi-search"></i> brreg.no</a>
break; break;
case "se": case "se":
<a class="btn btn-info" href="https://www.allabolag.se/what/@Company.Name" target="_blank"><i class="bi-search"></i> allabolag.se</a> <a class="btn btn-info" href="https://www.allabolag.se/what/@Company.Name" target="_blank"><i class="bi-search"></i> allabolag.se</a>
break; break;
} }
</div> </div>
@* Enable edit/save *@
<div class="col-sm-2 d-grid mx-auto">
<button type="button" class="btn btn-edit" @onclick="ToggleVatEdit"><i class="bi-pencil"></i> Moms/Org Nr.</button>
</div>
@* save vat number *@ @* save vat number *@
<div class="col-sm-3 d-grid mx-auto"> <div class="col-sm-3 d-grid mx-auto">
<button type="button" class="btn btn-warning d-block" @onclick="UpdateVatNumber" disabled="@(VatEditDisabled)"><i class="bi-cloud-arrow-up"></i> Moms/Org Nr.</button> <button type="button" class="btn btn-warning d-block" @onclick="UpdateVatNumber" disabled="@(VatEditDisabled)"><i class="bi-cloud-arrow-up"></i> Moms/Org Nr.</button>
</div> </div>
}
</div> </div>
<hr class="mb-3"/> <hr class="mb-3"/>
@* activity buttons *@ @* activity buttons *@
<div class="row mt-3 mb-3"> <div class="row mt-3 mb-3">
<div class="col-sm-3"> <div class="col-sm-3">
<a class="btn btn-danger d-block" href="/advisor/customers/@Company.CompanyId/invoices">Faktura</a> @if (!Kanvas)
{
<a class="btn btn-danger d-block" href="/advisor/customers/@Company.CompanyId/invoices">Faktura</a>
}
</div> </div>
<div class="col-sm-3"> <div class="col-sm-3">
<a class="btn btn-warning d-block" href="/advisor/customers/@Company.CompanyId/activities">Tidl. Besøg</a> @if (!Kanvas)
{
<a class="btn btn-warning d-block" href="/advisor/customers/@Company.CompanyId/activities">Tidl. Besøg</a>
}
</div> </div>
<div class="col-sm-3"> <div class="col-sm-3">
<a class="btn btn-success d-block" href="/advisor/customers/@Company.CompanyId/h/i">Produkter</a> @if (!Kanvas)
{
<a class="btn btn-success d-block" href="/advisor/customers/@Company.CompanyId/h/i">Produkter</a>
}
</div> </div>
<div class="col-sm-3"> <div class="col-sm-3">
<ActivityButton ActionLink="@ActionLink" <ActivityButton ActionLink="@ActionLink"
@ -158,104 +176,105 @@
</div> </div>
</div> </div>
<hr class="mb-3"/> @if (!Kanvas)
{
<hr class="mb-3"/>
@* crm context - OBS note *@ @* crm context - OBS note *@
<div class="row mb-2"> <div class="row mb-2">
<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-8"> <div class="col-sm-8">
@if (string.IsNullOrWhiteSpace(Company.Note)) @if (string.IsNullOrWhiteSpace(Company.Note))
{
<InputText name="note" id="note" class="form-control" @bind-Value="Company.Note"/>
}
else
{
<InputText name="note" id="note" class="form-control bg-warning text-black" @bind-Value="Company.Note"/>
}
<ValidationMessage For="@(() => Company.Note)"></ValidationMessage>
</div>
@* Save CRM data button *@
<div class="col-sm-3 d-grid mx-auto">
<button type="button" class="btn btn-warning" disabled="@(Company.Name == "ERROR")" @onclick="UpdateCrmData"><i class="bi-cloud-arrow-up"></i> CRM data</button>
</div>
</div>
@* crm context - contacts *@
<div class="row mb-4">
<label for="contacts" class="col-sm-1 col-form-label-sm">Kontakt</label>
<div id="contacts" class="col-sm-11">
<div class="list-group">
<div class="list-group-item list-group-item-action bg-dark text-white" @onclick="() => OpenContactPopup(DefaultContact)">
<div class="row">
<div class="col-sm-4">Stilling</div>
<div class="col-sm-4">Navn</div>
<div class="col-sm-3">Direkte</div>
<div class="col-sm-1 text-end">
<i class="bi-plus-circle"></i>
</div>
</div>
</div>
@if (Contacts.Any())
{ {
@foreach (var contact in Contacts) <InputText name="note" id="note" class="form-control" @bind-Value="Company.Note"/>
{ }
<div class="list-group-item list-group-item-action" @onclick="() => OpenContactPopup(contact)"> else
<div class="row g-2"> {
<div class="col-sm-4">@contact.JobTitle</div> <InputText name="note" id="note" class="form-control bg-warning text-black" @bind-Value="Company.Note"/>
<div class="col-sm-4">@contact.FirstName @contact.LastName</div> }
<div class="col-sm-3"> <ValidationMessage For="@(() => Company.Note)"></ValidationMessage>
@contact.PhoneDirect </div>
</div> @* Save CRM data button *@
<div class="col-sm-1 text-end"> <div class="col-sm-3 d-grid mx-auto">
<i class="bi-pencil"></i> <button type="button" class="btn btn-warning" disabled="@(Company.Name == "ERROR")" @onclick="UpdateCrmData"><i class="bi-cloud-arrow-up"></i> CRM data</button>
</div> </div>
</div>
@* crm context - contacts *@
<div class="row mb-4">
<label for="contacts" class="col-sm-1 col-form-label-sm">Kontakt</label>
<div id="contacts" class="col-sm-11">
<div class="list-group">
<div class="list-group-item list-group-item-action bg-dark text-white" @onclick="() => OpenContactPopup(DefaultContact)">
<div class="row">
<div class="col-sm-4">Stilling</div>
<div class="col-sm-4">Navn</div>
<div class="col-sm-3">Direkte</div>
<div class="col-sm-1 text-end">
<i class="bi-plus-circle"></i>
</div> </div>
</div> </div>
</div>
@if (Contacts.Any())
{
@foreach (var contact in Contacts)
{
<div class="list-group-item list-group-item-action" @onclick="() => OpenContactPopup(contact)">
<div class="row g-2">
<div class="col-sm-4">@contact.JobTitle</div>
<div class="col-sm-4">@contact.FirstName @contact.LastName</div>
<div class="col-sm-3">
@contact.PhoneDirect
</div>
<div class="col-sm-1 text-end">
<i class="bi-pencil"></i>
</div>
</div>
</div>
}
} }
} </div>
</div> </div>
</div> </div>
</div>
@* crm context - dates and interval *@ @* crm context - dates and interval *@
<div class="row mb-2"> <div class="row mb-2">
<label for="nextVisit" class="col-sm-1 col-form-label-sm">Næste besøg</label> <label for="nextVisit" class="col-sm-1 col-form-label-sm">Næste besøg</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">
<DisplayStateComponent StateClass="@VisitState"/> <DisplayStateComponent StateClass="@VisitState"/>
</span> </span>
<InputDate id="nextVisit" class="form-control" @bind-Value="@(NextVisit)"/> <InputDate id="nextVisit" class="form-control" @bind-Value="@(NextVisit)"/>
</div>
</div>
<label for="lastVisit" class="col-sm-1 col-form-label-sm">Sidse besøg</label>
<div class="col-sm-3">
<InputDate id="lastVisit" class="form-control" @bind-Value="@LastVisit"/>
</div>
<label for="interval" class="col-sm-2 col-form-label-sm">Uge Interval</label>
<div class="col-sm-2">
<InputNumber id="interval" class="form-control" @bind-Value="Company.Interval"/>
<ValidationMessage For="@(() => Company.Interval)"></ValidationMessage>
</div> </div>
</div> </div>
<label for="lastVisit" class="col-sm-1 col-form-label-sm">Sidse besøg</label> <div class="row mb-2">
<div class="col-sm-3"> <label for="crmNotes" class="col-sm-1 col-form-label-sm">Noter</label>
<InputDate id="lastVisit" class="form-control" @bind-Value="@LastVisit"/> <div class="col-sm-11">
<InputTextArea id="crmNotes" class="form-control" @bind-Value="Company.CrmNotes"/>
</div>
</div> </div>
<label for="interval" class="col-sm-2 col-form-label-sm">Uge Interval</label> <div class="row pt-3">
<div class="col-sm-2"> <div class="col-sm-3 d-grid">
<InputNumber id="interval" class="form-control" @bind-Value="Company.Interval"/> <button type="button" class="btn btn-danger" @onclick="ToggleVisibility">@ToggleButtonText</button>
<ValidationMessage For="@(() => Company.Interval)"></ValidationMessage> </div>
</div> </div>
</div> @*
<div class="row mb-2"> <div class="row mt-5">
<label for="crmNotes" class="col-sm-1 col-form-label-sm">Noter</label> <div class="col-sm-3">
<div class="col-sm-11"> <a class="btn btn-info" href="@($"/advisor/customers/{CompanyId}/workplaces")">Arbejdssteder</a>
<InputTextArea id="crmNotes" class="form-control" @bind-Value="Company.CrmNotes"/> </div>
</div> </div>
</div> *@
<div class="row pt-3"> }
<div class="col-sm-3 d-grid">
<button type="button" class="btn btn-danger" @onclick="ToggleVisibility">@ToggleButtonText</button>
</div>
</div>
</EditForm> </EditForm>
<div class="row mt-5">
<div class="col-sm-3">
<a class="btn btn-info" href="@($"/advisor/customers/{CompanyId}/workplaces")">Arbejdssteder</a>
</div>
</div>
} }
@if (Working) @if (Working)

View file

@ -71,6 +71,7 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
private ContactModal ContactPopup { get; set; } = new(); private ContactModal ContactPopup { get; set; } = new();
private UserManagerEditView UserInfo { get; set; } = new(); private UserManagerEditView UserInfo { get; set; } = new();
private string ToggleButtonText { get; set; } = ""; private string ToggleButtonText { get; set; } = "";
private bool Kanvas { get; set; }
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
@ -96,60 +97,67 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
Company = await CustomerRepo.GetCompanyById(CompanyId); Company = await CustomerRepo.GetCompanyById(CompanyId);
Logger.LogDebug("company => {}", JsonSerializer.Serialize(Company)); if (Company.Account.StartsWith("KANVAS"))
// toggle view button text Kanvas = true;
ToggleButtonText = Company.IsHidden == 0 ? "Udelad kunde i oversigt" : "Brug Normal Visning";
CurrentVat = Company.VatNumber;
Company.CountryCode = UserInfo.CountryCode.ToLower();
// internal flag // internal flag
EnableActivity = Company.ValidVat; EnableActivity = Company.ValidVat;
// override if canvas which has account property as empty string or "NY" // override if canvas which has account property as empty string or "NY"
if (Company.Account == "NY" || string.IsNullOrWhiteSpace(Company.Account)) if (Company.Account == "NY" || Company.Account.StartsWith("KANVAS") || string.IsNullOrWhiteSpace(Company.Account))
EnableActivity = 1; EnableActivity = 1;
// visit interval init
if (Company.Interval == 0)
Company.Interval = 8;
// visit date init
LastVisit = DateTime.Parse(Company.LastVisit);
NextVisit = DateTime.Parse(Company.NextVisit);
// if no previous visit is registered - force last visit date to 2020
if (LastVisit.Year < 2020)
LastVisit = DateTime.Parse("2020-01-01");
// set next visit according to last visit and interval
if (!Company.ValidDateSpan())
NextVisit = LastVisit.AddDays(Company.Interval * 7);
// display urgency of next visit
VisitState = Utils.GetVisitState($"{NextVisit:yyyy-MM-dd}");
// action link passed to activity button component // action link passed to activity button component
ActionLink = $"/advisor/customers/{CompanyId}/activities/new"; // used when drawing visit button ActionLink = $"/advisor/customers/{CompanyId}/activities/new"; // used when drawing visit button
// handle company out of business case // only execute if the company a 'real' customer
if (Company.HasFolded == 1) if (!Kanvas)
{ {
// this is only used if user has selected to show closed companies Logger.LogDebug("company => {}", JsonSerializer.Serialize(Company));
HasFolded = true; // toggle view button text
VatState = "the-dead"; ToggleButtonText = Company.IsHidden == 0 ? "Udelad kunde i oversigt" : "Brug Normal Visning";
VisitState = "the-dead"; CurrentVat = Company.VatNumber;
} Company.CountryCode = UserInfo.CountryCode.ToLower();
else // visit interval init
{ if (Company.Interval == 0)
// valid vat enum Company.Interval = 8;
Company.ValidVat = VatUtils.ValidateFormat(Company.CountryCode, Company.VatNumber) ? 1 : 0; // visit date init
// valid vat flag LastVisit = DateTime.Parse(Company.LastVisit);
ValidVat = Company.ValidVat == 1; // true/false flag set if company has a valid vatNumber NextVisit = DateTime.Parse(Company.NextVisit);
// vat state css class // if no previous visit is registered - force last visit date to 2020
VatState = Company.ValidVat == 1 ? "the-good" : "no-vat"; // assign css class if (LastVisit.Year < 2020)
} LastVisit = DateTime.Parse("2020-01-01");
// set next visit according to last visit and interval
if (!Company.ValidDateSpan())
NextVisit = LastVisit.AddDays(Company.Interval * 7);
// display urgency of next visit
VisitState = Utils.GetVisitState($"{NextVisit:yyyy-MM-dd}");
// handle company out of business case
if (Company.HasFolded == 1)
{
// this is only used if user has selected to show closed companies
HasFolded = true;
VatState = "the-dead";
VisitState = "the-dead";
}
else
{
// valid vat enum
Company.ValidVat = VatUtils.ValidateFormat(Company.CountryCode, Company.VatNumber) ? 1 : 0;
// valid vat flag
ValidVat = Company.ValidVat == 1; // true/false flag set if company has a valid vatNumber
// vat state css class
VatState = Company.ValidVat == 1 ? "the-good" : "no-vat"; // assign css class
}
// create search address from address // create search address from address
if (CountryIsDk) if (CountryIsDk)
CompanyVatAddress = PrepareVatAddress(Company); CompanyVatAddress = PrepareVatAddress(Company);
await FetchContacts(CompanyId); await FetchContacts(CompanyId);
await Task.Delay(100);
await RequestErpUpdate();
}
// remove loading image // remove loading image
Working = false; Working = false;
await Task.Delay(100);
await RequestErpUpdate();
} }
private void ToggleErpEdit() private void ToggleErpEdit()
@ -414,6 +422,7 @@ public partial class AdvisorCustomerViewEditPage : IDisposable
/// <param name="e"></param> /// <param name="e"></param>
private void HandleFieldChanged(object? sender, FieldChangedEventArgs? e) private void HandleFieldChanged(object? sender, FieldChangedEventArgs? e)
{ {
NextVisit = LastVisit.AddDays(Company.Interval * 7); NextVisit = LastVisit.AddDays(Company.Interval * 7);
// avoid nesting if by assuming ValidVat is false // avoid nesting if by assuming ValidVat is false
ValidVat = false; ValidVat = false;

View file

@ -153,7 +153,7 @@ public partial class OfficeOrderCreatePage : IDisposable
// setting up activity properties // setting up activity properties
Activity.ActivityStatusEnum = "noSale"; Activity.ActivityStatusEnum = "noSale";
Activity.VisitTypeEnum = "recall"; Activity.ActivityVisitEnum = "recall";
Activity.ActivityTypeEnum = "phone"; Activity.ActivityTypeEnum = "phone";
Activity.ActivityStatusEnum = "order"; Activity.ActivityStatusEnum = "order";
Activity.OurRef = $"T:{UserInfo.FirstName}"; Activity.OurRef = $"T:{UserInfo.FirstName}";

View file

@ -1,15 +1,15 @@
{ {
"appInfo": { "appInfo": {
"name": "Wonky Online", "name": "Wonky Online",
"version": "0.131.0", "version": "0.132.1",
"rc": true, "rc": true,
"sandBox": false, "sandBox": false,
"image": "grumpy-coder.png" "image": "grumpy-coder.png"
}, },
"Logging": { "Logging": {
"LogLevel": { "LogLevel": {
"Default": "Debug", "Default": "Information",
"System": "Debug", "System": "Information",
"Microsoft": "Information" "Microsoft": "Information"
}, },
"Debug": { "Debug": {
@ -19,7 +19,7 @@
} }
}, },
"apiConfig": { "apiConfig": {
"baseUrl": "https://dev.innotec.dk", "baseUrl": "https://zeta.innotec.dk",
"catalog": "api/v2/catalog/country", "catalog": "api/v2/catalog/country",
"crmCustomers": "api/v2/crm/companies", "crmCustomers": "api/v2/crm/companies",
"crmInventoryExt": "history/inventory", "crmInventoryExt": "history/inventory",

View file

@ -28,160 +28,196 @@ public class ActivityDto
/// <summary> /// <summary>
/// Sales representative identification /// Sales representative identification
/// </summary> /// </summary>
[Required] public string SalesRep { get; set; } = ""; [Required]
public string SalesRep { get; set; } = "";
/// <summary> /// <summary>
/// Sales representative entity Id /// Sales representative entity Id
/// </summary> /// </summary>
[Required] public string SalesRepId { get; set; } = ""; [Required]
public string SalesRepId { get; set; } = "";
/// <summary> /// <summary>
/// Company countryCode (ensure correct code when office create ordre) /// Company countryCode (ensure correct code when office create ordre)
/// </summary> /// </summary>
[Required] public string CountryCode { get; set; } = ""; [Required]
public string CountryCode { get; set; } = "";
/// <summary> /// <summary>
/// Company entity Id /// Company entity Id
/// </summary> /// </summary>
[Required] public string CompanyId { get; set; } = ""; [Required]
public string CompanyId { get; set; } = "";
/// <summary> /// <summary>
/// Business Central entity Id /// Business Central entity Id
/// </summary> /// </summary>
public string BcId { get; set; } = ""; public string BcId { get; set; } = "";
/// <summary> /// <summary>
/// Customer account /// Customer account
/// </summary> /// </summary>
public string Account { get; set; } = ""; public string Account { get; set; } = "";
/// <summary> /// <summary>
/// VAT number /// VAT number
/// </summary> /// </summary>
[MaxLength(20, ErrorMessage = "Du kan højst bruge 20 tegn")] [MaxLength(20, ErrorMessage = "Du kan højst bruge 20 tegn")]
public string VatNumber { get; set; } = ""; public string VatNumber { get; set; } = "";
/// <summary> /// <summary>
/// Customer name /// Customer name
/// </summary> /// </summary>
[Required(ErrorMessage = "Navn skal udfyldes")] [Required(ErrorMessage = "Navn skal udfyldes")]
[MaxLength(100, ErrorMessage = "Du kan højst bruge 100 tegn")] [MaxLength(100, ErrorMessage = "Du kan højst bruge 100 tegn")]
public string Name { get; set; } = ""; public string Name { get; set; } = "";
/// <summary> /// <summary>
/// Customer address city name /// Customer address city name
/// </summary> /// </summary>
[Required(ErrorMessage = "Byanvn skal udfyldes")] [Required(ErrorMessage = "Byanvn skal udfyldes")]
[MaxLength(30, ErrorMessage = "Du kan højst bruge 30 tegn")] [MaxLength(30, ErrorMessage = "Du kan højst bruge 30 tegn")]
public string City { get; set; }= ""; public string City { get; set; } = "";
/// <summary> /// <summary>
/// Customer address postal code /// Customer address postal code
/// </summary> /// </summary>
[Required(ErrorMessage = "Postnr. skal udfyldes")] [Required(ErrorMessage = "Postnr. skal udfyldes")]
[MaxLength(20, ErrorMessage = "Du kan højst bruge 20 tegn")] [MaxLength(20, ErrorMessage = "Du kan højst bruge 20 tegn")]
public string ZipCode { get; set; } = ""; public string ZipCode { get; set; } = "";
/// <summary> /// <summary>
/// Customer address line 1 /// Customer address line 1
/// </summary> /// </summary>
[MaxLength(100, ErrorMessage = "Du kan højst bruge 100 tegn")] [MaxLength(100, ErrorMessage = "Du kan højst bruge 100 tegn")]
public string Address1 { get; set; } = ""; public string Address1 { get; set; } = "";
/// <summary> /// <summary>
/// Customer address line 2 /// Customer address line 2
/// </summary> /// </summary>
[MaxLength(50, ErrorMessage = "Du kan højst bruge 50 tegn")] [MaxLength(50, ErrorMessage = "Du kan højst bruge 50 tegn")]
public string Address2 { get; set; } = ""; public string Address2 { get; set; } = "";
/// <summary> /// <summary>
/// Customer office phone /// Customer office phone
/// </summary> /// </summary>
[MaxLength(20, ErrorMessage = "Du kan højst bruge 20 tegn")] [MaxLength(20, ErrorMessage = "Du kan højst bruge 20 tegn")]
public string Phone { get; set; } = ""; public string Phone { get; set; } = "";
/// <summary> /// <summary>
/// Customer mobile phone /// Customer mobile phone
/// </summary> /// </summary>
[MaxLength(20, ErrorMessage = "Du kan højst bruge 20 tegn")] [MaxLength(20, ErrorMessage = "Du kan højst bruge 20 tegn")]
public string Mobile { get; set; } = ""; public string Mobile { get; set; } = "";
/// <summary> /// <summary>
/// Customer office email /// Customer office email
/// </summary> /// </summary>
[MaxLength(80, ErrorMessage = "Du kan højst bruge 80 tegn")] [MaxLength(80, ErrorMessage = "Du kan højst bruge 80 tegn")]
public string Email { get; set; } = ""; public string Email { get; set; } = "";
/// <summary> /// <summary>
/// Customer attention description /// Customer attention description
/// </summary> /// </summary>
[MaxLength(100, ErrorMessage = "Du kan højst bruge 100 tegn")] [MaxLength(100, ErrorMessage = "Du kan højst bruge 100 tegn")]
public string Attention { get; set; } = ""; public string Attention { get; set; } = "";
// Form entries
/// <summary>
/// Activity type enum as string
/// </summary>
[Required(ErrorMessage = "Vælg aktivitetstype")]
public string ActivityTypeEnum { get; set; } = "";
/// <summary> /// <summary>
/// Flag express order /// Flag express order
/// </summary> /// </summary>
public bool Express { get; set; } public bool Express { get; set; }
// Form entries
/// <summary> /// <summary>
/// Activity status enum as string /// Activity status enum as string
/// </summary> /// </summary>
[Required(ErrorMessage = "Vælg status for besøg ")] [Required(ErrorMessage = "Vælg status for besøg ")]
public string ActivityStatusEnum { get; set; } = ""; public string ActivityStatusEnum { get; set; } = "";
/// <summary>
/// Activity type enum as string
/// </summary>
[Required(ErrorMessage = "Vælg aktivitetstype")]
public string ActivityTypeEnum { get; set; } = "";
/// <summary> /// <summary>
/// Visit type enum as string /// Visit type enum as string
/// </summary> /// </summary>
public string VisitTypeEnum { get; set; } = "recall"; public string ActivityVisitEnum { get; set; } = "recall";
/// <summary> /// <summary>
/// Activity date /// Activity date
/// </summary> /// </summary>
[Required(ErrorMessage = "Dato skal angives")] [Required(ErrorMessage = "Dato skal angives")]
public string ActivityDate { get; set; } = ""; public string ActivityDate { get; set; } = "";
/// <summary> /// <summary>
/// Product demonstration /// Product demonstration
/// </summary> /// </summary>
[MaxLength(50, ErrorMessage = "Du kan højst bruge 50 tegn")] [MaxLength(50, ErrorMessage = "Du kan højst bruge 50 tegn")]
public string Demo { get; set; } = ""; public string Demo { get; set; } = "";
/// <summary> /// <summary>
/// Our reference - system generated /// Our reference - system generated
/// </summary> /// </summary>
[MaxLength(20, ErrorMessage = "Du kan højst bruge 20 tegn")] [MaxLength(20, ErrorMessage = "Du kan højst bruge 20 tegn")]
public string OurRef { get; set; } = ""; public string OurRef { get; set; } = "";
/// <summary> /// <summary>
/// Customer reference number /// Customer reference number
/// </summary> /// </summary>
[MaxLength(20, ErrorMessage = "Du kan højst bruge 20 tegn")] [MaxLength(20, ErrorMessage = "Du kan højst bruge 20 tegn")]
public string ReferenceNumber { get; set; } = ""; public string ReferenceNumber { get; set; } = "";
/// <summary> /// <summary>
/// Customer reference description /// Customer reference description
/// </summary> /// </summary>
[MaxLength(35, ErrorMessage = "Du kan højst bruge 35 tegn")] [MaxLength(35, ErrorMessage = "Du kan højst bruge 35 tegn")]
public string YourRef { get; set; } = ""; public string YourRef { get; set; } = "";
/// <summary> /// <summary>
/// Processing note to office /// Processing note to office
/// </summary> /// </summary>
[MaxLength(255, ErrorMessage = "Du kan højst bruge 255 tegn")] [MaxLength(255, ErrorMessage = "Du kan højst bruge 255 tegn")]
public string OrderMessage { get; set; } = ""; public string OrderMessage { get; set; } = "";
/// <summary> /// <summary>
/// CRM note for future reference /// CRM note for future reference
/// </summary> /// </summary>
[MaxLength(255, ErrorMessage = "Du kan højst bruge 255 tegn")] [MaxLength(255, ErrorMessage = "Du kan højst bruge 255 tegn")]
public string CrmNote { get; set; } = ""; public string CrmNote { get; set; } = "";
// Delivery address form entries // Delivery address form entries
/// <summary> /// <summary>
/// Customer delivery name /// Customer delivery name
/// </summary> /// </summary>
[MaxLength(100, ErrorMessage = "Du kan højst bruge 100 tegn")] [MaxLength(100, ErrorMessage = "Du kan højst bruge 100 tegn")]
public string DlvName { get; set; } = ""; public string DlvName { get; set; } = "";
/// <summary> /// <summary>
/// Customer delivery address line 1 /// Customer delivery address line 1
/// </summary> /// </summary>
[MaxLength(100, ErrorMessage = "Du kan højst bruge 100 tegn")] [MaxLength(100, ErrorMessage = "Du kan højst bruge 100 tegn")]
public string DlvAddress1 { get; set; } = ""; public string DlvAddress1 { get; set; } = "";
/// <summary> /// <summary>
/// Customer delivery address line 2 /// Customer delivery address line 2
/// </summary> /// </summary>
[MaxLength(50, ErrorMessage = "Du kan højst bruge 50 tegn")] [MaxLength(50, ErrorMessage = "Du kan højst bruge 50 tegn")]
public string DlvAddress2 { get; set; } = ""; public string DlvAddress2 { get; set; } = "";
/// <summary> /// <summary>
/// Customer delivery postal code /// Customer delivery postal code
/// </summary> /// </summary>
[MaxLength(20, ErrorMessage = "Du kan højst bruge 20 tegn")] [MaxLength(20, ErrorMessage = "Du kan højst bruge 20 tegn")]
public string DlvZipCode { get; set; } = ""; public string DlvZipCode { get; set; } = "";
/// <summary> /// <summary>
/// Customer delivery city name /// Customer delivery city name
/// </summary> /// </summary>
[MaxLength(30, ErrorMessage = "Du kan højst bruge 30 tegn")] [MaxLength(30, ErrorMessage = "Du kan højst bruge 30 tegn")]
public string DlvCity { get; set; } = ""; public string DlvCity { get; set; } = "";
// Lines // Lines
/// <summary> /// <summary>
/// Order lines /// Order lines