WIP: EAN
This commit is contained in:
parent
1389f99b81
commit
4357c2484a
7 changed files with 234 additions and 11 deletions
57
Wonky.Client/Models/Ean13Validator.cs
Normal file
57
Wonky.Client/Models/Ean13Validator.cs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
namespace Wonky.Client.Models;
|
||||||
|
|
||||||
|
public class Ean13Validator
|
||||||
|
{
|
||||||
|
private const int Size = 12;
|
||||||
|
|
||||||
|
public static bool Validate(string number)
|
||||||
|
{
|
||||||
|
if (number.Length != Size + 1)
|
||||||
|
return false;
|
||||||
|
return number == ParsedNumber(number[..12]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Ean13(int[]? firstDigits)
|
||||||
|
{
|
||||||
|
var summedProduct = 0;
|
||||||
|
var randomDigits = new Random();
|
||||||
|
bool isNull;
|
||||||
|
if (firstDigits == null)
|
||||||
|
{
|
||||||
|
firstDigits = new int[Size];
|
||||||
|
isNull = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
isNull = false;
|
||||||
|
for (var idx = 0; idx < Size; idx++)
|
||||||
|
{
|
||||||
|
var alt = idx % 2 == 0 ? 1 : 3;
|
||||||
|
int digit;
|
||||||
|
if (isNull)
|
||||||
|
{
|
||||||
|
digit = randomDigits.Next(10);
|
||||||
|
firstDigits[idx] = digit;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
digit = firstDigits[idx];
|
||||||
|
summedProduct += digit * alt;
|
||||||
|
}
|
||||||
|
var checkDigit = 10 - summedProduct % 10;
|
||||||
|
if (checkDigit == 10)
|
||||||
|
checkDigit = 0;
|
||||||
|
return string.Join("", firstDigits) + checkDigit;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ParsedNumber(string number)
|
||||||
|
{
|
||||||
|
var firstDigits = new int[Size];
|
||||||
|
if (number.Length != Size | !long.TryParse(number, out _))
|
||||||
|
return null;
|
||||||
|
for (var idx = 0; idx < Size; idx++)
|
||||||
|
{
|
||||||
|
var digit = int.Parse(number[idx].ToString());
|
||||||
|
firstDigits[idx] = digit;
|
||||||
|
}
|
||||||
|
return Ean13(firstDigits);
|
||||||
|
}
|
||||||
|
}
|
|
@ -163,13 +163,16 @@ public partial class AdvisorActivityCreatePage : IDisposable
|
||||||
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
|
||||||
Logger.LogDebug("AdvisorActivityCreatePage => DateTime parser => {}", UserPreference.WorkDate);
|
Logger.LogDebug("AdvisorActivityCreatePage => DateTime parser => {}", UserPreference.WorkDate);
|
||||||
SelectedDate = string.IsNullOrWhiteSpace(UserPreference.WorkDate)
|
SelectedDate = string.IsNullOrWhiteSpace(UserPreference.WorkDate)
|
||||||
? DateTime.Now
|
? DateTime.Now
|
||||||
: DateTime.Parse(UserPreference.WorkDate);
|
: DateTime.Parse(UserPreference.WorkDate);
|
||||||
|
|
||||||
// raise flag if report is closed
|
// raise flag if report is closed
|
||||||
ReportClosed = await ReportRepo.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 => {}", UserPreference.DateConfirmed);
|
Logger.LogDebug("Preferences.DateConfirmed => {}", UserPreference.DateConfirmed);
|
||||||
if (!UserPreference.DateConfirmed)
|
if (!UserPreference.DateConfirmed)
|
||||||
|
@ -299,6 +302,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
|
||||||
|
|
||||||
// #############################################################
|
// #############################################################
|
||||||
// callbacks
|
// callbacks
|
||||||
|
// pricelist callback
|
||||||
private async Task PriceListCallback(SelectedSku sku)
|
private async Task PriceListCallback(SelectedSku sku)
|
||||||
{
|
{
|
||||||
// get selected item
|
// get selected item
|
||||||
|
@ -311,7 +315,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// price history callback
|
||||||
private void PriceHistoryCallback(decimal price)
|
private void PriceHistoryCallback(decimal price)
|
||||||
{
|
{
|
||||||
if (price == 0)
|
if (price == 0)
|
||||||
|
@ -320,7 +324,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// inventory callback
|
||||||
private void OnInventoryCallback(DraftItem item)
|
private void OnInventoryCallback(DraftItem item)
|
||||||
{
|
{
|
||||||
Activity.ActivityStatusEnum = "order";
|
Activity.ActivityStatusEnum = "order";
|
||||||
|
@ -329,7 +333,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// confirm product check callback
|
||||||
private async Task ConfirmProductCheckCallback()
|
private async Task ConfirmProductCheckCallback()
|
||||||
{
|
{
|
||||||
ConfirmationCheckOverlay.Hide();
|
ConfirmationCheckOverlay.Hide();
|
||||||
|
@ -338,11 +342,10 @@ public partial class AdvisorActivityCreatePage : IDisposable
|
||||||
{
|
{
|
||||||
item.Check = false;
|
item.Check = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Storage.SetItemAsync($"{CompanyId}-products", CheckList);
|
await Storage.SetItemAsync($"{CompanyId}-products", CheckList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// workdate confirm callback
|
||||||
private async Task WorkDateConfirmCallback()
|
private async Task WorkDateConfirmCallback()
|
||||||
{
|
{
|
||||||
await PreferenceService.SetDateConfirmed(true);
|
await PreferenceService.SetDateConfirmed(true);
|
||||||
|
@ -351,7 +354,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// workdate changed callback
|
||||||
private async Task WorkDateChangedCallback(string workDate)
|
private async Task WorkDateChangedCallback(string workDate)
|
||||||
{
|
{
|
||||||
ReportClosed = await ReportRepo.ReportExist(workDate);
|
ReportClosed = await ReportRepo.ReportExist(workDate);
|
||||||
|
@ -362,6 +365,7 @@ public partial class AdvisorActivityCreatePage : IDisposable
|
||||||
|
|
||||||
// ################################################################################################
|
// ################################################################################################
|
||||||
// fetch invoices for customer
|
// fetch invoices for customer
|
||||||
|
// save invoices to storage
|
||||||
private async Task<InvoiceListView> FetchCompanyInvoices()
|
private async Task<InvoiceListView> FetchCompanyInvoices()
|
||||||
{
|
{
|
||||||
// no need to do for kanvas entry
|
// no need to do for kanvas entry
|
||||||
|
|
|
@ -21,6 +21,27 @@
|
||||||
@page "/advisor/customers/{CompanyId}"
|
@page "/advisor/customers/{CompanyId}"
|
||||||
<PageTitle>Kundekort for @Company.Name</PageTitle>
|
<PageTitle>Kundekort for @Company.Name</PageTitle>
|
||||||
|
|
||||||
|
@*
|
||||||
|
<div class="led-container">
|
||||||
|
<div class="led-box">
|
||||||
|
<div class="led-green"></div>
|
||||||
|
<p>Green LED</p>
|
||||||
|
</div>
|
||||||
|
<div class="led-box">
|
||||||
|
<div class="led-yellow"></div>
|
||||||
|
<p>Yellow LED</p>
|
||||||
|
</div>
|
||||||
|
<div class="led-box">
|
||||||
|
<div class="led-red"></div>
|
||||||
|
<p>Red LED</p>
|
||||||
|
</div>
|
||||||
|
<div class="led-box">
|
||||||
|
<div class="led-blue"></div>
|
||||||
|
<p>Blue LED</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
*@
|
||||||
|
|
||||||
@if (!string.IsNullOrWhiteSpace(Company.Account))
|
@if (!string.IsNullOrWhiteSpace(Company.Account))
|
||||||
{
|
{
|
||||||
@if (!string.IsNullOrWhiteSpace(Company.Blocked))
|
@if (!string.IsNullOrWhiteSpace(Company.Blocked))
|
||||||
|
@ -95,8 +116,30 @@
|
||||||
|
|
||||||
@if (!Kanvas)
|
@if (!Kanvas)
|
||||||
{
|
{
|
||||||
<div class="col-sm-4">@* ---- placeholder --- *@</div>
|
@if (AppInfo.Value.Rc)
|
||||||
|
{
|
||||||
|
<label for="segment" class="col-sm-1 col-form-label-sm">Segment</label>
|
||||||
|
<div class="col-sm-2">
|
||||||
|
<InputSelect id="segment" class="form-select bg-warning text-bg-warning" @bind-Value="@Company.Segment">
|
||||||
|
<option value="" disabled>segment</option>
|
||||||
|
<option value="1">AUTO</option>
|
||||||
|
<option value="2">INDUSTRI</option>
|
||||||
|
</InputSelect>
|
||||||
|
@* <ValidationMessage For="@(() => )"></ValidationMessage> *@
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-1">
|
||||||
|
<div class="led-box" style="display:@(DateTime.Now < DateTime.Parse("2023-10-01") ? "block" : "none")">
|
||||||
|
<div class="led-red"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="col-sm-4">
|
||||||
|
@* placeholder *@
|
||||||
|
</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>
|
||||||
|
@ -110,7 +153,7 @@
|
||||||
<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>
|
<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>
|
</div>
|
||||||
@* vat number*@
|
@* vat number*@
|
||||||
<label for="vatNumber" class="col-sm-1 col-form-label-sm">CVR/Org nr.</label>
|
<label for="vatNumber" class="col-sm-1 col-form-label-sm">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">
|
||||||
|
@ -122,7 +165,7 @@
|
||||||
</div>
|
</div>
|
||||||
@* Enable edit/save vatnumber *@
|
@* Enable edit/save vatnumber *@
|
||||||
<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="ToggleVatEdit"><i class="bi-pencil"></i> Moms/Org Nr.</button>
|
<button type="button" class="btn btn-edit" @onclick="ToggleVatEdit"><i class="bi-pencil"></i> ORG Nr.</button>
|
||||||
</div>
|
</div>
|
||||||
@* vat lookup *@
|
@* vat lookup *@
|
||||||
<div class="col-sm-3 d-grid mx-auto">
|
<div class="col-sm-3 d-grid mx-auto">
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"appInfo": {
|
"appInfo": {
|
||||||
"name": "Wonky Online",
|
"name": "Wonky Online",
|
||||||
"version": "0.135.1",
|
"version": "0.135.4",
|
||||||
"rc": true,
|
"rc": true,
|
||||||
"sandBox": false,
|
"sandBox": false,
|
||||||
"image": "grumpy-coder.png"
|
"image": "grumpy-coder.png"
|
||||||
|
|
|
@ -103,6 +103,114 @@ a, .btn-link {
|
||||||
|
|
||||||
/* end state elements */
|
/* end state elements */
|
||||||
|
|
||||||
|
/* led elements */
|
||||||
|
.led-box {
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
margin: auto;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.led-red {
|
||||||
|
margin: 5px;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
background-color: #F00;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #441313 0 -1px 9px, rgba(255, 0, 0, 0.5) 0 2px 12px;
|
||||||
|
-webkit-animation: blinkRed 0.5s infinite;
|
||||||
|
-moz-animation: blinkRed 0.5s infinite;
|
||||||
|
-ms-animation: blinkRed 0.5s infinite;
|
||||||
|
-o-animation: blinkRed 0.5s infinite;
|
||||||
|
animation: blinkRed 0.5s infinite;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes blinkRed {
|
||||||
|
from { background-color: #F00; }
|
||||||
|
50% { background-color: #A00; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #441313 0 -1px 9px, rgba(255, 0, 0, 0.5) 0 2px 0;}
|
||||||
|
to { background-color: #F00; }
|
||||||
|
}
|
||||||
|
@-moz-keyframes blinkRed {
|
||||||
|
from { background-color: #F00; }
|
||||||
|
50% { background-color: #A00; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #441313 0 -1px 9px, rgba(255, 0, 0, 0.5) 0 2px 0;}
|
||||||
|
to { background-color: #F00; }
|
||||||
|
}
|
||||||
|
@-ms-keyframes blinkRed {
|
||||||
|
from { background-color: #F00; }
|
||||||
|
50% { background-color: #A00; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #441313 0 -1px 9px, rgba(255, 0, 0, 0.5) 0 2px 0;}
|
||||||
|
to { background-color: #F00; }
|
||||||
|
}
|
||||||
|
@-o-keyframes blinkRed {
|
||||||
|
from { background-color: #F00; }
|
||||||
|
50% { background-color: #A00; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #441313 0 -1px 9px, rgba(255, 0, 0, 0.5) 0 2px 0;}
|
||||||
|
to { background-color: #F00; }
|
||||||
|
}
|
||||||
|
@keyframes blinkRed {
|
||||||
|
from { background-color: #F00; }
|
||||||
|
50% { background-color: #A00; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #441313 0 -1px 9px, rgba(255, 0, 0, 0.5) 0 2px 0;}
|
||||||
|
to { background-color: #F00; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.led-yellow {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
background-color: #FF0;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #808002 0 -1px 9px, #FF0 0 2px 12px;
|
||||||
|
-webkit-animation: blinkYellow 1s infinite;
|
||||||
|
-moz-animation: blinkYellow 1s infinite;
|
||||||
|
-ms-animation: blinkYellow 1s infinite;
|
||||||
|
-o-animation: blinkYellow 1s infinite;
|
||||||
|
animation: blinkYellow 1s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes blinkYellow {
|
||||||
|
from { background-color: #FF0; }
|
||||||
|
50% { background-color: #AA0; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #808002 0 -1px 9px, #FF0 0 2px 0; }
|
||||||
|
to { background-color: #FF0; }
|
||||||
|
}
|
||||||
|
@-moz-keyframes blinkYellow {
|
||||||
|
from { background-color: #FF0; }
|
||||||
|
50% { background-color: #AA0; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #808002 0 -1px 9px, #FF0 0 2px 0; }
|
||||||
|
to { background-color: #FF0; }
|
||||||
|
}
|
||||||
|
@-ms-keyframes blinkYellow {
|
||||||
|
from { background-color: #FF0; }
|
||||||
|
50% { background-color: #AA0; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #808002 0 -1px 9px, #FF0 0 2px 0; }
|
||||||
|
to { background-color: #FF0; }
|
||||||
|
}
|
||||||
|
@-o-keyframes blinkYellow {
|
||||||
|
from { background-color: #FF0; }
|
||||||
|
50% { background-color: #AA0; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #808002 0 -1px 9px, #FF0 0 2px 0; }
|
||||||
|
to { background-color: #FF0; }
|
||||||
|
}
|
||||||
|
@keyframes blinkYellow {
|
||||||
|
from { background-color: #FF0; }
|
||||||
|
50% { background-color: #AA0; box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #808002 0 -1px 9px, #FF0 0 2px 0; }
|
||||||
|
to { background-color: #FF0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.led-green {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
background-color: #ABFF00;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #304701 0 -1px 9px, #89FF00 0 2px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.led-blue {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
background-color: #24E0FF;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #006 0 -1px 9px, #3F8CFF 0 2px 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* end led elements */
|
||||||
|
|
||||||
/*#blazor-error-ui {*/
|
/*#blazor-error-ui {*/
|
||||||
/* background: lightyellow;*/
|
/* background: lightyellow;*/
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
<link href="/bootstrap/css/bootstrap-icons.css" rel="stylesheet" />
|
<link href="/bootstrap/css/bootstrap-icons.css" rel="stylesheet" />
|
||||||
<link href="/flag-icons/flag-icons.css" rel="stylesheet" />
|
<link href="/flag-icons/flag-icons.css" rel="stylesheet" />
|
||||||
<link href="/css/app.css" rel="stylesheet" />
|
<link href="/css/app.css" rel="stylesheet" />
|
||||||
|
<link href="/css/led.css" rel="stylesheet" />
|
||||||
<link href="/css/print.css" rel="stylesheet" />
|
<link href="/css/print.css" rel="stylesheet" />
|
||||||
<link href="/Wonky.Client.styles.css" rel="stylesheet" />
|
<link href="/Wonky.Client.styles.css" rel="stylesheet" />
|
||||||
<link href="/_content/Blazored.Toast/blazored-toast.min.css" rel="stylesheet" />
|
<link href="/_content/Blazored.Toast/blazored-toast.min.css" rel="stylesheet" />
|
||||||
|
|
|
@ -61,6 +61,11 @@ public class CompanyDto
|
||||||
[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>
|
||||||
|
/// Customer Group
|
||||||
|
/// </summary>
|
||||||
|
public string Segment { get; set; } = "";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Company Id
|
/// Company Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -75,6 +80,11 @@ public class CompanyDto
|
||||||
/// Crm note
|
/// Crm note
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string CrmNotes { get; set; } = "";
|
public string CrmNotes { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ean number
|
||||||
|
/// </summary>
|
||||||
|
public string EanNumber { get; set; } = "";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Office email
|
/// Office email
|
||||||
|
|
Loading…
Reference in a new issue