@things-factory/saas 10.1.236 → 10.1.239
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/dist-client/components/saas-candidates.d.ts +33 -0
- package/dist-client/components/saas-candidates.js +157 -0
- package/dist-client/components/saas-candidates.js.map +1 -0
- package/dist-client/index.d.ts +1 -0
- package/dist-client/index.js +1 -0
- package/dist-client/index.js.map +1 -1
- package/dist-client/pages/saas-account-list.js +3 -0
- package/dist-client/pages/saas-account-list.js.map +1 -1
- package/dist-client/pages/saas-account-page.js +7 -2
- package/dist-client/pages/saas-account-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/role-tasks.js +3 -1
- package/dist-server/role-tasks.js.map +1 -1
- package/dist-server/service/checks.d.ts +4 -1
- package/dist-server/service/checks.js +5 -2
- package/dist-server/service/checks.js.map +1 -1
- package/dist-server/service/index.d.ts +4 -1
- package/dist-server/service/index.js +5 -1
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/saas-bill.d.ts +1 -0
- package/dist-server/service/saas-bill.js +5 -0
- package/dist-server/service/saas-bill.js.map +1 -1
- package/dist-server/service/saas-hidden-vendor.d.ts +19 -0
- package/dist-server/service/saas-hidden-vendor.js +53 -0
- package/dist-server/service/saas-hidden-vendor.js.map +1 -0
- package/dist-server/service/saas-mutation.d.ts +8 -1
- package/dist-server/service/saas-mutation.js +69 -0
- package/dist-server/service/saas-mutation.js.map +1 -1
- package/dist-server/service/saas-query.d.ts +2 -1
- package/dist-server/service/saas-query.js +16 -0
- package/dist-server/service/saas-query.js.map +1 -1
- package/dist-server/service/saas-rules.d.ts +41 -0
- package/dist-server/service/saas-rules.js +49 -1
- package/dist-server/service/saas-rules.js.map +1 -1
- package/dist-server/service/saas-seat.d.ts +1 -0
- package/dist-server/service/saas-seat.js +5 -0
- package/dist-server/service/saas-seat.js.map +1 -1
- package/dist-server/service/saas-service.d.ts +30 -7
- package/dist-server/service/saas-service.js +136 -17
- package/dist-server/service/saas-service.js.map +1 -1
- package/dist-server/service/saas-subscription.d.ts +2 -0
- package/dist-server/service/saas-subscription.js +10 -0
- package/dist-server/service/saas-subscription.js.map +1 -1
- package/dist-server/service/saas-sync.d.ts +69 -0
- package/dist-server/service/saas-sync.js +166 -0
- package/dist-server/service/saas-sync.js.map +1 -0
- package/dist-server/service/saas-type.d.ts +69 -0
- package/dist-server/service/saas-type.js +257 -1
- package/dist-server/service/saas-type.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/translations/en.json +24 -0
- package/translations/es.json +24 -0
- package/translations/ja.json +24 -0
- package/translations/ko.json +24 -0
- package/translations/ms.json +24 -0
- package/translations/zh.json +24 -0
|
@@ -1 +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"]}
|
|
1
|
+
{"version":3,"file":"saas-query.js","sourceRoot":"","sources":["../../server/service/saas-query.ts"],"names":[],"mappings":"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","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, saasCandidatesOf, saasChangesOf, saasCostTrend, saasPaymentsOf, saasSeatsOf, saasSubscriptionsOf, saasTrendsOf } from './saas-service'\nimport { SaasSeat } from './saas-seat'\nimport { SaasSubscription } from './saas-subscription'\nimport { SaasAccountList, SaasCandidate, 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 /* Read from the bank and card lines (ADR-0139): the bank's door; the candidates are the keepers' (`saas`) on top. */\n @Directive('@privilege(category: \"bank\", privilege: \"query\", domainOwnerGranted: false)')\n @Query(type => [SaasCandidate], { description: 'The vendors the bank and card lines show paid month after month that are no account or recurring item yet; with hidden, the ones hidden' })\n async saasCandidates(@Arg('hidden', { nullable: true }) hidden: boolean, @Ctx() context: ResolverContext): Promise<SaasCandidate[]> {\n const manager = getRepository(SaasAccount).manager\n if ((await scopeOf(context.state, 'query', manager)) !== 'all') return []\n return saasCandidatesOf(manager, context.state.domain, today(), !!hidden) as any\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"]}
|
|
@@ -136,3 +136,44 @@ export declare const seatHeld: (seat: {
|
|
|
136
136
|
fromOn?: string | null;
|
|
137
137
|
untilOn?: string | null;
|
|
138
138
|
}, today: string) => boolean;
|
|
139
|
+
/**
|
|
140
|
+
* A line of money out - a bank withdrawal or a card's charge - and whom it paid: its counterparty, or for a line no
|
|
141
|
+
* counterparty is linked to, the name written on it (`key` is the counterparty id, else `name:` and the name's key).
|
|
142
|
+
*/
|
|
143
|
+
export interface VendorLine {
|
|
144
|
+
key: string;
|
|
145
|
+
counterpartyId?: string | null;
|
|
146
|
+
name?: string | null;
|
|
147
|
+
transactionDate: string;
|
|
148
|
+
amount: number;
|
|
149
|
+
currency?: string | null;
|
|
150
|
+
bankAccountId?: string | null;
|
|
151
|
+
}
|
|
152
|
+
export interface CandidateFacts {
|
|
153
|
+
key: string;
|
|
154
|
+
/** The counterparty paid; none when the lines only name it - a new account registers it. */
|
|
155
|
+
counterpartyId: string | null;
|
|
156
|
+
/** The name written most on the lines. */
|
|
157
|
+
name: string | null;
|
|
158
|
+
/** The months money went out in, of the window. */
|
|
159
|
+
months: number;
|
|
160
|
+
/** What went out a month, on average over those months. */
|
|
161
|
+
average: number;
|
|
162
|
+
/** The first line of the window - the subscription is known to have started by then. */
|
|
163
|
+
firstDate: string;
|
|
164
|
+
lastDate: string;
|
|
165
|
+
lastAmount: number;
|
|
166
|
+
currency: string;
|
|
167
|
+
/** The account or card it went out of most. */
|
|
168
|
+
paymentAccountId: string | null;
|
|
169
|
+
/** The day of the month it went out on most; a month's last day is 31. */
|
|
170
|
+
billingDay: number | null;
|
|
171
|
+
}
|
|
172
|
+
/** At least this many months of the window paid one vendor before it looks like a subscription. */
|
|
173
|
+
export declare const CANDIDATE_MIN_MONTHS = 3;
|
|
174
|
+
/**
|
|
175
|
+
* Whom the lines say is paid month after month - in at least `CANDIDATE_MIN_MONTHS` months, in the currency paid most -
|
|
176
|
+
* each with what a new account and its subscription would start from. Those whose key is in `skip` (already an
|
|
177
|
+
* account, a recurring item, hidden) are not candidates. Largest first.
|
|
178
|
+
*/
|
|
179
|
+
export declare function candidatesOf(lines: readonly VendorLine[], skip: ReadonlySet<string>): CandidateFacts[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
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;
|
|
3
|
+
exports.CANDIDATE_MIN_MONTHS = 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
4
|
exports.shiftMonth = shiftMonth;
|
|
5
5
|
exports.monthsFrom = monthsFrom;
|
|
6
6
|
exports.lastBilledMonth = lastBilledMonth;
|
|
@@ -12,6 +12,7 @@ exports.changesOf = changesOf;
|
|
|
12
12
|
exports.trendOf = trendOf;
|
|
13
13
|
exports.alertsOf = alertsOf;
|
|
14
14
|
exports.renewalDue = renewalDue;
|
|
15
|
+
exports.candidatesOf = candidatesOf;
|
|
15
16
|
const type_graphql_1 = require("type-graphql");
|
|
16
17
|
/* The day rule of accounting's recurring items, read without its entities (ADR-0177 decision 4: one rule for both). */
|
|
17
18
|
const recurring_schedule_js_1 = require("@things-factory/accounting/dist-server/service/recurring-cash-item/recurring-schedule.js");
|
|
@@ -214,4 +215,51 @@ function renewalDue(sub, today) {
|
|
|
214
215
|
/** Whether a seat is held on a day. */
|
|
215
216
|
const seatHeld = (seat, today) => (!seat.fromOn || seat.fromOn <= today) && (!seat.untilOn || seat.untilOn >= today);
|
|
216
217
|
exports.seatHeld = seatHeld;
|
|
218
|
+
/** At least this many months of the window paid one vendor before it looks like a subscription. */
|
|
219
|
+
exports.CANDIDATE_MIN_MONTHS = 3;
|
|
220
|
+
const isLastDay = (day) => {
|
|
221
|
+
const [year, month, date] = day.split('-').map(Number);
|
|
222
|
+
return date === new Date(Date.UTC(year, month, 0)).getUTCDate();
|
|
223
|
+
};
|
|
224
|
+
const mostOf = (values) => {
|
|
225
|
+
const counts = new Map();
|
|
226
|
+
for (const value of values)
|
|
227
|
+
counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
228
|
+
return [...counts].sort((a, b) => b[1] - a[1])[0]?.[0] ?? null;
|
|
229
|
+
};
|
|
230
|
+
/**
|
|
231
|
+
* Whom the lines say is paid month after month - in at least `CANDIDATE_MIN_MONTHS` months, in the currency paid most -
|
|
232
|
+
* each with what a new account and its subscription would start from. Those whose key is in `skip` (already an
|
|
233
|
+
* account, a recurring item, hidden) are not candidates. Largest first.
|
|
234
|
+
*/
|
|
235
|
+
function candidatesOf(lines, skip) {
|
|
236
|
+
const groups = new Map();
|
|
237
|
+
for (const line of lines)
|
|
238
|
+
if (line.key && !skip.has(line.key))
|
|
239
|
+
groups.set(line.key, [...(groups.get(line.key) ?? []), line]);
|
|
240
|
+
const out = [];
|
|
241
|
+
for (const [key, all] of groups) {
|
|
242
|
+
const currency = mostOf(all.map(line => line.currency || 'KRW'));
|
|
243
|
+
const paid = all.filter(line => (line.currency || 'KRW') === currency).sort((a, b) => a.transactionDate.localeCompare(b.transactionDate));
|
|
244
|
+
const months = new Set(paid.map(line => line.transactionDate.slice(0, 7)));
|
|
245
|
+
if (months.size < exports.CANDIDATE_MIN_MONTHS)
|
|
246
|
+
continue;
|
|
247
|
+
const last = paid[paid.length - 1];
|
|
248
|
+
out.push({
|
|
249
|
+
key,
|
|
250
|
+
counterpartyId: all[0].counterpartyId ?? null,
|
|
251
|
+
name: mostOf(paid.map(line => line.name?.trim() || null).filter(Boolean)),
|
|
252
|
+
months: months.size,
|
|
253
|
+
average: Math.round(paid.reduce((sum, line) => sum + num(line.amount), 0) / months.size),
|
|
254
|
+
firstDate: paid[0].transactionDate,
|
|
255
|
+
lastDate: last.transactionDate,
|
|
256
|
+
lastAmount: num(last.amount),
|
|
257
|
+
currency,
|
|
258
|
+
paymentAccountId: mostOf(paid.map(line => line.bankAccountId ?? null)),
|
|
259
|
+
/* A month's last day is 31 - the day a short month clamps to. */
|
|
260
|
+
billingDay: mostOf(paid.map(line => (isLastDay(line.transactionDate) ? 31 : Number(line.transactionDate.slice(8, 10)))))
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
return out.sort((a, b) => b.average - a.average);
|
|
264
|
+
}
|
|
217
265
|
//# sourceMappingURL=saas-rules.js.map
|
|
@@ -1 +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"]}
|
|
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\n/* ----------------------------------------------------------------------------------------------------------------- */\n/* What the books already say is subscribed to (ADR-0177 개정 1) */\n\n/**\n * A line of money out - a bank withdrawal or a card's charge - and whom it paid: its counterparty, or for a line no\n * counterparty is linked to, the name written on it (`key` is the counterparty id, else `name:` and the name's key).\n */\nexport interface VendorLine {\n key: string\n counterpartyId?: string | null\n name?: string | null\n transactionDate: string\n amount: number\n currency?: string | null\n bankAccountId?: string | null\n}\n\nexport interface CandidateFacts {\n key: string\n /** The counterparty paid; none when the lines only name it - a new account registers it. */\n counterpartyId: string | null\n /** The name written most on the lines. */\n name: string | null\n /** The months money went out in, of the window. */\n months: number\n /** What went out a month, on average over those months. */\n average: number\n /** The first line of the window - the subscription is known to have started by then. */\n firstDate: string\n lastDate: string\n lastAmount: number\n currency: string\n /** The account or card it went out of most. */\n paymentAccountId: string | null\n /** The day of the month it went out on most; a month's last day is 31. */\n billingDay: number | null\n}\n\n/** At least this many months of the window paid one vendor before it looks like a subscription. */\nexport const CANDIDATE_MIN_MONTHS = 3\n\nconst isLastDay = (day: string) => {\n const [year, month, date] = day.split('-').map(Number)\n return date === new Date(Date.UTC(year, month, 0)).getUTCDate()\n}\n\nconst mostOf = <T>(values: T[]): T | null => {\n const counts = new Map<T, number>()\n for (const value of values) counts.set(value, (counts.get(value) ?? 0) + 1)\n return [...counts].sort((a, b) => b[1] - a[1])[0]?.[0] ?? null\n}\n\n/**\n * Whom the lines say is paid month after month - in at least `CANDIDATE_MIN_MONTHS` months, in the currency paid most -\n * each with what a new account and its subscription would start from. Those whose key is in `skip` (already an\n * account, a recurring item, hidden) are not candidates. Largest first.\n */\nexport function candidatesOf(lines: readonly VendorLine[], skip: ReadonlySet<string>): CandidateFacts[] {\n const groups = new Map<string, VendorLine[]>()\n for (const line of lines) if (line.key && !skip.has(line.key)) groups.set(line.key, [...(groups.get(line.key) ?? []), line])\n const out: CandidateFacts[] = []\n for (const [key, all] of groups) {\n const currency = mostOf(all.map(line => line.currency || 'KRW'))!\n const paid = all.filter(line => (line.currency || 'KRW') === currency).sort((a, b) => a.transactionDate.localeCompare(b.transactionDate))\n const months = new Set(paid.map(line => line.transactionDate.slice(0, 7)))\n if (months.size < CANDIDATE_MIN_MONTHS) continue\n const last = paid[paid.length - 1]\n out.push({\n key,\n counterpartyId: all[0].counterpartyId ?? null,\n name: mostOf(paid.map(line => line.name?.trim() || null).filter(Boolean)),\n months: months.size,\n average: Math.round(paid.reduce((sum, line) => sum + num(line.amount), 0) / months.size),\n firstDate: paid[0].transactionDate,\n lastDate: last.transactionDate,\n lastAmount: num(last.amount),\n currency,\n paymentAccountId: mostOf(paid.map(line => line.bankAccountId ?? null)),\n /* A month's last day is 31 - the day a short month clamps to. */\n billingDay: mostOf(paid.map(line => (isLastDay(line.transactionDate) ? 31 : Number(line.transactionDate.slice(8, 10)))))\n })\n }\n return out.sort((a, b) => b.average - a.average)\n}\n"]}
|
|
@@ -49,6 +49,11 @@ tslib_1.__decorate([
|
|
|
49
49
|
(0, type_graphql_1.Field)({ nullable: true, description: 'Who holds it when it is no employee - a contractor, a shared mailbox' }),
|
|
50
50
|
tslib_1.__metadata("design:type", String)
|
|
51
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 login it is held by at the vendor - a Microsoft 365 user name; set by a sync, which opens and closes it" }),
|
|
55
|
+
tslib_1.__metadata("design:type", String)
|
|
56
|
+
], SaasSeat.prototype, "login", void 0);
|
|
52
57
|
tslib_1.__decorate([
|
|
53
58
|
(0, typeorm_1.Column)({ nullable: true }),
|
|
54
59
|
(0, type_graphql_1.Field)({ nullable: true, description: 'The day it was given - YYYY-MM-DD' }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"saas-seat.js","sourceRoot":"","sources":["../../server/service/saas-seat.ts"],"names":[],"mappings":";;;;AAAA,yDAAgD;AAChD,+DAAuD;AACvD,iDAA8C;AAC9C,+CAAoD;AACpD,qCAAoJ;AAEpJ,2DAAsD;AAEtD,mHAAmH;AAI5G,IAAM,QAAQ,GAAd,MAAM,QAAQ;
|
|
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 login it is held by at the vendor - a Microsoft 365 user name; set by a sync, which opens and closes it\" })\n login?: 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"]}
|
|
@@ -5,6 +5,7 @@ import { EntityManager } from 'typeorm';
|
|
|
5
5
|
import { SaasScope } from './saas-access';
|
|
6
6
|
import { SaasAccount } from './saas-account';
|
|
7
7
|
import { SaasBill } from './saas-bill';
|
|
8
|
+
import { CandidateFacts } from './saas-rules';
|
|
8
9
|
import { SaasSeat } from './saas-seat';
|
|
9
10
|
import { SaasSubscription } from './saas-subscription';
|
|
10
11
|
import { SaasTrend } from './saas-trend-type';
|
|
@@ -100,20 +101,42 @@ export declare function saasCostTrend(tx: EntityManager, domain: Domain, from: s
|
|
|
100
101
|
}[];
|
|
101
102
|
}>;
|
|
102
103
|
/**
|
|
103
|
-
* What
|
|
104
|
-
* - the bank
|
|
105
|
-
*
|
|
104
|
+
* What went out to an account's vendor (ADR-0177 decision 10, the user 2026-10-30: 「이테크 입금 이력이 있을텐데」;
|
|
105
|
+
* 개정 1) - the bank withdrawals and the card charges (accounting, CONFIRMED and CARD) to the account's counterparty,
|
|
106
|
+
* newest first. A line takes its counterparty when it lands (accounting `saveTransactions`), so a vendor's lines are
|
|
107
|
+
* its linked ones. Only lines in the account's currency count toward its months.
|
|
106
108
|
*/
|
|
107
|
-
export declare function
|
|
108
|
-
/** The bank lines that paid an account's vendor, every one - for its 360. */
|
|
109
|
+
export declare function vendorLinesOf(tx: EntityManager, domain: Domain, counterpartyIds: string[], from?: string, to?: string): Promise<Transaction[]>;
|
|
110
|
+
/** The bank and card lines that paid an account's vendor, every one - for its 360. */
|
|
109
111
|
export declare function saasPaymentsOf(tx: EntityManager, domain: Domain, scope: SaasScope, accountId: string): Promise<any[] | Transaction[]>;
|
|
110
112
|
/**
|
|
111
113
|
* Each account's last twelve months before this one and the next three expected (`saasExpected`). A month is its bill
|
|
112
|
-
* when one is written; else, for a reader of the bank lines (`withBank`), what
|
|
113
|
-
* (decision 10); else empty. One read for all the accounts of a list.
|
|
114
|
+
* when one is written; else, for a reader of the bank lines (`withBank`), what went out to its vendor that month from
|
|
115
|
+
* the bank or a card (decision 10, 개정 1); else empty. One read for all the accounts of a list.
|
|
114
116
|
*/
|
|
115
117
|
export declare function saasTrendsOf(tx: EntityManager, domain: Domain, accounts: SaasAccount[], today: string, withBank?: boolean): Promise<Map<string, SaasTrend>>;
|
|
116
118
|
/** What moved on an account and on each of its subscriptions, newest first. */
|
|
117
119
|
export declare function saasChangesOf(tx: EntityManager, domain: Domain, scope: SaasScope, accountId: string): Promise<(import("@things-factory/fact-change").FactChange & {
|
|
118
120
|
subject: string;
|
|
119
121
|
})[]>;
|
|
122
|
+
/** A candidate with its vendor's name, for the list. */
|
|
123
|
+
export type SaasCandidateRow = CandidateFacts & {
|
|
124
|
+
counterpartyName: string;
|
|
125
|
+
hidden: boolean;
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* Whom the bank and card lines of the last twelve months show paid month after month that is no account yet and no
|
|
129
|
+
* recurring item - what a keeper may make an account of with one press. A line on a subject outside profit and loss
|
|
130
|
+
* (a card bill, a tax, a transfer, a loan) pays no vendor, and a person (salary, expenses) is no vendor. With `hidden`,
|
|
131
|
+
* the ones a keeper hid instead.
|
|
132
|
+
*/
|
|
133
|
+
export declare function saasCandidatesOf(tx: EntityManager, domain: Domain, today: string, hidden?: boolean): Promise<SaasCandidateRow[]>;
|
|
134
|
+
/**
|
|
135
|
+
* Make an account of each candidate - named after the vendor, paid from where it was paid most, on the day it was paid
|
|
136
|
+
* most - with one usage subscription budgeted at the monthly average, so the months to come are expected at it until
|
|
137
|
+
* bills say otherwise. A vendor the lines only name is registered as a counterparty first; the lines written with its
|
|
138
|
+
* name are linked to it (accounting `linkLinesByName`). One that is no candidate any more is refused.
|
|
139
|
+
*/
|
|
140
|
+
export declare function createSaasAccountsFromCandidates(tx: EntityManager, domain: Domain, user: any, keys: string[], today: string): Promise<SaasAccount[]>;
|
|
141
|
+
/** Hide candidates - by their keys - or show them again. */
|
|
142
|
+
export declare function hideSaasCandidates(tx: EntityManager, domain: Domain, user: any, keys: string[], hide: boolean): Promise<boolean>;
|