diff --git a/server/DataList/SubscriptionItemsDataList.cs b/server/DataList/SubscriptionItemsDataList.cs new file mode 100644 index 0000000..fd49a68 --- /dev/null +++ b/server/DataList/SubscriptionItemsDataList.cs @@ -0,0 +1,146 @@ +using System.Collections.Generic; +using System.Linq; +using Sockeye.Biz; +using Sockeye.Models; + +namespace Sockeye.DataList +{ + internal class SubscriptionItemsDataList : DataListProcessingBase, IDataListInternalCriteria + { + public SubscriptionItemsDataList(long translationId) + { + DefaultListAType = SockType.Subscription; + SQLFrom = @"FROM asubscription +left join asubscriptionitem on asubscription.id=asubscriptionitem.subscriptionid +left join aproduct on (asubscriptionitem.productid = aproduct.id) +LEFT JOIN acustomer ON (asubscription.customerid = acustomer.id)"; + + var RoleSet = BizRoles.GetRoleSet(DefaultListAType); + AllowedRoles = RoleSet.ReadFullRecord | RoleSet.Change; + DefaultColumns = new List() { "PurchaseExpireDate", "Product", "SubSite", "Customer", "active" }; + DefaultSortBy = new Dictionary() { { "PurchaseExpireDate", "-" } }; + FieldDefinitions = new List(); + + + + //SUBSCRIPTION ITEMS + FieldDefinitions.Add(new DataListFieldDefinition + { + TKey = "Product", + FieldKey = "Product", + SockType = (int)SockType.Product, + UiFieldDataType = (int)UiFieldDataType.Text, + SqlIdColumnName = "asubscriptionitem.productid", + SqlValueColumnName = "aproduct.name" + }); + + FieldDefinitions.Add(new DataListFieldDefinition + { + TKey = "OriginalOrderDate", + FieldKey = "OriginalOrderDate", + SockType = (int)SockType.SubscriptionItem, + UiFieldDataType = (int)UiFieldDataType.DateTime, + SqlValueColumnName = "asubscriptionitem.originalorderdate", + SqlIdColumnName = "asubscriptionitem.id", + IsRowId = true + }); + + FieldDefinitions.Add(new DataListFieldDefinition + { + TKey = "PurchaseExpireDate", + FieldKey = "PurchaseExpireDate", + UiFieldDataType = (int)UiFieldDataType.DateTime, + SqlValueColumnName = "asubscriptionitem.expiredate" + }); + + //SUBSCRIPTION + FieldDefinitions.Add(new DataListFieldDefinition + { + TKey = "SubSite", + FieldKey = "SubSite", + SockType = (int)SockType.Subscription, + UiFieldDataType = (int)UiFieldDataType.Text, + SqlIdColumnName = "asubscription.id", + SqlValueColumnName = "asubscription.subsite" + }); + + FieldDefinitions.Add(new DataListFieldDefinition + { + TKey = "Customer", + FieldKey = "Customer", + SockType = (int)SockType.Customer, + UiFieldDataType = (int)UiFieldDataType.Text, + SqlIdColumnName = "asubscription.customerid", + SqlValueColumnName = "acustomer.name" + }); + + FieldDefinitions.Add(new DataListFieldDefinition + { + TKey = "Active", + FieldKey = "active", + UiFieldDataType = (int)UiFieldDataType.Bool, + SqlValueColumnName = "asubscriptionitem.active" + }); + + FieldDefinitions.Add(new DataListFieldDefinition + { + TKey = "Tags", + FieldKey = "tags", + UiFieldDataType = (int)UiFieldDataType.Tags, + SqlValueColumnName = "asubscription.tags" + }); + + //META column + FieldDefinitions.Add(new DataListFieldDefinition + { + FieldKey = "metacustomer", + UiFieldDataType = (int)UiFieldDataType.InternalId, + SqlIdColumnName = "asubscription.customerid", + SqlValueColumnName = "asubscription.customerid", + IsMeta = true + }); + + } + + public List DataListInternalCriteria(long currentUserId, AuthorizationRoles userRoles, string clientCriteria) + { + List ret = new List(); + + //ClientCriteria format for this list is "OBJECTID,AYATYPE" + var crit = (clientCriteria ?? "").Split(',').Select(z => z.Trim()).ToArray(); + if (crit.Length > 1) + { + //will be filtered from different types, show all records from Customer and nothing else at this time + int nType = 0; + if (!int.TryParse(crit[1], out nType)) return ret; + SockType forType = (SockType)nType; + if (forType != SockType.Customer) return ret;//only supports customer for now see workorderdatalist for alts + + long lId = 0; + if (!long.TryParse(crit[0], out lId)) return ret; + if (lId == 0) return ret; + + //Have valid type, have an id, so filter away + switch (forType) + { + case SockType.Customer: + { + DataListFilterOption FilterOption = new DataListFilterOption() { Column = "metacustomer" }; + FilterOption.Items.Add(new DataListColumnFilter() { value = crit[0], op = DataListFilterComparisonOperator.Equality }); + ret.Add(FilterOption); + } + break; + // case AyaType.Project: + // { + // DataListFilterOption FilterOption = new DataListFilterOption() { Column = "metaproject" }; + // FilterOption.Items.Add(new DataListColumnFilter() { value = crit[0], op = DataListFilterComparisonOperator.Equality }); + // ret.Add(FilterOption); + // } + // break; + } + } + return ret; + } + + }//eoc +}//eons \ No newline at end of file diff --git a/server/biz/SockType.cs b/server/biz/SockType.cs index 2d82b0f..436a385 100644 --- a/server/biz/SockType.cs +++ b/server/biz/SockType.cs @@ -79,7 +79,9 @@ namespace Sockeye.Biz [CoreBizObject, ReportableBizObject] VendorNotification = 99, [CoreBizObject, ReportableBizObject] - Subscription = 100 + Subscription = 100, + [CoreBizObject, ReportableBizObject] + SubscriptionItem = 101 diff --git a/server/models/SubscriptionItem.cs b/server/models/SubscriptionItem.cs index c8e42c5..0de671a 100644 --- a/server/models/SubscriptionItem.cs +++ b/server/models/SubscriptionItem.cs @@ -1,12 +1,15 @@ using System; +using System.Collections.Generic; +using Sockeye.Biz; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Newtonsoft.Json; + namespace Sockeye.Models { //https://stackoverflow.com/questions/46517584/how-to-add-a-parent-record-with-its-children-records-in-ef-core#46615455 - public class SubscriptionItem + public class SubscriptionItem : ICoreBizObjectModel { public long Id { get; set; } public uint Concurrency { get; set; } @@ -27,9 +30,18 @@ namespace Sockeye.Models [Required] public DateTime OriginalOrderDate { get; set; } + + public List Tags { get; set; } = new List(); + + //workaround for notification + [NotMapped, JsonIgnore] + public string Name { get; set; } [JsonIgnore] public Subscription Subscription { get; set; } + [NotMapped, JsonIgnore] + public SockType SType { get => SockType.SubscriptionItem; } + }//eoc }//eons \ No newline at end of file diff --git a/server/util/AySchema.cs b/server/util/AySchema.cs index 17e92af..bbcf63a 100644 --- a/server/util/AySchema.cs +++ b/server/util/AySchema.cs @@ -20,18 +20,18 @@ namespace Sockeye.Util /////////// CHANGE THIS ON NEW SCHEMA UPDATE //////////////////// //!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImportingAsync WHEN NEW TABLES ADDED!!!! - private const int DESIRED_SCHEMA_LEVEL = 19; + private const int DESIRED_SCHEMA_LEVEL = 22; - internal const long EXPECTED_COLUMN_COUNT = 549; + internal const long EXPECTED_COLUMN_COUNT = 553; internal const long EXPECTED_INDEX_COUNT = 77; - internal const long EXPECTED_CHECK_CONSTRAINTS = 264; + internal const long EXPECTED_CHECK_CONSTRAINTS = 267; internal const long EXPECTED_FOREIGN_KEY_CONSTRAINTS = 40; internal const long EXPECTED_VIEWS = 0; internal const long EXPECTED_ROUTINES = 2; //!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImportingAsync WHEN NEW TABLES ADDED!!!! - ///////////////////////////////////////// (C549:I77:CC264:FC40:V0:R2) + ///////////////////////////////////////// C553:I77:CC267:FC40:V0:R2 /* MAXIMUM POSTGRES OBJECT NAME LENGTH: 63 CHARACTERS @@ -1609,6 +1609,61 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); } + ////////////////////////////////////////////////// + // + // reporting for subscriptions required data + // + if (currentSchema < 20) + { + LogUpdateMessage(log); + + await ExecQueryAsync("ALTER TABLE aproduct ADD column initialprice DECIMAL(38,18) NOT NULL default 0"); + await ExecQueryAsync("ALTER TABLE aproduct ADD column renewprice DECIMAL(38,18) NOT NULL default 0"); + + await SetSchemaLevelAsync(++currentSchema); + + } + if (currentSchema < 21) + { + LogUpdateMessage(log); + + await ExecQueryAsync("ALTER TABLE asubscriptionitem ADD column renewal BOOL NOT NULL DEFAULT true"); + + //english translations + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'InitialPrice', 'Initial price' FROM atranslation t where t.baselanguage = 'en'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'RenewPrice', 'Renewal price' FROM atranslation t where t.baselanguage = 'en'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'Renewal', 'Renewal' FROM atranslation t where t.baselanguage = 'en'"); + + //spanish translations + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'InitialPrice', 'Initial price' FROM atranslation t where t.baselanguage = 'es'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'RenewPrice', 'Renewal price' FROM atranslation t where t.baselanguage = 'es'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'Renewal', 'Renewal' FROM atranslation t where t.baselanguage = 'es'"); + + //french translations + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'InitialPrice', 'Initial price' FROM atranslation t where t.baselanguage = 'fr'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'RenewPrice', 'Renewal price' FROM atranslation t where t.baselanguage = 'fr'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'Renewal', 'Renewal' FROM atranslation t where t.baselanguage = 'fr'"); + + //german translations + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'InitialPrice', 'Initial price' FROM atranslation t where t.baselanguage = 'de'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'RenewPrice', 'Renewal price' FROM atranslation t where t.baselanguage = 'de'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'Renewal', 'Renewal' FROM atranslation t where t.baselanguage = 'de'"); + + + + await SetSchemaLevelAsync(++currentSchema); + + } + if (currentSchema < 22) + { + LogUpdateMessage(log); + + await ExecQueryAsync("ALTER TABLE asubscriptionitem ADD column tags VARCHAR(255) ARRAY "); + + await SetSchemaLevelAsync(++currentSchema); + + } + //######################################### //!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImporting WHEN NEW TABLES ADDED!!!! diff --git a/todo.txt b/todo.txt index beeaf8b..fca2d87 100644 --- a/todo.txt +++ b/todo.txt @@ -4,10 +4,16 @@ TODO: - Subscriptions - Need to be able to run an revenue projected report based on timeframe - todo: subscription edit form should have "licenses" link to show all past licenses for this customer + TODO: Job that notifies me if a subscription passes it's expiry by the mycommerce grace period + + todo: report showing who subscribes to what and count of subscriptoin items for each type etc maybe going to need a sub list like workorder items after all?, would be much easier for reporting etc + select acustomer.name,asubscription.subsite,aproduct.name, asubscriptionitem.originalordernumber FROM asubscription +left join asubscriptionitem on asubscription.id=asubscriptionitem.subscriptionid +left join aproduct on (asubscriptionitem.productid = aproduct.id) +LEFT JOIN acustomer ON (asubscription.customerid = acustomer.id) TODO: dashboard items related to subscriptions - revenue per month for next 12 months graph