diff --git a/server/biz/GlobalBizSettingsBiz.cs b/server/biz/GlobalBizSettingsBiz.cs index e7a706c..2f46ca9 100644 --- a/server/biz/GlobalBizSettingsBiz.cs +++ b/server/biz/GlobalBizSettingsBiz.cs @@ -246,6 +246,7 @@ namespace Sockeye.Biz p.Active = true;//not entirely true but we can always deactivate there aren't many products p.VendorId = 1; p.OurCode = p.VendorCode = jPurchase["productCode"].Value(); + p.Group = ProductGroupFromProductCode(p.OurCode); if (p.VendorCode == "301028468")//subscription yearly { @@ -270,7 +271,7 @@ namespace Sockeye.Biz var s = new Purchase(); s.VendorId = 1; s.ProductId = p.Id; - + s.Group = p.Group; s.CustomerId = CurrentCustomerId; s.CancelDate = DateUtil.EpochToDateNullIsNull(jPurchase["cancelDate"].Value()); s.CouponCode = jPurchase["couponCode"].Value(); @@ -340,7 +341,7 @@ namespace Sockeye.Biz p.VendorData += ",\n"; p.VendorData += jVendorNotificationItem["data"].Value(); await ct.SaveChangesAsync(); - } + } }// all vendor notifications loop } @@ -541,7 +542,7 @@ namespace Sockeye.Biz var jKey = JObject.Parse(keyNoWS); l.LicenseExpire = DateTime.MaxValue.ToUniversalTime(); l.MaintenanceExpire = jKey["AyaNovaLicenseKey"]["Expires"].Value().ToUniversalTime(); - + l.Group = ProductGroup.AyaNova7; l.FetchCode = jLicense["code"].Value(); l.Tags.Add("v7"); } @@ -559,7 +560,7 @@ namespace Sockeye.Biz var jKey = JObject.Parse(keyNoWS); l.LicenseExpire = DateTime.MaxValue.ToUniversalTime(); l.MaintenanceExpire = jKey["AyaNovaLiteLicenseKey"]["Expires"].Value().ToUniversalTime(); - + l.Group = ProductGroup.AyaNova7; l.FetchCode = jLicense["code"].Value(); l.Tags.Add("v7"); l.Tags.Add("lite"); @@ -590,6 +591,10 @@ namespace Sockeye.Biz var jKey = JObject.Parse(keyNoWS); l.DbId = jKey["Key"]["DBID"].Value(); l.LicenseExpire = jKey["Key"]["LicenseExpiration"].Value().ToUniversalTime(); + if (jKey["Key"]["Perpetual"].Value()) + l.Group = ProductGroup.RavenPerpetual; + else + l.Group = ProductGroup.RavenSubscription; l.MaintenanceExpire = jKey["Key"]["MaintenanceExpiration"].Value().ToUniversalTime(); l.Tags.Add("raven"); } @@ -678,6 +683,70 @@ namespace Sockeye.Biz //await Task.CompletedTask; } + + private static ProductGroup ProductGroupFromProductCode(string productCode) + { + switch (productCode) + { + case "300740321"://wbi + case "300740317"://up to 5 + case "300740314"://RI + case "300740328"://Quick notification + case "300784766"://QBOI + case "300740315"://Single + case "300740323"://QBI + case "300740318"://Up to 10 + case "300740327"://Export to XLS + case "300740329"://ImportExport CSV duplicate + case "300740319"://Up to 20 + case "300740322"://MBI + case "300740324"://PTI + case "300740325"://OLI + case "300807973"://Up to 15 + case "300740326"://Outlook Schedule Export + case "300740316"://AyaNova LITE + case "999"://Up to 999 + return ProductGroup.AyaNova7; + case "300093112"://Key administration + case "301010670"://Custom 733918243 + case "300151145"://Custom 539230073 + return ProductGroup.Misc; + case "301028317"://AyaNova perpetual single user license includes one year maintenance plan + return ProductGroup.RavenPerpetual; + case "301028468"://AyaNova subscription one user YEARLY + case "301028467"://AyaNova subscription one user MONTHLY + return ProductGroup.RavenSubscription; + default: + throw new Exception($"Uknown product code {productCode}"); + } + } + /* + "id" "name" "active" "vendorid" "licenseinterval" "maintinterval" "vendorcode" "ourcode" "wiki" "tags" +1 "WBI" true 1 "00:00:00" "365 days" "300740321" "300740321" "{}" +2 "Up to 5" true 1 "00:00:00" "365 days" "300740317" "300740317" "{}" +3 "RI" true 1 "00:00:00" "365 days" "300740314" "300740314" "{}" +4 "Quick Notification" true 1 "00:00:00" "365 days" "300740328" "300740328" "{}" +5 "QBOI" true 1 "00:00:00" "365 days" "300784766" "300784766" "{}" +6 "Single" true 1 "00:00:00" "365 days" "300740315" "300740315" "{}" +7 "QBI" true 1 "00:00:00" "365 days" "300740323" "300740323" "{}" +8 "Up to 10" true 1 "00:00:00" "365 days" "300740318" "300740318" "{}" +9 "Export to XLS" true 1 "00:00:00" "365 days" "300740327" "300740327" "{}" +10 "Importexport.csv duplicate" true 1 "00:00:00" "365 days" "300740329" "300740329" "{}" +11 "Up to 20" true 1 "00:00:00" "365 days" "300740319" "300740319" "{}" +12 "MBI" true 1 "00:00:00" "365 days" "300740322" "300740322" "{}" +13 "Key Administration" true 1 "00:00:00" "365 days" "300093112" "300093112" "{}" +14 "PTI " true 1 "00:00:00" "365 days" "300740324" "300740324" "{}" +15 "OLI" true 1 "00:00:00" "365 days" "300740325" "300740325" "{}" +16 "Up to 15" true 1 "00:00:00" "365 days" "300807973" "300807973" "{}" +17 "Outlook Schedule Export" true 1 "00:00:00" "365 days" "300740326" "300740326" "{}" +18 "AyaNova Lite" true 1 "00:00:00" "365 days" "300740316" "300740316" "{}" +19 "Up to 999" true 1 "00:00:00" "365 days" "999" "999" "{}" +20 "AyaNova perpetual single user license includes one year maintenance plan" true 1 "00:00:00" "365 days" "301028317" "301028317" "{}" +21 "Custom 733918243" true 1 "00:00:00" "365 days" "301010670" "301010670" "{}" +22 "AyaNova subscription one user monthly" true 1 "31 days" "31 days" "301028467" "301028467" "{}" +23 "Custom 539230073" true 1 "00:00:00" "365 days" "300151145" "300151145" "{}" +24 "AyaNova subscription one user yearly" true 1 "365 days" "365 days" "301028468" "301028468" "{}" + */ ///////////////////////////////////////////////////////////////////// }//eoc diff --git a/server/biz/ProductGroup.cs b/server/biz/ProductGroup.cs new file mode 100644 index 0000000..ab6e1be --- /dev/null +++ b/server/biz/ProductGroup.cs @@ -0,0 +1,11 @@ +namespace Sockeye.Biz +{ + public enum ProductGroup + { + Misc = 0, + AyaNova7 = 1, + RavenPerpetual = 2, + RavenSubscription = 3 + } + +}//eons \ No newline at end of file diff --git a/server/models/License.cs b/server/models/License.cs index a8d47b8..e016ac6 100644 --- a/server/models/License.cs +++ b/server/models/License.cs @@ -19,12 +19,14 @@ namespace Sockeye.Models public long? CustomerId { get; set; } [NotMapped] public string CustomerViz { get; set; } + [Required] + public ProductGroup Group {get;set;} public string RegTo { get; set; } public string Key { get; set; } - public string FetchCode { get; set; } - public string FetchEmail { get; set; } + public string FetchCode { get; set; }//v7 uses + public string FetchEmail { get; set; }//v7 uses public DateTime? FetchedOn { get; set; } - public string DbId { get; set; } + public string DbId { get; set; }//v8 uses public DateTime LicenseExpire { get; set; } public DateTime MaintenanceExpire { get; set; } diff --git a/server/models/Product.cs b/server/models/Product.cs index e1c37ba..f3fbb60 100644 --- a/server/models/Product.cs +++ b/server/models/Product.cs @@ -15,6 +15,8 @@ namespace Sockeye.Models [Required] public string Name { get; set; } public bool Active { get; set; } + [Required] + public ProductGroup Group {get;set;} public long VendorId { get; set; } public TimeSpan LicenseInterval { get; set; } public TimeSpan MaintInterval { get; set; } diff --git a/server/models/Purchase.cs b/server/models/Purchase.cs index d999b91..22391fb 100644 --- a/server/models/Purchase.cs +++ b/server/models/Purchase.cs @@ -10,8 +10,7 @@ namespace Sockeye.Models public class Purchase : ICoreBizObjectModel { public long Id { get; set; } - public uint Concurrency { get; set; } - [Required] + public uint Concurrency { get; set; } public long CustomerId { get; set; } [NotMapped] public string CustomerViz { get; set; } @@ -21,6 +20,8 @@ namespace Sockeye.Models public string VendorViz { get; set; } [Required] public long ProductId { get; set; } + [Required] + public ProductGroup Group {get;set;} [NotMapped] public string ProductViz { get; set; } public string SalesOrderNumber { get; set; } diff --git a/server/util/AySchema.cs b/server/util/AySchema.cs index f943002..1a68652 100644 --- a/server/util/AySchema.cs +++ b/server/util/AySchema.cs @@ -874,7 +874,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); LogUpdateMessage(log); await ExecQueryAsync("CREATE TABLE alicense (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, created TIMESTAMPTZ NOT NULL, " - + "customerid BIGINT REFERENCES acustomer(id), regto TEXT NOT NULL, key TEXT NOT NULL, fetchcode TEXT, fetchemail TEXT, " + + "customerid BIGINT REFERENCES acustomer(id), group INTEGER NOT NULL DEFAULT 0, regto TEXT NOT NULL, key TEXT NOT NULL, fetchcode TEXT, fetchemail TEXT, " + "fetchedon TIMESTAMPTZ, dbid TEXT, licenseexpire TIMESTAMPTZ, maintenanceexpire TIMESTAMPTZ NOT NULL, " + "wiki TEXT, tags VARCHAR(255) ARRAY )"); @@ -892,12 +892,12 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); await ExecQueryAsync("CREATE TABLE aproduct (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, name TEXT NOT NULL UNIQUE, active BOOL NOT NULL DEFAULT true, " - + "vendorid BIGINT NOT NULL REFERENCES avendor(id), licenseinterval INTERVAL, maintinterval INTERVAL, vendorcode TEXT NOT NULL UNIQUE, ourcode TEXT NOT NULL UNIQUE, " + + "vendorid BIGINT NOT NULL REFERENCES avendor(id), group INTEGER NOT NULL DEFAULT 0, licenseinterval INTERVAL, maintinterval INTERVAL, vendorcode TEXT NOT NULL UNIQUE, ourcode TEXT NOT NULL UNIQUE, " + "wiki TEXT, tags VARCHAR(255) ARRAY )"); - await ExecQueryAsync("CREATE TABLE apurchase (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, customerid BIGINT NOT NULL REFERENCES acustomer(id) ON DELETE CASCADE, " - + "vendorid BIGINT NOT NULL REFERENCES avendor(id), productid BIGINT NOT NULL REFERENCES aproduct(id), salesordernumber TEXT NOT NULL, " + await ExecQueryAsync("CREATE TABLE apurchase (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, customerid BIGINT REFERENCES acustomer(id) ON DELETE CASCADE, " + + "vendorid BIGINT NOT NULL REFERENCES avendor(id), productid BIGINT NOT NULL REFERENCES aproduct(id), group INTEGER NOT NULL DEFAULT 0, salesordernumber TEXT NOT NULL, " + "purchasedate TIMESTAMPTZ NOT NULL, expiredate TIMESTAMPTZ, canceldate TIMESTAMPTZ, couponcode text, notes text, " + "renewnoticesent BOOL NOT NULL DEFAULT false, quantity INTEGER NOT NULL DEFAULT 1, " + "vendordata TEXT, processeddate TIMESTAMPTZ, " @@ -1104,6 +1104,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'ProductMaintInterval', 'Maintenance period' FROM atranslation t where t.baselanguage = 'en'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'ProductVendorCode', 'Vendor code' FROM atranslation t where t.baselanguage = 'en'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'ProductOurCode', 'Our code' FROM atranslation t where t.baselanguage = 'en'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'ProductGroup', 'Product group' FROM atranslation t where t.baselanguage = 'en'"); //spanish translations await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'Product', 'Product' FROM atranslation t where t.baselanguage = 'es'"); @@ -1113,6 +1114,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'ProductMaintInterval', 'Maintenance period' FROM atranslation t where t.baselanguage = 'es'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'ProductVendorCode', 'Vendor code' FROM atranslation t where t.baselanguage = 'es'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'ProductOurCode', 'Our code' FROM atranslation t where t.baselanguage = 'es'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'ProductGroup', 'Product group' FROM atranslation t where t.baselanguage = 'es'"); //french translations await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'Product', 'Product' FROM atranslation t where t.baselanguage = 'fr'"); @@ -1122,6 +1124,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'ProductMaintInterval', 'Maintenance period' FROM atranslation t where t.baselanguage = 'fr'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'ProductVendorCode', 'Vendor code' FROM atranslation t where t.baselanguage = 'fr'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'ProductOurCode', 'Our code' FROM atranslation t where t.baselanguage = 'fr'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'ProductGroup', 'Product group' FROM atranslation t where t.baselanguage = 'fr'"); //german translations @@ -1132,6 +1135,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'ProductMaintInterval', 'Maintenance period' FROM atranslation t where t.baselanguage = 'de'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'ProductVendorCode', 'Vendor code' FROM atranslation t where t.baselanguage = 'de'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'ProductOurCode', 'Our code' FROM atranslation t where t.baselanguage = 'de'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'ProductGroup', 'Product group' FROM atranslation t where t.baselanguage = 'de'"); #endregion product diff --git a/todo.txt b/todo.txt index 5b5cac2..25998d7 100644 --- a/todo.txt +++ b/todo.txt @@ -1,18 +1,62 @@ client - open / edit all the things -import all vendor data packets from shareit into purchases + +PURCHASE drives new licensing ui +To automate as much as possible need following: +- Purchase Customer id nullable as may not be able to match to an existing customer +- processed date important as shows what is not processed yet if empty +- product category for purchase automatically attributed, i.e. v7, raven perpet, raven sub, misc + + +License edit / entry form +- Pick license key type different fields appear + NEED TO ADD KEYTYPE ENUM TO LICENSE OBJECT + this will future proof and make UI and handling easier + Keytypes: AyaNova 7, RAVEN Perpetual, RAVEN Subscription +- generates key at server on save depending on what is new or changed I guess or needs to be edited after the fact? +- Generate v7 license from entered ad-hoc data, i.e. it should be from the license edit form and take the entries and make a key on save +- Generate v7 license for customer from all active purchases + NEED TO ADD PRODUCT CATEGORY FOR AUTO LICENSE GENERATION? + V7, RAVEN, MISC (not license keyed) + this way, can automatically create key for v7 from all purchases that have not yet expired for a customer +- Generate v8 license from entered data, '' +- Opening license existing in edit form should re-populate the controls even for v7 so that they can be changed, saved, edited etc +- Once it's fetched it's read-only but can duplicate!!!!! - direct open a case by case number like workorder (id's differ so it isn't easy to just open a case in the url) -- all the rockfish functionality for generating new licenses ad-hoc but with a server route doing the work so can be automated + +NOTE: v7 licensing will be hard to automate fully as it's a fucked up system +RAVEN is much easier to automate with only one key per db +So I'm thinking make v7 renewals and purchases as easy and semiautomatic as possible, perhaps it fills in and suggest the whole thing but requires me +to press a button to actually do it, whereas raven can ultimately just process automatically, particularly subscription monthly important!! + +AUTOMATION ROUTES REQUIRED +Both v7 and v8 point to "rockfish.ayanova.com" so maybe nginx can redirect? +forgot about that, was hoping for ability to parallel and keep rockfish going... hmmm... + +AyaNova 7 license fetch URL +"https://rockfish.ayanova.com/fetch/" + sFetchCode + "/" + sEmail; +rockfish FetchController.cs + +RAVEN trial request URL +POST to +$"{LICENSE_SERVER_URL_ROCKFISH}rvr"; + +RAVEN license fetch url +POST to + $"{LicenseServer}/rvf" + + License server url is one of: -new roles and bizroles set appropo - Developer - rw cases - Support - read cases, read sales and customer data? - Sales - licenses +LICENSE_SERVER_URL_ROCKFISH = "http://localhost:3001/";//dev testing +LICENSE_SERVER_URL_ROCKFISH = "https://rockfish.ayanova.com/";//1st default production primary +LICENSE_SERVER_URL_IO = "https://io.ayanova.com/";//2nd production +LICENSE_SERVER_URL_EUROPA = "https://europa.ayanova.com/";//3rd production +LICENSE_SERVER_URL_CALLISTO = "https://callisto.ayanova.com/";//4th production DTR @@ -22,3 +66,9 @@ automatic jobs to create purchase record from vendor data send license emails based on event etc, really gets into the new stuff here Need ui that shows money required for paying future for yearly subs so at a glance know what money need in bank each month Also, sidebar, check if can pay d.o. in advance, or do I want that?? + + +new roles and bizroles set appropo + Developer - rw cases + Support - read cases, read sales and customer data? + Sales - licenses \ No newline at end of file