Compare commits
20 Commits
befe6a753b
..
v0.6.6
| Author | SHA1 | Date | |
|---|---|---|---|
| 791eaddf58 | |||
| 5cb29aa75f | |||
| 3c0fea30f5 | |||
| f2df121435 | |||
| 7f4cfdc1b5 | |||
| f4eb6456be | |||
| f13fb3aaf0 | |||
| 9a39879804 | |||
| 11be424c38 | |||
| 1b9064a97c | |||
| 805f782c83 | |||
| 912206f52d | |||
| 825bd6f304 | |||
| 9ecad6aa79 | |||
| 68f1a2c091 | |||
| 59cd69ddaf | |||
| 7c23f9bdae | |||
| 6d53e35399 | |||
| 42eb68d431 | |||
| 0591d91f49 |
@@ -0,0 +1,55 @@
|
||||
name: Deploy
|
||||
on:
|
||||
push:
|
||||
tags: ["v[0-9]+.[0-9]+.[0-9]+"]
|
||||
jobs:
|
||||
deploy:
|
||||
name: Build and Deploy
|
||||
runs-on: windows-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Set APP_VERSION variable from tag
|
||||
shell: powershell
|
||||
run: |
|
||||
$APP_VERSION = $env:GITHUB_REF -replace '^refs/tags/v', ''
|
||||
Add-Content -Path $env:GITHUB_ENV -Value "APP_VERSION=$APP_VERSION"
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Check version in project
|
||||
shell: powershell
|
||||
run: |
|
||||
Select-String Elwig/Elwig.csproj -Pattern "<Version>"
|
||||
$res = Select-String Elwig/Elwig.csproj -Pattern "<Version>${{ env.APP_VERSION }}</Version>"
|
||||
if ($res -eq $null) {
|
||||
exit 1
|
||||
}
|
||||
- name: Setup MSBuild
|
||||
uses: microsoft/setup-msbuild@v1.1
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@v1
|
||||
- name: Restore NuGet packages
|
||||
shell: powershell
|
||||
run: $(& nuget restore Elwig.sln; $a=$lastexitcode) | findstr x*; exit $a
|
||||
- name: Build Setup
|
||||
shell: powershell
|
||||
run: $(& msbuild -verbosity:quiet Setup/Setup.wixproj -property:Configuration=Release -property:Platform=x64; $a=$lastexitcode) | findstr x*; exit $a
|
||||
- name: Rename artifact
|
||||
shell: powershell
|
||||
run: Move-Item Setup/bin/x64/Release/Elwig.exe Setup/bin/x64/Release/Elwig-${{ env.APP_VERSION }}.exe
|
||||
- name: Create release
|
||||
uses: akkuman/gitea-release-action@v1
|
||||
with:
|
||||
name: Elwig ${{ env.APP_VERSION }}
|
||||
files: |-
|
||||
Setup/bin/x64/Release/Elwig-${{ env.APP_VERSION }}.exe
|
||||
- name: Upload to website
|
||||
shell: powershell
|
||||
run: |
|
||||
$content = [System.IO.File]::ReadAllBytes("Setup/bin/x64/Release/Elwig-${{ env.APP_VERSION }}.exe")
|
||||
Invoke-WebRequest `
|
||||
-Uri "https://www.necronda.net/elwig/files/Elwig-${{ env.APP_VERSION }}.exe" `
|
||||
-Method PUT `
|
||||
-Body $content `
|
||||
-Headers @{ Authorization = "${{ secrets.API_AUTHORIZATION }}" } `
|
||||
-ContentType "application/octet-stream"
|
||||
@@ -0,0 +1,29 @@
|
||||
name: Test
|
||||
on:
|
||||
push:
|
||||
branches: ["**"]
|
||||
jobs:
|
||||
test:
|
||||
name: Run tests
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup MSBuild
|
||||
uses: microsoft/setup-msbuild@v1.1
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@v1
|
||||
- name: Restore NuGet packages
|
||||
shell: powershell
|
||||
run: $(& nuget restore Elwig.sln; $a=$lastexitcode) | findstr x*; exit $a
|
||||
- name: Build Elwig
|
||||
shell: powershell
|
||||
run: $(& msbuild -verbosity:quiet Elwig/Elwig.csproj -property:Configuration=Debug; $a=$lastexitcode) | findstr x*; exit $a
|
||||
- name: Build Tests
|
||||
shell: powershell
|
||||
run: $(& dotnet build Tests; $a=$lastexitcode) | findstr x*; exit $a
|
||||
- name: Run Tests
|
||||
shell: powershell
|
||||
run: |
|
||||
$env:PATH += ";$(pwd)\Installer\Files"
|
||||
$(& dotnet test Tests; $a=$lastexitcode) | findstr x*; exit $a
|
||||
@@ -4,3 +4,5 @@ bin/
|
||||
.vs
|
||||
.idea
|
||||
Tests/Resources/Sql/Create.sql
|
||||
*.exe
|
||||
!WinziPrint.exe
|
||||
|
||||
+17
-18
@@ -141,26 +141,10 @@ namespace Elwig {
|
||||
MessageBox.Show("Invalid branch name in config!", "Invalid Branch Config", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
Shutdown();
|
||||
} else {
|
||||
var entry = branches[Config.Branch.ToLower()];
|
||||
ZwstId = entry.Item1;
|
||||
BranchName = entry.Item2;
|
||||
BranchPlz = entry.Item3;
|
||||
BranchLocation = entry.Item4?.Split(" im ")[0].Split(" an ")[0].Split(" bei ")[0]; // FIXME
|
||||
BranchAddress = entry.Item5;
|
||||
BranchPhoneNr = entry.Item6;
|
||||
BranchFaxNr = entry.Item7;
|
||||
BranchMobileNr = entry.Item8;
|
||||
SetBranch(branches[Config.Branch.ToLower()]);
|
||||
}
|
||||
} else if (branches.Count == 1) {
|
||||
var entry = branches.First().Value;
|
||||
ZwstId = entry.Item1;
|
||||
BranchName = entry.Item2;
|
||||
BranchPlz = entry.Item3;
|
||||
BranchLocation = entry.Item4?.Split(" im ")[0].Split(" an ")[0].Split(" bei ")[0]; // FIXME
|
||||
BranchAddress = entry.Item5;
|
||||
BranchPhoneNr = entry.Item6;
|
||||
BranchFaxNr = entry.Item7;
|
||||
BranchMobileNr = entry.Item8;
|
||||
SetBranch(branches.First().Value);
|
||||
} else {
|
||||
MessageBox.Show("Unable to determine local branch!", "Invalid Branch Config", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
Shutdown();
|
||||
@@ -169,6 +153,21 @@ namespace Elwig {
|
||||
base.OnStartup(evt);
|
||||
}
|
||||
|
||||
public static void SetBranch(Branch b) {
|
||||
SetBranch((b.ZwstId, b.Name, b.PostalDest?.AtPlz?.Plz, b.PostalDest?.AtPlz?.Ort.Name, b.Address, b.PhoneNr, b.FaxNr, b.MobileNr));
|
||||
}
|
||||
|
||||
private static void SetBranch((string, string, int?, string?, string?, string?, string?, string?) entry) {
|
||||
ZwstId = entry.Item1;
|
||||
BranchName = entry.Item2;
|
||||
BranchPlz = entry.Item3;
|
||||
BranchLocation = entry.Item4?.Split(" im ")[0].Split(" an ")[0].Split(" bei ")[0]; // FIXME
|
||||
BranchAddress = entry.Item5;
|
||||
BranchPhoneNr = entry.Item6;
|
||||
BranchFaxNr = entry.Item7;
|
||||
BranchMobileNr = entry.Item8;
|
||||
}
|
||||
|
||||
private void PrintingReadyChanged() {
|
||||
Dispatcher.BeginInvoke(OnPrintingReadyChanged, new EventArgs());
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Elwig.Documents {
|
||||
// 3 - full
|
||||
public int DisplayStats = App.Client.ModeDeliveryNoteStats;
|
||||
|
||||
public DeliveryNote(Delivery d, AppDbContext ctx) : base($"Traubenübernahmeschein Nr. {d.LsNr}", d.Member) {
|
||||
public DeliveryNote(Delivery d, AppDbContext? ctx = null) : base($"Traubenübernahmeschein Nr. {d.LsNr}", d.Member) {
|
||||
UseBillingAddress = true;
|
||||
ShowDateAndLocation = true;
|
||||
Delivery = d;
|
||||
@@ -27,7 +27,7 @@ namespace Elwig.Documents {
|
||||
$"</tbody></table>";
|
||||
Text = App.Client.TextDeliveryNote;
|
||||
DocumentId = d.LsNr;
|
||||
MemberBuckets = ctx.GetMemberBuckets(d.Year, d.Member.MgNr).GetAwaiter().GetResult();
|
||||
MemberBuckets = ctx?.GetMemberBuckets(d.Year, d.Member.MgNr).GetAwaiter().GetResult() ?? [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,8 +63,8 @@
|
||||
<td colspan="5">
|
||||
@if (Model.Member.BillingAddress != null) {
|
||||
@Model.Member.BillingAddress.PostalDest.AtPlz?.Plz
|
||||
@Model.Member.BillingAddress.PostalDest.AtPlz?.Dest
|
||||
@("(")@Model.Member.BillingAddress.PostalDest.AtPlz?.Ort.Name@(")")
|
||||
@(" ")@Model.Member.BillingAddress.PostalDest.AtPlz?.Dest
|
||||
@(" (")@Model.Member.BillingAddress.PostalDest.AtPlz?.Ort.Name@(")")
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
<UseWPF>true</UseWPF>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
|
||||
<Version>0.6.4</Version>
|
||||
<Version>0.6.6</Version>
|
||||
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -160,16 +160,16 @@ namespace Elwig.Helpers {
|
||||
}
|
||||
|
||||
public async Task<int> NextMgNr() {
|
||||
int c = await Members.Select(m => m.MgNr).MinAsync();
|
||||
int c = 0;
|
||||
(await Members.OrderBy(m => m.MgNr).Select(m => m.MgNr).ToListAsync())
|
||||
.ForEach(a => { if (a <= c + 1000) c = a; });
|
||||
return c + 1;
|
||||
}
|
||||
|
||||
public async Task<int> NextFbNr() {
|
||||
int c = await AreaCommitments.Select(ac => ac.FbNr).MinAsync();
|
||||
int c = 0;
|
||||
(await AreaCommitments.OrderBy(ac => ac.FbNr).Select(ac => ac.FbNr).ToListAsync())
|
||||
.ForEach(a => { if (a <= c + 1000) c = a; });
|
||||
.ForEach(a => { if (a <= c + 10000) c = a; });
|
||||
return c + 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Elwig.Helpers {
|
||||
public static class AppDbUpdater {
|
||||
|
||||
// Don't forget to update value in Tests/fetch-resources.bat!
|
||||
public static readonly int RequiredSchemaVersion = 15;
|
||||
public static readonly int RequiredSchemaVersion = 16;
|
||||
|
||||
private static int VersionOffset = 0;
|
||||
|
||||
|
||||
@@ -45,7 +45,15 @@ namespace Elwig.Helpers.Billing {
|
||||
""");
|
||||
}
|
||||
|
||||
public async Task CalculateBuckets(bool allowAttrsIntoLower, bool avoidUnderDeliveries, bool honorGebunden, SqliteConnection? cnx = null) {
|
||||
public async Task CalculateBuckets(
|
||||
bool? honorGebundenField = null,
|
||||
bool? allowAttributesIntoLower = null,
|
||||
bool? avoidUnderDeliveries = null,
|
||||
SqliteConnection? cnx = null
|
||||
) {
|
||||
var honorGebunden = honorGebundenField ?? Season.Billing_HonorGebunden;
|
||||
var allowAttrsIntoLower = allowAttributesIntoLower ?? Season.Billing_AllowAttrsIntoLower;
|
||||
var avoidUnderDlvrs = avoidUnderDeliveries ?? Season.Billing_AvoidUnderDeliveries;
|
||||
var attrVals = Context.WineAttributes.ToDictionary(a => a.AttrId, a => (a.IsStrict, a.FillLower));
|
||||
var attrForced = attrVals.Where(a => a.Value.IsStrict && a.Value.FillLower == 0).Select(a => a.Key).ToArray();
|
||||
var ownCnx = cnx == null;
|
||||
@@ -125,7 +133,7 @@ namespace Elwig.Helpers.Billing {
|
||||
SET discr = excluded.discr, value = value + excluded.value;
|
||||
""");
|
||||
|
||||
if (!avoidUnderDeliveries) {
|
||||
if (!avoidUnderDlvrs) {
|
||||
if (ownCnx) await cnx.DisposeAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -18,14 +18,10 @@ namespace Elwig.Helpers.Billing {
|
||||
Data = PaymentBillingData.FromJson(PaymentVariant.Data, Utils.GetVaributes(Context, Year, onlyDelivered: false));
|
||||
}
|
||||
|
||||
public async Task Calculate() {
|
||||
public async Task Calculate(bool? honorGebunden = null, bool ? allowAttrsIntoLower = null, bool? avoidUnderDeliveries = null) {
|
||||
using var cnx = await AppDbContext.ConnectAsync();
|
||||
using var tx = await cnx.BeginTransactionAsync();
|
||||
await CalculateBuckets(
|
||||
Season.Billing_AllowAttrsIntoLower,
|
||||
Season.Billing_AvoidUnderDeliveries,
|
||||
Season.Billing_HonorGebunden,
|
||||
cnx);
|
||||
await CalculateBuckets(honorGebunden, allowAttrsIntoLower, avoidUnderDeliveries, cnx);
|
||||
await DeleteInDb(cnx);
|
||||
await SetCalcTime(cnx);
|
||||
await CalculatePrices(cnx);
|
||||
@@ -149,8 +145,9 @@ namespace Elwig.Helpers.Billing {
|
||||
foreach (var part in parts) {
|
||||
var ungeb = part.Discr == "_";
|
||||
var payAttrId = (part.Discr is "" or "_") ? null : part.Discr;
|
||||
var geb = !ungeb && payAttrId == part.AttrId;
|
||||
var price = Data.CalculatePrice(part.SortId, part.AttrId, part.QualId, geb, part.Oe, part.Kmw);
|
||||
var attrId = part.AttrId == "B" ? "B" : payAttrId; // FIXME
|
||||
var geb = !ungeb; // FIXME && payAttrId == part.AttrId;
|
||||
var price = Data.CalculatePrice(part.SortId, attrId, part.QualId, geb, part.Oe, part.Kmw);
|
||||
var priceL = PaymentVariant.Season.DecToDb(price);
|
||||
inserts.Add((part.Year, part.DId, part.DPNr, part.BktNr, priceL, priceL * part.Value));
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elwig.Helpers.Export {
|
||||
@@ -52,7 +53,7 @@ namespace Elwig.Helpers.Export {
|
||||
<CreDtTm>{DateTime.UtcNow:o}</CreDtTm>
|
||||
<NbOfTxs>{nbOfTxs}</NbOfTxs>
|
||||
<CtrlSum>{Transaction.FormatAmount(ctrlSum)}</CtrlSum>
|
||||
<InitgPty><Nm>{App.Client.NameFull}</Nm></InitgPty>
|
||||
<InitgPty><Nm>{SecurityElement.Escape(App.Client.NameFull)}</Nm></InitgPty>
|
||||
</GrpHdr>
|
||||
<PmtInf>
|
||||
<PmtInfId>{pmtInfId}</PmtInfId>
|
||||
@@ -60,7 +61,7 @@ namespace Elwig.Helpers.Export {
|
||||
<NbOfTxs>{nbOfTxs}</NbOfTxs>
|
||||
<CtrlSum>{Transaction.FormatAmount(ctrlSum)}</CtrlSum>
|
||||
<ReqdExctnDt>{(Version >= 8 ? "<Dt>" : "")}{Date:yyyy-MM-dd}{(Version >= 8 ? "</Dt>" : "")}</ReqdExctnDt>
|
||||
<Dbtr><Nm>{App.Client.NameFull}</Nm></Dbtr>
|
||||
<Dbtr><Nm>{SecurityElement.Escape(App.Client.NameFull)}</Nm></Dbtr>
|
||||
<DbtrAcct><Id><IBAN>{App.Client.Iban!.Replace(" ", "")}</IBAN></Id></DbtrAcct>
|
||||
<DbtrAgt><FinInstnId>{(Version >= 4 ? "<BICFI>" : "<BIC>")}{App.Client.Bic ?? "NOTPROVIDED"}{(Version >= 4 ? "</BICFI>" : "</BIC>")}</FinInstnId></DbtrAgt>
|
||||
""");
|
||||
@@ -76,15 +77,15 @@ namespace Elwig.Helpers.Export {
|
||||
<PmtId><EndToEndId>{id}</EndToEndId></PmtId>
|
||||
<Amt><InstdAmt Ccy="{tx.Currency}">{Transaction.FormatAmount(tx.Amount)}</InstdAmt></Amt>
|
||||
<Cdtr>
|
||||
<Nm>{a.Name[..Math.Min(140, a.Name.Length)]}</Nm>
|
||||
<Nm>{SecurityElement.Escape(a.Name[..Math.Min(140, a.Name.Length)])}</Nm>
|
||||
<PstlAdr>
|
||||
<StrtNm>{a1?[..Math.Min(70, a1.Length)]}</StrtNm><BldgNb>{a2?[..Math.Min(16, a2.Length)]}</BldgNb>
|
||||
<PstCd>{a.PostalDest.AtPlz?.Plz}</PstCd><TwnNm>{a.PostalDest.AtPlz?.Ort.Name}</TwnNm>
|
||||
<StrtNm>{a1?[..Math.Min(70, a1.Length)]}</StrtNm><BldgNb>{SecurityElement.Escape(a2?[..Math.Min(16, a2.Length)])}</BldgNb>
|
||||
<PstCd>{a.PostalDest.AtPlz?.Plz}</PstCd><TwnNm>{SecurityElement.Escape(a.PostalDest.AtPlz?.Ort.Name)}</TwnNm>
|
||||
<Ctry>{a.PostalDest.Country.Alpha2}</Ctry>
|
||||
</PstlAdr>
|
||||
</Cdtr>
|
||||
<CdtrAcct><Id><IBAN>{tx.Member.Iban!}</IBAN></Id></CdtrAcct>
|
||||
<RmtInf><Ustrd>{info}</Ustrd></RmtInf>
|
||||
<RmtInf><Ustrd>{SecurityElement.Escape(info)}</Ustrd></RmtInf>
|
||||
</CdtTrfTxInf>
|
||||
""");
|
||||
progress?.Report(100.0 * ++i / count);
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Elwig.Helpers.Printing {
|
||||
private static RazorLightEngine? Engine = null;
|
||||
public static bool IsReady => Engine != null;
|
||||
|
||||
public static async Task Init(Action evtHandler) {
|
||||
public static async Task Init(Action? evtHandler = null) {
|
||||
var e = new RazorLightEngineBuilder()
|
||||
.UseFileSystemProject(App.DataPath + "resources")
|
||||
.UseMemoryCachingProvider()
|
||||
@@ -24,7 +24,7 @@ namespace Elwig.Helpers.Printing {
|
||||
await e.CompileTemplateAsync("DeliveryConfirmation");
|
||||
|
||||
Engine = e;
|
||||
evtHandler();
|
||||
evtHandler?.Invoke();
|
||||
}
|
||||
|
||||
public static async Task<string> CompileRenderAsync(string key, object model) {
|
||||
|
||||
@@ -11,13 +11,20 @@ using System.Linq;
|
||||
namespace Elwig.Helpers.Printing {
|
||||
public static class Pdf {
|
||||
|
||||
private static readonly string PdfToPrinter = App.ExePath + "PDFtoPrinter.exe";
|
||||
private static readonly string WinziPrint = App.ExePath + "WinziPrint.exe";
|
||||
private static readonly string PdfToPrinter = new string[] { App.ExePath }
|
||||
.Union(Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? [])
|
||||
.Select(x => Path.Combine(x, "PDFtoPrinter.exe"))
|
||||
.Where(x => File.Exists(x))
|
||||
.FirstOrDefault() ?? throw new FileNotFoundException("PDFtoPrinter executable not found");
|
||||
private static readonly string WinziPrint = new string[] { App.ExePath }
|
||||
.Union(Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? [])
|
||||
.Select(x => Path.Combine(x, "WinziPrint.exe"))
|
||||
.Where(x => File.Exists(x))
|
||||
.FirstOrDefault() ?? throw new FileNotFoundException("WiniPrint executable not found");
|
||||
private static Process? WinziPrintProc;
|
||||
public static bool IsReady => WinziPrintProc != null;
|
||||
|
||||
|
||||
public static async Task Init(Action evtHandler) {
|
||||
public static async Task Init(Action? evtHandler = null) {
|
||||
var p = new Process() { StartInfo = new() {
|
||||
FileName = WinziPrint,
|
||||
CreateNoWindow = true,
|
||||
@@ -33,7 +40,7 @@ namespace Elwig.Helpers.Printing {
|
||||
p.StartInfo.ArgumentList.Add("-");
|
||||
p.Start();
|
||||
WinziPrintProc = p;
|
||||
evtHandler();
|
||||
evtHandler?.Invoke();
|
||||
}
|
||||
|
||||
public static async Task<IEnumerable<int>> Convert(string htmlPath, string pdfPath, bool doubleSided = false, IProgress<double>? progress = null) {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elwig.Helpers.Weighing {
|
||||
/// <summary>
|
||||
/// Interface for controlling a a scale which responds to commands sent to it
|
||||
/// </summary>
|
||||
public interface ICommandScale : IScale {
|
||||
/// <summary>
|
||||
/// Get the current weight on the scale without performing a weighing process
|
||||
/// </summary>
|
||||
/// <returns>Result of the weighing process (probably without a weighing id)</returns>
|
||||
Task<WeighingResult> GetCurrentWeight();
|
||||
|
||||
/// <summary>
|
||||
/// Perform a weighing process
|
||||
/// </summary>
|
||||
/// <returns>Result of the weighing process (including a weighing id)</returns>
|
||||
Task<WeighingResult> Weigh();
|
||||
|
||||
/// <summary>
|
||||
/// Empty the scale container or grant clearance to do so
|
||||
/// </summary>
|
||||
Task Empty();
|
||||
|
||||
/// <summary>
|
||||
/// Grant clearance to fill the scale container
|
||||
/// </summary>
|
||||
Task GrantFillingClearance();
|
||||
|
||||
/// <summary>
|
||||
/// Revoke clearance to fill the scale container
|
||||
/// </summary>
|
||||
Task RevokeFillingClearance();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Elwig.Helpers.Weighing {
|
||||
/// <summary>
|
||||
/// Interface for controlling a a scale which automatically sends weighing updates
|
||||
/// </summary>
|
||||
public interface IEventScale : IScale {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elwig.Helpers.Weighing {
|
||||
/// <summary>
|
||||
/// Interface for controlling a industrial scale
|
||||
/// Interface for controlling a industrial scale (industrial terminal, "IT")
|
||||
/// </summary>
|
||||
public interface IScale : IDisposable {
|
||||
/// <summary>
|
||||
@@ -45,32 +44,5 @@ namespace Elwig.Helpers.Weighing {
|
||||
/// Where to log the requests and responses from the scale to
|
||||
/// </summary>
|
||||
string? LogPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the current weight on the scale without performing a weighing process
|
||||
/// </summary>
|
||||
/// <returns>Result of the weighing process (probably without a weighing id)</returns>
|
||||
Task<WeighingResult> GetCurrentWeight();
|
||||
|
||||
/// <summary>
|
||||
/// Perform a weighing process
|
||||
/// </summary>
|
||||
/// <returns>Result of the weighing process (including a weighing id)</returns>
|
||||
Task<WeighingResult> Weigh();
|
||||
|
||||
/// <summary>
|
||||
/// Empty the scale container or grant clearance to do so
|
||||
/// </summary>
|
||||
Task Empty();
|
||||
|
||||
/// <summary>
|
||||
/// Grant clearance to fill the scale container
|
||||
/// </summary>
|
||||
Task GrantFillingClearance();
|
||||
|
||||
/// <summary>
|
||||
/// Revoke clearance to fill the scale container
|
||||
/// </summary>
|
||||
Task RevokeFillingClearance();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +1,19 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elwig.Helpers.Weighing {
|
||||
public class InvalidScale : IScale {
|
||||
public class InvalidScale(string id) : IScale {
|
||||
|
||||
public string Manufacturer => "NONE";
|
||||
public string Model => "NONE";
|
||||
public string ScaleId { get; private set; }
|
||||
public string ScaleId => id;
|
||||
public int InternalScaleNr => 0;
|
||||
public bool IsReady => false;
|
||||
public bool HasFillingClearance => false;
|
||||
public int? WeightLimit => null;
|
||||
public string? LogPath => null;
|
||||
|
||||
public InvalidScale(string id) {
|
||||
ScaleId = id;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public Task<WeighingResult> Weigh() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<WeighingResult> GetCurrentWeight() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task Empty() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task GrantFillingClearance() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task RevokeFillingClearance() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elwig.Helpers.Weighing {
|
||||
// TODO implement SchemberScale
|
||||
public class SchemberScale : IScale {
|
||||
public class SchemberScale : IEventScale {
|
||||
|
||||
public string Manufacturer => "Schember";
|
||||
public string Model => throw new NotImplementedException();
|
||||
@@ -15,27 +13,7 @@ namespace Elwig.Helpers.Weighing {
|
||||
public string? LogPath => throw new NotImplementedException();
|
||||
|
||||
public void Dispose() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task Empty() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<WeighingResult> GetCurrentWeight() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task GrantFillingClearance() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task RevokeFillingClearance() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<WeighingResult> Weigh() {
|
||||
throw new NotImplementedException();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elwig.Helpers.Weighing {
|
||||
public class SystecScale : IScale {
|
||||
public class SystecScale : ICommandScale {
|
||||
|
||||
protected enum Output { RTS, DTR, OUT1, OUT2 };
|
||||
|
||||
@@ -44,6 +44,8 @@ namespace Elwig.Helpers.Weighing {
|
||||
} else {
|
||||
throw new ArgumentException("Unsupported scheme");
|
||||
}
|
||||
Stream.WriteTimeout = 250;
|
||||
Stream.ReadTimeout = 11000;
|
||||
|
||||
if (empty != null) {
|
||||
var parts = empty.Split(':');
|
||||
@@ -105,7 +107,7 @@ namespace Elwig.Helpers.Weighing {
|
||||
line = await reader.ReadLineAsync();
|
||||
if (LogPath != null) await File.AppendAllTextAsync(LogPath, $"{line}\r\n");
|
||||
}
|
||||
if (line == null || line.Length < 4 || !line.StartsWith("<") || !line.EndsWith(">")) {
|
||||
if (line == null || line.Length < 4 || !line.StartsWith('<') || !line.EndsWith('>')) {
|
||||
throw new IOException("Invalid response from scale");
|
||||
}
|
||||
|
||||
@@ -162,7 +164,7 @@ namespace Elwig.Helpers.Weighing {
|
||||
var crc16 = line[52..60].Trim();
|
||||
|
||||
if (Utils.CalcCrc16Modbus(record[..54]) != ushort.Parse(crc16)) {
|
||||
throw new IOException($"Invalid response from scale: Invalid CRC16 checksum ({crc16} != {Utils.CalcCrc16Modbus(record[..54]).ToString()})");
|
||||
throw new IOException($"Invalid response from scale: Invalid CRC16 checksum ({crc16} != {Utils.CalcCrc16Modbus(record[..54])})");
|
||||
} else if (unit != "kg") {
|
||||
throw new IOException($"Unsupported unit in weighing response: '{unit}'");
|
||||
}
|
||||
|
||||
@@ -4,36 +4,38 @@ namespace Elwig.Helpers.Weighing {
|
||||
/// <summary>
|
||||
/// Result of a weighing process on an industrial scale
|
||||
/// </summary>
|
||||
public class WeighingResult {
|
||||
public struct WeighingResult {
|
||||
/// <summary>
|
||||
/// Measured net weight in kg
|
||||
/// </summary>
|
||||
public int? Weight = null;
|
||||
public int? Weight;
|
||||
|
||||
/// <summary>
|
||||
/// Weighing id (or IdentNr) provided by the scale
|
||||
/// </summary>
|
||||
public string? WeighingId = null;
|
||||
public string? WeighingId;
|
||||
|
||||
/// <summary>
|
||||
/// Wheighing id (or IdentNr) provided by the scale optionally combined with the current date
|
||||
/// </summary>
|
||||
public string? FullWeighingId = null;
|
||||
public string? FullWeighingId;
|
||||
|
||||
/// <summary>
|
||||
/// Date string provided by the scale
|
||||
/// </summary>
|
||||
public DateOnly? Date = null;
|
||||
public DateOnly? Date;
|
||||
|
||||
/// <summary>
|
||||
/// Time string provided by the scale
|
||||
/// </summary>
|
||||
public TimeOnly? Time = null;
|
||||
public TimeOnly? Time;
|
||||
|
||||
/// <returns><Weight/WeighingId/Date/Time></returns>
|
||||
override public string ToString() {
|
||||
public override readonly string ToString() {
|
||||
var w = Weight != null ? $"{Weight}kg" : "";
|
||||
return $"<{w}/{WeighingId}/{Date:yyyy-MM-dd}/{Time:HH:mm}>";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
-- schema version 15 to 16
|
||||
|
||||
INSERT INTO AT_plz_dest (plz, okz, dest)
|
||||
VALUES (2560, 3388, 'Grillenberg');
|
||||
|
||||
DELETE FROM AT_plz_dest WHERE (plz, okz) = (2561, 3388);
|
||||
|
||||
UPDATE AT_ort SET kgnr = 23351 WHERE okz = 5280;
|
||||
UPDATE AT_ort SET kgnr = 4311 WHERE okz = 3388;
|
||||
@@ -1,6 +1,7 @@
|
||||
using Elwig.Documents;
|
||||
using Elwig.Helpers;
|
||||
using Elwig.Helpers.Export;
|
||||
using Elwig.Helpers.Weighing;
|
||||
using Elwig.Models.Entities;
|
||||
using LinqKit;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -862,7 +863,7 @@ namespace Elwig.Windows {
|
||||
var originalMemberKgNr = d?.Member?.DefaultKgNr;
|
||||
if (d == null) {
|
||||
d = Context.CreateProxy<Delivery>();
|
||||
year = Utils.CurrentNextSeason;
|
||||
year = Utils.CurrentYear;
|
||||
did = await Context.NextDId(year);
|
||||
} else {
|
||||
year = d.Year;
|
||||
@@ -968,7 +969,8 @@ namespace Elwig.Windows {
|
||||
CancelCreatingButton.IsEnabled = false;
|
||||
try {
|
||||
var s = App.Scales[index];
|
||||
var res = await s.Weigh();
|
||||
if (s is not ICommandScale cs) return;
|
||||
var res = await cs.Weigh();
|
||||
if ((res.Weight ?? 0) > 0 && res.FullWeighingId != null) {
|
||||
WeightInput.Text = $"{res.Weight:N0}";
|
||||
ScaleId = s.ScaleId;
|
||||
@@ -1080,8 +1082,8 @@ namespace Elwig.Windows {
|
||||
|
||||
private void EmptyScale() {
|
||||
var scale = App.Scales.Where(s => s.ScaleId == ScaleId).FirstOrDefault();
|
||||
if (scale == null) return;
|
||||
scale.Empty();
|
||||
if (scale is not ICommandScale cs) return;
|
||||
cs.Empty();
|
||||
}
|
||||
|
||||
private async void NewDeliveryPartButton_Click(object sender, RoutedEventArgs evt) {
|
||||
|
||||
@@ -160,7 +160,7 @@ namespace Elwig.Windows {
|
||||
filter.RemoveAt(i--);
|
||||
} else if (e.Length > 2 && e.StartsWith('"') && e.EndsWith('"')) {
|
||||
filter[i] = e[1..^1];
|
||||
} else if (e.Length <= 2) {
|
||||
} else if (e.Length < 2) {
|
||||
filter.RemoveAt(i--);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,10 +57,7 @@ namespace Elwig.Windows {
|
||||
|
||||
var b = new Billing(year);
|
||||
await b.FinishSeason();
|
||||
await b.CalculateBuckets(
|
||||
s.Billing_AllowAttrsIntoLower,
|
||||
s.Billing_AvoidUnderDeliveries,
|
||||
s.Billing_HonorGebunden);
|
||||
await b.CalculateBuckets();
|
||||
Mouse.OverrideCursor = null;
|
||||
CalculateBucketsButton.IsEnabled = true;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using Elwig.Documents;
|
||||
using Elwig.Helpers;
|
||||
using Elwig.Helpers.Billing;
|
||||
using Elwig.Helpers.Export;
|
||||
using Elwig.Helpers.Weighing;
|
||||
using Elwig.Models.Dtos;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
@@ -27,7 +28,8 @@ namespace Elwig.Windows {
|
||||
|
||||
private async void WeighingButton1_Click(object sender, RoutedEventArgs evt) {
|
||||
try {
|
||||
var res = await App.Scales[0].GetCurrentWeight();
|
||||
if (App.Scales[0] is not ICommandScale cs) return;
|
||||
var res = await cs.GetCurrentWeight();
|
||||
Output.Text = res.ToString();
|
||||
} catch (Exception e) {
|
||||
MessageBox.Show($"Beim Wiegen ist ein Fehler aufgetreten:\n\n{e.Message}", "Waagenfehler",
|
||||
@@ -37,7 +39,8 @@ namespace Elwig.Windows {
|
||||
|
||||
private async void WeighingButton2_Click(object sender, RoutedEventArgs evt) {
|
||||
try {
|
||||
var res = await App.Scales[0].Weigh();
|
||||
if (App.Scales[0] is not ICommandScale cs) return;
|
||||
var res = await cs.Weigh();
|
||||
Output.Text = res.ToString();
|
||||
} catch (Exception e) {
|
||||
MessageBox.Show($"Beim Wiegen ist ein Fehler aufgetreten:\n\n{e.Message}", "Waagenfehler",
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
</Task>
|
||||
</UsingTask>
|
||||
<Target Name="CustomBeforeBuild" BeforeTargets="BeforeBuild">
|
||||
<Exec Command="curl -s -L "http://www.columbia.edu/~em36/PDFtoPrinter.exe" -z "$(TargetDir)PDFtoPrinter.exe" -o "$(TargetDir)PDFtoPrinter.exe"" />
|
||||
<Exec Command="dotnet publish "$(SolutionDir)Elwig\Elwig.csproj" "/p:PublishProfile=$(SolutionDir)\Elwig\Properties\PublishProfiles\FolderProfile.pubxml"" />
|
||||
<Exec Command="curl -s -L "http://www.columbia.edu/~em36/PDFtoPrinter.exe" -z "$(ProjectDir)\Files\PDFtoPrinter.exe" -o "$(ProjectDir)\Files\PDFtoPrinter.exe"" />
|
||||
<Exec Command="dotnet publish "$(ProjectDir)\..\Elwig\Elwig.csproj" "/p:PublishProfile=$(ProjectDir)\..\Elwig\Properties\PublishProfiles\FolderProfile.pubxml"" />
|
||||
<GetFileVersion AssemblyPath="..\Elwig\bin\Publish\Elwig.exe">
|
||||
<Output TaskParameter="Version" PropertyName="ElwigFileVersion" />
|
||||
</GetFileVersion>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<File Source="$(ProjectDir)\Files\WinziPrint.exe" Id="WinziPrint.exe"/>
|
||||
</Component>
|
||||
<Component Directory="InstallFolder">
|
||||
<File Source="$(TargetDir)\PDFtoPrinter.exe" Id="PDFtoPrinter.exe"/>
|
||||
<File Source="$(ProjectDir)\Files\PDFtoPrinter.exe" Id="PDFtoPrinter.exe"/>
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
</Fragment>
|
||||
|
||||
+21
-18
@@ -1,25 +1,28 @@
|
||||
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
|
||||
<Bundle Name="Elwig" Manufacturer="Elwig" Version="!(bind.packageVersion.ElwigMsi)" UpgradeCode="f3c8fcab-c37c-43aa-9ab8-e42f4bb518b7" IconSourceFile="$(var.ElwigProjectDir)\Resources\Images\Elwig.ico" >
|
||||
<BootstrapperApplication>
|
||||
<bal:WixStandardBootstrapperApplication LicenseUrl="" Theme="hyperlinkLicense" LogoFile="$(var.ElwigProjectDir)\Resources\Images\Elwig.png" SuppressOptionsUI="yes" ShowVersion="yes" />
|
||||
</BootstrapperApplication>
|
||||
|
||||
<util:RegistrySearch Id="VCredistx86" Variable="VCredistx86" Result="exists" Root="HKLM" Key="SOFTWARE\Classes\Installer\Dependencies\VC,redist.x86,x86,14.36,bundle" />
|
||||
<util:RegistrySearch Id="Webview2Machine" Variable="Webview2Machine" Result="exists" Root="HKLM" Key="SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" />
|
||||
<util:RegistrySearch Id="Webview2User" Variable="Webview2User" Result="exists" Root="HKCU" Key="Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" />
|
||||
<Bundle Name="Elwig" Manufacturer="Elwig" Version="!(bind.packageVersion.ElwigMsi)" UpgradeCode="f3c8fcab-c37c-43aa-9ab8-e42f4bb518b7"
|
||||
IconSourceFile="$(var.ElwigProjectDir)\Resources\Images\Elwig.ico">
|
||||
<BootstrapperApplication>
|
||||
<bal:WixStandardBootstrapperApplication LicenseUrl="" Theme="hyperlinkLicense" LogoFile="$(var.ElwigProjectDir)\Resources\Images\Elwig.png"
|
||||
SuppressOptionsUI="yes" ShowVersion="yes"/>
|
||||
</BootstrapperApplication>
|
||||
|
||||
<util:RegistrySearch Id="VCredistx86" Variable="VCredistx86" Result="exists"
|
||||
Root="HKLM" Key="SOFTWARE\Classes\Installer\Dependencies\VC,redist.x86,x86,14.36,bundle"/>
|
||||
<util:RegistrySearch Id="Webview2Machine" Variable="Webview2Machine" Result="exists"
|
||||
Root="HKLM" Key="SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}"/>
|
||||
<util:RegistrySearch Id="Webview2User" Variable="Webview2User" Result="exists"
|
||||
Root="HKCU" Key="Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}"/>
|
||||
|
||||
<Chain>
|
||||
<ExePackage Id="VCredistx86Installer" DisplayName="VC Redist x86 installer" Name="VC_redist.x86.exe" Cache="remove" Compressed="yes" PerMachine="yes"
|
||||
Permanent="yes" Vital="yes" SourceFile="$(TargetDir)VC_redist.x86.exe" InstallArguments="/install /passive /norestart" DetectCondition="VCredistx86">
|
||||
</ExePackage>
|
||||
|
||||
<ExePackage Id="MicrosoftEdgeWebview2" DisplayName="Microsoft Edge Webview2 Runtime" Name="MicrosoftEdgeWebview2Setup.exe" Cache="remove"
|
||||
Compressed="yes" PerMachine="yes" Permanent ="yes" Vital ="no" SourceFile="$(TargetDir)MicrosoftEdgeWebview2Setup.exe" InstallArguments="/silent /install"
|
||||
UninstallArguments="/silent /uninstall" DetectCondition="Webview2Machine OR Webview2User" >
|
||||
</ExePackage>
|
||||
|
||||
<ExePackage Id="VCredistx86Installer" DisplayName="VC Redist x86 installer" Name="VC_redist.x86.exe"
|
||||
SourceFile="$(ProjectDir)\Files\VC_redist.x86.exe"
|
||||
Cache="remove" Compressed="yes" PerMachine="yes" Permanent="yes" Vital="yes"
|
||||
InstallArguments="/install /passive /norestart" DetectCondition="VCredistx86"/>
|
||||
<ExePackage Id="MicrosoftEdgeWebview2" DisplayName="Microsoft Edge Webview2 Runtime" Name="MicrosoftEdgeWebview2Setup.exe"
|
||||
SourceFile="$(ProjectDir)\Files\MicrosoftEdgeWebview2Setup.exe"
|
||||
Cache="remove" Compressed="yes" PerMachine="yes" Permanent ="yes" Vital="no"
|
||||
InstallArguments="/silent /install" UninstallArguments="/silent /uninstall" DetectCondition="Webview2Machine OR Webview2User"/>
|
||||
<MsiPackage Id="ElwigMsi" SourceFile="$(var.Installer.TargetDir)\Elwig.msi" Permanent="no" Compressed="yes" Vital="yes"/>
|
||||
</Chain>
|
||||
|
||||
</Bundle>
|
||||
</Wix>
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@
|
||||
<Cultures>de-AT</Cultures>
|
||||
</PropertyGroup>
|
||||
<Target Name="CustomBeforeBuild" BeforeTargets="BeforeBuild">
|
||||
<Exec Command='curl -s -L "https://go.microsoft.com/fwlink/p/?LinkId=2124703" -z "$(TargetDir)MicrosoftEdgeWebview2Setup.exe" -o "$(TargetDir)MicrosoftEdgeWebview2Setup.exe"' />
|
||||
<Exec Command='curl -s -L "https://aka.ms/vs/17/release/vc_redist.x86.exe" -z "$(TargetDir)VC_redist.x86.exe" -o "$(TargetDir)VC_redist.x86.exe"' />
|
||||
<Exec Command='curl -s -L "https://go.microsoft.com/fwlink/p/?LinkId=2124703" -z "$(ProjectDir)\Files\MicrosoftEdgeWebview2Setup.exe" -o "$(ProjectDir)\Files\MicrosoftEdgeWebview2Setup.exe"' />
|
||||
<Exec Command='curl -s -L "https://aka.ms/vs/17/release/vc_redist.x86.exe" -z "$(ProjectDir)\Files\VC_redist.x86.exe" -o "$(ProjectDir)\Files\VC_redist.x86.exe"' />
|
||||
<PropertyGroup>
|
||||
<DefineConstants>ElwigProjectDir=..\Elwig</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace Tests {
|
||||
public void Setup_2_Client() {
|
||||
using var ctx = new AppDbContext();
|
||||
App.Client = new ClientParameters(ctx);
|
||||
App.SetBranch(ctx.Branches.Single());
|
||||
}
|
||||
|
||||
[OneTimeSetUp]
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using Elwig.Documents;
|
||||
using Elwig.Helpers;
|
||||
|
||||
namespace Tests.DocumentTests {
|
||||
[TestFixture]
|
||||
public class DeliveryNoteTest {
|
||||
|
||||
private readonly AppDbContext Context = new();
|
||||
|
||||
[Test]
|
||||
public async Task Test_01_OneDeliveryPart() {
|
||||
var d = await Context.Deliveries.FindAsync(2020, 1);
|
||||
using var doc = new DeliveryNote(d!, Context);
|
||||
var text = await Utils.GeneratePdfText(doc);
|
||||
Assert.Multiple(() => {
|
||||
Assert.That(text, Contains.Substring("""
|
||||
MUSTERMANN Max
|
||||
Winzerstraße 1
|
||||
2223 Hohenruppersdorf
|
||||
"""));
|
||||
Assert.That(text, Contains.Substring("1472583")); // Betriebsnummer
|
||||
Assert.That(text, Contains.Substring("pauschaliert"));
|
||||
Assert.That(text, Contains.Substring($"Wolkersdorf, am {DateTime.Now:dd.MM.yyyy}"));
|
||||
Assert.That(text, Contains.Substring("Traubenübernahmeschein Nr. 20201001X001"));
|
||||
// TODO
|
||||
Assert.That(text, Contains.Substring("Ich bin der Text, der auf einem Traubenübernahmeschein steht!"));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using Elwig.Helpers;
|
||||
using System.Reflection;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Elwig.Helpers.Printing;
|
||||
|
||||
namespace Tests.DocumentTests {
|
||||
[SetUpFixture]
|
||||
public class Setup {
|
||||
|
||||
private SqliteConnection? Connection;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task SetupDatabase() {
|
||||
Connection = await AppDbContext.ConnectAsync();
|
||||
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.DocumentInsert.sql");
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public async Task TeardownDatabase() {
|
||||
if (Connection == null) return;
|
||||
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.DocumentDelete.sql");
|
||||
await Connection.DisposeAsync();
|
||||
Connection = null;
|
||||
}
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task SetupPrinting() {
|
||||
await Html.Init();
|
||||
await Pdf.Init();
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void TeardownPrinting() {
|
||||
// no teardown needed yet
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Elwig.Documents;
|
||||
using NReco.PdfRenderer;
|
||||
|
||||
namespace Tests.DocumentTests {
|
||||
public static class Utils {
|
||||
|
||||
private static readonly string FileName = Path.Combine(Path.GetTempPath(), "test_document.pdf");
|
||||
|
||||
public static async Task<string> GeneratePdfText(Document doc) {
|
||||
await doc.Generate();
|
||||
try {
|
||||
doc.SaveTo(FileName);
|
||||
var conv = new PdfToTextConverter { CustomArgs = "-raw " };
|
||||
return conv.GenerateText(FileName);
|
||||
} finally {
|
||||
File.Delete(FileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,18 @@ namespace Tests.HelperTests {
|
||||
[TestFixture]
|
||||
public class BillingTest {
|
||||
|
||||
private const int Year1 = 2020, Year2 = 2020;
|
||||
private const int Year1 = 2020, Year2 = 2021;
|
||||
private const int MgNr1 = 101, MgNr2 = 102, MgNr3 = 103, MgNr4 = 104;
|
||||
|
||||
private const decimal
|
||||
GV_ungeb = 0.50m,
|
||||
GV_geb = 0.60m,
|
||||
GVB_ungeb = 0.54m,
|
||||
GVB_geb = 0.64m,
|
||||
GVK_ungeb = 0.61m,
|
||||
GVK_geb = 0.71m,
|
||||
WEI = 0.10m;
|
||||
|
||||
private SqliteConnection? Connection;
|
||||
|
||||
[OneTimeSetUp]
|
||||
@@ -26,6 +35,40 @@ namespace Tests.HelperTests {
|
||||
Connection = null;
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public async Task CreatePaymentVariant() {
|
||||
var json = """
|
||||
{
|
||||
"mode": "elwig",
|
||||
"version": 1,
|
||||
"payment": {
|
||||
"GV/": "curve:0",
|
||||
"GV/B": "curve:1",
|
||||
"GV/K": "curve:2"
|
||||
},
|
||||
"quality": {"WEI": 0.1},
|
||||
"curves": [{
|
||||
"id": 0,
|
||||
"mode": "oe",
|
||||
"data": {"15kmw": 0.5},
|
||||
"geb": 0.1
|
||||
}, {
|
||||
"id": 1,
|
||||
"mode": "oe",
|
||||
"data": {"15kmw": 0.54},
|
||||
"geb": 0.1
|
||||
}, {
|
||||
"id": 2,
|
||||
"mode": "oe",
|
||||
"data": {"15kmw": 0.61},
|
||||
"geb": 0.1
|
||||
}]
|
||||
}
|
||||
""";
|
||||
await InsertPaymentVariant(Year1, 1, json);
|
||||
await InsertPaymentVariant(Year2, 1, json);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public async Task CleanupDatabasePayment() {
|
||||
if (Connection == null) return;
|
||||
@@ -51,11 +94,11 @@ namespace Tests.HelperTests {
|
||||
return ctx.GetMemberPaymentBuckets(year, mgnr, Connection);
|
||||
}
|
||||
|
||||
private async Task<Dictionary<(string, string), int>> GetMemberDeliveryPrices(int year, int mgnr) {
|
||||
var buckets = new Dictionary<(string, string), int>();
|
||||
private async Task<Dictionary<(string, string), (int, decimal)>> GetMemberDeliveryPrices(int year, int mgnr) {
|
||||
var buckets = new Dictionary<(string, string), (int, decimal)>();
|
||||
using (var cmd = Connection!.CreateCommand()) {
|
||||
cmd.CommandText = $"""
|
||||
SELECT lsnr || '/' || d.dpnr, d.sortid || b.discr, price
|
||||
SELECT lsnr || '/' || d.dpnr, d.sortid || b.discr, b.value, p.price
|
||||
FROM v_delivery d
|
||||
LEFT JOIN payment_delivery_part_bucket p ON (p.year, p.did, p.dpnr) = (d.year, d.did, d.dpnr)
|
||||
LEFT JOIN delivery_part_bucket b ON (b.year, b.did, b.dpnr, b.bktnr) = (p.year, p.did, p.dpnr, p.bktnr)
|
||||
@@ -65,7 +108,7 @@ namespace Tests.HelperTests {
|
||||
while (await reader.ReadAsync()) {
|
||||
var lsnr = reader.GetString(0);
|
||||
var bucket = reader.GetString(1);
|
||||
buckets[(lsnr, bucket)] = reader.GetInt32(2);
|
||||
buckets[(lsnr, bucket)] = (reader.GetInt32(2), Utils.DecFromDb(reader.GetInt32(3), 4));
|
||||
}
|
||||
}
|
||||
return buckets;
|
||||
@@ -81,34 +124,6 @@ namespace Tests.HelperTests {
|
||||
[Test]
|
||||
public async Task Test_01_NoActiveAreaComs() {
|
||||
int mgnr = MgNr1, year = Year1;
|
||||
await InsertPaymentVariant(year, 1, """
|
||||
{
|
||||
"mode": "elwig",
|
||||
"version": 1,
|
||||
"payment": {
|
||||
"GV/": "curve:0",
|
||||
"GV/B": "curve:1",
|
||||
"GV/K": "curve:2"
|
||||
},
|
||||
"quality": {"WEI": 0.1},
|
||||
"curves": [{
|
||||
"id": 0,
|
||||
"mode": "oe",
|
||||
"data": {"15kmw": 0.5},
|
||||
"geb": 0.1
|
||||
}, {
|
||||
"id": 1,
|
||||
"mode": "oe",
|
||||
"data": {"15kmw": 0.54},
|
||||
"geb": 0.1
|
||||
}, {
|
||||
"id": 2,
|
||||
"mode": "oe",
|
||||
"data": {"15kmw": 0.61},
|
||||
"geb": 0.1
|
||||
}]
|
||||
}
|
||||
""");
|
||||
|
||||
var areaCom = await GetMemberAreaCommitmentBuckets(year, mgnr);
|
||||
Assert.That(areaCom, Is.Empty);
|
||||
@@ -133,25 +148,117 @@ namespace Tests.HelperTests {
|
||||
var prices = await GetMemberDeliveryPrices(year, mgnr);
|
||||
Assert.Multiple(() => {
|
||||
Assert.That(prices, Has.Count.EqualTo(6));
|
||||
Assert.That(prices[("20201001X001/1", "GV_")], Is.EqualTo(0_6100));
|
||||
Assert.That(prices[("20201001X001/2", "GV_")], Is.EqualTo(0_5000));
|
||||
Assert.That(prices[("20201001X002/1", "GV_")], Is.EqualTo(0_5400));
|
||||
Assert.That(prices[("20201001X002/2", "GV_")], Is.EqualTo(0_5400));
|
||||
Assert.That(prices[("20201001X003/1", "GV_")], Is.EqualTo(0_1000));
|
||||
Assert.That(prices[("20201001X003/2", "GV_")], Is.EqualTo(0_5000));
|
||||
// Kabinett
|
||||
Assert.That(prices[("20201001X001/1", "GV_")], Is.EqualTo((4_000, GV_ungeb)));
|
||||
// ohne Attribut
|
||||
Assert.That(prices[("20201001X001/2", "GV_")], Is.EqualTo((4_000, GV_ungeb)));
|
||||
// Bio
|
||||
Assert.That(prices[("20201001X002/1", "GV_")], Is.EqualTo((4_000, GVB_ungeb)));
|
||||
// Bio
|
||||
Assert.That(prices[("20201001X002/2", "GV_")], Is.EqualTo((4_000, GVB_ungeb)));
|
||||
// ohne Attribut
|
||||
Assert.That(prices[("20201001X003/1", "GV_")], Is.EqualTo(( 500, WEI)));
|
||||
// ohne Attribut
|
||||
Assert.That(prices[("20201001X003/2", "GV_")], Is.EqualTo(( 500, GV_ungeb)));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Ignore("Not implemented yet")]
|
||||
public async Task Test_02_SimpleNotStrictAreaComs() {
|
||||
// TODO
|
||||
int mgnr = MgNr1, year = Year2;
|
||||
|
||||
var areaCom = await GetMemberAreaCommitmentBuckets(year, mgnr);
|
||||
Assert.Multiple(() => {
|
||||
Assert.That(areaCom, Has.Count.EqualTo(1));
|
||||
Assert.That(areaCom["GV"], Is.EqualTo(new AreaComBucket(10_000, 5_000, 10_000)));
|
||||
});
|
||||
var delivery = await GetMemberDeliveryBuckets(year, mgnr);
|
||||
Assert.Multiple(() => {
|
||||
Assert.That(delivery, Has.Count.EqualTo(4));
|
||||
Assert.That(delivery["GV"], Is.EqualTo(16_000));
|
||||
Assert.That(delivery["GV_"], Is.EqualTo( 1_000));
|
||||
Assert.That(delivery["GVB"], Is.EqualTo( 8_000));
|
||||
Assert.That(delivery["GVK"], Is.EqualTo( 4_000));
|
||||
});
|
||||
|
||||
BillingVariant b = new(year, 1);
|
||||
await b.CalculateBuckets(false, false, false, Connection);
|
||||
var payment = await GetMemberPaymentBuckets(year, mgnr);
|
||||
Assert.Multiple(() => {
|
||||
Assert.That(payment, Has.Count.EqualTo(2));
|
||||
Assert.That(payment["GV_"], Is.EqualTo( 7_000));
|
||||
Assert.That(payment["GV"], Is.EqualTo(10_000));
|
||||
});
|
||||
|
||||
await b.Calculate(false, false, false);
|
||||
var prices = await GetMemberDeliveryPrices(year, mgnr);
|
||||
Assert.Multiple(() => {
|
||||
Assert.That(prices, Has.Count.EqualTo(10));
|
||||
// Kabinett
|
||||
Assert.That(prices[("20211001X001/1", "GV_")], Is.EqualTo((2_000, GV_ungeb)));
|
||||
Assert.That(prices[("20211001X001/1", "GV")] , Is.EqualTo((2_000, GV_geb)));
|
||||
// ohne Attribut
|
||||
Assert.That(prices[("20211001X001/2", "GV_")], Is.EqualTo((4_000, GV_ungeb)));
|
||||
Assert.That(prices[("20211001X001/2", "GV")], Is.EqualTo(( 0, GV_geb)));
|
||||
// Bio
|
||||
Assert.That(prices[("20211001X002/1", "GV_")], Is.EqualTo(( 0, GVB_ungeb)));
|
||||
Assert.That(prices[("20211001X002/1", "GV")], Is.EqualTo((4_000, GVB_geb)));
|
||||
// Bio
|
||||
Assert.That(prices[("20211001X002/2", "GV_")], Is.EqualTo(( 0, GVB_ungeb)));
|
||||
Assert.That(prices[("20211001X002/2", "GV")], Is.EqualTo((4_000, GVB_geb)));
|
||||
// ohne Attribut
|
||||
Assert.That(prices[("20211001X003/1", "GV_")], Is.EqualTo(( 500, WEI)));
|
||||
// ohne Attribut
|
||||
Assert.That(prices[("20211001X003/2", "GV_")], Is.EqualTo(( 500, GV_ungeb)));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Ignore("Not implemented yet")]
|
||||
public async Task Test_03_SimpleNotStrictAreaComs_HonorGebunden() {
|
||||
// TODO
|
||||
int mgnr = MgNr1, year = Year2;
|
||||
|
||||
var areaCom = await GetMemberAreaCommitmentBuckets(year, mgnr);
|
||||
Assert.Multiple(() => {
|
||||
Assert.That(areaCom, Has.Count.EqualTo(1));
|
||||
Assert.That(areaCom["GV"], Is.EqualTo(new AreaComBucket(10_000, 5_000, 10_000)));
|
||||
});
|
||||
var delivery = await GetMemberDeliveryBuckets(year, mgnr);
|
||||
Assert.Multiple(() => {
|
||||
Assert.That(delivery, Has.Count.EqualTo(4));
|
||||
Assert.That(delivery["GV"], Is.EqualTo(16_000));
|
||||
Assert.That(delivery["GV_"], Is.EqualTo( 1_000));
|
||||
Assert.That(delivery["GVB"], Is.EqualTo( 8_000));
|
||||
Assert.That(delivery["GVK"], Is.EqualTo( 4_000));
|
||||
});
|
||||
|
||||
BillingVariant b = new(year, 1);
|
||||
await b.CalculateBuckets(true, false, false, Connection);
|
||||
var payment = await GetMemberPaymentBuckets(year, mgnr);
|
||||
Assert.Multiple(() => {
|
||||
Assert.That(payment, Has.Count.EqualTo(2));
|
||||
Assert.That(payment["GV_"], Is.EqualTo(9_000));
|
||||
Assert.That(payment["GV"], Is.EqualTo(8_000));
|
||||
});
|
||||
|
||||
await b.Calculate(true, false, false);
|
||||
var prices = await GetMemberDeliveryPrices(year, mgnr);
|
||||
Assert.Multiple(() => {
|
||||
Assert.That(prices, Has.Count.EqualTo(8));
|
||||
// Kabinett
|
||||
Assert.That(prices[("20211001X001/1", "GV_")], Is.EqualTo(( 0, GV_ungeb)));
|
||||
Assert.That(prices[("20211001X001/1", "GV")], Is.EqualTo((4_000, GV_geb)));
|
||||
// ohne Attribut
|
||||
Assert.That(prices[("20211001X001/2", "GV_")], Is.EqualTo((4_000, GV_ungeb)));
|
||||
// Bio
|
||||
Assert.That(prices[("20211001X002/1", "GV_")], Is.EqualTo(( 0, GVB_ungeb)));
|
||||
Assert.That(prices[("20211001X002/1", "GV")], Is.EqualTo((4_000, GVB_geb)));
|
||||
// Bio
|
||||
Assert.That(prices[("20211001X002/2", "GV_")], Is.EqualTo((4_000, GVB_ungeb)));
|
||||
// ohne Attribut
|
||||
Assert.That(prices[("20211001X003/1", "GV_")], Is.EqualTo(( 500, WEI)));
|
||||
// ohne Attribut
|
||||
Assert.That(prices[("20211001X003/2", "GV_")], Is.EqualTo(( 500, GV_ungeb)));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -100,5 +100,40 @@ namespace Tests.HelperTests {
|
||||
Assert.That(Utils.SplitName("ABC GesbR", "Bauer"), Is.EqualTo(((string, string?))("ABC GesbR", null)));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_CalcCrc16Modbus() {
|
||||
Assert.Multiple(() => {
|
||||
Assert.That(Utils.CalcCrc16Modbus(""), Is.EqualTo(0xFFFF));
|
||||
Assert.That(Utils.CalcCrc16Modbus("abcd"), Is.EqualTo(0x1D97));
|
||||
Assert.That(Utils.CalcCrc16Modbus("ABCD"), Is.EqualTo(0x0F85));
|
||||
// Matzen (SysTec IT3000A)
|
||||
Assert.That(Utils.CalcCrc16Modbus("000011.08.2319:03 01 0 0 0kg 1"), Is.EqualTo(43166));
|
||||
Assert.That(Utils.CalcCrc16Modbus("000011.08.2319:04 01 0 0 0kg 1"), Is.EqualTo(21615));
|
||||
Assert.That(Utils.CalcCrc16Modbus("000011.08.2319:05 01 0 0 0kg 1"), Is.EqualTo(40446));
|
||||
Assert.That(Utils.CalcCrc16Modbus("000011.08.2319:06 01 0 0 0kg 1"), Is.EqualTo(34638));
|
||||
Assert.That(Utils.CalcCrc16Modbus("000011.08.2319:07 01 0 0 0kg 1"), Is.EqualTo(20191));
|
||||
Assert.That(Utils.CalcCrc16Modbus("000011.08.2319:08 01 0 0 0kg 1"), Is.EqualTo(16047));
|
||||
Assert.That(Utils.CalcCrc16Modbus("000011.08.2319:09 01 0 0 0kg 1"), Is.EqualTo(63294));
|
||||
Assert.That(Utils.CalcCrc16Modbus("000011.08.2319:10 01 0 0 0kg 1"), Is.EqualTo(7718));
|
||||
Assert.That(Utils.CalcCrc16Modbus("000011.08.2319:12 01 0 0 0kg 1"), Is.EqualTo(52487));
|
||||
// Wolkersdorf, Waage 1 (SysTec IT6000E)
|
||||
Assert.That(Utils.CalcCrc16Modbus("000004.09.2315:50 51 0 0 0kg A"), Is.EqualTo(10187));
|
||||
Assert.That(Utils.CalcCrc16Modbus("000004.09.2315:50 61 0 0 0kg A"), Is.EqualTo(20683));
|
||||
Assert.That(Utils.CalcCrc16Modbus("000004.09.2315:50 71 0 0 0kg A"), Is.EqualTo(48586));
|
||||
Assert.That(Utils.CalcCrc16Modbus("000004.09.2315:50 81 0 0 0kg A"), Is.EqualTo(22217));
|
||||
Assert.That(Utils.CalcCrc16Modbus("000004.09.2315:50 91 0 0 0kg A"), Is.EqualTo(48072));
|
||||
Assert.That(Utils.CalcCrc16Modbus("000004.09.2315:51 101 0 0 0kg A"), Is.EqualTo(30119));
|
||||
Assert.That(Utils.CalcCrc16Modbus("000004.09.2315:51 111 0 0 0kg A"), Is.EqualTo(39078));
|
||||
// Wolkersdorf, Waage 2 (SysTec IT6000E)
|
||||
Assert.That(Utils.CalcCrc16Modbus("000004.09.2315:51 41 0 0 0kg B"), Is.EqualTo(539));
|
||||
Assert.That(Utils.CalcCrc16Modbus("000004.09.2315:51 51 0 0 0kg B"), Is.EqualTo(61210));
|
||||
Assert.That(Utils.CalcCrc16Modbus("000004.09.2315:51 61 0 0 0kg B"), Is.EqualTo(38938));
|
||||
Assert.That(Utils.CalcCrc16Modbus("000004.09.2315:51 71 0 0 0kg B"), Is.EqualTo(29979));
|
||||
Assert.That(Utils.CalcCrc16Modbus("000004.09.2315:51 81 0 0 0kg B"), Is.EqualTo(40472));
|
||||
Assert.That(Utils.CalcCrc16Modbus("000004.09.2315:51 91 0 0 0kg B"), Is.EqualTo(29465));
|
||||
Assert.That(Utils.CalcCrc16Modbus("000004.09.2315:51 101 0 0 0kg B"), Is.EqualTo(29927));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
-- deletes for HelperTests.BillingTest
|
||||
|
||||
DELETE FROM credit;
|
||||
DELETE FROM payment_variant
|
||||
DELETE FROM payment_variant;
|
||||
DELETE FROM delivery;
|
||||
DELETE FROM area_commitment;
|
||||
DELETE FROM area_commitment_type;
|
||||
|
||||
@@ -16,17 +16,18 @@ INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lo
|
||||
('R', 'Vertrag R', TRUE, NULL, TRUE, 2);
|
||||
|
||||
INSERT INTO area_commitment_type (vtrgid, sortid, attrid, disc, min_kg_per_ha, penalty_per_kg, penalty_amount, penalty_none) VALUES
|
||||
('ZW', 'ZW', NULL, NULL, 2500, 600, NULL, NULL),
|
||||
('GV', 'GV', NULL, NULL, 5000, 500, NULL, NULL),
|
||||
('GVK', 'GV', 'K', NULL, 5000, 500, NULL, NULL),
|
||||
('GVD', 'GV', 'D', NULL, 5000, 1000, NULL, NULL),
|
||||
('GVP', 'GV', 'P', NULL, 5000, 1000, 1000000, NULL),
|
||||
('GVQ', 'GV', 'Q', NULL, 5000, 1000, 1000000, NULL),
|
||||
('GVR', 'GV', 'R', NULL, 5000, 1000, 1000000, NULL);
|
||||
('ZW', 'ZW', NULL, NULL, 2500, 600, NULL, NULL),
|
||||
('GV', 'GV', NULL, NULL, 5000, 500, NULL, NULL),
|
||||
('GVK', 'GV', 'K', NULL, 5000, 500, NULL, NULL),
|
||||
('GVD', 'GV', 'D', NULL, 5000, 1000, NULL, NULL),
|
||||
('GVP', 'GV', 'P', NULL, 5000, 1000, 1000000, NULL),
|
||||
('GVQ', 'GV', 'Q', NULL, 5000, 1000, 1000000, NULL),
|
||||
('GVR', 'GV', 'R', NULL, 5000, 1000, 1000000, NULL);
|
||||
|
||||
INSERT INTO area_commitment (fbnr, mgnr, vtrgid, cultid, area, kgnr, gstnr, rdnr, year_from, year_to) VALUES
|
||||
( 1, 101, 'GV', 'K', 10000, 06109, '123/4', NULL, 2000, 2019),
|
||||
( 2, 101, 'GV', 'B', 10000, 06109, '123/5', NULL, 2025, 2030);
|
||||
( 1, 101, 'GV', 'K', 10000, 06109, '123/4', NULL, 2000, 2019),
|
||||
( 2, 101, 'GV', 'K', 10000, 06109, '123/5', NULL, 2025, 2030),
|
||||
( 3, 101, 'GV', 'K', 10000, 06109, '123/6', NULL, 2021, 2031);
|
||||
|
||||
INSERT INTO season (year, currency, min_kg_per_bs, max_kg_per_bs, penalty_per_kg, penalty_amount, penalty_none, start_date, end_date) VALUES
|
||||
(2020, 'EUR', 1000, 2000, NULL, NULL, NULL, NULL, NULL),
|
||||
@@ -36,18 +37,30 @@ INSERT INTO modifier (year, modid, ordering, name, abs, rel, standard, quick_sel
|
||||
(2020, 'S', 0, 'Geschädigte Trauben', NULL, -0.1, FALSE, FALSE),
|
||||
(2020, 'A', 0, 'Keine Voranmeldung', -1000, NULL, FALSE, FALSE);
|
||||
|
||||
-- Test 01
|
||||
INSERT INTO delivery (mgnr, year, did, date, time, zwstid, lnr) VALUES
|
||||
(101, 2020, 1, '2020-10-01', NULL, 'X', 1),
|
||||
(101, 2020, 2, '2020-10-01', NULL, 'X', 2),
|
||||
(101, 2020, 3, '2020-10-01', NULL, 'X', 3);
|
||||
|
||||
INSERT INTO delivery_part (year, did, dpnr, sortid, attrid, weight, kmw, qualid, hkid, kgnr, gerebelt, manual_weighing, spl_check, scale_id, weighing_id, weighing_reason) VALUES
|
||||
(2020, 1, 1, 'GV', 'K', 4000, 17, 'KAB', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
|
||||
(2020, 1, 1, 'GV', 'K', 4000, 17, 'KAB', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
|
||||
(2020, 1, 2, 'GV', NULL, 4000, 16, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
|
||||
(2020, 2, 1, 'GV', 'B', 4000, 15, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
|
||||
(2020, 2, 2, 'GV', 'B', 4000, 16, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
|
||||
(2020, 2, 1, 'GV', 'B', 4000, 15, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
|
||||
(2020, 2, 2, 'GV', 'B', 4000, 16, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
|
||||
(2020, 3, 1, 'GV', NULL, 500, 15, 'WEI', 'OEST', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
|
||||
(2020, 3, 2, 'GV', NULL, 500, 14, 'LDW', 'WLXX', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL);
|
||||
|
||||
INSERT INTO delivery_part_modifier (year, did, dpnr, modid) VALUES
|
||||
(2020, 1, 2, 'S');
|
||||
|
||||
-- Test 02 + 03
|
||||
INSERT INTO delivery (mgnr, year, did, date, time, zwstid, lnr) VALUES
|
||||
(101, 2021, 1, '2021-10-01', NULL, 'X', 1),
|
||||
(101, 2021, 2, '2021-10-01', NULL, 'X', 2),
|
||||
(101, 2021, 3, '2021-10-01', NULL, 'X', 3);
|
||||
INSERT INTO delivery_part (year, did, dpnr, sortid, attrid, weight, kmw, qualid, hkid, kgnr, gebunden, gerebelt, manual_weighing, spl_check, scale_id, weighing_id, weighing_reason) VALUES
|
||||
(2021, 1, 1, 'GV', 'K', 4000, 17, 'KAB', 'WLNO', 06109, TRUE, TRUE, FALSE, FALSE, NULL, NULL, NULL),
|
||||
(2021, 1, 2, 'GV', NULL, 4000, 16, 'QUW', 'WLNO', 06109, FALSE, TRUE, FALSE, FALSE, NULL, NULL, NULL),
|
||||
(2021, 2, 1, 'GV', 'B', 4000, 15, 'QUW', 'WLNO', 06109, TRUE, TRUE, FALSE, FALSE, NULL, NULL, NULL),
|
||||
(2021, 2, 2, 'GV', 'B', 4000, 16, 'QUW', 'WLNO', 06109, FALSE, TRUE, FALSE, FALSE, NULL, NULL, NULL),
|
||||
(2021, 3, 1, 'GV', NULL, 500, 15, 'WEI', 'OEST', 06109, NULL, TRUE, FALSE, FALSE, NULL, NULL, NULL),
|
||||
(2021, 3, 2, 'GV', NULL, 500, 14, 'LDW', 'WLXX', 06109, NULL, TRUE, FALSE, FALSE, NULL, NULL, NULL);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
-- deletes for DocumentTests
|
||||
|
||||
DELETE FROM delivery;
|
||||
DELETE FROM season;
|
||||
DELETE FROM wine_attribute;
|
||||
@@ -0,0 +1,20 @@
|
||||
-- inserts for DocumentTests
|
||||
|
||||
INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES
|
||||
('B', 'Bio', TRUE, NULL, FALSE, 0),
|
||||
('K', 'Kabinett', TRUE, NULL, FALSE, 0);
|
||||
|
||||
INSERT INTO season (year, currency, min_kg_per_bs, max_kg_per_bs, penalty_per_kg, penalty_amount, penalty_none, start_date, end_date) VALUES
|
||||
(2020, 'EUR', 1000, 2000, NULL, NULL, NULL, NULL, NULL);
|
||||
|
||||
INSERT INTO modifier (year, modid, ordering, name, abs, rel, standard, quick_select) VALUES
|
||||
(2020, 'S', 0, 'Geschädigte Trauben', NULL, -0.1, FALSE, FALSE),
|
||||
(2020, 'A', 0, 'Keine Voranmeldung', -1000, NULL, FALSE, FALSE);
|
||||
|
||||
INSERT INTO delivery (mgnr, year, did, date, time, zwstid, lnr) VALUES
|
||||
(101, 2020, 1, '2020-10-01', NULL, 'X', 1),
|
||||
(101, 2020, 2, '2020-10-01', NULL, 'X', 2),
|
||||
(101, 2020, 3, '2020-10-01', NULL, 'X', 3);
|
||||
|
||||
INSERT INTO delivery_part (year, did, dpnr, sortid, attrid, weight, kmw, qualid, hkid, kgnr, gerebelt, manual_weighing, spl_check, scale_id, weighing_id, weighing_reason) VALUES
|
||||
(2020, 1, 1, 'GV', NULL, 4000, 17, 'KAB', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL);
|
||||
@@ -1,31 +1,9 @@
|
||||
-- inserts for DatabaseSetup
|
||||
|
||||
INSERT INTO client_parameter (param, value) VALUES
|
||||
('CLIENT_NAME_TOKEN', 'WGT'),
|
||||
('CLIENT_NAME_SHORT', 'WG Test'),
|
||||
('CLIENT_NAME', 'Winzergenossenschaft Test'),
|
||||
('CLIENT_NAME_SUFFIX', NULL),
|
||||
('CLIENT_NAME_TYPE', 'reg.Gen.m.b.H.'),
|
||||
('CLIENT_PLZ', '1234'),
|
||||
('CLIENT_ORT', 'Musterstadt'),
|
||||
('CLIENT_ADDRESS', 'Musterstraße 1'),
|
||||
('CLIENT_IBAN', 'AT12 3456 7890 1234 5678');
|
||||
|
||||
INSERT INTO branch (zwstid, name) VALUES
|
||||
('X', 'Test');
|
||||
|
||||
INSERT INTO wb_gl (glnr, name) VALUES
|
||||
(1, 'Matzner Hügel'),
|
||||
(2, 'Wolkersdorfer Hochleithen');
|
||||
|
||||
INSERT INTO AT_gem (gkz, name) VALUES
|
||||
(30828, 'Hohenruppersdorf'),
|
||||
(31655, 'Wolkersdorf im Weinviertel');
|
||||
|
||||
INSERT INTO wb_gem (gkz, hkid) VALUES
|
||||
(30828, 'WLWV'),
|
||||
(31655, 'WLWV');
|
||||
|
||||
INSERT INTO AT_kg (kgnr, gkz, name) VALUES
|
||||
(06109, 30828, 'Hohenruppersdorf'),
|
||||
(15209, 31655, 'Münichsthal'),
|
||||
@@ -34,14 +12,6 @@ INSERT INTO AT_kg (kgnr, gkz, name) VALUES
|
||||
(15216, 31655, 'Riedentahl'),
|
||||
(15224, 31655, 'Wolkersdorf');
|
||||
|
||||
INSERT INTO wb_kg (kgnr, glnr) VALUES
|
||||
(06109, 1),
|
||||
(15209, 2),
|
||||
(15211, 2),
|
||||
(15212, 2),
|
||||
(15216, 2),
|
||||
(15224, 2);
|
||||
|
||||
INSERT INTO AT_ort (okz, gkz, kgnr, name) VALUES
|
||||
(03524, 30828, 06109, 'Hohenruppersdorf'),
|
||||
(05092, 31655, 15211, 'Obersdorf'),
|
||||
@@ -63,8 +33,43 @@ INSERT INTO AT_plz_dest (plz, okz, dest) VALUES
|
||||
(2122, 05136, 'Pfösing'),
|
||||
(2122, 05137, 'Riedenthal');
|
||||
|
||||
INSERT INTO member (mgnr, given_name, family_name, zwstid, volllieferant, buchführend, country, postal_dest, address, default_kgnr, iban) VALUES
|
||||
(101, 'Max', 'Mustermann', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 1', 06109, 'AT123456789012345678'),
|
||||
(102, 'Wernhardt', 'Weinbauer', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 2', 06109, 'AT123456789012345678'),
|
||||
(103, 'Matthäus', 'Musterbauer', 'X', FALSE, FALSE, 40, 212005138, 'Brünner Straße 10', 15224, 'AT123456789012345678'),
|
||||
(104, 'Waltraud', 'Winzer', 'X', FALSE, FALSE, 40, 212005138, 'Wiener Straße 15', 15224, 'AT123456789012345678');
|
||||
INSERT INTO client_parameter (param, value) VALUES
|
||||
('CLIENT_NAME_TOKEN', 'WGT'),
|
||||
('CLIENT_NAME_SHORT', 'WG Test'),
|
||||
('CLIENT_NAME', 'Winzergenossenschaft Test'),
|
||||
('CLIENT_NAME_SUFFIX', NULL),
|
||||
('CLIENT_NAME_TYPE', 'reg.Gen.m.b.H.'),
|
||||
('CLIENT_PLZ', '2120'),
|
||||
('CLIENT_ORT', 'Wolkersdorf'),
|
||||
('CLIENT_ADDRESS', 'Genossenschaftsstraße 1'),
|
||||
('CLIENT_IBAN', 'AT12 3456 7890 1234 5678'),
|
||||
('TEXT_DELIVERYNOTE', 'Ich bin der Text, der auf einem Traubenübernahmeschein steht!');
|
||||
|
||||
INSERT INTO branch (zwstid, name, country, postal_dest, address) VALUES
|
||||
('X', 'Test', 40, 212005138, 'Genossenschaftsstraße 1');
|
||||
|
||||
INSERT INTO wb_gl (glnr, name) VALUES
|
||||
(1, 'Matzner Hügel'),
|
||||
(2, 'Wolkersdorfer Hochleithen');
|
||||
|
||||
INSERT INTO wb_gem (gkz, hkid) VALUES
|
||||
(30828, 'WLWV'),
|
||||
(31655, 'WLWV');
|
||||
|
||||
INSERT INTO wb_kg (kgnr, glnr) VALUES
|
||||
(06109, 1),
|
||||
(15209, 2),
|
||||
(15211, 2),
|
||||
(15212, 2),
|
||||
(15216, 2),
|
||||
(15224, 2);
|
||||
|
||||
INSERT INTO member (mgnr, given_name, family_name, zwstid, volllieferant, buchführend, country, postal_dest, address, default_kgnr, iban, lfbis_nr) VALUES
|
||||
(101, 'Max', 'Mustermann', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 1', 06109, 'AT123456789012345678', '1472583'),
|
||||
(102, 'Wernhardt', 'Weinbauer', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 2', 06109, 'AT123456789012345678', '4725836'),
|
||||
(103, 'Matthäus', 'Musterbauer', 'X', FALSE, FALSE, 40, 212005138, 'Brünner Straße 10', 15224, 'AT123456789012345678', '7258369'),
|
||||
(104, 'Waltraud', 'Winzer', 'X', FALSE, FALSE, 40, 212005138, 'Wiener Straße 15', 15224, 'AT123456789012345678', '2583691');
|
||||
|
||||
INSERT INTO member_billing_address (mgnr, name, country, postal_dest, address) VALUES
|
||||
(102, 'W&B Weinbauer GesbR', 40, 222303524, 'Winzerstraße 2'),
|
||||
(104, 'Weinbau Waltraud Winzer GmbH', 40, 212205137, 'Hauptstraße 1');
|
||||
|
||||
+4
-3
@@ -4,9 +4,9 @@
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -19,10 +19,11 @@
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageReference Include="NReco.PdfRenderer" Version="1.5.3" />
|
||||
<PackageReference Include="NUnit" Version="4.0.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
<PackageReference Include="NUnit.Analyzers" Version="3.10.0">
|
||||
<PackageReference Include="NUnit.Analyzers" Version="4.0.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
using System.Net.Sockets;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace Tests.WeighingTests {
|
||||
public abstract class MockScale : IDisposable {
|
||||
|
||||
protected readonly TcpListener Server;
|
||||
|
||||
public int IdentNr { get; set; } = 0;
|
||||
public int Weight { get; set; } = 0;
|
||||
public string? Error { get; set; } = null;
|
||||
|
||||
protected MockScale(int port) {
|
||||
Server = new TcpListener(IPAddress.Loopback, port);
|
||||
Server.Start(4);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
Server.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class CommandMockScale : MockScale {
|
||||
|
||||
private readonly Func<string, int, string?, int, (string, bool)> Handler;
|
||||
private readonly Thread ServerThread;
|
||||
private bool IsRunning = true;
|
||||
|
||||
public CommandMockScale(int port, Func<string, int, string?, int, (string, bool)> handler) :
|
||||
base(port) {
|
||||
Handler = handler;
|
||||
ServerThread = new Thread(new ParameterizedThreadStart(Serve));
|
||||
ServerThread.Start();
|
||||
}
|
||||
|
||||
private async void Serve(object? parameters) {
|
||||
byte[] buffer = new byte[16];
|
||||
while (IsRunning) {
|
||||
try {
|
||||
using var client = await Server.AcceptTcpClientAsync();
|
||||
if (client == null) continue;
|
||||
using var stream = client.GetStream();
|
||||
while (true) {
|
||||
int read = await stream.ReadAsync(buffer);
|
||||
var (res, inc) = Handler(Encoding.ASCII.GetString(buffer, 0, read), Weight, Error, IdentNr + 1);
|
||||
if (inc) IdentNr++;
|
||||
await stream.WriteAsync(Encoding.ASCII.GetBytes(res));
|
||||
}
|
||||
} catch (Exception) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public new void Dispose() {
|
||||
IsRunning = false;
|
||||
ServerThread.Interrupt();
|
||||
ServerThread.Join();
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public class EventMockScale : MockScale {
|
||||
public EventMockScale(int port, Func<int, string?, int, (string, bool)> handler) :
|
||||
base(port) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
using Elwig.Helpers.Weighing;
|
||||
|
||||
namespace Tests.WeighingTests {
|
||||
[TestFixture]
|
||||
class ScaleTestMatzen {
|
||||
|
||||
private MockScale? Mock;
|
||||
private SystecScale? Scale;
|
||||
|
||||
private static (string, bool) ScaleHandler(string req, int weight, string? error, int identNr) {
|
||||
var modes = error?.Split(';') ?? [];
|
||||
var overloaded = modes.Contains("overloaded");
|
||||
var moving = modes.Contains("moving");
|
||||
var invalid = modes.Contains("invalid");
|
||||
var crc = modes.Contains("crc");
|
||||
var unit = modes.Contains("unit");
|
||||
|
||||
if (invalid) {
|
||||
return ("abcd\r\n", false);
|
||||
} else if (!req.StartsWith('<') || !req.EndsWith('>')) {
|
||||
return ("<31>\r\n", false);
|
||||
}
|
||||
req = req[1..^1];
|
||||
|
||||
bool incr;
|
||||
if (req.Length > 3) {
|
||||
return ("<32>\r\n", false);
|
||||
} else if (req.StartsWith("RN")) {
|
||||
incr = true;
|
||||
} else if (req.StartsWith("RM")) {
|
||||
incr = false;
|
||||
} else {
|
||||
return ("<32>\r\n", false);
|
||||
}
|
||||
|
||||
if (overloaded) {
|
||||
return ("<12>\r\n", false);
|
||||
} else if (weight == 0) {
|
||||
incr = false;
|
||||
}
|
||||
|
||||
if (moving && incr)
|
||||
return ("<13>\r\n", false);
|
||||
|
||||
string data = $"00{(moving ? 1 : 0)}0{DateTime.Now:dd.MM.yyHH:mm}{(incr ? identNr : 0),4}1" +
|
||||
$"{weight,8}{0,8}{weight,8}{(unit ? "lb" : "kg")} {1,3}";
|
||||
ushort checksum = Elwig.Helpers.Utils.CalcCrc16Modbus(data);
|
||||
if (crc) checksum += 10;
|
||||
return ($"<{data}{checksum,8}>\r\n", incr);
|
||||
}
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void SetupScale() {
|
||||
Mock = new CommandMockScale(12345, ScaleHandler);
|
||||
Scale = new("1", "IT3000A", "tcp://127.0.0.1:12345");
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void TeardownScale() {
|
||||
Mock?.Dispose();
|
||||
Scale?.Dispose();
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void ResetScale() {
|
||||
Mock!.IdentNr = 0;
|
||||
Mock!.Weight = 0;
|
||||
Mock!.Error = null;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test_01_CurrentWeight() {
|
||||
Mock!.Weight = 1234;
|
||||
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 1234, Date = Utils.Today, Time = Utils.Time,
|
||||
}));
|
||||
Mock!.Weight = 1235;
|
||||
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 1235, Date = Utils.Today, Time = Utils.Time,
|
||||
}));
|
||||
Mock!.Weight = 1236;
|
||||
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 1236, Date = Utils.Today, Time = Utils.Time,
|
||||
}));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test_02_Normal() {
|
||||
Mock!.Weight = 1234;
|
||||
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 1234, WeighingId = "1",
|
||||
FullWeighingId = $"{DateTime.Today:yyyy-MM-dd}/1",
|
||||
Date = Utils.Today, Time = Utils.Time,
|
||||
}));
|
||||
Mock!.Weight = 3333;
|
||||
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 3333, WeighingId = "2",
|
||||
FullWeighingId = $"{DateTime.Today:yyyy-MM-dd}/2",
|
||||
Date = Utils.Today, Time = Utils.Time,
|
||||
}));
|
||||
Mock!.Weight = 4321;
|
||||
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 4321, WeighingId = "3",
|
||||
FullWeighingId = $"{DateTime.Today:yyyy-MM-dd}/3",
|
||||
Date = Utils.Today, Time = Utils.Time,
|
||||
}));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_03_Moving() {
|
||||
Mock!.Weight = 1_000;
|
||||
Mock!.Error = "moving";
|
||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_04_Overloaded() {
|
||||
Mock!.Weight = 10_000;
|
||||
Mock!.Error = "overloaded";
|
||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||
Assert.That(ex.Message, Contains.Substring("Waage in Überlast"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_05_InvalidResponse() {
|
||||
Mock!.Weight = 1_000;
|
||||
Mock!.Error = "invalid";
|
||||
Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_06_InvalidCrc() {
|
||||
Mock!.Weight = 1_000;
|
||||
Mock!.Error = "crc";
|
||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||
Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_07_InvalidUnit() {
|
||||
Mock!.Weight = 1_000;
|
||||
Mock!.Error = "unit";
|
||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Tests.WeighingTests {
|
||||
[TestFixture]
|
||||
public class ScaleTestWolkersdorf {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Tests.WeighingTests {
|
||||
public static class Utils {
|
||||
public static DateOnly Today => DateOnly.FromDateTime(DateTime.Now);
|
||||
public static TimeOnly Time => new(DateTime.Now.Hour, DateTime.Now.Minute);
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
curl -s -L "https://www.necronda.net/elwig/files/create.sql?v=15" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql"
|
||||
curl -s -L "https://www.necronda.net/elwig/files/create.sql?v=16" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql"
|
||||
|
||||
Reference in New Issue
Block a user