Fertigstellung - Testing fehlt noch

This commit is contained in:
Jan Conze 2024-09-02 13:55:13 +02:00
parent 6953b36930
commit eac696921a
20 changed files with 277 additions and 54 deletions

View File

@ -24,9 +24,9 @@
"RelativeDocumentMoniker": "Projekt_Calcan_Conze\\Program.cs", "RelativeDocumentMoniker": "Projekt_Calcan_Conze\\Program.cs",
"ToolTip": "C:\\Jan_bib_Module\\PMC\\Projekt\\Projekt_Calcan_Conze\\Projekt_Calcan_Conze\\Program.cs", "ToolTip": "C:\\Jan_bib_Module\\PMC\\Projekt\\Projekt_Calcan_Conze\\Projekt_Calcan_Conze\\Program.cs",
"RelativeToolTip": "Projekt_Calcan_Conze\\Program.cs", "RelativeToolTip": "Projekt_Calcan_Conze\\Program.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAsAAAAAAAAAAAAAAA==", "ViewState": "AgIAAAAAAAAAAAAAAAAAAAsAAAA7AAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2024-08-30T08:39:28.715Z", "WhenOpened": "2024-09-02T11:44:49.844Z",
"EditorCaption": "" "EditorCaption": ""
} }
] ]

View File

@ -1,13 +1,27 @@
namespace Projekt_Calcan_Conze_Import; namespace Core;
internal static class Constants public static class Constants
{ {
public const char Separator = ';'; //set the correct values for your server, user, password and database name
public const string FemaleAttributeIdentifier = "Frau"; public const string ConnectionString = "server=localhost;uid=root;pwd=root;database=import_export";
public const string MaleAttributeIdentifier = "Herr";
public const string DiverseAttributeIdentifier = "Divers";
public const string AddressAttributeIdentifier = "Adresse";
public const string EmailAttributeIdentifier = "E-Mail";
public const string PhoneNumberAttributeIdentifier = "Telefon";
public const string DateOfBirthFormat = "dd.MM.yyyy"; public const string DateOfBirthFormat = "dd.MM.yyyy";
public const string DateFormat = "yyyy-MM-dd";
public const string MoneyFormat = "F3";
public const char Separator = ';';
public const string FemaleAttributeIdentifier = "Frau";
public const string MaleAttributeIdentifier = "Herr";
public const string DiverseAttributeIdentifier = "Divers";
public const string AddressAttributeIdentifier = "Adresse";
public const string EmailAttributeIdentifier = "E-Mail";
public const string PhoneNumberAttributeIdentifier = "Telefon";
} }

