From 06fa68a4e052ea0225e6bacb3226db2c599ae408 Mon Sep 17 00:00:00 2001 From: Leopold Strobl Date: Tue, 24 Mar 2026 14:43:35 +0100 Subject: [PATCH] =?UTF-8?q?Projektdateien=20hinzuf=C3=BCgen.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- EpiAutoReminder.slnx | 3 + EpiAutoReminder/.gitignore | 1 + EpiAutoReminder/ConfigLoader.cs | 114 ++ EpiAutoReminder/EpiAutoReminder.csproj | 33 + EpiAutoReminder/EpiOrders.cs | 56 + EpiAutoReminder/Logger.cs.cs | 26 + EpiAutoReminder/Model/ContactDetail.cs | 1166 +++++++++++++++ EpiAutoReminder/Model/ContactPersonDetail.cs | 239 +++ EpiAutoReminder/Model/OrderList.cs | 1377 ++++++++++++++++++ EpiAutoReminder/O365Connector.cs | 177 +++ EpiAutoReminder/Program.cs | 183 +++ EpiAutoReminder/appsettings.CHANGEME.json | 24 + EpiAutoReminder/log4net.config | 24 + 13 files changed, 3423 insertions(+) create mode 100644 EpiAutoReminder.slnx create mode 100644 EpiAutoReminder/.gitignore create mode 100644 EpiAutoReminder/ConfigLoader.cs create mode 100644 EpiAutoReminder/EpiAutoReminder.csproj create mode 100644 EpiAutoReminder/EpiOrders.cs create mode 100644 EpiAutoReminder/Logger.cs.cs create mode 100644 EpiAutoReminder/Model/ContactDetail.cs create mode 100644 EpiAutoReminder/Model/ContactPersonDetail.cs create mode 100644 EpiAutoReminder/Model/OrderList.cs create mode 100644 EpiAutoReminder/O365Connector.cs create mode 100644 EpiAutoReminder/Program.cs create mode 100644 EpiAutoReminder/appsettings.CHANGEME.json create mode 100644 EpiAutoReminder/log4net.config diff --git a/EpiAutoReminder.slnx b/EpiAutoReminder.slnx new file mode 100644 index 0000000..13cad60 --- /dev/null +++ b/EpiAutoReminder.slnx @@ -0,0 +1,3 @@ + + + diff --git a/EpiAutoReminder/.gitignore b/EpiAutoReminder/.gitignore new file mode 100644 index 0000000..d7be6ac --- /dev/null +++ b/EpiAutoReminder/.gitignore @@ -0,0 +1 @@ +appsettings.json \ No newline at end of file diff --git a/EpiAutoReminder/ConfigLoader.cs b/EpiAutoReminder/ConfigLoader.cs new file mode 100644 index 0000000..c225747 --- /dev/null +++ b/EpiAutoReminder/ConfigLoader.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; + +namespace EpiAutoReminder +{ + public class ConfigLoader + { + public AzureAdConfig AzureAd { get; set; } + public GraphConfig Graph { get; set; } + public EpirentConfig Epirent { get; set; } + + // Neues Mapping + public Dictionary MailMappings { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); + + private static readonly string configFilePath = "appsettings.json"; + private static ConfigLoader _instance; + + public static ConfigLoader Instance => _instance ??= LoadConfig(); + + public ConfigLoader() { } + + public static ConfigLoader LoadConfig() + { + Logger.Info("Lade Konfigurationsdatei..."); + + try + { + if (!File.Exists(configFilePath)) + { + Logger.Error($"Konfigurationsdatei '{configFilePath}' wurde nicht gefunden!"); + throw new FileNotFoundException($"Config-Datei '{configFilePath}' wurde nicht gefunden!"); + } + + string json = File.ReadAllText(configFilePath); + + var options = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }; + + var config = JsonSerializer.Deserialize(json, options); + + if (config == null) + { + Logger.Error("Fehler: Konfiguration konnte nicht deserialisiert werden."); + throw new Exception("Konfiguration konnte nicht geladen werden."); + } + + config.MailMappings ??= new Dictionary(StringComparer.OrdinalIgnoreCase); + + Logger.Info("Konfiguration erfolgreich geladen."); + return config; + } + catch (Exception ex) + { + Logger.Error("Fehler beim Laden der Konfiguration!", ex); + throw; + } + } + + /// + /// Liefert die Mailadresse zu einem Kürzel, z.B. AR -> axel.reisinger@vt-media.de + /// Gibt null zurück, wenn kein Mapping gefunden wurde. + /// + public string GetMailAddressByShortcut(string shortcut) + { + if (string.IsNullOrWhiteSpace(shortcut)) + return null; + + if (MailMappings != null && MailMappings.TryGetValue(shortcut.Trim(), out var mailAddress)) + return mailAddress; + + return null; + } + + /// + /// Liefert die Mailadresse zu einem Kürzel oder wirft eine Exception, wenn nichts gefunden wurde. + /// + public string GetRequiredMailAddressByShortcut(string shortcut) + { + var mailAddress = GetMailAddressByShortcut(shortcut); + + if (string.IsNullOrWhiteSpace(mailAddress)) + throw new KeyNotFoundException($"Für das Kürzel '{shortcut}' wurde keine Mailadresse gefunden."); + + return mailAddress; + } + } + + public class AzureAdConfig + { + public string ClientId { get; set; } + public string TenantId { get; set; } + public string ClientSecret { get; set; } + } + + public class GraphConfig + { + public string BaseUrl { get; set; } + } + + public class EpirentConfig + { + public string Server { get; set; } + public int Port { get; set; } + public string Token { get; set; } + public int Mandant { get; set; } + public int ReminderDaysBeforeDispoStart { get; set; } + public int OfferDateOlderThan { get; set; } + } +} \ No newline at end of file diff --git a/EpiAutoReminder/EpiAutoReminder.csproj b/EpiAutoReminder/EpiAutoReminder.csproj new file mode 100644 index 0000000..7656eb3 --- /dev/null +++ b/EpiAutoReminder/EpiAutoReminder.csproj @@ -0,0 +1,33 @@ + + + + Exe + net10.0 + enable + enable + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + diff --git a/EpiAutoReminder/EpiOrders.cs b/EpiAutoReminder/EpiOrders.cs new file mode 100644 index 0000000..cb9a7b6 --- /dev/null +++ b/EpiAutoReminder/EpiOrders.cs @@ -0,0 +1,56 @@ +using Newtonsoft.Json; + +using RestSharp; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace EpiAutoReminder +{ + internal class EpiOrders + { + ConfigLoader Configuration = ConfigLoader.Instance; + RestClient epirentserver; + + + public EpiOrders() + { + epirentserver = new RestClient("http://" + Configuration.Epirent.Server + ":" + Configuration.Epirent.Port); + + } + public OrderList getOrderList() + { + OrderList orderlist = JsonConvert.DeserializeObject(getOrderJson()); + return orderlist; + } + + public ContactDetail getContact(int ContactPrimaryKey) + { + RestRequest request = new RestRequest("/v1/contact/" + ContactPrimaryKey + "?cl=" + Configuration.Epirent.Mandant, RestSharp.Method.Get); + request.AddHeader("X-EPI-NO-SESSION", "True"); + request.AddHeader("X-EPI-ACC-TOK", Configuration.Epirent.Token); + request.RequestFormat = DataFormat.Json; + return JsonConvert.DeserializeObject(epirentserver.ExecuteGet(request).Content); + } + public ContactPersonDetail getContactPersonDetail(int ContactPersonPrimaryKey) + { + RestRequest request = new RestRequest("/v1/cperson/" + ContactPersonPrimaryKey + "?cl=" + Configuration.Epirent.Mandant, RestSharp.Method.Get); + request.AddHeader("X-EPI-NO-SESSION", "True"); + request.AddHeader("X-EPI-ACC-TOK", Configuration.Epirent.Token); + request.RequestFormat = DataFormat.Json; + return JsonConvert.DeserializeObject(epirentserver.ExecuteGet(request).Content); + } + + + private String getOrderJson() + { + RestRequest request = new RestRequest("/v1/order/filter?cl="+ Configuration.Epirent.Mandant + "&ir=true&ia=false&icl=false&ico=false&icv=true", RestSharp.Method.Get); + request.AddHeader("X-EPI-NO-SESSION", "True"); + request.AddHeader("X-EPI-ACC-TOK", Configuration.Epirent.Token); + request.RequestFormat = DataFormat.Json; + return epirentserver.ExecuteGet(request).Content; + + } + } +} diff --git a/EpiAutoReminder/Logger.cs.cs b/EpiAutoReminder/Logger.cs.cs new file mode 100644 index 0000000..9b77c42 --- /dev/null +++ b/EpiAutoReminder/Logger.cs.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using log4net.Config; +using log4net; + +namespace EpiAutoReminder +{ + + public static class Logger + { + private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + static Logger() + { + var logRepository = LogManager.GetRepository(Assembly.GetEntryAssembly()); + XmlConfigurator.Configure(logRepository, new FileInfo("log4net.config")); + } + + public static void Info(string message) => log.Info(message); + public static void Warn(string message) => log.Warn(message); + public static void Error(string message, Exception ex = null) => log.Error(message, ex); + public static void Debug(string message) => log.Debug(message); + } + } diff --git a/EpiAutoReminder/Model/ContactDetail.cs b/EpiAutoReminder/Model/ContactDetail.cs new file mode 100644 index 0000000..718e343 --- /dev/null +++ b/EpiAutoReminder/Model/ContactDetail.cs @@ -0,0 +1,1166 @@ +// +// +// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do: +// +// using EpiAutoReminder; +// +// var contactDetail = ContactDetail.FromJson(jsonString); + +namespace EpiAutoReminder +{ + using System; + using System.Collections.Generic; + using Newtonsoft.Json; + + public partial class ContactDetail + { + [JsonProperty("success")] + public bool Success { get; set; } + + [JsonProperty("namespace")] + public string Namespace { get; set; } + + [JsonProperty("action")] + public string Action { get; set; } + + [JsonProperty("templateSignature")] + public string TemplateSignature { get; set; } + + [JsonProperty("payload_length")] + public long PayloadLength { get; set; } + + [JsonProperty("is_send_payload")] + public bool IsSendPayload { get; set; } + + [JsonProperty("payload")] + public List Payload { get; set; } + + // Diese Typen kommen bereits aus OrderList.cs + //[JsonProperty("filter_options")] + //public FilterOptions FilterOptions { get; set; } + + //[JsonProperty("request_duration")] + //public long RequestDuration { get; set; } + + //[JsonProperty("req_datetime")] + //public ReqDatetime ReqDatetime { get; set; } + + //[JsonProperty("_dbg_request_data")] + //public DbgRequestData DbgRequestData { get; set; } + + //[JsonProperty("_dbg_user_info")] + //public UserInfo DbgUserInfo { get; set; } + + public static ContactDetail FromJson(string json) + => JsonConvert.DeserializeObject(json, EpiAutoReminder.Converter.Settings); + } + + public partial class ContactDetailPayload + { + [JsonProperty("primary_key")] + public long PrimaryKey { get; set; } + + [JsonProperty("customer_no")] + public long CustomerNo { get; set; } + + [JsonProperty("supplier_no")] + public long SupplierNo { get; set; } + + [JsonProperty("is_single_person")] + public bool IsSinglePerson { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("name1")] + public string Name1 { get; set; } + + [JsonProperty("name2")] + public string Name2 { get; set; } + + [JsonProperty("name3")] + public string Name3 { get; set; } + + [JsonProperty("matchcode")] + public string Matchcode { get; set; } + + [JsonProperty("is_interested")] + public bool IsInterested { get; set; } + + [JsonProperty("is_customer")] + public bool IsCustomer { get; set; } + + [JsonProperty("is_supplier")] + public bool IsSupplier { get; set; } + + [JsonProperty("is_location")] + public bool IsLocation { get; set; } + + [JsonProperty("is_vip")] + public bool IsVip { get; set; } + + [JsonProperty("is_colleague")] + public bool IsColleague { get; set; } + + [JsonProperty("salutation")] + public string Salutation { get; set; } + + [JsonProperty("grade")] + public string Grade { get; set; } + + [JsonProperty("notes_positive")] + public string NotesPositive { get; set; } + + [JsonProperty("notes_negative")] + public string NotesNegative { get; set; } + + [JsonProperty("date_of_birth")] + public string DateOfBirth { get; set; } + + [JsonProperty("date_changed")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? DateChanged { get; set; } + + [JsonProperty("time_changed")] + public long TimeChanged { get; set; } + + [JsonProperty("date_created")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? DateCreated { get; set; } + + [JsonProperty("time_created")] + public long TimeCreated { get; set; } + + [JsonProperty("is_active")] + public bool IsActive { get; set; } + + [JsonProperty("terms_of_payment_pk")] + public long TermsOfPaymentPk { get; set; } + + [JsonProperty("is_sysstd_terms_of_payment")] + public bool IsSysstdTermsOfPayment { get; set; } + + [JsonProperty("vat_id")] + public string VatId { get; set; } + + [JsonProperty("eori_no")] + public string EoriNo { get; set; } + + [JsonProperty("tax_no")] + public string TaxNo { get; set; } + + [JsonProperty("tax_office")] + public string TaxOffice { get; set; } + + [JsonProperty("commercial_register_no")] + public string CommercialRegisterNo { get; set; } + + [JsonProperty("commercial_register_office")] + public string CommercialRegisterOffice { get; set; } + + [JsonProperty("is_blocked_order")] + public bool IsBlockedOrder { get; set; } + + [JsonProperty("is_separate_invoice_address")] + public bool IsSeparateInvoiceAddress { get; set; } + + [JsonProperty("invoice_address")] + public InvoiceAddress InvoiceAddress { get; set; } + + [JsonProperty("has_image")] + public bool HasImage { get; set; } + + [JsonProperty("address")] + public ContactDetailAddress Address { get; set; } + + [JsonProperty("address_list")] + public List AddressList { get; set; } + + [JsonProperty("address_delivery")] + public object AddressDelivery { get; set; } + + [JsonProperty("communication")] + public List Communication { get; set; } + + [JsonProperty("_communication_length")] + public long CommunicationLength { get; set; } + + [JsonProperty("contact_person")] + public List ContactPerson { get; set; } + + [JsonProperty("_contact_person_length")] + public long ContactPersonLength { get; set; } + + [JsonProperty("contact_chrono")] + public List ContactChrono { get; set; } + + [JsonProperty("_contact_chrono_length")] + public long ContactChronoLength { get; set; } + + [JsonProperty("order")] + public List Order { get; set; } + + [JsonProperty("_order_length")] + public long OrderLength { get; set; } + + [JsonProperty("banking_customer")] + public BankingCustomer BankingCustomer { get; set; } + + [JsonProperty("banking_supplier")] + public BankingSupplier BankingSupplier { get; set; } + + [JsonProperty("price_range")] + public PriceRange PriceRange { get; set; } + + [JsonProperty("searchindex")] + public List Searchindex { get; set; } + + [JsonProperty("_searchindex_length")] + public long SearchindexLength { get; set; } + + [JsonProperty("terms_of_payment")] + public TermsOfPayment TermsOfPayment { get; set; } + + [JsonProperty("contact_data")] + public ContactDetailContactData ContactData { get; set; } + + [JsonProperty("_address_list_length")] + public long AddressListLength { get; set; } + + [JsonProperty("_contact_data_length")] + public long ContactDataLength { get; set; } + + [JsonIgnore] + public DateTimeOffset? CreatedAt => EpiDateHelper.CombineDateAndSeconds(DateCreated, TimeCreated); + + [JsonIgnore] + public DateTimeOffset? ChangedAt => EpiDateHelper.CombineDateAndSeconds(DateChanged, TimeChanged); + } + + public partial class ContactDetailAddress + { + [JsonProperty("primary_key")] + public long PrimaryKey { get; set; } + + [JsonProperty("postal_code")] + [JsonConverter(typeof(FluffyParseStringConverter))] + public long PostalCode { get; set; } + + [JsonProperty("street")] + public string Street { get; set; } + + [JsonProperty("city")] + public string City { get; set; } + + [JsonProperty("country")] + public string Country { get; set; } + + [JsonProperty("country_iso3")] + public string CountryIso3 { get; set; } + + [JsonProperty("federal_state")] + public string FederalState { get; set; } + } + + public partial class BankingCustomer + { + [JsonProperty("account_holder")] + public string AccountHolder { get; set; } + + [JsonProperty("iban")] + public string Iban { get; set; } + + [JsonProperty("bic")] + public string Bic { get; set; } + + [JsonProperty("bank_name")] + public string BankName { get; set; } + + [JsonProperty("account_no")] + public string AccountNo { get; set; } + + [JsonProperty("creditworthiness")] + public bool Creditworthiness { get; set; } + + [JsonProperty("executed_on")] + public string ExecutedOn { get; set; } + + [JsonProperty("credit_rating")] + public long CreditRating { get; set; } + + [JsonProperty("employees_count")] + public long EmployeesCount { get; set; } + + [JsonProperty("company_foundation")] + public string CompanyFoundation { get; set; } + + [JsonProperty("sepa")] + public Sepa Sepa { get; set; } + } + + public partial class Sepa + { + [JsonProperty("is_active")] + public bool IsActive { get; set; } + + [JsonProperty("mandate")] + public string Mandate { get; set; } + + [JsonProperty("date_signature")] + public string DateSignature { get; set; } + } + + public partial class BankingSupplier + { + [JsonProperty("account_holder")] + public string AccountHolder { get; set; } + + [JsonProperty("iban")] + public string Iban { get; set; } + + [JsonProperty("bic")] + public string Bic { get; set; } + + [JsonProperty("bank_name")] + public string BankName { get; set; } + + [JsonProperty("account_no")] + public string AccountNo { get; set; } + } + + public partial class Communication + { + [JsonProperty("primary_key")] + public long PrimaryKey { get; set; } + + [JsonProperty("contact_pk")] + public long ContactPk { get; set; } + + [JsonProperty("contact_person_pk")] + public long ContactPersonPk { get; set; } + + [JsonProperty("uplink")] + public string Uplink { get; set; } + + [JsonProperty("type")] + public long Type { get; set; } + + [JsonProperty("description")] + public string Description { get; set; } + + [JsonProperty("phone_match_number")] + public string PhoneMatchNumber { get; set; } + + [JsonProperty("position")] + public long Position { get; set; } + + [JsonProperty("is_newsletter")] + public bool IsNewsletter { get; set; } + + [JsonProperty("is_invoice")] + public bool IsInvoice { get; set; } + } + + public partial class ContactChrono + { + [JsonProperty("primary_key")] + public long PrimaryKey { get; set; } + + [JsonProperty("contact_pk")] + public long ContactPk { get; set; } + + [JsonProperty("contact_person_pk")] + public long ContactPersonPk { get; set; } + + [JsonProperty("keyword")] + public string Keyword { get; set; } + + [JsonProperty("contact_partner")] + public string ContactPartner { get; set; } + + [JsonProperty("date")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? Date { get; set; } + + [JsonProperty("time")] + public long Time { get; set; } + + [JsonProperty("duration")] + public long Duration { get; set; } + + [JsonProperty("com_type")] + public long ComType { get; set; } + + [JsonProperty("com_type_str")] + public string ComTypeStr { get; set; } + + [JsonProperty("uplink")] + public string Uplink { get; set; } + + [JsonProperty("_ref_contact_chrono")] + public string RefContactChrono { get; set; } + + [JsonProperty("date_changed")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? DateChanged { get; set; } + + [JsonProperty("time_changed")] + public long TimeChanged { get; set; } + + [JsonProperty("user_changed")] + public string UserChanged { get; set; } + + [JsonProperty("date_created")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? DateCreated { get; set; } + + [JsonProperty("time_created")] + public long TimeCreated { get; set; } + + [JsonProperty("user_created")] + public string UserCreated { get; set; } + + [JsonIgnore] + public DateTimeOffset? CreatedAt => EpiDateHelper.CombineDateAndSeconds(DateCreated, TimeCreated); + + [JsonIgnore] + public DateTimeOffset? ChangedAt => EpiDateHelper.CombineDateAndSeconds(DateChanged, TimeChanged); + } + + public partial class ContactDetailContactData + { + [JsonProperty("primary_key")] + public long PrimaryKey { get; set; } + + [JsonProperty("contact_pk")] + public long ContactPk { get; set; } + + [JsonProperty("supplier_no")] + public long SupplierNo { get; set; } + + [JsonProperty("customer_no")] + public long CustomerNo { get; set; } + + [JsonProperty("debitor_no")] + public string DebitorNo { get; set; } + + [JsonProperty("creditor_no")] + public string CreditorNo { get; set; } + + [JsonProperty("rentshop_contact_uri")] + public string RentshopContactUri { get; set; } + + [JsonProperty("is_rentshop_active")] + public bool IsRentshopActive { get; set; } + + [JsonProperty("discount_rent_customer")] + public long DiscountRentCustomer { get; set; } + + [JsonProperty("discount_sale_customer")] + public long DiscountSaleCustomer { get; set; } + + [JsonProperty("is_vat_active_customer")] + public bool IsVatActiveCustomer { get; set; } + + [JsonProperty("country_type_customer")] + public long CountryTypeCustomer { get; set; } + + [JsonProperty("discount_rent_supplier")] + public long DiscountRentSupplier { get; set; } + + [JsonProperty("discount_sale_supplier")] + public long DiscountSaleSupplier { get; set; } + + [JsonProperty("is_vat_active_supplier")] + public bool IsVatActiveSupplier { get; set; } + + [JsonProperty("country_type_supplier")] + public long CountryTypeSupplier { get; set; } + } + + public partial class ContactDetailContactPerson + { + [JsonProperty("primary_key")] + public long PrimaryKey { get; set; } + + [JsonProperty("contact_pk")] + public long ContactPk { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("first_name")] + public string FirstName { get; set; } + + [JsonProperty("last_name")] + public string LastName { get; set; } + + [JsonProperty("nickname")] + public string Nickname { get; set; } + + [JsonProperty("position")] + public string Position { get; set; } + + [JsonProperty("has_image")] + public bool HasImage { get; set; } + + [JsonProperty("_ref_contact")] + public string RefContact { get; set; } + + [JsonProperty("_ref_contact_person")] + public string RefContactPerson { get; set; } + + [JsonProperty("date_changed")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? DateChanged { get; set; } + + [JsonProperty("time_changed")] + public long TimeChanged { get; set; } + + [JsonProperty("user_changed")] + public string UserChanged { get; set; } + + [JsonProperty("date_created")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? DateCreated { get; set; } + + [JsonProperty("time_created")] + public long TimeCreated { get; set; } + + [JsonProperty("user_created")] + public string UserCreated { get; set; } + + [JsonIgnore] + public DateTimeOffset? CreatedAt => EpiDateHelper.CombineDateAndSeconds(DateCreated, TimeCreated); + + [JsonIgnore] + public DateTimeOffset? ChangedAt => EpiDateHelper.CombineDateAndSeconds(DateChanged, TimeChanged); + } + + public partial class InvoiceAddress + { + [JsonProperty("name1")] + public string Name1 { get; set; } + + [JsonProperty("name2")] + public string Name2 { get; set; } + + [JsonProperty("name3")] + public string Name3 { get; set; } + + [JsonProperty("contact_person")] + public string ContactPerson { get; set; } + + [JsonProperty("department")] + public string Department { get; set; } + + [JsonProperty("street")] + public string Street { get; set; } + + [JsonProperty("postal_code")] + public string PostalCode { get; set; } + + [JsonProperty("city")] + public string City { get; set; } + + [JsonProperty("country")] + public string Country { get; set; } + + [JsonProperty("country_iso3")] + public string CountryIso3 { get; set; } + + [JsonProperty("debitor_no")] + public string DebitorNo { get; set; } + } + + public partial class ContactDetailOrder + { + [JsonProperty("primary_key")] + public long PrimaryKey { get; set; } + + [JsonProperty("client_id")] + public long ClientId { get; set; } + + [JsonProperty("order_no")] + public long OrderNo { get; set; } + + [JsonProperty("order_no_fmt")] + public string OrderNoFmt { get; set; } + + [JsonProperty("order_date")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? OrderDate { get; set; } + + [JsonProperty("date_delivery")] + [JsonConverter(typeof(SafeDateTimeConverter))] + public DateTime? DateDelivery { get; set; } + + [JsonProperty("date_shipping")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? DateShipping { get; set; } + + [JsonProperty("_vipgroup")] + public long Vipgroup { get; set; } + + [JsonProperty("editor_id")] + public long EditorId { get; set; } + + [JsonProperty("editor_name")] + public string EditorName { get; set; } + + [JsonProperty("editor_short")] + public string EditorShort { get; set; } + + [JsonProperty("file_archive_id")] + public long FileArchiveId { get; set; } + + [JsonProperty("customer_no")] + public long CustomerNo { get; set; } + + [JsonProperty("customer_cp_pk")] + public long CustomerCpPk { get; set; } + + [JsonProperty("is_teamagenda_notvisible")] + public bool IsTeamagendaNotvisible { get; set; } + + [JsonProperty("teamagenda_category")] + public string TeamagendaCategory { get; set; } + + [JsonProperty("event")] + public string Event { get; set; } + + [JsonProperty("status")] + public string Status { get; set; } + + [JsonProperty("is_current_version")] + public bool IsCurrentVersion { get; set; } + + [JsonProperty("is_confirmed")] + public bool IsConfirmed { get; set; } + + [JsonProperty("is_canceled")] + public bool IsCanceled { get; set; } + + [JsonProperty("is_archived")] + public bool IsArchived { get; set; } + + [JsonProperty("all_inclusive")] + public bool AllInclusive { get; set; } + + [JsonProperty("is_bail_all_inclusive")] + public bool IsBailAllInclusive { get; set; } + + [JsonProperty("is_mrp")] + public bool IsMrp { get; set; } + + [JsonProperty("is_sale")] + public bool IsSale { get; set; } + + [JsonProperty("rentshop_state")] + public long RentshopState { get; set; } + + [JsonProperty("rentshop_order_uri")] + public string RentshopOrderUri { get; set; } + + [JsonProperty("rentshop_contact_uri")] + public string RentshopContactUri { get; set; } + + [JsonProperty("is_contact_connected")] + public bool IsContactConnected { get; set; } + + [JsonProperty("rentshop_person_uri")] + public string RentshopPersonUri { get; set; } + + [JsonProperty("is_person_connected")] + public bool IsPersonConnected { get; set; } + + [JsonProperty("is_vat")] + public bool IsVat { get; set; } + + [JsonProperty("vat1")] + public long Vat1 { get; set; } + + [JsonProperty("vat2")] + public long Vat2 { get; set; } + + [JsonProperty("vat3")] + public ContactDetailVat3 Vat3 { get; set; } + + [JsonProperty("sum_net")] + public double SumNet { get; set; } + + [JsonProperty("sum_gro")] + public double SumGro { get; set; } + + [JsonProperty("sum_salestax1")] + public double SumSalestax1 { get; set; } + + [JsonProperty("sum_salestax2")] + public long SumSalestax2 { get; set; } + + [JsonProperty("sum_discount")] + public double SumDiscount { get; set; } + + [JsonProperty("sum_rent")] + public double SumRent { get; set; } + + [JsonProperty("sum_sales")] + public double SumSales { get; set; } + + [JsonProperty("sum_transport")] + public double SumTransport { get; set; } + + [JsonProperty("sum_service")] + public double SumService { get; set; } + + [JsonProperty("sum_insurance")] + public long SumInsurance { get; set; } + + [JsonProperty("sum_special")] + public long SumSpecial { get; set; } + + [JsonProperty("sum_bail")] + public long SumBail { get; set; } + + [JsonProperty("sum_net_no_discount")] + public double SumNetNoDiscount { get; set; } + + [JsonProperty("sum_gro_no_discount")] + public double SumGroNoDiscount { get; set; } + + [JsonProperty("sum_rent_no_discount")] + public double SumRentNoDiscount { get; set; } + + [JsonProperty("sum_sales_no_discount")] + public double SumSalesNoDiscount { get; set; } + + [JsonProperty("sum_transport_no_discount")] + public double SumTransportNoDiscount { get; set; } + + [JsonProperty("sum_service_no_discount")] + public double SumServiceNoDiscount { get; set; } + + [JsonProperty("sum_special_no_discount")] + public long SumSpecialNoDiscount { get; set; } + + [JsonProperty("discount_rate_rent")] + public long DiscountRateRent { get; set; } + + [JsonProperty("discount_rate_sales")] + public long DiscountRateSales { get; set; } + + [JsonProperty("revenue_account")] + public string RevenueAccount { get; set; } + + [JsonProperty("cost_centre")] + public string CostCentre { get; set; } + + [JsonProperty("amount_uncleared")] + public long AmountUncleared { get; set; } + + [JsonProperty("amount_cleared")] + public long AmountCleared { get; set; } + + [JsonProperty("amount_open_checkin")] + public long AmountOpenCheckin { get; set; } + + [JsonProperty("amount_checkin")] + public long AmountCheckin { get; set; } + + [JsonProperty("amount_open_checkout")] + public long AmountOpenCheckout { get; set; } + + [JsonProperty("amount_checkout")] + public long AmountCheckout { get; set; } + + [JsonProperty("amount_open_packaging")] + public long AmountOpenPackaging { get; set; } + + [JsonProperty("amount_packaging")] + public long AmountPackaging { get; set; } + + [JsonProperty("amount_open_booked")] + public long AmountOpenBooked { get; set; } + + [JsonProperty("amount_booked")] + public long AmountBooked { get; set; } + + [JsonProperty("amount_open_packagingnote")] + public long AmountOpenPackagingnote { get; set; } + + [JsonProperty("amount_packagingnote")] + public long AmountPackagingnote { get; set; } + + [JsonProperty("date_changed")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? DateChanged { get; set; } + + [JsonProperty("time_changed")] + public long TimeChanged { get; set; } + + [JsonProperty("user_changed")] + public string UserChanged { get; set; } + + [JsonProperty("user_changed_short")] + public string UserChangedShort { get; set; } + + [JsonProperty("date_created")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? DateCreated { get; set; } + + [JsonProperty("time_created")] + public long TimeCreated { get; set; } + + [JsonProperty("user_created")] + public string UserCreated { get; set; } + + [JsonProperty("user_created_short")] + public string UserCreatedShort { get; set; } + + [JsonProperty("is_self_pickup")] + public bool IsSelfPickup { get; set; } + + [JsonProperty("is_self_redeliver")] + public bool IsSelfRedeliver { get; set; } + + [JsonProperty("is_rent")] + public bool IsRent { get; set; } + + [JsonProperty("order_state")] + public object OrderState { get; set; } + + [JsonProperty("order_schedule")] + public List OrderSchedule { get; set; } + + [JsonProperty("_ref_order")] + public string RefOrder { get; set; } + + [JsonProperty("contact")] + public ContactDetailEmbeddedContact Contact { get; set; } + + [JsonProperty("contact_person")] + public ContactDetailContactPerson ContactPerson { get; set; } + + [JsonProperty("_order_schedule_length")] + public long OrderScheduleLength { get; set; } + + [JsonProperty("state_billing")] + public string StateBilling { get; set; } + + [JsonProperty("state_checkin")] + public string StateCheckin { get; set; } + + [JsonProperty("state_checkout")] + public string StateCheckout { get; set; } + + [JsonProperty("state_booked")] + public string StateBooked { get; set; } + + [JsonProperty("state_packagingnote")] + public string StatePackagingnote { get; set; } + + [JsonProperty("state_packaging")] + public string StatePackaging { get; set; } + + [JsonIgnore] + public DateTimeOffset? CreatedAt => EpiDateHelper.CombineDateAndSeconds(DateCreated, TimeCreated); + + [JsonIgnore] + public DateTimeOffset? ChangedAt => EpiDateHelper.CombineDateAndSeconds(DateChanged, TimeChanged); + } + + public partial class ContactDetailEmbeddedContact + { + [JsonProperty("primary_key")] + public long PrimaryKey { get; set; } + + [JsonProperty("customer_no")] + public long CustomerNo { get; set; } + + [JsonProperty("supplier_no")] + public long SupplierNo { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("name1")] + public string Name1 { get; set; } + + [JsonProperty("name2")] + public string Name2 { get; set; } + + [JsonProperty("name3")] + public string Name3 { get; set; } + + [JsonProperty("matchcode")] + public string Matchcode { get; set; } + + [JsonProperty("salutation")] + public string Salutation { get; set; } + + [JsonProperty("grade")] + public string Grade { get; set; } + + [JsonProperty("is_interested")] + public bool IsInterested { get; set; } + + [JsonProperty("is_customer")] + public bool IsCustomer { get; set; } + + [JsonProperty("is_supplier")] + public bool IsSupplier { get; set; } + + [JsonProperty("is_location")] + public bool IsLocation { get; set; } + + [JsonProperty("is_vip")] + public bool IsVip { get; set; } + + [JsonProperty("is_colleague")] + public bool IsColleague { get; set; } + + [JsonProperty("date_changed")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? DateChanged { get; set; } + + [JsonProperty("time_changed")] + public long TimeChanged { get; set; } + + [JsonProperty("date_created")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? DateCreated { get; set; } + + [JsonProperty("time_created")] + public long TimeCreated { get; set; } + + [JsonProperty("is_active")] + public bool IsActive { get; set; } + + [JsonProperty("terms_of_payment_pk")] + public long TermsOfPaymentPk { get; set; } + + [JsonProperty("is_sysstd_terms_of_payment")] + public bool IsSysstdTermsOfPayment { get; set; } + + [JsonProperty("vat_id")] + public string VatId { get; set; } + + [JsonProperty("eori_no")] + public string EoriNo { get; set; } + + [JsonProperty("tax_no")] + public string TaxNo { get; set; } + + [JsonProperty("tax_office")] + public string TaxOffice { get; set; } + + [JsonProperty("commercial_register_no")] + public string CommercialRegisterNo { get; set; } + + [JsonProperty("commercial_register_office")] + public string CommercialRegisterOffice { get; set; } + + [JsonProperty("has_image")] + public bool HasImage { get; set; } + + [JsonProperty("_ref_contact")] + public string RefContact { get; set; } + + [JsonProperty("contact_data")] + public ContactDetailContactData ContactData { get; set; } + + [JsonProperty("_contact_data_length")] + public long ContactDataLength { get; set; } + } + + public partial class ContactDetailOrderSchedule + { + [JsonProperty("primary_key")] + public long PrimaryKey { get; set; } + + [JsonProperty("type")] + public long Type { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("date_start")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? DateStart { get; set; } + + [JsonProperty("date_end")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? DateEnd { get; set; } + + [JsonProperty("time_start")] + public long TimeStart { get; set; } + + [JsonProperty("time_end")] + public long TimeEnd { get; set; } + + [JsonProperty("is_start_date")] + public bool IsStartDate { get; set; } + + [JsonProperty("is_end_date")] + public bool IsEndDate { get; set; } + + [JsonIgnore] + public DateTimeOffset? StartAt => EpiDateHelper.CombineDateAndSeconds(DateStart, TimeStart); + + [JsonIgnore] + public DateTimeOffset? EndAt => EpiDateHelper.CombineDateAndSeconds(DateEnd, TimeEnd); + } + + public partial class ContactDetailVat3 + { + [JsonProperty("VAT")] + public List Vat { get; set; } + } + + public partial class PriceRange + { + [JsonProperty("manual")] + public List Manual { get; set; } + + [JsonProperty("automatic")] + public List Automatic { get; set; } + } + + public partial class TermsOfPayment + { + [JsonProperty("primary_key")] + public long PrimaryKey { get; set; } + + [JsonProperty("client_id")] + public long ClientId { get; set; } + + [JsonProperty("order_pk")] + public long OrderPk { get; set; } + + [JsonProperty("invoice_pk")] + public long InvoicePk { get; set; } + + [JsonProperty("incinvoice_pk")] + public long IncinvoicePk { get; set; } + + [JsonProperty("is_masterdata")] + public bool IsMasterdata { get; set; } + + [JsonProperty("info")] + public string Info { get; set; } + + [JsonProperty("is_default")] + public bool IsDefault { get; set; } + + [JsonProperty("purchase_pk")] + public long PurchasePk { get; set; } + + [JsonProperty("type")] + public long Type { get; set; } + + [JsonProperty("payment_period")] + public long PaymentPeriod { get; set; } + + [JsonProperty("payment_period_type")] + public long PaymentPeriodType { get; set; } + + [JsonProperty("payment_area_type")] + public long PaymentAreaType { get; set; } + + [JsonProperty("is_rent")] + public bool IsRent { get; set; } + + [JsonProperty("is_sales")] + public bool IsSales { get; set; } + + [JsonProperty("is_rent_sales")] + public bool IsRentSales { get; set; } + + [JsonProperty("payment_method")] + public long PaymentMethod { get; set; } + + [JsonProperty("discount_value_1")] + public long DiscountValue1 { get; set; } + + [JsonProperty("discount_days_1")] + public long DiscountDays1 { get; set; } + + [JsonProperty("discount_value_2")] + public long DiscountValue2 { get; set; } + + [JsonProperty("discount_days_2")] + public long DiscountDays2 { get; set; } + + [JsonProperty("discount_value_3")] + public long DiscountValue3 { get; set; } + + [JsonProperty("discount_days_3")] + public long DiscountDays3 { get; set; } + + [JsonProperty("discount_value_4")] + public long DiscountValue4 { get; set; } + + [JsonProperty("discount_days_4")] + public long DiscountDays4 { get; set; } + + [JsonProperty("discount_value_5")] + public long DiscountValue5 { get; set; } + + [JsonProperty("discount_days_5")] + public long DiscountDays5 { get; set; } + + [JsonProperty("sage_token")] + public string SageToken { get; set; } + + [JsonProperty("advance_payment_value")] + public long AdvancePaymentValue { get; set; } + + [JsonProperty("_ref_order")] + public string RefOrder { get; set; } + + [JsonProperty("_ref_invoice")] + public string RefInvoice { get; set; } + + [JsonProperty("_ref_incinvoice")] + public string RefIncinvoice { get; set; } + + [JsonProperty("mlang_description")] + public List MlangDescription { get; set; } + + [JsonProperty("_mlang_description_length")] + public long MlangDescriptionLength { get; set; } + } + + public partial class MlangDescription + { + [JsonProperty("primary_key")] + public long PrimaryKey { get; set; } + + [JsonProperty("terms_of_payment_pk")] + public long TermsOfPaymentPk { get; set; } + + [JsonProperty("description_order_short")] + public string DescriptionOrderShort { get; set; } + + [JsonProperty("description_invoice_short")] + public string DescriptionInvoiceShort { get; set; } + + [JsonProperty("condition_text_order")] + public string ConditionTextOrder { get; set; } + + [JsonProperty("condition_text_invoice")] + public string ConditionTextInvoice { get; set; } + + [JsonProperty("lang_pk")] + public long LangPk { get; set; } + + [JsonProperty("lang_str")] + public string LangStr { get; set; } + + [JsonProperty("iso_639_1")] + public string Iso639_1 { get; set; } + + [JsonProperty("iso_639_2")] + public string Iso639_2 { get; set; } + } +} \ No newline at end of file diff --git a/EpiAutoReminder/Model/ContactPersonDetail.cs b/EpiAutoReminder/Model/ContactPersonDetail.cs new file mode 100644 index 0000000..f551472 --- /dev/null +++ b/EpiAutoReminder/Model/ContactPersonDetail.cs @@ -0,0 +1,239 @@ +// +// +// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do: +// +// using EpiAutoReminder; +// +// var contactPersonDetail = ContactPersonDetail.FromJson(jsonString); + +namespace EpiAutoReminder +{ + using System; + using System.Collections.Generic; + using Newtonsoft.Json; + + public partial class ContactPersonDetail + { + [JsonProperty("success")] + public bool Success { get; set; } + + [JsonProperty("namespace")] + public string Namespace { get; set; } + + [JsonProperty("action")] + public string Action { get; set; } + + [JsonProperty("templateSignature")] + public string TemplateSignature { get; set; } + + [JsonProperty("payload_length")] + public long PayloadLength { get; set; } + + [JsonProperty("is_send_payload")] + public bool IsSendPayload { get; set; } + + [JsonProperty("payload")] + public List Payload { get; set; } + + [JsonProperty("request_duration")] + public long RequestDuration { get; set; } + + // shared types from OrderList.cs + [JsonProperty("req_datetime")] + public ReqDatetime ReqDatetime { get; set; } + + [JsonProperty("_dbg_request_data")] + public DbgRequestData DbgRequestData { get; set; } + + [JsonProperty("_dbg_user_info")] + public UserInfo DbgUserInfo { get; set; } + + public static ContactPersonDetail FromJson(string json) + => JsonConvert.DeserializeObject(json, EpiAutoReminder.Converter.Settings); + } + + public partial class ContactPersonDetailPayload + { + [JsonProperty("primary_key")] + public long PrimaryKey { get; set; } + + [JsonProperty("contact_pk")] + public long ContactPk { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("first_name")] + public string FirstName { get; set; } + + [JsonProperty("last_name")] + public string LastName { get; set; } + + [JsonProperty("nickname")] + public string Nickname { get; set; } + + [JsonProperty("position")] + public string Position { get; set; } + + [JsonProperty("date_of_birth")] + public string DateOfBirth { get; set; } + + [JsonProperty("department")] + public string Department { get; set; } + + [JsonProperty("salutation")] + public string Salutation { get; set; } + + [JsonProperty("grade")] + public string Grade { get; set; } + + [JsonProperty("has_image")] + public bool HasImage { get; set; } + + [JsonProperty("communication")] + public List Communication { get; set; } + + [JsonProperty("contact_chrono")] + public List ContactChrono { get; set; } + + [JsonProperty("searchindex")] + public List Searchindex { get; set; } + + [JsonProperty("date_changed")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? DateChanged { get; set; } + + [JsonProperty("time_changed")] + public long TimeChanged { get; set; } + + [JsonProperty("user_changed")] + public string UserChanged { get; set; } + + [JsonProperty("date_created")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? DateCreated { get; set; } + + [JsonProperty("time_created")] + public long TimeCreated { get; set; } + + [JsonProperty("user_created")] + public string UserCreated { get; set; } + + [JsonProperty("_communication_length")] + public long CommunicationLength { get; set; } + + [JsonProperty("_contact_chrono_length")] + public long ContactChronoLength { get; set; } + + [JsonIgnore] + public DateTimeOffset? CreatedAt => EpiDateHelper.CombineDateAndSeconds(DateCreated, TimeCreated); + + [JsonIgnore] + public DateTimeOffset? ChangedAt => EpiDateHelper.CombineDateAndSeconds(DateChanged, TimeChanged); + } + + public partial class ContactPersonDetailCommunication + { + [JsonProperty("primary_key")] + public long PrimaryKey { get; set; } + + [JsonProperty("contact_pk")] + public long ContactPk { get; set; } + + [JsonProperty("contact_person_pk")] + public long ContactPersonPk { get; set; } + + [JsonProperty("uplink")] + public string Uplink { get; set; } + + [JsonProperty("type")] + public long Type { get; set; } + + [JsonProperty("description")] + public string Description { get; set; } + + [JsonProperty("phone_match_number")] + public string PhoneMatchNumber { get; set; } + + [JsonProperty("position")] + public long Position { get; set; } + + [JsonProperty("is_newsletter")] + public bool IsNewsletter { get; set; } + + [JsonProperty("is_invoice")] + public bool IsInvoice { get; set; } + } + + public partial class ContactPersonDetailChrono + { + [JsonProperty("primary_key")] + public long PrimaryKey { get; set; } + + [JsonProperty("contact_pk")] + public long ContactPk { get; set; } + + [JsonProperty("contact_person_pk")] + public long ContactPersonPk { get; set; } + + [JsonProperty("keyword")] + public string Keyword { get; set; } + + [JsonProperty("contact_partner")] + public string ContactPartner { get; set; } + + [JsonProperty("date")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? Date { get; set; } + + [JsonProperty("time")] + public long Time { get; set; } + + [JsonProperty("duration")] + public long Duration { get; set; } + + [JsonProperty("com_type")] + public long ComType { get; set; } + + [JsonProperty("com_type_str")] + public string ComTypeStr { get; set; } + + [JsonProperty("uplink")] + public string Uplink { get; set; } + + [JsonProperty("_ref_contact_chrono")] + public string RefContactChrono { get; set; } + + [JsonProperty("date_changed")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? DateChanged { get; set; } + + [JsonProperty("time_changed")] + public long TimeChanged { get; set; } + + [JsonProperty("user_changed")] + public string UserChanged { get; set; } + + [JsonProperty("date_created")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? DateCreated { get; set; } + + [JsonProperty("time_created")] + public long TimeCreated { get; set; } + + [JsonProperty("user_created")] + public string UserCreated { get; set; } + + [JsonIgnore] + public DateTimeOffset? CreatedAt => EpiDateHelper.CombineDateAndSeconds(DateCreated, TimeCreated); + + [JsonIgnore] + public DateTimeOffset? ChangedAt => EpiDateHelper.CombineDateAndSeconds(DateChanged, TimeChanged); + } + + public static class ContactPersonDetailSerialize + { + public static string ToJson(this ContactPersonDetail self) + => JsonConvert.SerializeObject(self, EpiAutoReminder.Converter.Settings); + } +} \ No newline at end of file diff --git a/EpiAutoReminder/Model/OrderList.cs b/EpiAutoReminder/Model/OrderList.cs new file mode 100644 index 0000000..14fa3b1 --- /dev/null +++ b/EpiAutoReminder/Model/OrderList.cs @@ -0,0 +1,1377 @@ +// +// +// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do: +// +// using EpiAutoReminder; +// +// var orderList = OrderList.FromJson(jsonString); + +namespace EpiAutoReminder +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using Newtonsoft.Json; + using Newtonsoft.Json.Converters; + + public partial class OrderList + { + [JsonProperty("success")] + public bool Success { get; set; } + + [JsonProperty("namespace")] + public string Namespace { get; set; } + + [JsonProperty("action")] + public string Action { get; set; } + + [JsonProperty("templateSignature")] + public string TemplateSignature { get; set; } + + [JsonProperty("payload_length")] + public long PayloadLength { get; set; } + + [JsonProperty("is_send_payload")] + public bool IsSendPayload { get; set; } + + [JsonProperty("payload")] + public List Payload { get; set; } + + //[JsonProperty("filter_options")] + //public FilterOptions FilterOptions { get; set; } + + //[JsonProperty("request_duration")] + //public long RequestDuration { get; set; } + + //[JsonProperty("req_datetime")] + //public ReqDatetime ReqDatetime { get; set; } + + //[JsonProperty("_dbg_request_data")] + //public DbgRequestData DbgRequestData { get; set; } + + [JsonProperty("_dbg_user_info")] + public UserInfo DbgUserInfo { get; set; } + } + + public partial class DbgRequestData + { + [JsonProperty("req_start_ms")] + public long ReqStartMs { get; set; } + + [JsonProperty("pid")] + public long Pid { get; set; } + + [JsonProperty("max_records")] + public long MaxRecords { get; set; } + + [JsonProperty("is_exceeds_limit")] + public bool IsExceedsLimit { get; set; } + + [JsonProperty("http_mime_type")] + public string HttpMimeType { get; set; } + + [JsonProperty("is_redirected")] + public bool IsRedirected { get; set; } + + [JsonProperty("debuglog4d")] + public bool Debuglog4D { get; set; } + + [JsonProperty("headers")] + public Headers Headers { get; set; } + + [JsonProperty("headers_raw")] + public List HeadersRaw { get; set; } + + [JsonProperty("has_post_data")] + public bool HasPostData { get; set; } + + [JsonProperty("post_data_length")] + public long PostDataLength { get; set; } + + [JsonProperty("is_new_session")] + public bool IsNewSession { get; set; } + + [JsonProperty("is_demo_mode")] + public bool IsDemoMode { get; set; } + + [JsonProperty("is_epiNoSession_Accepted")] + public bool IsEpiNoSessionAccepted { get; set; } + + [JsonProperty("is_global_token")] + public bool IsGlobalToken { get; set; } + + [JsonProperty("user_info")] + public UserInfo UserInfo { get; set; } + + [JsonProperty("is_send_payload")] + public bool IsSendPayload { get; set; } + + [JsonProperty("is_make_paging")] + public bool IsMakePaging { get; set; } + + [JsonProperty("is_page_requested")] + public bool IsPageRequested { get; set; } + + [JsonProperty("is_debug")] + public bool IsDebug { get; set; } + + [JsonProperty("session_id")] + public string SessionId { get; set; } + + [JsonProperty("url_path")] + public string UrlPath { get; set; } + + [JsonProperty("ip_client")] + public string IpClient { get; set; } + + [JsonProperty("ip_server")] + public string IpServer { get; set; } + + [JsonProperty("endpoint")] + public string Endpoint { get; set; } + + [JsonProperty("options_str")] + public string OptionsStr { get; set; } + + [JsonProperty("has_options")] + public bool HasOptions { get; set; } + + [JsonProperty("is_versionized_endpoint")] + public bool IsVersionizedEndpoint { get; set; } + + [JsonProperty("path_parsed")] + public PathParsed PathParsed { get; set; } + + [JsonProperty("path_parsed_length")] + public long PathParsedLength { get; set; } + + [JsonProperty("urlopt_basekey")] + public string UrloptBasekey { get; set; } + + [JsonProperty("url_options")] + public List UrlOptions { get; set; } + + [JsonProperty("url_options_dict")] + public UrlOptionsDict UrlOptionsDict { get; set; } + + [JsonProperty("options_parsed_length")] + public long OptionsParsedLength { get; set; } + + [JsonProperty("options_ignored_length")] + public long OptionsIgnoredLength { get; set; } + + [JsonProperty("crud")] + public Crud Crud { get; set; } + + [JsonProperty("is_epiNoSession")] + public bool IsEpiNoSession { get; set; } + + [JsonProperty("req_end_ms")] + public long ReqEndMs { get; set; } + + [JsonProperty("req_duration")] + public long ReqDuration { get; set; } + } + + public partial class Crud + { + [JsonProperty("http_method")] + public string HttpMethod { get; set; } + + [JsonProperty("is_create")] + public bool IsCreate { get; set; } + + [JsonProperty("is_read")] + public bool IsRead { get; set; } + + [JsonProperty("is_update")] + public bool IsUpdate { get; set; } + + [JsonProperty("is_delete")] + public bool IsDelete { get; set; } + + [JsonProperty("is_preflight")] + public bool IsPreflight { get; set; } + + [JsonProperty("is_options")] + public bool IsOptions { get; set; } + + [JsonProperty("is_writeable")] + public bool IsWriteable { get; set; } + } + + public partial class Headers + { + [JsonProperty("x-method")] + public string XMethod { get; set; } + + [JsonProperty("x-url")] + public string XUrl { get; set; } + + [JsonProperty("x-version")] + public string XVersion { get; set; } + + [JsonProperty("accept")] + public string Accept { get; set; } + + [JsonProperty("accept-encoding")] + public string AcceptEncoding { get; set; } + + [JsonProperty("authorization")] + public string Authorization { get; set; } + + [JsonProperty("cache-control")] + public string CacheControl { get; set; } + + [JsonProperty("connection")] + public string Connection { get; set; } + + [JsonProperty("host")] + public string Host { get; set; } + + [JsonProperty("postman-token")] + public Guid PostmanToken { get; set; } + + [JsonProperty("user-agent")] + public string UserAgent { get; set; } + + [JsonProperty("x-epi-acc-tok")] + public string XEpiAccTok { get; set; } + + [JsonProperty("x-epi-debug-info")] + [JsonConverter(typeof(PurpleParseStringConverter))] + public bool XEpiDebugInfo { get; set; } + + [JsonProperty("x-epi-no-session")] + [JsonConverter(typeof(PurpleParseStringConverter))] + public bool XEpiNoSession { get; set; } + } + + public partial class HeadersRaw + { + [JsonProperty("key")] + public string Key { get; set; } + + [JsonProperty("value")] + public string Value { get; set; } + } + + public partial class PathParsed + { + [JsonProperty("patharray")] + public List Patharray { get; set; } + + [JsonProperty("api_version")] + public string ApiVersion { get; set; } + + [JsonProperty("namespace")] + public string Namespace { get; set; } + + [JsonProperty("action")] + public string Action { get; set; } + } + + public partial class ClientId + { + [JsonProperty("key")] + public string Key { get; set; } + + [JsonProperty("value")] + public Value Value { get; set; } + + [JsonProperty("operator")] + public string Operator { get; set; } + + [JsonProperty("value2", NullValueHandling = NullValueHandling.Ignore)] + public Value? Value2 { get; set; } + } + + public partial class UrlOptionsDict + { + [JsonProperty("cl")] + public ClientId Cl { get; set; } + + [JsonProperty("ir")] + public ClientId Ir { get; set; } + + [JsonProperty("ia")] + public ClientId Ia { get; set; } + + [JsonProperty("icl")] + public ClientId Icl { get; set; } + + [JsonProperty("ico")] + public ClientId Ico { get; set; } + + [JsonProperty("icv")] + public ClientId Icv { get; set; } + } + + public partial class UserInfo + { + [JsonProperty("is_client_id_changable")] + public bool IsClientIdChangable { get; set; } + + [JsonProperty("is_logout")] + public bool IsLogout { get; set; } + + [JsonProperty("epiNoSession")] + public bool EpiNoSession { get; set; } + + [JsonProperty("selected_mandant_id")] + public long SelectedMandantId { get; set; } + + [JsonProperty("selected_mandant_name")] + public string SelectedMandantName { get; set; } + + [JsonProperty("prefered_lang_id")] + public long PreferedLangId { get; set; } + + [JsonProperty("prefered_lang_str")] + public string PreferedLangStr { get; set; } + + [JsonProperty("lang_id")] + public long LangId { get; set; } + } + + public partial class FilterOptions + { + [JsonProperty("__error__")] + public List Error { get; set; } + + [JsonProperty("__apply_filter__")] + public bool ApplyFilter { get; set; } + + [JsonProperty("__debug__")] + public List Debug { get; set; } + + [JsonProperty("client_id")] + public long FilterOptionsClientId { get; set; } + + [JsonProperty("__client_id__")] + public ClientId ClientId { get; set; } + + [JsonProperty("is_rent")] + public bool FilterOptionsIsRent { get; set; } + + [JsonProperty("__is_rent__")] + public ClientId IsRent { get; set; } + + [JsonProperty("is_archived")] + public bool FilterOptionsIsArchived { get; set; } + + [JsonProperty("__is_archived__")] + public ClientId IsArchived { get; set; } + + [JsonProperty("is_canceled")] + public bool FilterOptionsIsCanceled { get; set; } + + [JsonProperty("__is_canceled__")] + public ClientId IsCanceled { get; set; } + + [JsonProperty("is_confirmed")] + public bool FilterOptionsIsConfirmed { get; set; } + + [JsonProperty("__is_confirmed__")] + public ClientId IsConfirmed { get; set; } + + [JsonProperty("is_current_version")] + public bool FilterOptionsIsCurrentVersion { get; set; } + + [JsonProperty("__is_current_version__")] + public ClientId IsCurrentVersion { get; set; } + + [JsonProperty("__is_versionized__")] + public bool IsVersionized { get; set; } + } + + public partial class Payload + { + [JsonProperty("primary_key")] + public long PrimaryKey { get; set; } + + [JsonProperty("client_id")] + public long ClientId { get; set; } + + [JsonProperty("order_no")] + public long OrderNo { get; set; } + + [JsonProperty("order_no_fmt")] + public string OrderNoFmt { get; set; } + + [JsonProperty("order_date")] + [JsonConverter(typeof(SafeDateTimeConverter))] + public DateTime? OrderDate { get; set; } + + [JsonProperty("date_delivery")] + [JsonConverter(typeof(SafeDateTimeConverter))] + public DateTime? DateDelivery { get; set; } + + [JsonProperty("date_shipping")] + [JsonConverter(typeof(SafeDateTimeConverter))] + public DateTime? DateShipping { get; set; } + + [JsonProperty("_vipgroup")] + public long Vipgroup { get; set; } + + [JsonProperty("editor_id")] + public long EditorId { get; set; } + + [JsonProperty("editor_name")] + public string EditorName { get; set; } + + [JsonProperty("editor_short")] + public string EditorShort { get; set; } + + [JsonProperty("file_archive_id")] + public long FileArchiveId { get; set; } + + [JsonProperty("customer_no")] + public long CustomerNo { get; set; } + + [JsonProperty("customer_cp_pk")] + public long CustomerCpPk { get; set; } + + [JsonProperty("is_teamagenda_notvisible")] + public bool IsTeamagendaNotvisible { get; set; } + + [JsonProperty("teamagenda_category")] + public string TeamagendaCategory { get; set; } + + [JsonProperty("event")] + public string Event { get; set; } + + [JsonProperty("status")] + public string Status { get; set; } + + [JsonProperty("is_current_version")] + public bool IsCurrentVersion { get; set; } + + [JsonProperty("is_confirmed")] + public bool IsConfirmed { get; set; } + + [JsonProperty("is_canceled")] + public bool IsCanceled { get; set; } + + [JsonProperty("is_archived")] + public bool IsArchived { get; set; } + + [JsonProperty("all_inclusive")] + public bool AllInclusive { get; set; } + + [JsonProperty("is_bail_all_inclusive")] + public bool IsBailAllInclusive { get; set; } + + [JsonProperty("is_mrp")] + public bool IsMrp { get; set; } + + [JsonProperty("is_sale")] + public bool IsSale { get; set; } + + [JsonProperty("rentshop_state")] + public long RentshopState { get; set; } + + [JsonProperty("rentshop_order_uri")] + public string RentshopOrderUri { get; set; } + + [JsonProperty("rentshop_contact_uri")] + public string RentshopContactUri { get; set; } + + [JsonProperty("is_contact_connected")] + public bool IsContactConnected { get; set; } + + [JsonProperty("rentshop_person_uri")] + public string RentshopPersonUri { get; set; } + + [JsonProperty("is_person_connected")] + public bool IsPersonConnected { get; set; } + + [JsonProperty("is_vat")] + public bool IsVat { get; set; } + + [JsonProperty("vat1")] + public long Vat1 { get; set; } + + [JsonProperty("vat2")] + public long Vat2 { get; set; } + + [JsonProperty("vat3")] + public Vat3 Vat3 { get; set; } + + [JsonProperty("sum_net")] + public double SumNet { get; set; } + + [JsonProperty("sum_gro")] + public double SumGro { get; set; } + + [JsonProperty("sum_salestax1")] + public double SumSalestax1 { get; set; } + + [JsonProperty("sum_salestax2")] + public long SumSalestax2 { get; set; } + + [JsonProperty("sum_discount")] + public double SumDiscount { get; set; } + + [JsonProperty("sum_rent")] + public double SumRent { get; set; } + + [JsonProperty("sum_sales")] + public double SumSales { get; set; } + + [JsonProperty("sum_transport")] + public double SumTransport { get; set; } + + [JsonProperty("sum_service")] + public double SumService { get; set; } + + [JsonProperty("sum_insurance")] + public long SumInsurance { get; set; } + + [JsonProperty("sum_special")] + public long SumSpecial { get; set; } + + [JsonProperty("sum_bail")] + public long SumBail { get; set; } + + [JsonProperty("sum_net_no_discount")] + public double SumNetNoDiscount { get; set; } + + [JsonProperty("sum_gro_no_discount")] + public double SumGroNoDiscount { get; set; } + + [JsonProperty("sum_rent_no_discount")] + public double SumRentNoDiscount { get; set; } + + [JsonProperty("sum_sales_no_discount")] + public double SumSalesNoDiscount { get; set; } + + [JsonProperty("sum_transport_no_discount")] + public double SumTransportNoDiscount { get; set; } + + [JsonProperty("sum_service_no_discount")] + public double SumServiceNoDiscount { get; set; } + + [JsonProperty("sum_special_no_discount")] + public long SumSpecialNoDiscount { get; set; } + + [JsonProperty("discount_rate_rent")] + public long DiscountRateRent { get; set; } + + [JsonProperty("discount_rate_sales")] + public long DiscountRateSales { get; set; } + + [JsonProperty("revenue_account")] + public string RevenueAccount { get; set; } + + [JsonProperty("cost_centre")] + public string CostCentre { get; set; } + + [JsonProperty("amount_uncleared")] + public long AmountUncleared { get; set; } + + [JsonProperty("amount_cleared")] + public long AmountCleared { get; set; } + + [JsonProperty("amount_open_checkin")] + public long AmountOpenCheckin { get; set; } + + [JsonProperty("amount_checkin")] + public long AmountCheckin { get; set; } + + [JsonProperty("amount_open_checkout")] + public long AmountOpenCheckout { get; set; } + + [JsonProperty("amount_checkout")] + public long AmountCheckout { get; set; } + + [JsonProperty("amount_open_packaging")] + public long AmountOpenPackaging { get; set; } + + [JsonProperty("amount_packaging")] + public long AmountPackaging { get; set; } + + [JsonProperty("amount_open_booked")] + public long AmountOpenBooked { get; set; } + + [JsonProperty("amount_booked")] + public long AmountBooked { get; set; } + + [JsonProperty("amount_open_packagingnote")] + public long AmountOpenPackagingnote { get; set; } + + [JsonProperty("amount_packagingnote")] + public long AmountPackagingnote { get; set; } + + [JsonProperty("date_changed")] + [JsonConverter(typeof(SafeDateTimeConverter))] + public DateTime? DateChanged { get; set; } + + [JsonProperty("time_changed")] + public long TimeChanged { get; set; } + + [JsonProperty("user_changed")] + public string UserChanged { get; set; } + + [JsonProperty("user_changed_short")] + public string UserChangedShort { get; set; } + + [JsonProperty("date_created")] + [JsonConverter(typeof(SafeDateTimeConverter))] + public DateTime? DateCreated { get; set; } + + [JsonProperty("time_created")] + public long TimeCreated { get; set; } + + [JsonProperty("user_created")] + public string UserCreated { get; set; } + + [JsonProperty("user_created_short")] + public string UserCreatedShort { get; set; } + + [JsonProperty("is_self_pickup")] + public bool IsSelfPickup { get; set; } + + [JsonProperty("is_self_redeliver")] + public bool IsSelfRedeliver { get; set; } + + [JsonProperty("is_rent")] + public bool IsRent { get; set; } + + [JsonProperty("order_state")] + public List OrderState { get; set; } + + [JsonProperty("order_schedule")] + public List OrderSchedule { get; set; } + + [JsonProperty("_ref_order")] + public string RefOrder { get; set; } + + [JsonProperty("contact")] + public Contact Contact { get; set; } + + [JsonProperty("contact_person")] + public ContactPerson ContactPerson { get; set; } + + [JsonProperty("_order_schedule_length")] + public long OrderScheduleLength { get; set; } + + [JsonProperty("state_billing")] + public string StateBilling { get; set; } + + [JsonProperty("state_checkin")] + public string StateCheckin { get; set; } + + [JsonProperty("state_checkout")] + public string StateCheckout { get; set; } + + [JsonProperty("state_booked")] + public string StateBooked { get; set; } + + [JsonProperty("state_packagingnote")] + public string StatePackagingnote { get; set; } + + [JsonProperty("state_packaging")] + public string StatePackaging { get; set; } + + [JsonProperty("_order_state_length", NullValueHandling = NullValueHandling.Ignore)] + public long? OrderStateLength { get; set; } + + [JsonIgnore] + public DateTime? CreatedAt => EpiDateHelper.CombineDateAndSeconds(DateCreated, TimeCreated); + + [JsonIgnore] + public DateTime? ChangedAt => EpiDateHelper.CombineDateAndSeconds(DateChanged, TimeChanged); + } + + public partial class Contact + { + [JsonProperty("primary_key")] + public long PrimaryKey { get; set; } + + [JsonProperty("customer_no")] + public long CustomerNo { get; set; } + + [JsonProperty("supplier_no")] + public long SupplierNo { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("name1")] + public string Name1 { get; set; } + + [JsonProperty("name2")] + public string Name2 { get; set; } + + [JsonProperty("name3")] + public string Name3 { get; set; } + + [JsonProperty("matchcode")] + public string Matchcode { get; set; } + + [JsonProperty("salutation")] + public string Salutation { get; set; } + + [JsonProperty("grade")] + public string Grade { get; set; } + + [JsonProperty("is_interested")] + public bool IsInterested { get; set; } + + [JsonProperty("is_customer")] + public bool IsCustomer { get; set; } + + [JsonProperty("is_supplier")] + public bool IsSupplier { get; set; } + + [JsonProperty("is_location")] + public bool IsLocation { get; set; } + + [JsonProperty("is_vip")] + public bool IsVip { get; set; } + + [JsonProperty("is_colleague")] + public bool IsColleague { get; set; } + + [JsonProperty("date_changed")] + [JsonConverter(typeof(SafeDateTimeConverter))] + public DateTime? DateChanged { get; set; } + + [JsonProperty("time_changed")] + public long TimeChanged { get; set; } + + [JsonProperty("date_created")] + [JsonConverter(typeof(SafeDateTimeConverter))] + public DateTime? DateCreated { get; set; } + + [JsonProperty("time_created")] + public long TimeCreated { get; set; } + + [JsonProperty("is_active")] + public bool IsActive { get; set; } + + [JsonProperty("terms_of_payment_pk")] + public long TermsOfPaymentPk { get; set; } + + [JsonProperty("is_sysstd_terms_of_payment")] + public bool IsSysstdTermsOfPayment { get; set; } + + [JsonProperty("vat_id")] + public string VatId { get; set; } + + [JsonProperty("eori_no")] + public string EoriNo { get; set; } + + [JsonProperty("tax_no")] + public string TaxNo { get; set; } + + [JsonProperty("tax_office")] + public string TaxOffice { get; set; } + + [JsonProperty("commercial_register_no")] + public string CommercialRegisterNo { get; set; } + + [JsonProperty("commercial_register_office")] + public string CommercialRegisterOffice { get; set; } + + [JsonProperty("has_image")] + public bool HasImage { get; set; } + + [JsonProperty("_ref_contact")] + public string RefContact { get; set; } + + [JsonProperty("contact_data")] + public ContactData ContactData { get; set; } + + [JsonProperty("_contact_data_length")] + public long ContactDataLength { get; set; } + + [JsonIgnore] + public DateTime? CreatedAt => EpiDateHelper.CombineDateAndSeconds(DateCreated, TimeCreated); + + [JsonIgnore] + public DateTime? ChangedAt => EpiDateHelper.CombineDateAndSeconds(DateChanged, TimeChanged); + } + + public partial class ContactData + { + [JsonProperty("primary_key")] + public long PrimaryKey { get; set; } + + [JsonProperty("contact_pk")] + public long ContactPk { get; set; } + + [JsonProperty("supplier_no")] + public long SupplierNo { get; set; } + + [JsonProperty("customer_no")] + public long CustomerNo { get; set; } + + [JsonProperty("debitor_no")] + public string DebitorNo { get; set; } + + [JsonProperty("creditor_no")] + public string CreditorNo { get; set; } + + [JsonProperty("rentshop_contact_uri")] + public string RentshopContactUri { get; set; } + + [JsonProperty("is_rentshop_active")] + public bool IsRentshopActive { get; set; } + + [JsonProperty("discount_rent_customer")] + public long DiscountRentCustomer { get; set; } + + [JsonProperty("discount_sale_customer")] + public long DiscountSaleCustomer { get; set; } + + [JsonProperty("is_vat_active_customer")] + public bool IsVatActiveCustomer { get; set; } + + [JsonProperty("country_type_customer")] + public long CountryTypeCustomer { get; set; } + + [JsonProperty("discount_rent_supplier")] + public long DiscountRentSupplier { get; set; } + + [JsonProperty("discount_sale_supplier")] + public long DiscountSaleSupplier { get; set; } + + [JsonProperty("is_vat_active_supplier")] + public bool IsVatActiveSupplier { get; set; } + + [JsonProperty("country_type_supplier")] + public long CountryTypeSupplier { get; set; } + } + + public partial class ContactPerson + { + [JsonProperty("primary_key")] + public long PrimaryKey { get; set; } + + [JsonProperty("contact_pk")] + public long ContactPk { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("first_name")] + public string FirstName { get; set; } + + [JsonProperty("last_name")] + public string LastName { get; set; } + + [JsonProperty("nickname")] + public string Nickname { get; set; } + + [JsonProperty("position")] + public string Position { get; set; } + + [JsonProperty("has_image")] + public bool HasImage { get; set; } + + [JsonProperty("_ref_contact")] + public string RefContact { get; set; } + + [JsonProperty("_ref_contact_person")] + public string RefContactPerson { get; set; } + + [JsonProperty("date_changed")] + [JsonConverter(typeof(SafeDateTimeConverter))] + public DateTime? DateChanged { get; set; } + + [JsonProperty("time_changed")] + public long TimeChanged { get; set; } + + [JsonProperty("user_changed")] + public string UserChanged { get; set; } + + [JsonProperty("date_created")] + [JsonConverter(typeof(SafeDateTimeConverter))] + public DateTime? DateCreated { get; set; } + + [JsonProperty("time_created")] + public long TimeCreated { get; set; } + + [JsonProperty("user_created")] + public string UserCreated { get; set; } + + [JsonIgnore] + public DateTime? CreatedAt => EpiDateHelper.CombineDateAndSeconds(DateCreated, TimeCreated); + + [JsonIgnore] + public DateTime? ChangedAt => EpiDateHelper.CombineDateAndSeconds(DateChanged, TimeChanged); + } + + public partial class OrderSchedule + { + [JsonProperty("primary_key")] + public long PrimaryKey { get; set; } + + [JsonProperty("type")] + public long Type { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("date_start")] + [JsonConverter(typeof(SafeDateTimeConverter))] + public DateTime? DateStart { get; set; } + + [JsonProperty("date_end")] + [JsonConverter(typeof(SafeDateTimeConverter))] + public DateTime? DateEnd { get; set; } + + [JsonProperty("time_start")] + public long TimeStart { get; set; } + + [JsonProperty("time_end")] + public long TimeEnd { get; set; } + + [JsonProperty("is_start_date")] + public bool IsStartDate { get; set; } + + [JsonProperty("is_end_date")] + public bool IsEndDate { get; set; } + + [JsonIgnore] + public DateTime? StartAt => EpiDateHelper.CombineDateAndSeconds(DateStart, TimeStart); + + [JsonIgnore] + public DateTime? EndAt => EpiDateHelper.CombineDateAndSeconds(DateEnd, TimeEnd); + } + + public partial class OrderState + { + [JsonProperty("primary_key")] + public long PrimaryKey { get; set; } + + [JsonProperty("order_pk")] + public long OrderPk { get; set; } + + [JsonProperty("order_no")] + public long OrderNo { get; set; } + + [JsonProperty("down_payment_count")] + public long DownPaymentCount { get; set; } + + [JsonProperty("is_invoice")] + public bool IsInvoice { get; set; } + + [JsonProperty("invoice_pk")] + public long InvoicePk { get; set; } + + [JsonProperty("invoice_no")] + public long InvoiceNo { get; set; } + + [JsonProperty("invoice_date")] + [JsonConverter(typeof(SafeDateTimeConverter))] + public DateTime? InvoiceDate { get; set; } + + [JsonProperty("packingnote_count")] + public long PackingnoteCount { get; set; } + + [JsonProperty("is_finished")] + public bool IsFinished { get; set; } + + [JsonProperty("epits_finished")] + public long EpitsFinished { get; set; } + + [JsonProperty("user_finished")] + public string UserFinished { get; set; } + + [JsonProperty("date_finished")] + [JsonConverter(typeof(SafeDateTimeConverter))] + public DateTime? DateFinished { get; set; } + + [JsonProperty("time_finished")] + [JsonConverter(typeof(NullableLongConverter))] + public long? TimeFinished { get; set; } + + [JsonIgnore] + public DateTime? FinishedAt => EpiDateHelper.CombineDateAndSeconds(DateFinished, TimeFinished ?? 0); + } + + public partial class Vat3 + { + [JsonProperty("VAT")] + public List Vat { get; set; } + } + + public partial class ReqDatetime + { + [JsonProperty("date")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? Date { get; set; } + + [JsonProperty("time")] + public long Time { get; set; } + + [JsonProperty("timestamp")] + public long Timestamp { get; set; } + + [JsonProperty("isodate")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? Isodate { get; set; } + + [JsonProperty("isodate_gmt")] + [JsonConverter(typeof(SafeDateTimeOffsetConverter))] + public DateTimeOffset? IsodateGmt { get; set; } + + [JsonProperty("formatedDateTime")] + public string FormatedDateTime { get; set; } + + [JsonProperty("yyyymmdd")] + [JsonConverter(typeof(FluffyParseStringConverter))] + public long Yyyymmdd { get; set; } + + [JsonProperty("has_date")] + public bool HasDate { get; set; } + + [JsonProperty("has_time")] + public bool HasTime { get; set; } + + [JsonIgnore] + public DateTimeOffset? CombinedAt => EpiDateHelper.CombineDateAndSeconds(Date, Time); + } + + + + public partial struct Value + { + public bool? Bool; + public long? Integer; + + public static implicit operator Value(bool Bool) => new Value { Bool = Bool }; + public static implicit operator Value(long Integer) => new Value { Integer = Integer }; + } + + public partial class OrderList + { + public static OrderList FromJson(string json) => JsonConvert.DeserializeObject(json, EpiAutoReminder.Converter.Settings); + } + + public static class Serialize + { + public static string ToJson(this OrderList self) => JsonConvert.SerializeObject(self, EpiAutoReminder.Converter.Settings); + } + + internal static class EpiDateHelper + { + public static DateTime? CombineDateAndSeconds(DateTime? date, long seconds) + { + if (!date.HasValue) + return null; + + if (seconds < 0) + seconds = 0; + + return date.Value.Date.AddSeconds(seconds); + } + + public static DateTimeOffset? CombineDateAndSeconds(DateTimeOffset? date, long seconds) + { + if (!date.HasValue) + return null; + + if (seconds < 0) + seconds = 0; + + return new DateTimeOffset(date.Value.Date.AddSeconds(seconds), date.Value.Offset); + } + } + + internal static class Converter + { + public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings + { + MetadataPropertyHandling = MetadataPropertyHandling.Ignore, + DateParseHandling = DateParseHandling.None, + NullValueHandling = NullValueHandling.Ignore, + Converters = + { + + PurpleValueConverter.Singleton, + FluffyValueConverter.Singleton, + + new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal } + }, + }; + } + + internal class SafeDateTimeConverter : JsonConverter + { + private static readonly string[] Formats = + { + "yyyy-MM-dd", + "yyyy-MM-dd HH:mm:ss", + "yyyy-MM-ddTHH:mm:ss", + "yyyy-MM-ddTHH:mm:ss.fff", + "yyyy-MM-ddTHH:mm:ssZ", + "yyyy-MM-ddTHH:mm:ssK", + "yyyy-MM-ddTHH:mm:ss.fffK" + }; + + public override DateTime? ReadJson(JsonReader reader, Type objectType, DateTime? existingValue, bool hasExistingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + return null; + + var value = reader.Value?.ToString(); + + if (string.IsNullOrWhiteSpace(value) || + value == "0000-00-00" || + value == "0000-00-00 00:00:00") + { + return null; + } + + if (DateTime.TryParseExact(value, Formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var exact)) + return exact; + + if (DateTime.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var parsed)) + return parsed; + + return null; + } + + public override void WriteJson(JsonWriter writer, DateTime? value, JsonSerializer serializer) + { + if (!value.HasValue) + { + writer.WriteNull(); + return; + } + + writer.WriteValue(value.Value.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)); + } + } + + internal class SafeDateTimeOffsetConverter : JsonConverter + { + private static readonly string[] Formats = + { + "yyyy-MM-dd", + "yyyy-MM-dd HH:mm:ss", + "yyyy-MM-ddTHH:mm:ss", + "yyyy-MM-ddTHH:mm:ss.fff", + "yyyy-MM-ddTHH:mm:ssZ", + "yyyy-MM-ddTHH:mm:ssK", + "yyyy-MM-ddTHH:mm:ss.fffK" + }; + + public override DateTimeOffset? ReadJson(JsonReader reader, Type objectType, DateTimeOffset? existingValue, bool hasExistingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + return null; + + var value = reader.Value?.ToString(); + + if (string.IsNullOrWhiteSpace(value) || + value == "0000-00-00" || + value == "0000-00-00 00:00:00") + { + return null; + } + + if (DateTimeOffset.TryParseExact(value, Formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var exact)) + return exact; + + if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var parsed)) + return parsed; + + return null; + } + + public override void WriteJson(JsonWriter writer, DateTimeOffset? value, JsonSerializer serializer) + { + if (!value.HasValue) + { + writer.WriteNull(); + return; + } + + writer.WriteValue(value.Value.ToString("o", CultureInfo.InvariantCulture)); + } + } + + internal class NullableLongConverter : JsonConverter + { + public override long? ReadJson(JsonReader reader, Type objectType, long? existingValue, bool hasExistingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + return null; + + var value = reader.Value?.ToString(); + + if (string.IsNullOrWhiteSpace(value)) + return null; + + if (long.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) + return result; + + return null; + } + + public override void WriteJson(JsonWriter writer, long? value, JsonSerializer serializer) + { + if (!value.HasValue) + { + writer.WriteNull(); + return; + } + + writer.WriteValue(value.Value); + } + } + + internal class PurpleParseStringConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(bool) || t == typeof(bool?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + bool b; + if (Boolean.TryParse(value, out b)) + { + return b; + } + throw new Exception("Cannot unmarshal type bool"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (bool)untypedValue; + var boolString = value ? "true" : "false"; + serializer.Serialize(writer, boolString); + } + + public static readonly PurpleParseStringConverter Singleton = new PurpleParseStringConverter(); + } + + + + internal class PurpleValueConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(Value) || t == typeof(Value?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + switch (reader.TokenType) + { + case JsonToken.String: + case JsonToken.Date: + var stringValue = serializer.Deserialize(reader); + bool b; + if (Boolean.TryParse(stringValue, out b)) + { + return new Value { Bool = b }; + } + long l; + if (Int64.TryParse(stringValue, out l)) + { + return new Value { Integer = l }; + } + break; + } + throw new Exception("Cannot unmarshal type Value"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + var value = (Value)untypedValue; + if (value.Bool != null) + { + var boolString = value.Bool.Value ? "true" : "false"; + serializer.Serialize(writer, boolString); + return; + } + if (value.Integer != null) + { + serializer.Serialize(writer, value.Integer.Value.ToString()); + return; + } + throw new Exception("Cannot marshal type Value"); + } + + public static readonly PurpleValueConverter Singleton = new PurpleValueConverter(); + } + + internal class FluffyValueConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(Value) || t == typeof(Value?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + switch (reader.TokenType) + { + case JsonToken.Integer: + var integerValue = serializer.Deserialize(reader); + return new Value { Integer = integerValue }; + case JsonToken.Boolean: + var boolValue = serializer.Deserialize(reader); + return new Value { Bool = boolValue }; + } + throw new Exception("Cannot unmarshal type Value"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + var value = (Value)untypedValue; + if (value.Integer != null) + { + serializer.Serialize(writer, value.Integer.Value); + return; + } + if (value.Bool != null) + { + serializer.Serialize(writer, value.Bool.Value); + return; + } + throw new Exception("Cannot marshal type Value"); + } + + public static readonly FluffyValueConverter Singleton = new FluffyValueConverter(); + } + + + + + internal class FluffyParseStringConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(long) || t == typeof(long?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + long l; + if (Int64.TryParse(value, out l)) + { + return l; + } + throw new Exception("Cannot unmarshal type long"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (long)untypedValue; + serializer.Serialize(writer, value.ToString()); + } + + public static readonly FluffyParseStringConverter Singleton = new FluffyParseStringConverter(); + } +} \ No newline at end of file diff --git a/EpiAutoReminder/O365Connector.cs b/EpiAutoReminder/O365Connector.cs new file mode 100644 index 0000000..1bdd48c --- /dev/null +++ b/EpiAutoReminder/O365Connector.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using log4net; +using Microsoft.Graph; +using Microsoft.Graph.Models; +using Microsoft.Identity.Client; +using Microsoft.Kiota.Abstractions.Authentication; + +namespace EpiAutoReminder +{ + internal class O365Connector + { + private static readonly ILog Logger = LogManager.GetLogger(typeof(O365Connector)); + + private readonly string clientID; + private readonly string clientSecret; + private readonly string tenantID; + private readonly string baseUrl; + + public O365Connector(string clientID, string clientSecret, string tenantID, string baseUrl) + { + this.clientID = clientID; + this.clientSecret = clientSecret; + this.tenantID = tenantID; + this.baseUrl = baseUrl; + } + + public async Task CreateDraftMailAsync( + string to, + string sender, + string subject, + string body, + string cc = null, + string bcc = null) + { + try + { + Logger.Info("CreateDraftMailAsync gestartet."); + + if (string.IsNullOrWhiteSpace(to)) + throw new ArgumentException("TO darf nicht leer sein.", nameof(to)); + + if (string.IsNullOrWhiteSpace(sender)) + throw new ArgumentException("Sender darf nicht leer sein.", nameof(sender)); + + var graphClient = await GetGraphClient(); + + var message = new Message + { + Subject = subject ?? string.Empty, + Body = new ItemBody + { + ContentType = BodyType.Text, + Content = body ?? string.Empty + }, + ToRecipients = new List + { + new Recipient + { + EmailAddress = new EmailAddress + { + Address = to.Trim() + } + } + }, + CcRecipients = string.IsNullOrWhiteSpace(cc) + ? new List() + : new List + { + new Recipient + { + EmailAddress = new EmailAddress + { + Address = cc.Trim() + } + } + }, + BccRecipients = string.IsNullOrWhiteSpace(bcc) + ? new List() + : new List + { + new Recipient + { + EmailAddress = new EmailAddress + { + Address = bcc.Trim() + } + } + } + }; + + Logger.Info($"Erstelle Draft für Sender '{sender}' an '{to}'."); + + var createdDraft = await graphClient + .Users[sender] + .Messages + .PostAsync(message); + + if (createdDraft == null || string.IsNullOrWhiteSpace(createdDraft.Id)) + throw new Exception("Entwurf konnte nicht erstellt werden."); + + Logger.Info($"Draft mail created for '{sender}' with id '{createdDraft.Id}'."); + + return createdDraft.Id; + } + catch (Exception ex) + { + Logger.Error("Fehler beim Draft erstellen", ex); + throw; + } + } + private async Task GetGraphClient() + { + try + { + Logger.Info("Initialisiere GraphServiceClient..."); + + var clientApp = ConfidentialClientApplicationBuilder.Create(clientID) + .WithClientSecret(clientSecret) + .WithAuthority(new Uri($"{baseUrl}/{tenantID}")) + .Build(); + + var accessTokenProvider = new TokenAcquisitionProvider(clientApp); + var authProvider = new BaseBearerTokenAuthenticationProvider(accessTokenProvider); + + Logger.Info("GraphServiceClient erfolgreich initialisiert."); + + return new GraphServiceClient(authProvider); + } + catch (Exception ex) + { + Logger.Error("Error initializing GraphServiceClient: " + ex.Message, ex); + throw; + } + } + } + + public class TokenAcquisitionProvider : IAccessTokenProvider + { + private static readonly ILog Logger = LogManager.GetLogger(typeof(TokenAcquisitionProvider)); + private readonly IConfidentialClientApplication _clientApp; + + public TokenAcquisitionProvider(IConfidentialClientApplication clientApp) + { + _clientApp = clientApp; + } + + public async Task GetAuthorizationTokenAsync( + Uri uri, + Dictionary? additionalAuthenticationContext = null, + CancellationToken cancellationToken = default) + { + try + { + Logger.Info("Fordere Access Token für Microsoft Graph an..."); + + var result = await _clientApp + .AcquireTokenForClient(new[] { "https://graph.microsoft.com/.default" }) + .ExecuteAsync(cancellationToken); + + Logger.Info("Access Token erfolgreich geholt."); + + return result.AccessToken; + } + catch (Exception ex) + { + Logger.Error("Error acquiring token: " + ex.Message, ex); + throw; + } + } + + public AllowedHostsValidator AllowedHostsValidator { get; } = new AllowedHostsValidator(); + } +} \ No newline at end of file diff --git a/EpiAutoReminder/Program.cs b/EpiAutoReminder/Program.cs new file mode 100644 index 0000000..48be093 --- /dev/null +++ b/EpiAutoReminder/Program.cs @@ -0,0 +1,183 @@ +using Microsoft.Graph.Models; +using Microsoft.Graph.Models.IdentityGovernance; +using System.ClientModel.Primitives; +using System.Reflection; + +namespace EpiAutoReminder +{ + internal class Program + { + private static ConfigLoader Configuration; + private static OrderList OrderList; + private static O365Connector O365; + + + static void Main(string[] args) + { + try + { + Logger.Info("Initializing configuration."); + Configuration = ConfigLoader.Instance; + Logger.Info("Configuration loaded successfully."); + } + catch (Exception ex) + { + Logger.Error("Error loading configuration: " + ex.Message); + return; + } + + try + { + Logger.Info("Initializing O365 connector."); + O365 = new O365Connector(Configuration.AzureAd.ClientId, Configuration.AzureAd.ClientSecret, Configuration.AzureAd.TenantId, Configuration.Graph.BaseUrl); + Logger.Info("O365 connector initialized."); + } + catch (Exception ex) + { + Logger.Error("Error initializing O365 connector: " + ex.Message); + return; + } + + try + { + Logger.Info("Fetching Epirent Orders."); + OrderList = new EpiOrders().getOrderList(); + } + catch (Exception ex) + { + Logger.Error("Error fetching contacts: " + ex.Message); + return; + } + + + //Prozessroutine + + foreach (Payload Order in OrderList.Payload) + { + + try + { + var dispo = Order.OrderSchedule.Find(x => x.Name == "Dispo"); + var offerdate = Order.OrderDate; + + if (dispo?.DateStart == null) + { + continue; + } + if (offerdate == null) + { + continue; + } + + var today = DateTime.Now.Date; + var dispoDate = dispo.DateStart.Value.Date; + var offerDate = offerdate.Value.Date; + + var daysUntilDispo = (dispoDate - today).Days; + var offerAgeDays = (today - offerDate).Days; + + if (daysUntilDispo != Configuration.Epirent.ReminderDaysBeforeDispoStart) + { + continue; + } + + if (offerAgeDays < Configuration.Epirent.OfferDateOlderThan) + { + continue; + } + + Logger.Info($"Order found: {Order.Event}. Dispo in {daysUntilDispo} days. Offer is {offerAgeDays} days old."); + + // Jetzt Daten für die Mail zusammensuchen. + // Dazu brauchen wir die Ansprechpartnermailadresse, den Bearbeiter inkl. Kontaktdaten + // und ein paar Infos über den Job wie Name, ID und evtl. Bestellnummer. + + ContactPersonDetailPayload epiContactPerson = null; + ContactDetailPayload epiContactDetail = null; + string recipientMailAddress = ""; + string recipientNameAndSalutation = ""; + string pronoun = ""; + + Logger.Info($"Versuche Kontakte zu finden"); + // Erst Kontaktperson versuchen + if (Order.ContactPerson != null) + { + Logger.Info($"Aktive Kontaktperson gefunden"); + epiContactPerson = new EpiOrders() + .getContactPersonDetail((int)Order.ContactPerson.PrimaryKey)? + .Payload? + .FirstOrDefault(); + Logger.Info($"Kontaktperson: " + epiContactPerson.Name); + + var personMail = epiContactPerson?.Communication? + .FirstOrDefault(x => x.Type == 3); + + if (personMail != null && !string.IsNullOrWhiteSpace(personMail.Uplink)) + { + Logger.Info($"Kontaktperson hat Mail: " + personMail.Uplink); + + recipientMailAddress = personMail.Uplink; + recipientNameAndSalutation = $"Hallo {epiContactPerson.FirstName} {epiContactPerson.LastName}"; + pronoun = "dir"; + } + else + { + epiContactPerson = null; // Fallback auf Kontakt + Logger.Info("Kontaktperson hat keine mail - falle zurück auf Kontakt"); + } + } + + // Fallback: normalen Kontakt verwenden + if (epiContactPerson == null) + { + Logger.Info($"Nutze Kontaktdatensatz."); + + epiContactDetail = new EpiOrders() + .getContact((int)Order.Contact.PrimaryKey)? + .Payload? + .FirstOrDefault(); + + var contactMail = epiContactDetail?.Communication? + .FirstOrDefault(x => x.Type == 3); + + if (contactMail == null || string.IsNullOrWhiteSpace(contactMail.Uplink)) + { + // kein Mailkontakt möglich + Logger.Info("Keine Mailadresse im Kontakt gefunden - kein Mailkontakt möglich"); + continue; + } + + recipientMailAddress = contactMail.Uplink; + recipientNameAndSalutation = $"Liebes Team von {epiContactDetail.Name}"; + pronoun = "euch"; + Logger.Info("Mailadressen gefunden."); + } + var senderMail = ConfigLoader.Instance.GetMailAddressByShortcut(Order.EditorShort); + + Logger.Info("Versuche Mail an " + recipientNameAndSalutation + " (" + recipientMailAddress + ") von " + senderMail + " zu senden"); + + var bodyText = + recipientNameAndSalutation + "," + Environment.NewLine + Environment.NewLine + + $"ich hoffe, es geht {pronoun} gut." + Environment.NewLine + + $"vor einiger Zeit habe ich {pronoun} unser Angebot \"{Order.Event}\" (Angebotsnummer: {Order.OrderNoFmt}) zukommen lassen. \r\nDa ich bisher noch nichts gehört habe, wollte ich kurz nachfragen, ob das Angebot noch aktuell ist oder ob ich bei der weiteren Planung unterstützen kann." + Environment.NewLine + Environment.NewLine + + "Gerne stehe ich für Rückfragen, Anpassungen oder weitere Abstimmungen jederzeit zur Verfügung." + Environment.NewLine + Environment.NewLine + + "Ich freue mich auf eine Rückmeldung."; + + var draftId = O365.CreateDraftMailAsync( + recipientMailAddress, + senderMail, + + "Erinnerung zum offenen Angebot " + Order.OrderNoFmt + " " + Order.Event, + bodyText, null, senderMail).GetAwaiter().GetResult(); + + Logger.Info("Draft erstellt mit ID: " + draftId); + } + catch (Exception ex) + { + Logger.Error("Error processing order " + Order.Event + ": " + ex.Message); + } + + } + } + } +} diff --git a/EpiAutoReminder/appsettings.CHANGEME.json b/EpiAutoReminder/appsettings.CHANGEME.json new file mode 100644 index 0000000..b878a24 --- /dev/null +++ b/EpiAutoReminder/appsettings.CHANGEME.json @@ -0,0 +1,24 @@ +{ + "AzureAd": { + "ClientId": "xxx", + "TenantId": "xxx", + "ClientSecret": "xxx" + }, + "Graph": { + "BaseUrl": "https://login.microsoftonline.com" + }, + "Epirent": { + "Server": "xxx", + "Port": 8080, + "Token": "xxx", + "Mandant": 0, + "ReminderDaysBeforeDispoStart": 21, + "OfferDateOlderThan": 14 + }, + "MailMappings": { + "TU": "test.user@vt-media.de", + + } +} + + diff --git a/EpiAutoReminder/log4net.config b/EpiAutoReminder/log4net.config new file mode 100644 index 0000000..b85d48e --- /dev/null +++ b/EpiAutoReminder/log4net.config @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + +