@things-factory/saas 0.0.0-stage → 10.1.234
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -2
- package/dist-client/bootstrap.d.ts +1 -0
- package/dist-client/bootstrap.js +5 -0
- package/dist-client/bootstrap.js.map +1 -0
- package/dist-client/components/saas-bill-editor.d.ts +59 -0
- package/dist-client/components/saas-bill-editor.js +211 -0
- package/dist-client/components/saas-bill-editor.js.map +1 -0
- package/dist-client/entity360/saas-account-360.d.ts +12 -0
- package/dist-client/entity360/saas-account-360.js +231 -0
- package/dist-client/entity360/saas-account-360.js.map +1 -0
- package/dist-client/index.d.ts +3 -0
- package/dist-client/index.js +4 -0
- package/dist-client/index.js.map +1 -0
- package/dist-client/pages/saas-account-list.d.ts +57 -0
- package/dist-client/pages/saas-account-list.js +175 -0
- package/dist-client/pages/saas-account-list.js.map +1 -0
- package/dist-client/pages/saas-account-page.d.ts +54 -0
- package/dist-client/pages/saas-account-page.js +352 -0
- package/dist-client/pages/saas-account-page.js.map +1 -0
- package/dist-client/pages/saas-cost-trend.d.ts +23 -0
- package/dist-client/pages/saas-cost-trend.js +219 -0
- package/dist-client/pages/saas-cost-trend.js.map +1 -0
- package/dist-client/route.d.ts +1 -0
- package/dist-client/route.js +16 -0
- package/dist-client/route.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-client/words.d.ts +47 -0
- package/dist-client/words.js +39 -0
- package/dist-client/words.js.map +1 -0
- package/dist-server/index.d.ts +1 -0
- package/dist-server/index.js +9 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/role-tasks.d.ts +2 -0
- package/dist-server/role-tasks.js +16 -0
- package/dist-server/role-tasks.js.map +1 -0
- package/dist-server/service/checks.d.ts +32 -0
- package/dist-server/service/checks.js +83 -0
- package/dist-server/service/checks.js.map +1 -0
- package/dist-server/service/index.d.ts +20 -0
- package/dist-server/service/index.js +38 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/service/saas-access.d.ts +20 -0
- package/dist-server/service/saas-access.js +36 -0
- package/dist-server/service/saas-access.js.map +1 -0
- package/dist-server/service/saas-account.d.ts +43 -0
- package/dist-server/service/saas-account.js +166 -0
- package/dist-server/service/saas-account.js.map +1 -0
- package/dist-server/service/saas-bill.d.ts +41 -0
- package/dist-server/service/saas-bill.js +160 -0
- package/dist-server/service/saas-bill.js.map +1 -0
- package/dist-server/service/saas-mutation.d.ts +12 -0
- package/dist-server/service/saas-mutation.js +103 -0
- package/dist-server/service/saas-mutation.js.map +1 -0
- package/dist-server/service/saas-query.d.ts +69 -0
- package/dist-server/service/saas-query.js +215 -0
- package/dist-server/service/saas-query.js.map +1 -0
- package/dist-server/service/saas-rules.d.ts +138 -0
- package/dist-server/service/saas-rules.js +217 -0
- package/dist-server/service/saas-rules.js.map +1 -0
- package/dist-server/service/saas-seat.d.ts +25 -0
- package/dist-server/service/saas-seat.js +105 -0
- package/dist-server/service/saas-seat.js.map +1 -0
- package/dist-server/service/saas-service.d.ts +119 -0
- package/dist-server/service/saas-service.js +492 -0
- package/dist-server/service/saas-service.js.map +1 -0
- package/dist-server/service/saas-subscription.d.ts +44 -0
- package/dist-server/service/saas-subscription.js +174 -0
- package/dist-server/service/saas-subscription.js.map +1 -0
- package/dist-server/service/saas-trend-type.d.ts +9 -0
- package/dist-server/service/saas-trend-type.js +37 -0
- package/dist-server/service/saas-trend-type.js.map +1 -0
- package/dist-server/service/saas-type.d.ts +113 -0
- package/dist-server/service/saas-type.js +430 -0
- package/dist-server/service/saas-type.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/package.json +49 -4
- package/things-factory.config.js +13 -0
- package/translations/en.json +185 -0
- package/translations/es.json +185 -0
- package/translations/ja.json +185 -0
- package/translations/ko.json +185 -0
- package/translations/ms.json +185 -0
- package/translations/zh.json +185 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"saas-query.js","sourceRoot":"","sources":["../../server/service/saas-query.ts"],"names":[],"mappings":";;;;AAAA,2DAAqE;AACrE,yDAAiE;AACjE,uDAAuD;AACvD,iDAAsH;AACtH,+CAA6G;AAC7G,qCAA4B;AAE5B,+CAAkE;AAClE,iDAA4C;AAC5C,2CAAsC;AACtC,iDAA0I;AAC1I,2CAAsC;AACtC,2DAAsD;AACtD,2CAAwE;AAExE,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AAEzD,6GAA6G;AAC7G,KAAK,UAAU,UAAU,CAAC,EAAO,EAAE,KAAU,EAAE,KAAoB;IACjE,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IAC/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,oBAAoB,EAAE,GAAG,KAAK,CAAA;IAC9D,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,0BAAW,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;IAC7H,gHAAgH;IAChH,MAAM,QAAQ,GAAG,MAAM,IAAA,2BAAe,EAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,oBAAoB,CAAC,CAAA;IACnJ,MAAM,MAAM,GAAG,MAAM,IAAA,2BAAY,EAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,QAAQ,CAAC,CAAA;IACvE,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,EAAE,MAAM,IAAI,EAAE,CAAA;QAC9D,GAAG,CAAC,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAChC,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAIM,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAGrB,AAAN,KAAK,CAAC,YAAY,CAA0B,MAAiB,EAAS,OAAwB;QAC5F,MAAM,KAAK,GAAG,IAAA,qCAA6B,EAAC,EAAE,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,IAAA,qBAAa,EAAC,0BAAW,CAAC,EAAE,WAAW,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,CAAC,EAAE,CAAC,CAAA;QAC5M,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,KAAK,CAAC,eAAe,EAAE,CAAA;QACpD,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC,IAAA,qBAAa,EAAC,0BAAW,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,CAAA;IACrG,CAAC;IAIK,AAAN,KAAK,CAAC,cAAc,CAAQ,OAAwB;QAClD,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACtC,MAAM,OAAO,GAAG,IAAA,qBAAa,EAAC,0BAAW,CAAC,CAAC,OAAO,CAAA;QAClD,MAAM,GAAG,GAAG,MAAM,IAAA,4BAAc,EAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAA;QAC3D,IAAI,CAAC,GAAG,CAAC,MAAM;YAAE,OAAO,EAAE,CAAA;QAC1B,OAAO,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,IAAA,qBAAa,EAAC,0BAAW,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,GAAG,CAAC,EAAS,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAS,EAAE,CAAC,CAAC,CAAA;IAC/K,CAAC;IAIK,AAAN,KAAK,CAAC,WAAW,CAAY,EAAU,EAAS,OAAwB;QACtE,MAAM,OAAO,GAAG,IAAA,qBAAa,EAAC,0BAAW,CAAC,CAAC,OAAO,CAAA;QAClD,MAAM,OAAO,GAAG,MAAM,IAAA,uBAAS,EAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,IAAA,qBAAO,EAAC,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAA;QAClH,OAAO,CAAC,MAAM,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACjE,CAAC;IAIK,AAAN,KAAK,CAAC,iBAAiB,CAAmB,SAAiB,EAAS,OAAwB;QAC1F,MAAM,OAAO,GAAG,IAAA,qBAAa,EAAC,0BAAW,CAAC,CAAC,OAAO,CAAA;QAClD,OAAO,IAAA,kCAAmB,EAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,IAAA,qBAAO,EAAC,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAA;IAC/H,CAAC;IAIK,AAAN,KAAK,CAAC,SAAS,CAAwB,cAAsB,EAAS,OAAwB;QAC5F,MAAM,OAAO,GAAG,IAAA,qBAAa,EAAC,0BAAW,CAAC,CAAC,OAAO,CAAA;QAClD,OAAO,IAAA,0BAAW,EAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,IAAA,qBAAO,EAAC,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,cAAc,CAAC,CAAA;IACnH,CAAC;IAIK,AAAN,KAAK,CAAC,SAAS,CAAmB,SAAiB,EAAS,OAAwB;QAClF,MAAM,OAAO,GAAG,IAAA,qBAAa,EAAC,0BAAW,CAAC,CAAC,OAAO,CAAA;QAClD,OAAO,IAAA,0BAAW,EAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,IAAA,qBAAO,EAAC,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,CAAA;IAC9G,CAAC;IAIK,AAAN,KAAK,CAAC,WAAW,CAAmB,SAAiB,EAAS,OAAwB;QACpF,MAAM,OAAO,GAAG,IAAA,qBAAa,EAAC,0BAAW,CAAC,CAAC,OAAO,CAAA;QAClD,MAAM,IAAI,GAAG,MAAM,IAAA,4BAAa,EAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,IAAA,qBAAO,EAAC,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,CAAA;QAC1H,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAQ,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IACjM,CAAC;IAED,sHAAsH;IAGhH,AAAN,KAAK,CAAC,YAAY,CAAmB,SAAiB,EAAS,OAAwB;QACrF,MAAM,OAAO,GAAG,IAAA,qBAAa,EAAC,0BAAW,CAAC,CAAC,OAAO,CAAA;QAClD,OAAO,IAAA,6BAAc,EAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,IAAA,qBAAO,EAAC,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,SAAS,CAAC,CAAA;IACjH,CAAC;IAID,aAAa,CAAc,IAAY,EAAa,EAAU,EAAS,OAAwB;QAC7F,OAAO,IAAA,4BAAa,EAAC,IAAA,qBAAa,EAAC,0BAAW,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAA;IAC1F,CAAC;IAGD,YAAY,CAAS,GAAgB;QACnC,OAAO,GAAG,CAAC,YAAY,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,IAAA,qBAAa,EAAC,uBAAY,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IACjI,CAAC;IAGD,MAAM,CAAS,GAAgB;QAC7B,OAAO,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAA,qBAAa,EAAC,0BAAW,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IAC9G,CAAC;IAGD,cAAc,CAAS,GAAgB;QACrC,OAAO,GAAG,CAAC,cAAc,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAA,qBAAa,EAAC,wBAAW,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IACtI,CAAC;IAGD,MAAM,CAAS,GAAgB;QAC7B,OAAO,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAA,qBAAa,EAAC,cAAM,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IACzG,CAAC;IAGD,OAAO,CAAS,GAAgB;QAC9B,OAAO,GAAG,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAA,qBAAa,EAAC,gBAAI,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IAC1G,CAAC;CACF,CAAA;AA9FY,4CAAgB;AAGrB;IAFL,IAAA,wBAAS,EAAC,4EAA4E,CAAC;IACvF,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,2BAAe,EAAE,EAAE,WAAW,EAAE,+DAA+D,EAAE,CAAC;IAC7F,mBAAA,IAAA,mBAAI,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAS,CAAC,CAAA;IAAqB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAAjB,iBAAS;;oDAI5D;AAIK;IAFL,IAAA,wBAAS,EAAC,iFAAiF,CAAC;IAC5F,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,0BAAW,CAAC,EAAE,EAAE,WAAW,EAAE,wDAAwD,EAAE,CAAC;IAClF,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;sDAM1B;AAIK;IAFL,IAAA,wBAAS,EAAC,iFAAiF,CAAC;IAC5F,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,0BAAW,EAAE,EAAE,WAAW,EAAE,sDAAsD,EAAE,CAAC;IACjF,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;mDAI9C;AAIK;IAFL,IAAA,wBAAS,EAAC,iFAAiF,CAAC;IAC5F,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,oCAAgB,CAAC,EAAE,EAAE,WAAW,EAAE,4DAA4D,EAAE,CAAC;IACxF,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAAqB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;yDAGlE;AAIK;IAFL,IAAA,wBAAS,EAAC,iFAAiF,CAAC;IAC5F,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,oBAAQ,CAAC,EAAE,EAAE,WAAW,EAAE,6BAA6B,EAAE,CAAC;IACzD,mBAAA,IAAA,kBAAG,EAAC,gBAAgB,CAAC,CAAA;IAA0B,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;iDAGpE;AAIK;IAFL,IAAA,wBAAS,EAAC,iFAAiF,CAAC;IAC5F,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,oBAAQ,CAAC,EAAE,EAAE,WAAW,EAAE,yDAAyD,EAAE,CAAC;IACrF,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAAqB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;iDAG1D;AAIK;IAFL,IAAA,wBAAS,EAAC,iFAAiF,CAAC;IAC5F,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,sBAAU,CAAC,EAAE,EAAE,WAAW,EAAE,8DAA8D,EAAE,CAAC;IAC1F,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAAqB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;mDAI5D;AAKK;IAFL,IAAA,wBAAS,EAAC,6EAA6E,CAAC;IACxF,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,wBAAW,CAAC,EAAE,EAAE,WAAW,EAAE,4DAA4D,EAAE,CAAC;IACxF,mBAAA,IAAA,kBAAG,EAAC,WAAW,CAAC,CAAA;IAAqB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;oDAG7D;AAID;IAFC,IAAA,wBAAS,EAAC,4EAA4E,CAAC;IACvF,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,yBAAa,EAAE,EAAE,WAAW,EAAE,mFAAmF,EAAE,CAAC;IACpH,mBAAA,IAAA,kBAAG,EAAC,MAAM,CAAC,CAAA;IAAgB,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;qDAErE;AAGD;IADC,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,uBAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1C,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAM,0BAAW;;oDAEpC;AAGD;IADC,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,0BAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC/C,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAM,0BAAW;;8CAE9B;AAGD;IADC,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,wBAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACvC,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAM,0BAAW;;sDAEtC;AAGD;IADC,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,cAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1C,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAM,0BAAW;;8CAE9B;AAGD;IADC,IAAA,4BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,gBAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACvC,mBAAA,IAAA,mBAAI,GAAE,CAAA;;6CAAM,0BAAW;;+CAE/B;2BA7FU,gBAAgB;IAF5B,IAAA,4BAAa,EAAC,qBAAa,CAAC;IAC5B,IAAA,uBAAQ,EAAC,0BAAW,CAAC;GACT,gBAAgB,CA8F5B","sourcesContent":["import { BankAccount, Transaction } from '@things-factory/accounting'\nimport { checkPermission, User } from '@things-factory/auth-base'\nimport { Counterparty } from '@things-factory/biz-base'\nimport { Domain, getQueryBuilderFromListParams, getRepository, ListParam, SpeakRefusals } from '@things-factory/shell'\nimport { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root, UseMiddleware } from 'type-graphql'\nimport { In } from 'typeorm'\n\nimport { accountIn, administeredBy, scopeOf } from './saas-access'\nimport { SaasAccount } from './saas-account'\nimport { SaasBill } from './saas-bill'\nimport { saasBillsOf, saasChangesOf, saasCostTrend, saasPaymentsOf, saasSeatsOf, saasSubscriptionsOf, saasTrendsOf } from './saas-service'\nimport { SaasSeat } from './saas-seat'\nimport { SaasSubscription } from './saas-subscription'\nimport { SaasAccountList, SaasChange, SaasCostTrend } from './saas-type'\n\nconst today = () => new Date().toISOString().slice(0, 10)\n\n/** The accounts with their admins and their months (decision 10) - the list's relations in one read each. */\nasync function withAdmins(tx: any, state: any, items: SaasAccount[]) {\n if (!items.length) return items\n const { domain, user, unsafeIP, prohibitedPrivileges } = state\n const rows = await tx.getRepository(SaasAccount).find({ where: { id: In(items.map(one => one.id)) }, relations: ['admins'] })\n /* What left the bank is the bank's fact (ADR-0139) - read into the months only for whoever reads bank lines. */\n const withBank = await checkPermission({ category: 'bank', privilege: 'query', owner: false } as any, user, domain, unsafeIP, prohibitedPrivileges)\n const trends = await saasTrendsOf(tx, domain, items, today(), withBank)\n for (const one of items) {\n one.admins = rows.find(row => row.id === one.id)?.admins ?? []\n one.trend = trends.get(one.id)\n }\n return items\n}\n\n@UseMiddleware(SpeakRefusals)\n@Resolver(SaasAccount)\nexport class SaasAccountQuery {\n @Directive('@privilege(category: \"saas\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => SaasAccountList, { description: 'To fetch a page of the accounts the domain holds with vendors' })\n async saasAccounts(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<SaasAccountList> {\n const query = getQueryBuilderFromListParams({ domain: context.state.domain, params, repository: getRepository(SaasAccount), searchables: ['name', 'identifier', 'purpose', 'billingEmail', 'description'] })\n const [items, total] = await query.getManyAndCount()\n return { items: await withAdmins(getRepository(SaasAccount).manager, context.state, items), total }\n }\n\n @Directive('@privilege(category: \"saas-self\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => [SaasAccount], { description: 'The accounts the signed-in person is named an admin of' })\n async mySaasAccounts(@Ctx() context: ResolverContext): Promise<SaasAccount[]> {\n const { domain, user } = context.state\n const manager = getRepository(SaasAccount).manager\n const ids = await administeredBy(manager, domain, user?.id)\n if (!ids.length) return []\n return withAdmins(manager, context.state, await getRepository(SaasAccount).find({ where: { domain: { id: domain.id }, id: In(ids) } as any, order: { name: 'ASC' } as any }))\n }\n\n @Directive('@privilege(category: \"saas-self\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => SaasAccount, { description: 'To fetch an account - a reader’s any, an admin’s own' })\n async saasAccount(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<SaasAccount> {\n const manager = getRepository(SaasAccount).manager\n const account = await accountIn(manager, context.state.domain, await scopeOf(context.state, 'query', manager), id)\n return (await withAdmins(manager, context.state, [account]))[0]\n }\n\n @Directive('@privilege(category: \"saas-self\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => [SaasSubscription], { description: 'The subscriptions of an account, with the seats held today' })\n async saasSubscriptions(@Arg('accountId') accountId: string, @Ctx() context: ResolverContext): Promise<SaasSubscription[]> {\n const manager = getRepository(SaasAccount).manager\n return saasSubscriptionsOf(manager, context.state.domain, await scopeOf(context.state, 'query', manager), accountId, today())\n }\n\n @Directive('@privilege(category: \"saas-self\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => [SaasSeat], { description: 'The seats of a subscription' })\n async saasSeats(@Arg('subscriptionId') subscriptionId: string, @Ctx() context: ResolverContext): Promise<SaasSeat[]> {\n const manager = getRepository(SaasAccount).manager\n return saasSeatsOf(manager, context.state.domain, await scopeOf(context.state, 'query', manager), subscriptionId)\n }\n\n @Directive('@privilege(category: \"saas-self\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => [SaasBill], { description: 'The bills of an account, latest first, with their lines' })\n async saasBills(@Arg('accountId') accountId: string, @Ctx() context: ResolverContext): Promise<SaasBill[]> {\n const manager = getRepository(SaasAccount).manager\n return saasBillsOf(manager, context.state.domain, await scopeOf(context.state, 'query', manager), accountId)\n }\n\n @Directive('@privilege(category: \"saas-self\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => [SaasChange], { description: 'What moved on an account and its subscriptions, newest first' })\n async saasChanges(@Arg('accountId') accountId: string, @Ctx() context: ResolverContext): Promise<SaasChange[]> {\n const manager = getRepository(SaasAccount).manager\n const rows = await saasChangesOf(manager, context.state.domain, await scopeOf(context.state, 'query', manager), accountId)\n return rows.map((one: any) => ({ subject: one.subject, factKind: one.factKind, field: one.field, fromValue: one.fromValue, toValue: one.toValue, at: one.at, changedBy: one.changedBy?.name }))\n }\n\n /* The bank lines are the bank's facts (ADR-0139): this door asks the bank's privilege, the account's scope on top. */\n @Directive('@privilege(category: \"bank\", privilege: \"query\", domainOwnerGranted: false)')\n @Query(type => [Transaction], { description: \"The bank lines that paid an account's vendor, newest first\" })\n async saasPayments(@Arg('accountId') accountId: string, @Ctx() context: ResolverContext): Promise<Transaction[]> {\n const manager = getRepository(SaasAccount).manager\n return saasPaymentsOf(manager, context.state.domain, await scopeOf(context.state, 'query', manager), accountId)\n }\n\n @Directive('@privilege(category: \"saas\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(type => SaasCostTrend, { description: 'The cost trend from the bills, month by month from one month to another (YYYY-MM)' })\n saasCostTrend(@Arg('from') from: string, @Arg('to') to: string, @Ctx() context: ResolverContext) {\n return saasCostTrend(getRepository(SaasAccount).manager, context.state.domain, from, to)\n }\n\n @FieldResolver(type => Counterparty, { nullable: true })\n counterparty(@Root() row: SaasAccount) {\n return row.counterparty ?? (row.counterpartyId ? getRepository(Counterparty).findOneBy({ id: row.counterpartyId }) : undefined)\n }\n\n @FieldResolver(type => SaasAccount, { nullable: true })\n parent(@Root() row: SaasAccount) {\n return row.parent ?? (row.parentId ? getRepository(SaasAccount).findOneBy({ id: row.parentId }) : undefined)\n }\n\n @FieldResolver(type => BankAccount, { nullable: true })\n paymentAccount(@Root() row: SaasAccount) {\n return row.paymentAccount ?? (row.paymentAccountId ? getRepository(BankAccount).findOneBy({ id: row.paymentAccountId }) : undefined)\n }\n\n @FieldResolver(type => Domain, { nullable: true })\n domain(@Root() row: SaasAccount) {\n return row.domain ?? (row.domainId ? getRepository(Domain).findOneBy({ id: row.domainId }) : undefined)\n }\n\n @FieldResolver(type => User, { nullable: true })\n updater(@Root() row: SaasAccount) {\n return row.updater ?? (row.updaterId ? getRepository(User).findOneBy({ id: row.updaterId }) : undefined)\n }\n}\n"]}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
export declare enum SaasAccountStatus {
|
|
2
|
+
ACTIVE = "ACTIVE",
|
|
3
|
+
CLOSED = "CLOSED"
|
|
4
|
+
}
|
|
5
|
+
export declare enum SaasBilling {
|
|
6
|
+
SEAT = "SEAT",
|
|
7
|
+
FIXED = "FIXED",
|
|
8
|
+
USAGE = "USAGE",
|
|
9
|
+
COMMITMENT = "COMMITMENT",
|
|
10
|
+
OWNED = "OWNED"
|
|
11
|
+
}
|
|
12
|
+
export declare enum SaasSubscriptionStatus {
|
|
13
|
+
ACTIVE = "ACTIVE",
|
|
14
|
+
CANCELLING = "CANCELLING",
|
|
15
|
+
CANCELLED = "CANCELLED"
|
|
16
|
+
}
|
|
17
|
+
export declare const DATE: RegExp;
|
|
18
|
+
export declare const MONTH: RegExp;
|
|
19
|
+
/** An account's bill rising more than this over the month before is told, unless the account names its own rate. */
|
|
20
|
+
export declare const DEFAULT_INCREASE_ALERT_RATE = 0.2;
|
|
21
|
+
/** Metered usage is expected at the average of so many latest bills. */
|
|
22
|
+
export declare const USAGE_AVERAGE_MONTHS = 3;
|
|
23
|
+
export interface SubscriptionFacts {
|
|
24
|
+
id: string;
|
|
25
|
+
billing: SaasBilling | string;
|
|
26
|
+
quantity?: number | null;
|
|
27
|
+
unitPrice?: number | null;
|
|
28
|
+
amount?: number | null;
|
|
29
|
+
budget?: number | null;
|
|
30
|
+
everyMonths?: number | null;
|
|
31
|
+
startOn: string;
|
|
32
|
+
renewalOn?: string | null;
|
|
33
|
+
endedOn?: string | null;
|
|
34
|
+
status: SaasSubscriptionStatus | string;
|
|
35
|
+
}
|
|
36
|
+
export interface BillFacts {
|
|
37
|
+
accountId: string;
|
|
38
|
+
month: string;
|
|
39
|
+
currency: string;
|
|
40
|
+
lines: {
|
|
41
|
+
subscriptionId?: string | null;
|
|
42
|
+
amount: number;
|
|
43
|
+
}[];
|
|
44
|
+
}
|
|
45
|
+
export declare const monthKey: (year: number, month: number) => string;
|
|
46
|
+
/** The month a number of months from a `YYYY-MM`. */
|
|
47
|
+
export declare function shiftMonth(key: string, by: number): string;
|
|
48
|
+
/** The months from one `YYYY-MM` to another, both in. */
|
|
49
|
+
export declare function monthsFrom(from: string, to: string): string[];
|
|
50
|
+
/**
|
|
51
|
+
* The last month it is billed in: a cancelled one in the month it ended, when the bill of that month falls on or before
|
|
52
|
+
* the day it ended, else the month before; one not to be renewed in the month before its renewal day. Open otherwise.
|
|
53
|
+
*/
|
|
54
|
+
export declare function lastBilledMonth(sub: SubscriptionFacts, billingDay?: number | null): string | null;
|
|
55
|
+
/** Whether it is billed in a month - from the month it started, every so many months, to its last. An owned license never is. */
|
|
56
|
+
export declare function billedIn(sub: SubscriptionFacts, year: number, month: number, billingDay?: number | null): boolean;
|
|
57
|
+
/** What one billing of it costs, as the subscription says: seats times the unit price, the fee. Usage says nothing - null. */
|
|
58
|
+
export declare function cycleAmountOf(sub: SubscriptionFacts): number | null;
|
|
59
|
+
/** Metered usage expected for a month: the average of its latest billed months (decision Q3), else its budget, else nothing. */
|
|
60
|
+
export declare function usageEstimate(sub: SubscriptionFacts, billed: {
|
|
61
|
+
month: string;
|
|
62
|
+
amount: number;
|
|
63
|
+
}[]): number | null;
|
|
64
|
+
/**
|
|
65
|
+
* What an account is expected to be billed in a month (decision 4): the subscriptions billed in it, each at what it says
|
|
66
|
+
* or, for usage, at its estimate - on the account's billing day. Nothing for a closed account, a sub-account or a month
|
|
67
|
+
* nothing falls in.
|
|
68
|
+
*/
|
|
69
|
+
export declare function expectedOfMonth(account: {
|
|
70
|
+
status: string;
|
|
71
|
+
parentId?: string | null;
|
|
72
|
+
billingDay?: number | null;
|
|
73
|
+
}, subs: SubscriptionFacts[], billedBySubscription: Map<string, {
|
|
74
|
+
month: string;
|
|
75
|
+
amount: number;
|
|
76
|
+
}[]>, year: number, month: number): {
|
|
77
|
+
amount: number;
|
|
78
|
+
day: string | null;
|
|
79
|
+
parts: {
|
|
80
|
+
subscriptionId: string;
|
|
81
|
+
amount: number;
|
|
82
|
+
}[];
|
|
83
|
+
} | null;
|
|
84
|
+
/** A bill's total - the sum of its lines, credits below zero. */
|
|
85
|
+
export declare const billTotal: (lines: {
|
|
86
|
+
amount: number;
|
|
87
|
+
}[]) => number;
|
|
88
|
+
/** How a series moved: the last month over the one before, and over the same month a year before - null when nothing to compare. */
|
|
89
|
+
export declare function changesOf(amounts: number[]): {
|
|
90
|
+
monthOverMonth: number | null;
|
|
91
|
+
yearOverYear: number | null;
|
|
92
|
+
};
|
|
93
|
+
export interface TrendSeries {
|
|
94
|
+
key: string;
|
|
95
|
+
accountId?: string | null;
|
|
96
|
+
subscriptionId?: string | null;
|
|
97
|
+
currency: string;
|
|
98
|
+
amounts: number[];
|
|
99
|
+
total: number;
|
|
100
|
+
monthOverMonth: number | null;
|
|
101
|
+
yearOverYear: number | null;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* The cost trend from the bills (decision 9), month by month from `from` to `to`, in each bill's own currency: one series
|
|
105
|
+
* an account, one a subscription (lines on no subscription - tax, credits - in `rest` of the account), one a currency in
|
|
106
|
+
* all. Computed, never stored.
|
|
107
|
+
*/
|
|
108
|
+
export declare function trendOf(bills: BillFacts[], from: string, to: string): {
|
|
109
|
+
months: string[];
|
|
110
|
+
all: TrendSeries[];
|
|
111
|
+
accounts: TrendSeries[];
|
|
112
|
+
subscriptions: TrendSeries[];
|
|
113
|
+
};
|
|
114
|
+
export type SaasAlertKind = 'usage-over-budget' | 'bill-increased' | 'seat-bill-differs';
|
|
115
|
+
/**
|
|
116
|
+
* What a month's bill of an account is worth telling (decision 9): usage over its budget, the bill up more than the
|
|
117
|
+
* account's rate over the month before, a seat plan billed other than its seats times its price (a price rise, a seat
|
|
118
|
+
* added by the vendor).
|
|
119
|
+
*/
|
|
120
|
+
export declare function alertsOf(account: {
|
|
121
|
+
id: string;
|
|
122
|
+
increaseAlertRate?: number | null;
|
|
123
|
+
billingDay?: number | null;
|
|
124
|
+
}, subs: SubscriptionFacts[], bill: BillFacts, previous?: BillFacts | null): {
|
|
125
|
+
kind: SaasAlertKind;
|
|
126
|
+
subscriptionId?: string;
|
|
127
|
+
amount: number;
|
|
128
|
+
limit: number;
|
|
129
|
+
}[];
|
|
130
|
+
/** Whether its renewal is near: from so many days before its renewal day (none - the day itself) to the day, while in use. */
|
|
131
|
+
export declare function renewalDue(sub: SubscriptionFacts & {
|
|
132
|
+
noticeDays?: number | null;
|
|
133
|
+
}, today: string): boolean;
|
|
134
|
+
/** Whether a seat is held on a day. */
|
|
135
|
+
export declare const seatHeld: (seat: {
|
|
136
|
+
fromOn?: string | null;
|
|
137
|
+
untilOn?: string | null;
|
|
138
|
+
}, today: string) => boolean;
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.seatHeld = exports.billTotal = exports.monthKey = exports.USAGE_AVERAGE_MONTHS = exports.DEFAULT_INCREASE_ALERT_RATE = exports.MONTH = exports.DATE = exports.SaasSubscriptionStatus = exports.SaasBilling = exports.SaasAccountStatus = void 0;
|
|
4
|
+
exports.shiftMonth = shiftMonth;
|
|
5
|
+
exports.monthsFrom = monthsFrom;
|
|
6
|
+
exports.lastBilledMonth = lastBilledMonth;
|
|
7
|
+
exports.billedIn = billedIn;
|
|
8
|
+
exports.cycleAmountOf = cycleAmountOf;
|
|
9
|
+
exports.usageEstimate = usageEstimate;
|
|
10
|
+
exports.expectedOfMonth = expectedOfMonth;
|
|
11
|
+
exports.changesOf = changesOf;
|
|
12
|
+
exports.trendOf = trendOf;
|
|
13
|
+
exports.alertsOf = alertsOf;
|
|
14
|
+
exports.renewalDue = renewalDue;
|
|
15
|
+
const type_graphql_1 = require("type-graphql");
|
|
16
|
+
/* The day rule of accounting's recurring items, read without its entities (ADR-0177 decision 4: one rule for both). */
|
|
17
|
+
const recurring_schedule_js_1 = require("@things-factory/accounting/dist-server/service/recurring-cash-item/recurring-schedule.js");
|
|
18
|
+
/*
|
|
19
|
+
* The subscriptions' rules (ADR-0177) - when a subscription is billed and for how much, what a month is still expected
|
|
20
|
+
* to bring, the cost trend from the bills and what in them is worth telling. Apart from the entities, so the screens'
|
|
21
|
+
* words and the tests read them without a type-graphql class. Pure.
|
|
22
|
+
*/
|
|
23
|
+
var SaasAccountStatus;
|
|
24
|
+
(function (SaasAccountStatus) {
|
|
25
|
+
SaasAccountStatus["ACTIVE"] = "ACTIVE";
|
|
26
|
+
SaasAccountStatus["CLOSED"] = "CLOSED";
|
|
27
|
+
})(SaasAccountStatus || (exports.SaasAccountStatus = SaasAccountStatus = {}));
|
|
28
|
+
(0, type_graphql_1.registerEnumType)(SaasAccountStatus, { name: 'SaasAccountStatus', description: 'Whether an account is in use or closed' });
|
|
29
|
+
var SaasBilling;
|
|
30
|
+
(function (SaasBilling) {
|
|
31
|
+
SaasBilling["SEAT"] = "SEAT";
|
|
32
|
+
SaasBilling["FIXED"] = "FIXED";
|
|
33
|
+
SaasBilling["USAGE"] = "USAGE";
|
|
34
|
+
SaasBilling["COMMITMENT"] = "COMMITMENT";
|
|
35
|
+
SaasBilling["OWNED"] = "OWNED";
|
|
36
|
+
})(SaasBilling || (exports.SaasBilling = SaasBilling = {}));
|
|
37
|
+
(0, type_graphql_1.registerEnumType)(SaasBilling, {
|
|
38
|
+
name: 'SaasBilling',
|
|
39
|
+
description: 'How a subscription is paid for - seats times a unit price, a fixed fee, metered usage, a commitment for a term, or a license owned (the asset register carries its cost)'
|
|
40
|
+
});
|
|
41
|
+
var SaasSubscriptionStatus;
|
|
42
|
+
(function (SaasSubscriptionStatus) {
|
|
43
|
+
SaasSubscriptionStatus["ACTIVE"] = "ACTIVE";
|
|
44
|
+
SaasSubscriptionStatus["CANCELLING"] = "CANCELLING";
|
|
45
|
+
SaasSubscriptionStatus["CANCELLED"] = "CANCELLED";
|
|
46
|
+
})(SaasSubscriptionStatus || (exports.SaasSubscriptionStatus = SaasSubscriptionStatus = {}));
|
|
47
|
+
(0, type_graphql_1.registerEnumType)(SaasSubscriptionStatus, {
|
|
48
|
+
name: 'SaasSubscriptionStatus',
|
|
49
|
+
description: 'In use, not to be renewed (it ends on its renewal day) or ended'
|
|
50
|
+
});
|
|
51
|
+
exports.DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
52
|
+
exports.MONTH = /^\d{4}-(0[1-9]|1[0-2])$/;
|
|
53
|
+
/** An account's bill rising more than this over the month before is told, unless the account names its own rate. */
|
|
54
|
+
exports.DEFAULT_INCREASE_ALERT_RATE = 0.2;
|
|
55
|
+
/** Metered usage is expected at the average of so many latest bills. */
|
|
56
|
+
exports.USAGE_AVERAGE_MONTHS = 3;
|
|
57
|
+
const num = (value) => (value === null || value === undefined || value === '' ? 0 : Number(value) || 0);
|
|
58
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
59
|
+
const monthKey = (year, month) => `${year}-${pad(month)}`;
|
|
60
|
+
exports.monthKey = monthKey;
|
|
61
|
+
/** The month a number of months from a `YYYY-MM`. */
|
|
62
|
+
function shiftMonth(key, by) {
|
|
63
|
+
const [year, month] = key.split('-').map(Number);
|
|
64
|
+
const index = year * 12 + (month - 1) + by;
|
|
65
|
+
return (0, exports.monthKey)(Math.floor(index / 12), (index % 12) + 1);
|
|
66
|
+
}
|
|
67
|
+
/** The months from one `YYYY-MM` to another, both in. */
|
|
68
|
+
function monthsFrom(from, to) {
|
|
69
|
+
const months = [];
|
|
70
|
+
for (let key = from; key <= to; key = shiftMonth(key, 1))
|
|
71
|
+
months.push(key);
|
|
72
|
+
return months;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The last month it is billed in: a cancelled one in the month it ended, when the bill of that month falls on or before
|
|
76
|
+
* the day it ended, else the month before; one not to be renewed in the month before its renewal day. Open otherwise.
|
|
77
|
+
*/
|
|
78
|
+
function lastBilledMonth(sub, billingDay) {
|
|
79
|
+
if (sub.status === SaasSubscriptionStatus.CANCELLED && sub.endedOn) {
|
|
80
|
+
const day = Number(sub.endedOn.slice(8, 10));
|
|
81
|
+
return day >= (billingDay || 1) ? sub.endedOn.slice(0, 7) : shiftMonth(sub.endedOn.slice(0, 7), -1);
|
|
82
|
+
}
|
|
83
|
+
if (sub.status === SaasSubscriptionStatus.CANCELLING && sub.renewalOn)
|
|
84
|
+
return shiftMonth(sub.renewalOn.slice(0, 7), -1);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
/** Whether it is billed in a month - from the month it started, every so many months, to its last. An owned license never is. */
|
|
88
|
+
function billedIn(sub, year, month, billingDay) {
|
|
89
|
+
if (sub.billing === SaasBilling.OWNED || !sub.startOn)
|
|
90
|
+
return false;
|
|
91
|
+
return (0, recurring_schedule_js_1.fallsIn)({ id: sub.id, name: '', type: 'EXPENSE', amount: 0, startMonth: sub.startOn.slice(0, 7), endMonth: lastBilledMonth(sub, billingDay), everyMonths: sub.everyMonths ?? 1 }, year, month);
|
|
92
|
+
}
|
|
93
|
+
/** What one billing of it costs, as the subscription says: seats times the unit price, the fee. Usage says nothing - null. */
|
|
94
|
+
function cycleAmountOf(sub) {
|
|
95
|
+
switch (sub.billing) {
|
|
96
|
+
case SaasBilling.SEAT:
|
|
97
|
+
return num(sub.quantity) * num(sub.unitPrice);
|
|
98
|
+
case SaasBilling.FIXED:
|
|
99
|
+
case SaasBilling.COMMITMENT:
|
|
100
|
+
return num(sub.amount);
|
|
101
|
+
case SaasBilling.OWNED:
|
|
102
|
+
return 0;
|
|
103
|
+
default:
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/** Metered usage expected for a month: the average of its latest billed months (decision Q3), else its budget, else nothing. */
|
|
108
|
+
function usageEstimate(sub, billed) {
|
|
109
|
+
const byMonth = new Map();
|
|
110
|
+
for (const line of billed)
|
|
111
|
+
byMonth.set(line.month, (byMonth.get(line.month) ?? 0) + num(line.amount));
|
|
112
|
+
const latest = [...byMonth.keys()].sort().reverse().slice(0, exports.USAGE_AVERAGE_MONTHS);
|
|
113
|
+
if (latest.length)
|
|
114
|
+
return latest.reduce((sum, key) => sum + byMonth.get(key), 0) / latest.length;
|
|
115
|
+
return sub.budget === null || sub.budget === undefined ? null : num(sub.budget);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* What an account is expected to be billed in a month (decision 4): the subscriptions billed in it, each at what it says
|
|
119
|
+
* or, for usage, at its estimate - on the account's billing day. Nothing for a closed account, a sub-account or a month
|
|
120
|
+
* nothing falls in.
|
|
121
|
+
*/
|
|
122
|
+
function expectedOfMonth(account, subs, billedBySubscription, year, month) {
|
|
123
|
+
if (account.status !== SaasAccountStatus.ACTIVE || account.parentId)
|
|
124
|
+
return null;
|
|
125
|
+
const parts = [];
|
|
126
|
+
for (const sub of subs) {
|
|
127
|
+
if (!billedIn(sub, year, month, account.billingDay))
|
|
128
|
+
continue;
|
|
129
|
+
const amount = cycleAmountOf(sub) ?? usageEstimate(sub, billedBySubscription.get(sub.id) ?? []);
|
|
130
|
+
if (amount)
|
|
131
|
+
parts.push({ subscriptionId: sub.id, amount });
|
|
132
|
+
}
|
|
133
|
+
if (!parts.length)
|
|
134
|
+
return null;
|
|
135
|
+
return { amount: parts.reduce((sum, one) => sum + one.amount, 0), day: (0, recurring_schedule_js_1.recurringDay)({ dayOfMonth: account.billingDay }, year, month), parts };
|
|
136
|
+
}
|
|
137
|
+
/** A bill's total - the sum of its lines, credits below zero. */
|
|
138
|
+
const billTotal = (lines) => lines.reduce((sum, line) => sum + num(line.amount), 0);
|
|
139
|
+
exports.billTotal = billTotal;
|
|
140
|
+
/** How a series moved: the last month over the one before, and over the same month a year before - null when nothing to compare. */
|
|
141
|
+
function changesOf(amounts) {
|
|
142
|
+
const last = amounts.length - 1;
|
|
143
|
+
const ratio = (now, before) => (before ? (now - before) / before : null);
|
|
144
|
+
return { monthOverMonth: last >= 1 ? ratio(amounts[last], amounts[last - 1]) : null, yearOverYear: last >= 12 ? ratio(amounts[last], amounts[last - 12]) : null };
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* The cost trend from the bills (decision 9), month by month from `from` to `to`, in each bill's own currency: one series
|
|
148
|
+
* an account, one a subscription (lines on no subscription - tax, credits - in `rest` of the account), one a currency in
|
|
149
|
+
* all. Computed, never stored.
|
|
150
|
+
*/
|
|
151
|
+
function trendOf(bills, from, to) {
|
|
152
|
+
const months = monthsFrom(from, to);
|
|
153
|
+
const index = new Map(months.map((key, i) => [key, i]));
|
|
154
|
+
const series = new Map();
|
|
155
|
+
const add = (key, base, at, amount) => {
|
|
156
|
+
let one = series.get(key);
|
|
157
|
+
if (!one)
|
|
158
|
+
series.set(key, (one = { key, ...base, amounts: months.map(() => 0), total: 0, monthOverMonth: null, yearOverYear: null }));
|
|
159
|
+
one.amounts[at] += amount;
|
|
160
|
+
};
|
|
161
|
+
for (const bill of bills) {
|
|
162
|
+
const at = index.get(bill.month);
|
|
163
|
+
if (at === undefined)
|
|
164
|
+
continue;
|
|
165
|
+
for (const line of bill.lines) {
|
|
166
|
+
const amount = num(line.amount);
|
|
167
|
+
add(`all|${bill.currency}`, { currency: bill.currency }, at, amount);
|
|
168
|
+
add(`account|${bill.accountId}|${bill.currency}`, { accountId: bill.accountId, currency: bill.currency }, at, amount);
|
|
169
|
+
add(`subscription|${line.subscriptionId ?? `rest:${bill.accountId}`}|${bill.currency}`, { accountId: bill.accountId, subscriptionId: line.subscriptionId ?? null, currency: bill.currency }, at, amount);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
for (const one of series.values()) {
|
|
173
|
+
one.total = one.amounts.reduce((sum, amount) => sum + amount, 0);
|
|
174
|
+
Object.assign(one, changesOf(one.amounts));
|
|
175
|
+
}
|
|
176
|
+
const of = (kind) => [...series.values()].filter(one => one.key.startsWith(`${kind}|`)).sort((a, b) => b.total - a.total);
|
|
177
|
+
return { months, all: of('all'), accounts: of('account'), subscriptions: of('subscription') };
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* What a month's bill of an account is worth telling (decision 9): usage over its budget, the bill up more than the
|
|
181
|
+
* account's rate over the month before, a seat plan billed other than its seats times its price (a price rise, a seat
|
|
182
|
+
* added by the vendor).
|
|
183
|
+
*/
|
|
184
|
+
function alertsOf(account, subs, bill, previous) {
|
|
185
|
+
const alerts = [];
|
|
186
|
+
const [year, month] = bill.month.split('-').map(Number);
|
|
187
|
+
for (const sub of subs) {
|
|
188
|
+
const amount = (0, exports.billTotal)(bill.lines.filter(line => line.subscriptionId === sub.id));
|
|
189
|
+
if (sub.billing === SaasBilling.USAGE && sub.budget && amount > num(sub.budget))
|
|
190
|
+
alerts.push({ kind: 'usage-over-budget', subscriptionId: sub.id, amount, limit: num(sub.budget) });
|
|
191
|
+
if (sub.billing === SaasBilling.SEAT && billedIn(sub, year, month, account.billingDay) && bill.lines.some(line => line.subscriptionId === sub.id)) {
|
|
192
|
+
const expected = cycleAmountOf(sub);
|
|
193
|
+
if (Math.abs(amount - expected) >= 0.5)
|
|
194
|
+
alerts.push({ kind: 'seat-bill-differs', subscriptionId: sub.id, amount, limit: expected });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (previous) {
|
|
198
|
+
const now = (0, exports.billTotal)(bill.lines);
|
|
199
|
+
const before = (0, exports.billTotal)(previous.lines);
|
|
200
|
+
const rate = account.increaseAlertRate ?? exports.DEFAULT_INCREASE_ALERT_RATE;
|
|
201
|
+
if (before > 0 && (now - before) / before > rate)
|
|
202
|
+
alerts.push({ kind: 'bill-increased', amount: now, limit: before * (1 + rate) });
|
|
203
|
+
}
|
|
204
|
+
return alerts;
|
|
205
|
+
}
|
|
206
|
+
/** Whether its renewal is near: from so many days before its renewal day (none - the day itself) to the day, while in use. */
|
|
207
|
+
function renewalDue(sub, today) {
|
|
208
|
+
if (sub.status !== SaasSubscriptionStatus.ACTIVE || !sub.renewalOn || sub.billing === SaasBilling.OWNED)
|
|
209
|
+
return false;
|
|
210
|
+
const from = new Date(`${sub.renewalOn}T00:00:00Z`);
|
|
211
|
+
from.setUTCDate(from.getUTCDate() - Math.max(0, num(sub.noticeDays)));
|
|
212
|
+
return today >= from.toISOString().slice(0, 10) && today <= sub.renewalOn;
|
|
213
|
+
}
|
|
214
|
+
/** Whether a seat is held on a day. */
|
|
215
|
+
const seatHeld = (seat, today) => (!seat.fromOn || seat.fromOn <= today) && (!seat.untilOn || seat.untilOn >= today);
|
|
216
|
+
exports.seatHeld = seatHeld;
|
|
217
|
+
//# sourceMappingURL=saas-rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"saas-rules.js","sourceRoot":"","sources":["../../server/service/saas-rules.ts"],"names":[],"mappings":"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","sourcesContent":["import { registerEnumType } from 'type-graphql'\n\n/* The day rule of accounting's recurring items, read without its entities (ADR-0177 decision 4: one rule for both). */\nimport { fallsIn, recurringDay } from '@things-factory/accounting/dist-server/service/recurring-cash-item/recurring-schedule.js'\n\n/*\n * The subscriptions' rules (ADR-0177) - when a subscription is billed and for how much, what a month is still expected\n * to bring, the cost trend from the bills and what in them is worth telling. Apart from the entities, so the screens'\n * words and the tests read them without a type-graphql class. Pure.\n */\n\nexport enum SaasAccountStatus {\n ACTIVE = 'ACTIVE',\n CLOSED = 'CLOSED'\n}\nregisterEnumType(SaasAccountStatus, { name: 'SaasAccountStatus', description: 'Whether an account is in use or closed' })\n\nexport enum SaasBilling {\n SEAT = 'SEAT',\n FIXED = 'FIXED',\n USAGE = 'USAGE',\n COMMITMENT = 'COMMITMENT',\n OWNED = 'OWNED'\n}\nregisterEnumType(SaasBilling, {\n name: 'SaasBilling',\n description: 'How a subscription is paid for - seats times a unit price, a fixed fee, metered usage, a commitment for a term, or a license owned (the asset register carries its cost)'\n})\n\nexport enum SaasSubscriptionStatus {\n ACTIVE = 'ACTIVE',\n CANCELLING = 'CANCELLING',\n CANCELLED = 'CANCELLED'\n}\nregisterEnumType(SaasSubscriptionStatus, {\n name: 'SaasSubscriptionStatus',\n description: 'In use, not to be renewed (it ends on its renewal day) or ended'\n})\n\nexport const DATE = /^\\d{4}-\\d{2}-\\d{2}$/\nexport const MONTH = /^\\d{4}-(0[1-9]|1[0-2])$/\n/** An account's bill rising more than this over the month before is told, unless the account names its own rate. */\nexport const DEFAULT_INCREASE_ALERT_RATE = 0.2\n/** Metered usage is expected at the average of so many latest bills. */\nexport const USAGE_AVERAGE_MONTHS = 3\n\nexport interface SubscriptionFacts {\n id: string\n billing: SaasBilling | string\n quantity?: number | null\n unitPrice?: number | null\n amount?: number | null\n budget?: number | null\n everyMonths?: number | null\n startOn: string\n renewalOn?: string | null\n endedOn?: string | null\n status: SaasSubscriptionStatus | string\n}\n\nexport interface BillFacts {\n accountId: string\n month: string\n currency: string\n lines: { subscriptionId?: string | null; amount: number }[]\n}\n\nconst num = (value: any) => (value === null || value === undefined || value === '' ? 0 : Number(value) || 0)\nconst pad = (n: number) => String(n).padStart(2, '0')\nexport const monthKey = (year: number, month: number) => `${year}-${pad(month)}`\n\n/** The month a number of months from a `YYYY-MM`. */\nexport function shiftMonth(key: string, by: number): string {\n const [year, month] = key.split('-').map(Number)\n const index = year * 12 + (month - 1) + by\n return monthKey(Math.floor(index / 12), (index % 12) + 1)\n}\n\n/** The months from one `YYYY-MM` to another, both in. */\nexport function monthsFrom(from: string, to: string): string[] {\n const months: string[] = []\n for (let key = from; key <= to; key = shiftMonth(key, 1)) months.push(key)\n return months\n}\n\n/**\n * The last month it is billed in: a cancelled one in the month it ended, when the bill of that month falls on or before\n * the day it ended, else the month before; one not to be renewed in the month before its renewal day. Open otherwise.\n */\nexport function lastBilledMonth(sub: SubscriptionFacts, billingDay?: number | null): string | null {\n if (sub.status === SaasSubscriptionStatus.CANCELLED && sub.endedOn) {\n const day = Number(sub.endedOn.slice(8, 10))\n return day >= (billingDay || 1) ? sub.endedOn.slice(0, 7) : shiftMonth(sub.endedOn.slice(0, 7), -1)\n }\n if (sub.status === SaasSubscriptionStatus.CANCELLING && sub.renewalOn) return shiftMonth(sub.renewalOn.slice(0, 7), -1)\n return null\n}\n\n/** Whether it is billed in a month - from the month it started, every so many months, to its last. An owned license never is. */\nexport function billedIn(sub: SubscriptionFacts, year: number, month: number, billingDay?: number | null): boolean {\n if (sub.billing === SaasBilling.OWNED || !sub.startOn) return false\n return fallsIn(\n { id: sub.id, name: '', type: 'EXPENSE', amount: 0, startMonth: sub.startOn.slice(0, 7), endMonth: lastBilledMonth(sub, billingDay), everyMonths: sub.everyMonths ?? 1 },\n year,\n month\n )\n}\n\n/** What one billing of it costs, as the subscription says: seats times the unit price, the fee. Usage says nothing - null. */\nexport function cycleAmountOf(sub: SubscriptionFacts): number | null {\n switch (sub.billing) {\n case SaasBilling.SEAT:\n return num(sub.quantity) * num(sub.unitPrice)\n case SaasBilling.FIXED:\n case SaasBilling.COMMITMENT:\n return num(sub.amount)\n case SaasBilling.OWNED:\n return 0\n default:\n return null\n }\n}\n\n/** Metered usage expected for a month: the average of its latest billed months (decision Q3), else its budget, else nothing. */\nexport function usageEstimate(sub: SubscriptionFacts, billed: { month: string; amount: number }[]): number | null {\n const byMonth = new Map<string, number>()\n for (const line of billed) byMonth.set(line.month, (byMonth.get(line.month) ?? 0) + num(line.amount))\n const latest = [...byMonth.keys()].sort().reverse().slice(0, USAGE_AVERAGE_MONTHS)\n if (latest.length) return latest.reduce((sum, key) => sum + byMonth.get(key)!, 0) / latest.length\n return sub.budget === null || sub.budget === undefined ? null : num(sub.budget)\n}\n\n/**\n * What an account is expected to be billed in a month (decision 4): the subscriptions billed in it, each at what it says\n * or, for usage, at its estimate - on the account's billing day. Nothing for a closed account, a sub-account or a month\n * nothing falls in.\n */\nexport function expectedOfMonth(\n account: { status: string; parentId?: string | null; billingDay?: number | null },\n subs: SubscriptionFacts[],\n billedBySubscription: Map<string, { month: string; amount: number }[]>,\n year: number,\n month: number\n): { amount: number; day: string | null; parts: { subscriptionId: string; amount: number }[] } | null {\n if (account.status !== SaasAccountStatus.ACTIVE || account.parentId) return null\n const parts: { subscriptionId: string; amount: number }[] = []\n for (const sub of subs) {\n if (!billedIn(sub, year, month, account.billingDay)) continue\n const amount = cycleAmountOf(sub) ?? usageEstimate(sub, billedBySubscription.get(sub.id) ?? [])\n if (amount) parts.push({ subscriptionId: sub.id, amount })\n }\n if (!parts.length) return null\n return { amount: parts.reduce((sum, one) => sum + one.amount, 0), day: recurringDay({ dayOfMonth: account.billingDay }, year, month), parts }\n}\n\n/** A bill's total - the sum of its lines, credits below zero. */\nexport const billTotal = (lines: { amount: number }[]) => lines.reduce((sum, line) => sum + num(line.amount), 0)\n\n/** How a series moved: the last month over the one before, and over the same month a year before - null when nothing to compare. */\nexport function changesOf(amounts: number[]): { monthOverMonth: number | null; yearOverYear: number | null } {\n const last = amounts.length - 1\n const ratio = (now: number, before: number | undefined) => (before ? (now - before) / before : null)\n return { monthOverMonth: last >= 1 ? ratio(amounts[last], amounts[last - 1]) : null, yearOverYear: last >= 12 ? ratio(amounts[last], amounts[last - 12]) : null }\n}\n\nexport interface TrendSeries {\n key: string\n accountId?: string | null\n subscriptionId?: string | null\n currency: string\n amounts: number[]\n total: number\n monthOverMonth: number | null\n yearOverYear: number | null\n}\n\n/**\n * The cost trend from the bills (decision 9), month by month from `from` to `to`, in each bill's own currency: one series\n * an account, one a subscription (lines on no subscription - tax, credits - in `rest` of the account), one a currency in\n * all. Computed, never stored.\n */\nexport function trendOf(bills: BillFacts[], from: string, to: string) {\n const months = monthsFrom(from, to)\n const index = new Map(months.map((key, i) => [key, i]))\n const series = new Map<string, TrendSeries>()\n const add = (key: string, base: Omit<TrendSeries, 'amounts' | 'total' | 'monthOverMonth' | 'yearOverYear' | 'key'>, at: number, amount: number) => {\n let one = series.get(key)\n if (!one) series.set(key, (one = { key, ...base, amounts: months.map(() => 0), total: 0, monthOverMonth: null, yearOverYear: null }))\n one.amounts[at] += amount\n }\n for (const bill of bills) {\n const at = index.get(bill.month)\n if (at === undefined) continue\n for (const line of bill.lines) {\n const amount = num(line.amount)\n add(`all|${bill.currency}`, { currency: bill.currency }, at, amount)\n add(`account|${bill.accountId}|${bill.currency}`, { accountId: bill.accountId, currency: bill.currency }, at, amount)\n add(`subscription|${line.subscriptionId ?? `rest:${bill.accountId}`}|${bill.currency}`, { accountId: bill.accountId, subscriptionId: line.subscriptionId ?? null, currency: bill.currency }, at, amount)\n }\n }\n for (const one of series.values()) {\n one.total = one.amounts.reduce((sum, amount) => sum + amount, 0)\n Object.assign(one, changesOf(one.amounts))\n }\n const of = (kind: string) => [...series.values()].filter(one => one.key.startsWith(`${kind}|`)).sort((a, b) => b.total - a.total)\n return { months, all: of('all'), accounts: of('account'), subscriptions: of('subscription') }\n}\n\nexport type SaasAlertKind = 'usage-over-budget' | 'bill-increased' | 'seat-bill-differs'\n\n/**\n * What a month's bill of an account is worth telling (decision 9): usage over its budget, the bill up more than the\n * account's rate over the month before, a seat plan billed other than its seats times its price (a price rise, a seat\n * added by the vendor).\n */\nexport function alertsOf(\n account: { id: string; increaseAlertRate?: number | null; billingDay?: number | null },\n subs: SubscriptionFacts[],\n bill: BillFacts,\n previous?: BillFacts | null\n): { kind: SaasAlertKind; subscriptionId?: string; amount: number; limit: number }[] {\n const alerts: { kind: SaasAlertKind; subscriptionId?: string; amount: number; limit: number }[] = []\n const [year, month] = bill.month.split('-').map(Number)\n for (const sub of subs) {\n const amount = billTotal(bill.lines.filter(line => line.subscriptionId === sub.id))\n if (sub.billing === SaasBilling.USAGE && sub.budget && amount > num(sub.budget)) alerts.push({ kind: 'usage-over-budget', subscriptionId: sub.id, amount, limit: num(sub.budget) })\n if (sub.billing === SaasBilling.SEAT && billedIn(sub, year, month, account.billingDay) && bill.lines.some(line => line.subscriptionId === sub.id)) {\n const expected = cycleAmountOf(sub)!\n if (Math.abs(amount - expected) >= 0.5) alerts.push({ kind: 'seat-bill-differs', subscriptionId: sub.id, amount, limit: expected })\n }\n }\n if (previous) {\n const now = billTotal(bill.lines)\n const before = billTotal(previous.lines)\n const rate = account.increaseAlertRate ?? DEFAULT_INCREASE_ALERT_RATE\n if (before > 0 && (now - before) / before > rate) alerts.push({ kind: 'bill-increased', amount: now, limit: before * (1 + rate) })\n }\n return alerts\n}\n\n/** Whether its renewal is near: from so many days before its renewal day (none - the day itself) to the day, while in use. */\nexport function renewalDue(sub: SubscriptionFacts & { noticeDays?: number | null }, today: string): boolean {\n if (sub.status !== SaasSubscriptionStatus.ACTIVE || !sub.renewalOn || sub.billing === SaasBilling.OWNED) return false\n const from = new Date(`${sub.renewalOn}T00:00:00Z`)\n from.setUTCDate(from.getUTCDate() - Math.max(0, num(sub.noticeDays)))\n return today >= from.toISOString().slice(0, 10) && today <= sub.renewalOn\n}\n\n/** Whether a seat is held on a day. */\nexport const seatHeld = (seat: { fromOn?: string | null; untilOn?: string | null }, today: string) => (!seat.fromOn || seat.fromOn <= today) && (!seat.untilOn || seat.untilOn >= today)\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { User } from '@things-factory/auth-base';
|
|
2
|
+
import { Employee } from '@things-factory/organization';
|
|
3
|
+
import { Domain } from '@things-factory/shell';
|
|
4
|
+
import { SaasSubscription } from './saas-subscription';
|
|
5
|
+
/** Who holds one place of a subscription, and from when to when (ADR-0177 decision 6) - an employee, or a name. */
|
|
6
|
+
export declare class SaasSeat {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
domain?: Domain;
|
|
9
|
+
domainId?: string;
|
|
10
|
+
subscription?: SaasSubscription;
|
|
11
|
+
subscriptionId?: string;
|
|
12
|
+
employee?: Employee;
|
|
13
|
+
employeeId?: string;
|
|
14
|
+
holder?: string;
|
|
15
|
+
fromOn?: string;
|
|
16
|
+
untilOn?: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
createdAt?: Date;
|
|
19
|
+
updatedAt?: Date;
|
|
20
|
+
deletedAt?: Date;
|
|
21
|
+
creator?: User;
|
|
22
|
+
creatorId?: string;
|
|
23
|
+
updater?: User;
|
|
24
|
+
updaterId?: string;
|
|
25
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SaasSeat = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const auth_base_1 = require("@things-factory/auth-base");
|
|
6
|
+
const organization_1 = require("@things-factory/organization");
|
|
7
|
+
const shell_1 = require("@things-factory/shell");
|
|
8
|
+
const type_graphql_1 = require("type-graphql");
|
|
9
|
+
const typeorm_1 = require("typeorm");
|
|
10
|
+
const saas_subscription_1 = require("./saas-subscription");
|
|
11
|
+
/** Who holds one place of a subscription, and from when to when (ADR-0177 decision 6) - an employee, or a name. */
|
|
12
|
+
let SaasSeat = class SaasSeat {
|
|
13
|
+
};
|
|
14
|
+
exports.SaasSeat = SaasSeat;
|
|
15
|
+
tslib_1.__decorate([
|
|
16
|
+
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
|
|
17
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.ID, { description: 'Unique identifier of the seat' }),
|
|
18
|
+
tslib_1.__metadata("design:type", String)
|
|
19
|
+
], SaasSeat.prototype, "id", void 0);
|
|
20
|
+
tslib_1.__decorate([
|
|
21
|
+
(0, typeorm_1.ManyToOne)(type => shell_1.Domain),
|
|
22
|
+
(0, type_graphql_1.Field)(type => shell_1.Domain, { nullable: true, description: 'The domain the seat belongs to' }),
|
|
23
|
+
tslib_1.__metadata("design:type", shell_1.Domain)
|
|
24
|
+
], SaasSeat.prototype, "domain", void 0);
|
|
25
|
+
tslib_1.__decorate([
|
|
26
|
+
(0, typeorm_1.RelationId)((seat) => seat.domain),
|
|
27
|
+
tslib_1.__metadata("design:type", String)
|
|
28
|
+
], SaasSeat.prototype, "domainId", void 0);
|
|
29
|
+
tslib_1.__decorate([
|
|
30
|
+
(0, typeorm_1.ManyToOne)(type => saas_subscription_1.SaasSubscription, { onDelete: 'CASCADE' }),
|
|
31
|
+
(0, type_graphql_1.Field)(type => saas_subscription_1.SaasSubscription, { nullable: true, description: 'The subscription it is a place of' }),
|
|
32
|
+
tslib_1.__metadata("design:type", saas_subscription_1.SaasSubscription)
|
|
33
|
+
], SaasSeat.prototype, "subscription", void 0);
|
|
34
|
+
tslib_1.__decorate([
|
|
35
|
+
(0, typeorm_1.RelationId)((seat) => seat.subscription),
|
|
36
|
+
tslib_1.__metadata("design:type", String)
|
|
37
|
+
], SaasSeat.prototype, "subscriptionId", void 0);
|
|
38
|
+
tslib_1.__decorate([
|
|
39
|
+
(0, typeorm_1.ManyToOne)(type => organization_1.Employee, { nullable: true, onDelete: 'SET NULL' }),
|
|
40
|
+
(0, type_graphql_1.Field)(type => organization_1.Employee, { nullable: true, description: 'The employee who holds it' }),
|
|
41
|
+
tslib_1.__metadata("design:type", organization_1.Employee)
|
|
42
|
+
], SaasSeat.prototype, "employee", void 0);
|
|
43
|
+
tslib_1.__decorate([
|
|
44
|
+
(0, typeorm_1.RelationId)((seat) => seat.employee),
|
|
45
|
+
tslib_1.__metadata("design:type", String)
|
|
46
|
+
], SaasSeat.prototype, "employeeId", void 0);
|
|
47
|
+
tslib_1.__decorate([
|
|
48
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
49
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Who holds it when it is no employee - a contractor, a shared mailbox' }),
|
|
50
|
+
tslib_1.__metadata("design:type", String)
|
|
51
|
+
], SaasSeat.prototype, "holder", void 0);
|
|
52
|
+
tslib_1.__decorate([
|
|
53
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
54
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'The day it was given - YYYY-MM-DD' }),
|
|
55
|
+
tslib_1.__metadata("design:type", String)
|
|
56
|
+
], SaasSeat.prototype, "fromOn", void 0);
|
|
57
|
+
tslib_1.__decorate([
|
|
58
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
59
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'The day it was taken back - YYYY-MM-DD' }),
|
|
60
|
+
tslib_1.__metadata("design:type", String)
|
|
61
|
+
], SaasSeat.prototype, "untilOn", void 0);
|
|
62
|
+
tslib_1.__decorate([
|
|
63
|
+
(0, typeorm_1.Column)({ type: 'text', nullable: true }),
|
|
64
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Notes - the login it uses' }),
|
|
65
|
+
tslib_1.__metadata("design:type", String)
|
|
66
|
+
], SaasSeat.prototype, "description", void 0);
|
|
67
|
+
tslib_1.__decorate([
|
|
68
|
+
(0, typeorm_1.CreateDateColumn)(),
|
|
69
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'When the seat was registered' }),
|
|
70
|
+
tslib_1.__metadata("design:type", Date)
|
|
71
|
+
], SaasSeat.prototype, "createdAt", void 0);
|
|
72
|
+
tslib_1.__decorate([
|
|
73
|
+
(0, typeorm_1.UpdateDateColumn)(),
|
|
74
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'When the seat last changed' }),
|
|
75
|
+
tslib_1.__metadata("design:type", Date)
|
|
76
|
+
], SaasSeat.prototype, "updatedAt", void 0);
|
|
77
|
+
tslib_1.__decorate([
|
|
78
|
+
(0, typeorm_1.DeleteDateColumn)(),
|
|
79
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'When the seat was deleted' }),
|
|
80
|
+
tslib_1.__metadata("design:type", Date)
|
|
81
|
+
], SaasSeat.prototype, "deletedAt", void 0);
|
|
82
|
+
tslib_1.__decorate([
|
|
83
|
+
(0, typeorm_1.ManyToOne)(type => auth_base_1.User, { nullable: true }),
|
|
84
|
+
(0, type_graphql_1.Field)(type => auth_base_1.User, { nullable: true, description: 'Who registered it' }),
|
|
85
|
+
tslib_1.__metadata("design:type", auth_base_1.User)
|
|
86
|
+
], SaasSeat.prototype, "creator", void 0);
|
|
87
|
+
tslib_1.__decorate([
|
|
88
|
+
(0, typeorm_1.RelationId)((seat) => seat.creator),
|
|
89
|
+
tslib_1.__metadata("design:type", String)
|
|
90
|
+
], SaasSeat.prototype, "creatorId", void 0);
|
|
91
|
+
tslib_1.__decorate([
|
|
92
|
+
(0, typeorm_1.ManyToOne)(type => auth_base_1.User, { nullable: true }),
|
|
93
|
+
(0, type_graphql_1.Field)(type => auth_base_1.User, { nullable: true, description: 'Who last changed it' }),
|
|
94
|
+
tslib_1.__metadata("design:type", auth_base_1.User)
|
|
95
|
+
], SaasSeat.prototype, "updater", void 0);
|
|
96
|
+
tslib_1.__decorate([
|
|
97
|
+
(0, typeorm_1.RelationId)((seat) => seat.updater),
|
|
98
|
+
tslib_1.__metadata("design:type", String)
|
|
99
|
+
], SaasSeat.prototype, "updaterId", void 0);
|
|
100
|
+
exports.SaasSeat = SaasSeat = tslib_1.__decorate([
|
|
101
|
+
(0, typeorm_1.Entity)(),
|
|
102
|
+
(0, typeorm_1.Index)('ix_saas_seat_0', (seat) => [seat.domain, seat.subscription]),
|
|
103
|
+
(0, type_graphql_1.ObjectType)({ description: 'Entity for SaasSeat — who holds one place of a subscription' })
|
|
104
|
+
], SaasSeat);
|
|
105
|
+
//# sourceMappingURL=saas-seat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"saas-seat.js","sourceRoot":"","sources":["../../server/service/saas-seat.ts"],"names":[],"mappings":"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","sourcesContent":["import { User } from '@things-factory/auth-base'\nimport { Employee } from '@things-factory/organization'\nimport { Domain } from '@things-factory/shell'\nimport { Field, ID, ObjectType } from 'type-graphql'\nimport { Column, CreateDateColumn, DeleteDateColumn, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId, UpdateDateColumn } from 'typeorm'\n\nimport { SaasSubscription } from './saas-subscription'\n\n/** Who holds one place of a subscription, and from when to when (ADR-0177 decision 6) - an employee, or a name. */\n@Entity()\n@Index('ix_saas_seat_0', (seat: SaasSeat) => [seat.domain, seat.subscription])\n@ObjectType({ description: 'Entity for SaasSeat — who holds one place of a subscription' })\nexport class SaasSeat {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier of the seat' })\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'The domain the seat belongs to' })\n domain?: Domain\n\n @RelationId((seat: SaasSeat) => seat.domain)\n domainId?: string\n\n @ManyToOne(type => SaasSubscription, { onDelete: 'CASCADE' })\n @Field(type => SaasSubscription, { nullable: true, description: 'The subscription it is a place of' })\n subscription?: SaasSubscription\n\n @RelationId((seat: SaasSeat) => seat.subscription)\n subscriptionId?: string\n\n @ManyToOne(type => Employee, { nullable: true, onDelete: 'SET NULL' })\n @Field(type => Employee, { nullable: true, description: 'The employee who holds it' })\n employee?: Employee\n\n @RelationId((seat: SaasSeat) => seat.employee)\n employeeId?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Who holds it when it is no employee - a contractor, a shared mailbox' })\n holder?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The day it was given - YYYY-MM-DD' })\n fromOn?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The day it was taken back - YYYY-MM-DD' })\n untilOn?: string\n\n @Column({ type: 'text', nullable: true })\n @Field({ nullable: true, description: 'Notes - the login it uses' })\n description?: string\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'When the seat was registered' })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'When the seat last changed' })\n updatedAt?: Date\n\n @DeleteDateColumn()\n @Field({ nullable: true, description: 'When the seat was deleted' })\n deletedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who registered it' })\n creator?: User\n\n @RelationId((seat: SaasSeat) => seat.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who last changed it' })\n updater?: User\n\n @RelationId((seat: SaasSeat) => seat.updater)\n updaterId?: string\n}\n"]}
|