View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -5,7 +5,9 @@ VisualStudioVersion = 17.10.34916.146
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Projekt_Calcan_Conze_Import", "Projekt_Calcan_Conze\Projekt_Calcan_Conze_Import.csproj", "{44DD7752-6BB5-4C3A-9053-671D8ADE49C4}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Projekt_Calcan_Conze_Import", "Projekt_Calcan_Conze\Projekt_Calcan_Conze_Import.csproj", "{44DD7752-6BB5-4C3A-9053-671D8ADE49C4}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Projekt_Calcan_Conze_Export", "Projekt_Calcan_Conze_Export\Projekt_Calcan_Conze_Export.csproj", "{9E018F4D-EED3-44D0-9179-E6637B984D74}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Projekt_Calcan_Conze_Export", "Projekt_Calcan_Conze_Export\Projekt_Calcan_Conze_Export.csproj", "{9E018F4D-EED3-44D0-9179-E6637B984D74}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "Core\Core.csproj", "{1A945077-A135-4756-974C-A87109DF411E}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -21,6 +23,10 @@ Global
{9E018F4D-EED3-44D0-9179-E6637B984D74}.Debug|Any CPU.Build.0 = Debug|Any CPU {9E018F4D-EED3-44D0-9179-E6637B984D74}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E018F4D-EED3-44D0-9179-E6637B984D74}.Release|Any CPU.ActiveCfg = Release|Any CPU {9E018F4D-EED3-44D0-9179-E6637B984D74}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9E018F4D-EED3-44D0-9179-E6637B984D74}.Release|Any CPU.Build.0 = Release|Any CPU {9E018F4D-EED3-44D0-9179-E6637B984D74}.Release|Any CPU.Build.0 = Release|Any CPU
{1A945077-A135-4756-974C-A87109DF411E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1A945077-A135-4756-974C-A87109DF411E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1A945077-A135-4756-974C-A87109DF411E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1A945077-A135-4756-974C-A87109DF411E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -1,4 +1,6 @@
namespace Projekt_Calcan_Conze_Import.DTOs; using Projekt_Calcan_Conze_Import.DTOs;
namespace Projekt_Calcan_Conze_Import.DTOs;
internal class UserDto internal class UserDto
{ {

View File

@ -1,7 +1,10 @@
using System.Net.Mail; using System.Net.Mail;
using Core;
using Projekt_Calcan_Conze_Import.DTOs; using Projekt_Calcan_Conze_Import.DTOs;
using Projekt_Calcan_Conze_Import.Models; using Projekt_Calcan_Conze_Import.Models;
using Projekt_Calcan_Conze_Import.DTOs;
using Projekt_Calcan_Conze_Import.Models; using Projekt_Calcan_Conze_Import.Models;
namespace Projekt_Calcan_Conze_Import; namespace Projekt_Calcan_Conze_Import;

View File

@ -1,5 +1,8 @@
using System.Text; using System.Text;
using Core;
using Projekt_Calcan_Conze_Import.Models;
namespace Projekt_Calcan_Conze_Import.Models; namespace Projekt_Calcan_Conze_Import.Models;
internal class User internal class User

View File

@ -1,5 +1,6 @@
using Projekt_Calcan_Conze_Import; using Projekt_Calcan_Conze_Import;
using Projekt_Calcan_Conze_Import.Repositories; using Projekt_Calcan_Conze_Import.Repositories;
using Projekt_Calcan_Conze_Import;
try try
{ {

View File

@ -11,4 +11,8 @@
<PackageReference Include="MySql.Data" Version="8.4.0" /> <PackageReference Include="MySql.Data" Version="8.4.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core\Core.csproj" />
</ItemGroup>
</Project> </Project>

View File

@ -1,16 +1,18 @@
using System.Data; using System.Data;
using Core;
using Projekt_Calcan_Conze_Import.DTOs; using Projekt_Calcan_Conze_Import.DTOs;
using Projekt_Calcan_Conze_Import.Models; using Projekt_Calcan_Conze_Import.Models;
namespace Projekt_Calcan_Conze_Import.Repositories; namespace Projekt_Calcan_Conze_Import.Repositories;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
using Projekt_Calcan_Conze_Import.DTOs;
using Projekt_Calcan_Conze_Import.Models;
internal static class Database internal static class Database
{ {
//set the correct values for your server, user, password and database name
private const string ConnectionString = "server=localhost;uid=root;pwd=root;database=import_export";
public static async Task<List<UserIdentifierDto>> GetUserIdentifiers(string clientNumber) public static async Task<List<UserIdentifierDto>> GetUserIdentifiers(string clientNumber)
{ {
@ -19,7 +21,7 @@ internal static class Database
try try
{ {
// use "using" to automatically close the connection when done // use "using" to automatically close the connection when done
await using var dbConnection = new MySqlConnection(ConnectionString); await using var dbConnection = new MySqlConnection(Constants.ConnectionString);
// open a connection // open a connection
await dbConnection.OpenAsync(); await dbConnection.OpenAsync();
@ -67,7 +69,7 @@ internal static class Database
{ {
try try
{ {
await using var dbConnection = new MySqlConnection(ConnectionString); await using var dbConnection = new MySqlConnection(Constants.ConnectionString);
await dbConnection.OpenAsync(); await dbConnection.OpenAsync();
var transaction = await dbConnection.BeginTransactionAsync(); var transaction = await dbConnection.BeginTransactionAsync();

View File

@ -535,6 +535,16 @@
"runtime": { "runtime": {
"lib/net7.0/ZstdSharp.dll": {} "lib/net7.0/ZstdSharp.dll": {}
} }
},
"Core/1.0.0": {
"type": "project",
"framework": ".NETCoreApp,Version=v8.0",
"compile": {
"bin/placeholder/Core.dll": {}
},
"runtime": {
"bin/placeholder/Core.dll": {}
}
} }
} }
}, },
@ -1778,10 +1788,16 @@
"zstdsharp.port.0.7.1.nupkg.sha512", "zstdsharp.port.0.7.1.nupkg.sha512",
"zstdsharp.port.nuspec" "zstdsharp.port.nuspec"
] ]
},
"Core/1.0.0": {
"type": "project",
"path": "../Core/Core.csproj",
"msbuildProject": "../Core/Core.csproj"
} }
}, },
"projectFileDependencyGroups": { "projectFileDependencyGroups": {
"net8.0": [ "net8.0": [
"Core >= 1.0.0",
"MySql.Data >= 8.4.0" "MySql.Data >= 8.4.0"
] ]
}, },
@ -1811,7 +1827,11 @@
"frameworks": { "frameworks": {
"net8.0": { "net8.0": {
"targetAlias": "net8.0", "targetAlias": "net8.0",
"projectReferences": {} "projectReferences": {
"C:\\Jan_bib_Module\\PMC\\Projekt\\Projekt_Calcan_Conze\\Core\\Core.csproj": {
"projectPath": "C:\\Jan_bib_Module\\PMC\\Projekt\\Projekt_Calcan_Conze\\Core\\Core.csproj"
}
}
} }
}, },
"warningProperties": { "warningProperties": {

View File

@ -1,6 +1,6 @@
{ {
"version": 2, "version": 2,
"dgSpecHash": "xTy/jRXZQLw=", "dgSpecHash": "4Akg//gURuM=",
"success": true, "success": true,
"projectFilePath": "C:\\Jan_bib_Module\\PMC\\Projekt\\Projekt_Calcan_Conze\\Projekt_Calcan_Conze\\Projekt_Calcan_Conze_Import.csproj", "projectFilePath": "C:\\Jan_bib_Module\\PMC\\Projekt\\Projekt_Calcan_Conze\\Projekt_Calcan_Conze\\Projekt_Calcan_Conze_Import.csproj",
"expectedPackageFiles": [ "expectedPackageFiles": [

View File

@ -1,10 +0,0 @@
namespace Projekt_Calcan_Conze_Export;
internal static class Constants
{
public const string DateFormat = "yyyy-MM-dd";
public const string Separator = ";";
public const string MoneyFormat = "F2";
}

View File

@ -1,4 +1,6 @@
namespace Projekt_Calcan_Conze_Export.Models; using Core;
namespace Projekt_Calcan_Conze_Export.Models;
internal class Billing internal class Billing
{ {
@ -74,7 +76,7 @@ internal class BillingPosition
public double BaseAmount { get; } public double BaseAmount { get; }
public int Count { get; } public int Count { get; set; }
public double TotalAmount => this.BaseAmount * this.Count; public double TotalAmount => this.BaseAmount * this.Count;
} }

View File

@ -1,8 +1,9 @@
using System.Globalization; using System.Globalization;
using Projekt_Calcan_Conze_Export; using Core;
using Projekt_Calcan_Conze_Export.Models;
using Microsoft.VisualBasic; using Projekt_Calcan_Conze_Export.Repositories;
using Projekt_Calcan_Conze_Export.Repositories;
try try
{ {
@ -83,26 +84,22 @@ try
} }
} }
List<Billing> billings = Console.WriteLine();
[
new Billing( var billing = await Database.CreateBilling(startDate.Value, endDate.Value, clientNumber);
startDate: startDate.Value,
endDate: endDate.Value, if (billing is null)
customerNumber: clientNumber, {
customerName: "Max Mustermann", Console.WriteLine("Es konnte keine Rechnung erstellt werden.");
positions: }
[ else
new BillingPosition( {
description: "Testposition", string fileName =
baseAmount: 100.0, $"{DateTime.Today.ToString(Constants.DateFormat)}_{clientNumber}_{startDate.Value.ToString(Constants.DateFormat)}_{endDate.Value.ToString(Constants.DateFormat)}.csv";
count: 3) string filePath = Path.Combine(directoryPath, fileName);
]) File.WriteAllLines(filePath, billing.ToCsvLines());
]; Console.WriteLine($"Die Rechnung wurde unter \"{filePath}\" abgelegt.");
string fileName = }
$"{DateTime.Today.ToString(Constants.DateFormat)}_{clientNumber}_{startDate.Value.ToString(Constants.DateFormat)}_{endDate.Value.ToString(Constants.DateFormat)}.csv";
File.WriteAllLines(
Path.Combine(directoryPath, fileName),
billings.SelectMany(billing => billing.ToCsvLines()));
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -7,4 +7,12 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Core\Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MySql.Data" Version="9.0.0" />
</ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,162 @@
using System.Data;
using Core;
using Projekt_Calcan_Conze_Export.Models;
using MySql.Data.MySqlClient;
using Projekt_Calcan_Conze_Export.Models;
namespace Projekt_Calcan_Conze_Export.Repositories;
internal static class Database
{
public static async Task<Billing?> CreateBilling(DateOnly startDate, DateOnly endDate, string customerNumber)
{
Billing? billing = null;
try
{
await using var dbConnection = new MySqlConnection(Constants.ConnectionString);
await dbConnection.OpenAsync();
var transaction = await dbConnection.BeginTransactionAsync();
try
{
// ensure wasBilled column exists and activityDateTime does not refresh on update
var createWasBilledColumnCommand =
new MySqlCommand
{
Connection = dbConnection,
Transaction = transaction,
CommandText =
"""
ALTER TABLE activity ADD IF NOT EXISTS wasBilled BIT NOT NULL DEFAULT 0;
ALTER TABLE activity CHANGE COLUMN activityDatetime activityDatetime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;
ALTER TABLE activity ALTER COLUMN activityDatetime DROP DEFAULT;
"""
};
await createWasBilledColumnCommand.ExecuteNonQueryAsync();
// tables: activityType, activity, contract, user, client
// one activityType can be assigned to multiple contracts, one contract has one activityType
// one contract can have multiple activities, one activity belongs to one contract
// one activity has one user, one user can have multiple activities
// one user belongs to one client, one client can have multiple users
// one contract belongs to one client, one client can have multiple contracts
// get all contracts of the client by the client number and the start and end date
// include all activities of the contracts
// include the client name (column of client table)
// only get activities that were not billed yet (wasBilled = 0)
var getActivities =
new MySqlCommand
{
Connection = dbConnection,
Transaction = transaction,
CommandText =
"""
SELECT
activity.id,
activity.contractId,
contract.amount,
contract.description
FROM activity
JOIN contract ON activity.contractId = contract.id
JOIN activityType ON contract.activityTypeId = activityType.id
JOIN user ON activity.userId = user.id
JOIN client ON contract.clientId = client.id
WHERE client.clientno = @clientNumber
AND activity.activityDateTime >= @startDate
AND activity.activityDateTime <= @endDate
AND activity.wasBilled = 0
"""
};
getActivities.Parameters.AddWithValue("@clientNumber", customerNumber);
getActivities.Parameters.AddWithValue("@startDate", startDate.ToString("yyyy-MM-dd"));
getActivities.Parameters.AddWithValue("@endDate", endDate.ToString("yyyy-MM-dd"));
await using var activityReader = await getActivities.ExecuteReaderAsync();
Dictionary<int, BillingPosition> positions = [];
List<int> activityIds = [];
while (await activityReader.ReadAsync())
{
activityIds.Add(activityReader.GetInt32("id"));
int contractId = activityReader.GetInt32("contractId");
if (positions.TryGetValue(contractId, out var positionDto))
{
positionDto.Count++;
}
else
{
positions[contractId] =
new BillingPosition(
description: activityReader.GetString("description"),
baseAmount: activityReader.GetDouble("amount"),
count: 1);
}
}
await activityReader.CloseAsync();
// get the customer name separately in case we have no activities
var getCustomerNameCommand =
new MySqlCommand
{
Connection = dbConnection,
Transaction = transaction,
CommandText =
"""
SELECT name
FROM client
WHERE clientno = @clientNumber
"""
};
getCustomerNameCommand.Parameters.AddWithValue("@clientNumber", customerNumber);
string customerName = await getCustomerNameCommand.ExecuteScalarAsync() as string ?? string.Empty;
billing =
new Billing(
startDate: startDate,
endDate: endDate,
customerNumber: customerNumber,
customerName: customerName,
positions: positions.Values.ToList());
// set wasBilled to 1 for all retrieved activities
string activityIdsString = string.Join(',', activityIds.Select((_, i) => $"@activityId{i}"));
var updateWasBilledCommand =
new MySqlCommand
{
Connection = dbConnection,
Transaction = transaction,
CommandText = $"UPDATE activity SET wasBilled = 1 WHERE id IN ({activityIdsString})"
};
for (int i = 0; i < activityIds.Count; i++)
{
updateWasBilledCommand.Parameters.AddWithValue($"@activityId{i}", activityIds[i]);
}
await updateWasBilledCommand.ExecuteNonQueryAsync();
}
catch (Exception)
{
await transaction.RollbackAsync();
throw;
}
await transaction.CommitAsync();
}
catch (Exception ex)
{
Console.WriteLine("An error occurred while writing to or reading from the database: " + ex.Message);
}
return billing;
}
}