@powerhousedao/service-offering 1.0.0-dev.4 → 1.0.0-dev.6

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.
Files changed (235) hide show
  1. package/dist/document-models/facet/v1/actions.d.ts +3 -1
  2. package/dist/document-models/facet/v1/actions.d.ts.map +1 -1
  3. package/dist/document-models/facet/v1/gen/controller.d.ts +4 -0
  4. package/dist/document-models/facet/v1/gen/controller.d.ts.map +1 -0
  5. package/dist/document-models/facet/v1/gen/controller.js +3 -0
  6. package/dist/document-models/facet/v1/gen/document-model.d.ts.map +1 -1
  7. package/dist/document-models/facet/v1/gen/document-model.js +31 -7
  8. package/dist/document-models/facet/v1/gen/document-schema.d.ts +6 -6
  9. package/dist/document-models/facet/v1/gen/index.d.ts +1 -0
  10. package/dist/document-models/facet/v1/gen/index.d.ts.map +1 -1
  11. package/dist/document-models/facet/v1/gen/index.js +1 -0
  12. package/dist/document-models/facet/v1/gen/option-management/error.d.ts +27 -1
  13. package/dist/document-models/facet/v1/gen/option-management/error.d.ts.map +1 -1
  14. package/dist/document-models/facet/v1/gen/option-management/error.js +23 -1
  15. package/dist/document-models/facet/v1/gen/ph-factories.js +2 -2
  16. package/dist/document-models/facet/v1/gen/schema/types.d.ts +2 -2
  17. package/dist/document-models/facet/v1/gen/schema/types.d.ts.map +1 -1
  18. package/dist/document-models/facet/v1/gen/schema/zod.js +2 -2
  19. package/dist/document-models/facet/v1/gen/utils.js +2 -2
  20. package/dist/document-models/facet/v1/module.d.ts +1 -1
  21. package/dist/document-models/facet/v1/module.d.ts.map +1 -1
  22. package/dist/document-models/facet/v1/module.js +4 -1
  23. package/dist/document-models/resource-instance/v1/actions.d.ts +4 -1
  24. package/dist/document-models/resource-instance/v1/actions.d.ts.map +1 -1
  25. package/dist/document-models/resource-instance/v1/gen/configuration-management/error.d.ts +34 -1
  26. package/dist/document-models/resource-instance/v1/gen/configuration-management/error.d.ts.map +1 -1
  27. package/dist/document-models/resource-instance/v1/gen/configuration-management/error.js +32 -1
  28. package/dist/document-models/resource-instance/v1/gen/controller.d.ts +4 -0
  29. package/dist/document-models/resource-instance/v1/gen/controller.d.ts.map +1 -0
  30. package/dist/document-models/resource-instance/v1/gen/controller.js +3 -0
  31. package/dist/document-models/resource-instance/v1/gen/document-model.d.ts.map +1 -1
  32. package/dist/document-models/resource-instance/v1/gen/document-model.js +222 -70
  33. package/dist/document-models/resource-instance/v1/gen/document-schema.d.ts +0 -6
  34. package/dist/document-models/resource-instance/v1/gen/document-schema.d.ts.map +1 -1
  35. package/dist/document-models/resource-instance/v1/gen/index.d.ts +1 -0
  36. package/dist/document-models/resource-instance/v1/gen/index.d.ts.map +1 -1
  37. package/dist/document-models/resource-instance/v1/gen/index.js +1 -0
  38. package/dist/document-models/resource-instance/v1/gen/instance-management/actions.d.ts +6 -2
  39. package/dist/document-models/resource-instance/v1/gen/instance-management/actions.d.ts.map +1 -1
  40. package/dist/document-models/resource-instance/v1/gen/instance-management/creators.d.ts +3 -2
  41. package/dist/document-models/resource-instance/v1/gen/instance-management/creators.d.ts.map +1 -1
  42. package/dist/document-models/resource-instance/v1/gen/instance-management/creators.js +2 -1
  43. package/dist/document-models/resource-instance/v1/gen/instance-management/error.d.ts +98 -1
  44. package/dist/document-models/resource-instance/v1/gen/instance-management/error.d.ts.map +1 -1
  45. package/dist/document-models/resource-instance/v1/gen/instance-management/error.js +112 -1
  46. package/dist/document-models/resource-instance/v1/gen/instance-management/operations.d.ts +2 -1
  47. package/dist/document-models/resource-instance/v1/gen/instance-management/operations.d.ts.map +1 -1
  48. package/dist/document-models/resource-instance/v1/gen/ph-factories.d.ts.map +1 -1
  49. package/dist/document-models/resource-instance/v1/gen/ph-factories.js +0 -2
  50. package/dist/document-models/resource-instance/v1/gen/reducer.d.ts.map +1 -1
  51. package/dist/document-models/resource-instance/v1/gen/reducer.js +6 -1
  52. package/dist/document-models/resource-instance/v1/gen/schema/types.d.ts +3 -3
  53. package/dist/document-models/resource-instance/v1/gen/schema/types.d.ts.map +1 -1
  54. package/dist/document-models/resource-instance/v1/gen/schema/zod.d.ts +2 -1
  55. package/dist/document-models/resource-instance/v1/gen/schema/zod.d.ts.map +1 -1
  56. package/dist/document-models/resource-instance/v1/gen/schema/zod.js +5 -3
  57. package/dist/document-models/resource-instance/v1/gen/utils.d.ts.map +1 -1
  58. package/dist/document-models/resource-instance/v1/gen/utils.js +0 -2
  59. package/dist/document-models/resource-instance/v1/module.d.ts +1 -1
  60. package/dist/document-models/resource-instance/v1/module.d.ts.map +1 -1
  61. package/dist/document-models/resource-instance/v1/module.js +4 -1
  62. package/dist/document-models/resource-instance/v1/src/reducers/configuration-management.d.ts.map +1 -1
  63. package/dist/document-models/resource-instance/v1/src/reducers/configuration-management.js +57 -53
  64. package/dist/document-models/resource-instance/v1/src/reducers/instance-management.d.ts.map +1 -1
  65. package/dist/document-models/resource-instance/v1/src/reducers/instance-management.js +57 -21
  66. package/dist/document-models/resource-instance/v1/tests/instance-management.test.js +11 -1
  67. package/dist/document-models/resource-template/v1/actions.d.ts +3 -1
  68. package/dist/document-models/resource-template/v1/actions.d.ts.map +1 -1
  69. package/dist/document-models/resource-template/v1/gen/audience-management/error.d.ts +20 -1
  70. package/dist/document-models/resource-template/v1/gen/audience-management/error.d.ts.map +1 -1
  71. package/dist/document-models/resource-template/v1/gen/audience-management/error.js +16 -1
  72. package/dist/document-models/resource-template/v1/gen/controller.d.ts +4 -0
  73. package/dist/document-models/resource-template/v1/gen/controller.d.ts.map +1 -0
  74. package/dist/document-models/resource-template/v1/gen/controller.js +3 -0
  75. package/dist/document-models/resource-template/v1/gen/document-model.d.ts.map +1 -1
  76. package/dist/document-models/resource-template/v1/gen/document-model.js +207 -89
  77. package/dist/document-models/resource-template/v1/gen/document-schema.d.ts +12 -12
  78. package/dist/document-models/resource-template/v1/gen/facet-targeting/error.d.ts +27 -1
  79. package/dist/document-models/resource-template/v1/gen/facet-targeting/error.d.ts.map +1 -1
  80. package/dist/document-models/resource-template/v1/gen/facet-targeting/error.js +23 -1
  81. package/dist/document-models/resource-template/v1/gen/index.d.ts +1 -0
  82. package/dist/document-models/resource-template/v1/gen/index.d.ts.map +1 -1
  83. package/dist/document-models/resource-template/v1/gen/index.js +1 -0
  84. package/dist/document-models/resource-template/v1/gen/option-group-management/error.d.ts +27 -1
  85. package/dist/document-models/resource-template/v1/gen/option-group-management/error.d.ts.map +1 -1
  86. package/dist/document-models/resource-template/v1/gen/option-group-management/error.js +23 -1
  87. package/dist/document-models/resource-template/v1/gen/ph-factories.js +3 -3
  88. package/dist/document-models/resource-template/v1/gen/schema/types.d.ts +4 -4
  89. package/dist/document-models/resource-template/v1/gen/schema/types.d.ts.map +1 -1
  90. package/dist/document-models/resource-template/v1/gen/schema/zod.js +4 -4
  91. package/dist/document-models/resource-template/v1/gen/service-management/error.d.ts +51 -1
  92. package/dist/document-models/resource-template/v1/gen/service-management/error.d.ts.map +1 -1
  93. package/dist/document-models/resource-template/v1/gen/service-management/error.js +49 -1
  94. package/dist/document-models/resource-template/v1/gen/utils.js +3 -3
  95. package/dist/document-models/resource-template/v1/module.d.ts +1 -1
  96. package/dist/document-models/resource-template/v1/module.d.ts.map +1 -1
  97. package/dist/document-models/resource-template/v1/module.js +4 -1
  98. package/dist/document-models/resource-template/v1/src/reducers/option-group-management.d.ts.map +1 -1
  99. package/dist/document-models/resource-template/v1/src/reducers/option-group-management.js +2 -21
  100. package/dist/document-models/service-offering/v1/actions.d.ts +3 -1
  101. package/dist/document-models/service-offering/v1/actions.d.ts.map +1 -1
  102. package/dist/document-models/service-offering/v1/gen/controller.d.ts +4 -0
  103. package/dist/document-models/service-offering/v1/gen/controller.d.ts.map +1 -0
  104. package/dist/document-models/service-offering/v1/gen/controller.js +3 -0
  105. package/dist/document-models/service-offering/v1/gen/document-model.d.ts.map +1 -1
  106. package/dist/document-models/service-offering/v1/gen/document-model.js +421 -199
  107. package/dist/document-models/service-offering/v1/gen/document-schema.d.ts +9 -9
  108. package/dist/document-models/service-offering/v1/gen/index.d.ts +1 -0
  109. package/dist/document-models/service-offering/v1/gen/index.d.ts.map +1 -1
  110. package/dist/document-models/service-offering/v1/gen/index.js +1 -0
  111. package/dist/document-models/service-offering/v1/gen/offering/error.d.ts +41 -1
  112. package/dist/document-models/service-offering/v1/gen/offering/error.d.ts.map +1 -1
  113. package/dist/document-models/service-offering/v1/gen/offering/error.js +37 -1
  114. package/dist/document-models/service-offering/v1/gen/option-groups/error.d.ts +55 -1
  115. package/dist/document-models/service-offering/v1/gen/option-groups/error.d.ts.map +1 -1
  116. package/dist/document-models/service-offering/v1/gen/option-groups/error.js +53 -1
  117. package/dist/document-models/service-offering/v1/gen/ph-factories.js +3 -3
  118. package/dist/document-models/service-offering/v1/gen/schema/types.d.ts +134 -61
  119. package/dist/document-models/service-offering/v1/gen/schema/types.d.ts.map +1 -1
  120. package/dist/document-models/service-offering/v1/gen/schema/zod.d.ts +35 -10
  121. package/dist/document-models/service-offering/v1/gen/schema/zod.d.ts.map +1 -1
  122. package/dist/document-models/service-offering/v1/gen/schema/zod.js +182 -64
  123. package/dist/document-models/service-offering/v1/gen/services/error.d.ts +20 -1
  124. package/dist/document-models/service-offering/v1/gen/services/error.d.ts.map +1 -1
  125. package/dist/document-models/service-offering/v1/gen/services/error.js +16 -1
  126. package/dist/document-models/service-offering/v1/gen/tiers/error.d.ts +100 -1
  127. package/dist/document-models/service-offering/v1/gen/tiers/error.d.ts.map +1 -1
  128. package/dist/document-models/service-offering/v1/gen/tiers/error.js +106 -1
  129. package/dist/document-models/service-offering/v1/gen/utils.js +4 -4
  130. package/dist/document-models/service-offering/v1/module.d.ts +1 -1
  131. package/dist/document-models/service-offering/v1/module.d.ts.map +1 -1
  132. package/dist/document-models/service-offering/v1/module.js +4 -1
  133. package/dist/document-models/service-offering/v1/src/reducers/offering.d.ts.map +1 -1
  134. package/dist/document-models/service-offering/v1/src/reducers/offering.js +20 -12
  135. package/dist/document-models/service-offering/v1/src/reducers/option-groups.d.ts.map +1 -1
  136. package/dist/document-models/service-offering/v1/src/reducers/option-groups.js +157 -39
  137. package/dist/document-models/service-offering/v1/src/reducers/services.d.ts.map +1 -1
  138. package/dist/document-models/service-offering/v1/src/reducers/services.js +17 -14
  139. package/dist/document-models/service-offering/v1/src/reducers/tiers.d.ts.map +1 -1
  140. package/dist/document-models/service-offering/v1/src/reducers/tiers.js +111 -78
  141. package/dist/document-models/service-offering/v1/src/utils.d.ts +60 -1
  142. package/dist/document-models/service-offering/v1/src/utils.d.ts.map +1 -1
  143. package/dist/document-models/service-offering/v1/src/utils.js +173 -1
  144. package/dist/document-models/service-offering/v1/tests/option-groups.test.js +1 -1
  145. package/dist/document-models/service-offering/v1/utils.d.ts +3 -0
  146. package/dist/document-models/service-offering/v1/utils.d.ts.map +1 -1
  147. package/dist/document-models/subscription-instance/v1/actions.d.ts +3 -1
  148. package/dist/document-models/subscription-instance/v1/actions.d.ts.map +1 -1
  149. package/dist/document-models/subscription-instance/v1/gen/controller.d.ts +4 -0
  150. package/dist/document-models/subscription-instance/v1/gen/controller.d.ts.map +1 -0
  151. package/dist/document-models/subscription-instance/v1/gen/controller.js +3 -0
  152. package/dist/document-models/subscription-instance/v1/gen/document-model.d.ts.map +1 -1
  153. package/dist/document-models/subscription-instance/v1/gen/document-model.js +488 -246
  154. package/dist/document-models/subscription-instance/v1/gen/document-schema.d.ts +3 -3
  155. package/dist/document-models/subscription-instance/v1/gen/index.d.ts +1 -0
  156. package/dist/document-models/subscription-instance/v1/gen/index.d.ts.map +1 -1
  157. package/dist/document-models/subscription-instance/v1/gen/index.js +1 -0
  158. package/dist/document-models/subscription-instance/v1/gen/metrics/error.d.ts +73 -1
  159. package/dist/document-models/subscription-instance/v1/gen/metrics/error.d.ts.map +1 -1
  160. package/dist/document-models/subscription-instance/v1/gen/metrics/error.js +86 -1
  161. package/dist/document-models/subscription-instance/v1/gen/ph-factories.js +1 -1
  162. package/dist/document-models/subscription-instance/v1/gen/schema/types.d.ts +199 -82
  163. package/dist/document-models/subscription-instance/v1/gen/schema/types.d.ts.map +1 -1
  164. package/dist/document-models/subscription-instance/v1/gen/schema/zod.d.ts +22 -12
  165. package/dist/document-models/subscription-instance/v1/gen/schema/zod.d.ts.map +1 -1
  166. package/dist/document-models/subscription-instance/v1/gen/schema/zod.js +230 -84
  167. package/dist/document-models/subscription-instance/v1/gen/service/error.d.ts +62 -1
  168. package/dist/document-models/subscription-instance/v1/gen/service/error.d.ts.map +1 -1
  169. package/dist/document-models/subscription-instance/v1/gen/service/error.js +60 -1
  170. package/dist/document-models/subscription-instance/v1/gen/service-group/error.d.ts +39 -1
  171. package/dist/document-models/subscription-instance/v1/gen/service-group/error.d.ts.map +1 -1
  172. package/dist/document-models/subscription-instance/v1/gen/service-group/error.js +39 -1
  173. package/dist/document-models/subscription-instance/v1/gen/subscription/error.d.ts +55 -1
  174. package/dist/document-models/subscription-instance/v1/gen/subscription/error.d.ts.map +1 -1
  175. package/dist/document-models/subscription-instance/v1/gen/subscription/error.js +51 -1
  176. package/dist/document-models/subscription-instance/v1/gen/utils.js +2 -2
  177. package/dist/document-models/subscription-instance/v1/module.d.ts +1 -1
  178. package/dist/document-models/subscription-instance/v1/module.d.ts.map +1 -1
  179. package/dist/document-models/subscription-instance/v1/module.js +4 -1
  180. package/dist/document-models/subscription-instance/v1/src/reducers/customer.d.ts.map +1 -1
  181. package/dist/document-models/subscription-instance/v1/src/reducers/customer.js +1 -0
  182. package/dist/document-models/subscription-instance/v1/src/reducers/metrics.d.ts.map +1 -1
  183. package/dist/document-models/subscription-instance/v1/src/reducers/metrics.js +70 -45
  184. package/dist/document-models/subscription-instance/v1/src/reducers/service-group.d.ts.map +1 -1
  185. package/dist/document-models/subscription-instance/v1/src/reducers/service-group.js +108 -30
  186. package/dist/document-models/subscription-instance/v1/src/reducers/service.d.ts.map +1 -1
  187. package/dist/document-models/subscription-instance/v1/src/reducers/service.js +108 -39
  188. package/dist/document-models/subscription-instance/v1/src/reducers/subscription.d.ts.map +1 -1
  189. package/dist/document-models/subscription-instance/v1/src/reducers/subscription.js +193 -35
  190. package/dist/editors/resource-instance-editor/editor.d.ts.map +1 -1
  191. package/dist/editors/resource-instance-editor/editor.js +13 -3
  192. package/dist/editors/service-offering-editor/components/ResourceTemplateSelector.d.ts.map +1 -1
  193. package/dist/editors/service-offering-editor/components/ResourceTemplateSelector.js +4 -2
  194. package/dist/editors/service-offering-editor/components/ServiceCatalog.d.ts.map +1 -1
  195. package/dist/editors/service-offering-editor/components/ServiceCatalog.js +189 -32
  196. package/dist/editors/service-offering-editor/components/TheMatrix.d.ts +1 -1
  197. package/dist/editors/service-offering-editor/components/TheMatrix.d.ts.map +1 -1
  198. package/dist/editors/service-offering-editor/components/TheMatrix.js +295 -140
  199. package/dist/editors/service-offering-editor/components/TierDefinition.d.ts.map +1 -1
  200. package/dist/editors/service-offering-editor/components/TierDefinition.js +2 -0
  201. package/dist/editors/service-offering-editor/components/TierPricingOptionsPanel.js +3 -3
  202. package/dist/editors/service-offering-editor/components/pricing-utils.d.ts.map +1 -1
  203. package/dist/editors/service-offering-editor/components/pricing-utils.js +26 -7
  204. package/dist/editors/subscription-instance-editor/components/BillingPanel.d.ts.map +1 -1
  205. package/dist/editors/subscription-instance-editor/components/BillingPanel.js +4 -4
  206. package/dist/editors/subscription-instance-editor/components/CustomerInfo.d.ts.map +1 -1
  207. package/dist/editors/subscription-instance-editor/components/CustomerInfo.js +3 -2
  208. package/dist/editors/subscription-instance-editor/components/ImportServiceConfigButton.d.ts +3 -0
  209. package/dist/editors/subscription-instance-editor/components/ImportServiceConfigButton.d.ts.map +1 -1
  210. package/dist/editors/subscription-instance-editor/components/ImportServiceConfigButton.js +12 -0
  211. package/dist/editors/subscription-instance-editor/components/MetricActions.d.ts.map +1 -1
  212. package/dist/editors/subscription-instance-editor/components/MetricActions.js +4 -2
  213. package/dist/editors/subscription-instance-editor/components/MockDataButton.d.ts.map +1 -1
  214. package/dist/editors/subscription-instance-editor/components/MockDataButton.js +214 -2
  215. package/dist/editors/subscription-instance-editor/components/OperatorNotes.js +1 -1
  216. package/dist/editors/subscription-instance-editor/components/ServicesPanel.d.ts.map +1 -1
  217. package/dist/editors/subscription-instance-editor/components/ServicesPanel.js +9 -20
  218. package/dist/editors/subscription-instance-editor/components/SubscriptionActions.d.ts.map +1 -1
  219. package/dist/editors/subscription-instance-editor/components/SubscriptionActions.js +8 -9
  220. package/dist/editors/subscription-instance-editor/components/SubscriptionHeader.d.ts.map +1 -1
  221. package/dist/editors/subscription-instance-editor/components/SubscriptionHeader.js +1 -1
  222. package/dist/editors/subscription-instance-editor/components/billing-utils.d.ts +14 -6
  223. package/dist/editors/subscription-instance-editor/components/billing-utils.d.ts.map +1 -1
  224. package/dist/editors/subscription-instance-editor/components/billing-utils.js +19 -23
  225. package/dist/editors/subscription-instance-editor/components/mapOfferingToSubscription.d.ts +16 -2
  226. package/dist/editors/subscription-instance-editor/components/mapOfferingToSubscription.d.ts.map +1 -1
  227. package/dist/editors/subscription-instance-editor/components/mapOfferingToSubscription.js +155 -6
  228. package/dist/powerhouse.manifest.json +29 -3
  229. package/dist/style.css +14 -0
  230. package/dist/subgraphs/resources-services/resolvers.d.ts +1 -1
  231. package/dist/subgraphs/resources-services/resolvers.d.ts.map +1 -1
  232. package/dist/subgraphs/resources-services/resolvers.js +273 -158
  233. package/dist/subgraphs/resources-services/schema.d.ts.map +1 -1
  234. package/dist/subgraphs/resources-services/schema.js +107 -41
  235. package/package.json +22 -18
@@ -5,8 +5,8 @@ export const documentModel = {
5
5
  name: "Powerhouse",
6
6
  website: "https://www.powerhouse.inc/",
7
7
  },
8
- extension: "phsi",
9
- description: "Tracks an individual subscription instance for a service offering, including customer info, tier selection, billing, services, service groups, and usage metrics.",
8
+ extension: "",
9
+ description: "Tracks an individual subscription instance for a service offering, including customer info, tier selection, billing, services, service groups, and usage metrics",
10
10
  specifications: [
11
11
  {
12
12
  state: {
@@ -16,200 +16,256 @@ export const documentModel = {
16
16
  initialValue: "",
17
17
  },
18
18
  global: {
19
- schema: "type SubscriptionInstanceState {\n customerId: PHID\n customerName: String\n customerEmail: String\n customerType: CustomerType\n teamMemberCount: Int\n operatorId: PHID\n serviceOfferingId: PHID\n tierName: String\n tierPricingOptionId: OID\n tierPrice: Amount_Money\n tierCurrency: Currency\n tierPricingMode: TierPricingMode\n selectedBillingCycle: BillingCycle\n globalCurrency: Currency\n resource: ResourceDocument\n status: SubscriptionStatus!\n createdAt: DateTime\n activatedSince: DateTime\n pausedSince: DateTime\n expiringSince: DateTime\n renewalDate: DateTime\n cancelledSince: DateTime\n cancellationReason: String\n autoRenew: Boolean!\n operatorNotes: String\n budget: BudgetCategory\n nextBillingDate: DateTime\n projectedBillAmount: Amount_Money\n projectedBillCurrency: Currency\n services: [SubscriptionService!]!\n serviceGroups: [ServiceGroup!]!\n}\n\ntype ResourceDocument {\n documentId: PHID!\n documentType: String!\n}\n\ntype BudgetCategory {\n id: OID!\n name: String!\n description: String\n}\n\nenum CustomerType {\n INDIVIDUAL\n TEAM\n ENTERPRISE\n}\n\nenum TierPricingMode {\n FIXED\n PER_SEAT\n CUSTOM\n}\n\nenum BillingCycle {\n MONTHLY\n QUARTERLY\n SEMI_ANNUAL\n ANNUAL\n}\n\nenum SubscriptionStatus {\n DRAFT\n ACTIVE\n PAUSED\n EXPIRING\n CANCELLED\n}\n\nenum DiscountType {\n PERCENTAGE\n FIXED_AMOUNT\n}\n\nenum DiscountSource {\n TIER\n CUSTOM\n}\n\nenum GroupCostType {\n SETUP\n RECURRING\n}\n\nenum ResetPeriod {\n DAILY\n WEEKLY\n MONTHLY\n QUARTERLY\n ANNUAL\n}\n\ntype SubscriptionService {\n id: OID!\n name: String!\n description: String\n customValue: String\n facetSelections: [FacetSelection!]!\n setupCost: ServiceCost\n recurringCost: ServiceCost\n metrics: [ServiceMetric!]!\n}\n\ntype FacetSelection {\n id: OID!\n facetName: String!\n selectedOption: String!\n}\n\ntype ServiceCost {\n amount: Amount_Money!\n currency: Currency!\n paidAmount: Amount_Money\n paidAt: DateTime\n}\n\ntype ServiceMetric {\n id: OID!\n name: String!\n unitName: String\n limit: Int\n freeLimit: Int\n paidLimit: Int\n unitCost: Amount_Money\n currentUsage: Int!\n usageResetPeriod: ResetPeriod\n nextUsageReset: DateTime\n}\n\ntype ServiceGroup {\n id: OID!\n optional: Boolean!\n name: String!\n costType: GroupCostType\n setupCost: ServiceCost\n recurringCost: ServiceCost\n services: [OID!]!\n}",
19
+ schema: "type SubscriptionInstanceState {\n customerId: PHID\n customerName: String\n customerEmail: EmailAddress\n customerType: CustomerType\n teamMemberCount: Int\n operatorId: PHID\n serviceOfferingId: PHID\n tierName: String\n tierPricingOptionId: OID\n tierPrice: Amount_Money\n tierCurrency: Currency\n tierPricingMode: TierPricingMode\n selectedBillingCycle: BillingCycle\n globalCurrency: Currency\n resource: ResourceDocument\n status: SubscriptionStatus!\n createdAt: DateTime\n activatedSince: DateTime\n pausedSince: DateTime\n expiringSince: DateTime\n renewalDate: DateTime\n cancelledSince: DateTime\n cancellationReason: String\n autoRenew: Boolean!\n operatorNotes: String\n budget: BudgetCategory\n nextBillingDate: DateTime\n projectedBillAmount: Amount_Money\n projectedBillCurrency: Currency\n services: [Service!]!\n serviceGroups: [ServiceGroup!]!\n}\n\nenum TierPricingMode {\n CALCULATED\n MANUAL_OVERRIDE\n}\n\nenum CustomerType {\n INDIVIDUAL\n TEAM\n}\n\nenum GroupCostType {\n RECURRING\n SETUP\n}\n\nenum SubscriptionStatus {\n PENDING\n ACTIVE\n PAUSED\n EXPIRING\n CANCELLED\n}\n\nenum DiscountType {\n PERCENTAGE\n FLAT_AMOUNT\n}\n\nenum DiscountSource {\n TIER_INHERITED\n GROUP_INDEPENDENT\n BUNDLE\n}\n\nenum BillingCycle {\n MONTHLY\n QUARTERLY\n SEMI_ANNUAL\n ANNUAL\n ONE_TIME\n}\n\nenum ResetPeriod {\n HOURLY\n DAILY\n WEEKLY\n MONTHLY\n QUARTERLY\n SEMI_ANNUAL\n ANNUAL\n}\n\ntype DiscountInfo {\n originalAmount: Amount_Money!\n discountType: DiscountType!\n discountValue: Float!\n source: DiscountSource!\n}\n\ntype SetupCost {\n amount: Amount_Money!\n currency: Currency!\n billingDate: DateTime\n paymentDate: DateTime\n}\n\ntype RecurringCost {\n amount: Amount_Money!\n currency: Currency!\n billingCycle: BillingCycle!\n nextBillingDate: DateTime\n lastPaymentDate: DateTime\n discount: DiscountInfo\n}\n\ntype ResourceDocument {\n id: PHID!\n label: String\n thumbnailUrl: URL\n}\n\ntype BudgetCategory {\n id: OID!\n label: String!\n}\n\ntype ServiceFacetSelection {\n id: OID!\n facetName: String!\n selectedOption: String!\n}\n\ntype ServiceMetric {\n id: OID!\n name: String!\n unitName: String!\n limit: Int\n freeLimit: Int\n paidLimit: Int\n unitCost: RecurringCost\n currentUsage: Int!\n usageResetPeriod: ResetPeriod\n nextUsageReset: DateTime\n}\n\ntype Service {\n id: OID!\n name: String\n description: String\n customValue: String\n facetSelections: [ServiceFacetSelection!]!\n setupCost: SetupCost\n recurringCost: RecurringCost\n metrics: [ServiceMetric!]!\n}\n\ntype ServiceGroup {\n id: OID!\n optional: Boolean!\n name: String!\n costType: GroupCostType\n setupCost: SetupCost\n recurringCost: RecurringCost\n services: [Service!]!\n}",
20
20
  examples: [],
21
- initialValue: '{"customerId": null, "customerName": null, "customerEmail": null, "customerType": null, "teamMemberCount": null, "operatorId": null, "serviceOfferingId": null, "tierName": null, "tierPricingOptionId": null, "tierPrice": null, "tierCurrency": null, "tierPricingMode": null, "selectedBillingCycle": null, "globalCurrency": null, "resource": null, "status": "DRAFT", "createdAt": null, "activatedSince": null, "pausedSince": null, "expiringSince": null, "renewalDate": null, "cancelledSince": null, "cancellationReason": null, "autoRenew": false, "operatorNotes": null, "budget": null, "nextBillingDate": null, "projectedBillAmount": null, "projectedBillCurrency": null, "services": [], "serviceGroups": []}',
21
+ initialValue: '{"customerId":null,"customerName":null,"customerEmail":null,"customerType":null,"teamMemberCount":null,"operatorId":null,"serviceOfferingId":null,"tierName":null,"tierPricingOptionId":null,"tierPrice":null,"tierCurrency":null,"tierPricingMode":null,"selectedBillingCycle":null,"globalCurrency":null,"resource":null,"status":"PENDING","createdAt":null,"activatedSince":null,"pausedSince":null,"expiringSince":null,"renewalDate":null,"cancelledSince":null,"cancellationReason":null,"autoRenew":false,"operatorNotes":null,"budget":null,"nextBillingDate":null,"projectedBillAmount":null,"projectedBillCurrency":null,"services":[],"serviceGroups":[]}',
22
22
  },
23
23
  },
24
24
  modules: [
25
25
  {
26
- id: "subscription",
27
- name: "Subscription",
28
- description: "Operations for managing subscription lifecycle",
26
+ id: "mod-subscription",
27
+ name: "subscription",
28
+ description: "Subscription lifecycle, customer info, tier, billing, and general management operations",
29
29
  operations: [
30
30
  {
31
- id: "initialize-subscription",
31
+ id: "op-initialize-subscription",
32
32
  name: "INITIALIZE_SUBSCRIPTION",
33
- description: "Initializes a subscription",
34
- schema: "input InitializeSubscriptionInput {\n customerId: PHID\n customerName: String\n customerEmail: String\n operatorId: PHID\n serviceOfferingId: PHID\n tierName: String\n tierPricingOptionId: OID\n tierPrice: Amount_Money\n tierCurrency: Currency\n tierPricingMode: TierPricingMode\n selectedBillingCycle: BillingCycle\n globalCurrency: Currency\n createdAt: DateTime!\n}",
35
- template: "Initializes a subscription",
36
- reducer: 'state.customerId = action.input.customerId || null;\nstate.customerName = action.input.customerName || null;\nstate.customerEmail = action.input.customerEmail || null;\nstate.operatorId = action.input.operatorId || null;\nstate.serviceOfferingId = action.input.serviceOfferingId || null;\nstate.tierName = action.input.tierName || null;\nstate.tierPricingOptionId = action.input.tierPricingOptionId || null;\nstate.tierPrice = action.input.tierPrice || null;\nstate.tierCurrency = action.input.tierCurrency || null;\nstate.tierPricingMode = action.input.tierPricingMode || null;\nstate.selectedBillingCycle = action.input.selectedBillingCycle || null;\nstate.globalCurrency = action.input.globalCurrency || null;\nstate.createdAt = action.input.createdAt;\nstate.status = "DRAFT";',
33
+ description: "Initialize a subscription from a service offering",
34
+ schema: "input InitializeFacetSelectionInput {\n id: OID!\n facetName: String!\n selectedOption: String!\n}\n\ninput DiscountInfoInitInput {\n originalAmount: Amount_Money!\n discountType: DiscountType!\n discountValue: Float!\n source: DiscountSource!\n}\n\ninput InitializeMetricInput {\n id: OID!\n name: String!\n unitName: String!\n limit: Int\n freeLimit: Int\n paidLimit: Int\n currentUsage: Int!\n usageResetPeriod: ResetPeriod\n unitCostAmount: Amount_Money\n unitCostCurrency: Currency\n unitCostBillingCycle: BillingCycle\n}\n\ninput InitializeServiceInput {\n id: OID!\n name: String\n description: String\n customValue: String\n facetSelections: [InitializeFacetSelectionInput!]\n setupAmount: Amount_Money\n setupCurrency: Currency\n recurringAmount: Amount_Money\n recurringCurrency: Currency\n recurringBillingCycle: BillingCycle\n recurringDiscount: DiscountInfoInitInput\n metrics: [InitializeMetricInput!]\n}\n\ninput InitializeServiceGroupInput {\n id: OID!\n name: String!\n optional: Boolean!\n costType: GroupCostType\n setupAmount: Amount_Money\n setupCurrency: Currency\n setupBillingDate: DateTime\n recurringAmount: Amount_Money\n recurringCurrency: Currency\n recurringBillingCycle: BillingCycle\n recurringDiscount: DiscountInfoInitInput\n services: [InitializeServiceInput!]\n}\n\ninput InitializeSubscriptionInput {\n customerId: PHID\n customerName: String\n customerEmail: EmailAddress\n serviceOfferingId: PHID\n tierName: String\n tierPricingOptionId: OID\n tierPrice: Amount_Money\n tierCurrency: Currency\n tierPricingMode: TierPricingMode\n selectedBillingCycle: BillingCycle\n globalCurrency: Currency\n resourceId: PHID\n resourceLabel: String\n resourceThumbnailUrl: URL\n autoRenew: Boolean\n createdAt: DateTime!\n services: [InitializeServiceInput!]\n serviceGroups: [InitializeServiceGroupInput!]\n}",
35
+ template: "Initialize a subscription from a service offering",
36
+ reducer: 'state.customerId = action.input.customerId || null;\nstate.customerName = action.input.customerName || null;\nstate.customerEmail = action.input.customerEmail || null;\nstate.serviceOfferingId = action.input.serviceOfferingId || null;\nstate.tierName = action.input.tierName || null;\nstate.tierPricingOptionId = action.input.tierPricingOptionId || null;\nstate.tierPrice = action.input.tierPrice || null;\nstate.tierCurrency = action.input.tierCurrency || null;\nstate.tierPricingMode = action.input.tierPricingMode || null;\nstate.selectedBillingCycle = action.input.selectedBillingCycle || null;\nstate.globalCurrency = action.input.globalCurrency || null;\nif (action.input.resourceId) {\n state.resource = {\n id: action.input.resourceId,\n label: action.input.resourceLabel || null,\n thumbnailUrl: action.input.resourceThumbnailUrl || null,\n };\n}\nstate.autoRenew = action.input.autoRenew || false;\nstate.createdAt = action.input.createdAt;\nstate.status = "PENDING";\nstate.services = (action.input.services || []).map((s) => ({\n id: s.id,\n name: s.name || null,\n description: s.description || null,\n customValue: s.customValue || null,\n facetSelections: (s.facetSelections || []).map((fs) => ({\n id: fs.id,\n facetName: fs.facetName,\n selectedOption: fs.selectedOption,\n })),\n setupCost: s.setupAmount && s.setupCurrency ? {\n amount: s.setupAmount,\n currency: s.setupCurrency,\n billingDate: null,\n paymentDate: null,\n } : null,\n recurringCost: s.recurringAmount && s.recurringCurrency && s.recurringBillingCycle ? {\n amount: s.recurringAmount,\n currency: s.recurringCurrency,\n billingCycle: s.recurringBillingCycle,\n nextBillingDate: null,\n lastPaymentDate: null,\n discount: s.recurringDiscount ? {\n originalAmount: s.recurringDiscount.originalAmount,\n discountType: s.recurringDiscount.discountType,\n discountValue: s.recurringDiscount.discountValue,\n source: s.recurringDiscount.source,\n } : null,\n } : null,\n metrics: (s.metrics || []).map((m) => ({\n id: m.id,\n name: m.name,\n unitName: m.unitName,\n limit: m.limit || null,\n freeLimit: m.freeLimit || null,\n paidLimit: m.paidLimit || null,\n unitCost: m.unitCostAmount && m.unitCostCurrency && m.unitCostBillingCycle ? {\n amount: m.unitCostAmount,\n currency: m.unitCostCurrency,\n billingCycle: m.unitCostBillingCycle,\n nextBillingDate: null,\n lastPaymentDate: null,\n discount: null,\n } : null,\n currentUsage: m.currentUsage,\n usageResetPeriod: m.usageResetPeriod || null,\n nextUsageReset: null,\n })),\n}));\nstate.serviceGroups = (action.input.serviceGroups || []).map((sg) => ({\n id: sg.id,\n name: sg.name,\n optional: sg.optional,\n costType: sg.costType || null,\n setupCost: sg.setupAmount && sg.setupCurrency ? {\n amount: sg.setupAmount,\n currency: sg.setupCurrency,\n billingDate: sg.setupBillingDate || null,\n paymentDate: null,\n } : null,\n recurringCost: sg.recurringAmount && sg.recurringCurrency && sg.recurringBillingCycle ? {\n amount: sg.recurringAmount,\n currency: sg.recurringCurrency,\n billingCycle: sg.recurringBillingCycle,\n nextBillingDate: null,\n lastPaymentDate: null,\n discount: sg.recurringDiscount ? {\n originalAmount: sg.recurringDiscount.originalAmount,\n discountType: sg.recurringDiscount.discountType,\n discountValue: sg.recurringDiscount.discountValue,\n source: sg.recurringDiscount.source,\n } : null,\n } : null,\n services: (sg.services || []).map((s) => ({\n id: s.id,\n name: s.name || null,\n description: s.description || null,\n customValue: s.customValue || null,\n facetSelections: (s.facetSelections || []).map((fs) => ({\n id: fs.id,\n facetName: fs.facetName,\n selectedOption: fs.selectedOption,\n })),\n setupCost: s.setupAmount && s.setupCurrency ? {\n amount: s.setupAmount,\n currency: s.setupCurrency,\n billingDate: null,\n paymentDate: null,\n } : null,\n recurringCost: s.recurringAmount && s.recurringCurrency && s.recurringBillingCycle ? {\n amount: s.recurringAmount,\n currency: s.recurringCurrency,\n billingCycle: s.recurringBillingCycle,\n nextBillingDate: null,\n lastPaymentDate: null,\n discount: s.recurringDiscount ? {\n originalAmount: s.recurringDiscount.originalAmount,\n discountType: s.recurringDiscount.discountType,\n discountValue: s.recurringDiscount.discountValue,\n source: s.recurringDiscount.source,\n } : null,\n } : null,\n metrics: (s.metrics || []).map((m) => ({\n id: m.id,\n name: m.name,\n unitName: m.unitName,\n limit: m.limit || null,\n freeLimit: m.freeLimit || null,\n paidLimit: m.paidLimit || null,\n unitCost: m.unitCostAmount && m.unitCostCurrency && m.unitCostBillingCycle ? {\n amount: m.unitCostAmount,\n currency: m.unitCostCurrency,\n billingCycle: m.unitCostBillingCycle,\n nextBillingDate: null,\n lastPaymentDate: null,\n discount: null,\n } : null,\n currentUsage: m.currentUsage,\n usageResetPeriod: m.usageResetPeriod || null,\n nextUsageReset: null,\n })),\n })),\n}));',
37
37
  errors: [],
38
38
  examples: [],
39
39
  scope: "global",
40
40
  },
41
41
  {
42
- id: "set-resource-document",
42
+ id: "op-set-resource-document",
43
43
  name: "SET_RESOURCE_DOCUMENT",
44
- description: "Sets the resource document reference",
45
- schema: "input SetResourceDocumentInput {\n documentId: PHID!\n documentType: String!\n}",
46
- template: "Sets the resource document reference",
47
- reducer: "state.resource = { documentId: action.input.documentId, documentType: action.input.documentType };",
44
+ description: "Link a resource document to the subscription",
45
+ schema: "input SetResourceDocumentInput {\n resourceId: PHID!\n resourceLabel: String\n resourceThumbnailUrl: URL\n}",
46
+ template: "Link a resource document to the subscription",
47
+ reducer: "state.resource = {\n id: action.input.resourceId,\n label: action.input.resourceLabel || null,\n thumbnailUrl: action.input.resourceThumbnailUrl || null,\n};",
48
48
  errors: [],
49
49
  examples: [],
50
50
  scope: "global",
51
51
  },
52
52
  {
53
- id: "update-subscription-status",
53
+ id: "op-update-subscription-status",
54
54
  name: "UPDATE_SUBSCRIPTION_STATUS",
55
- description: "Updates subscription status",
55
+ description: "Directly update the subscription status",
56
56
  schema: "input UpdateSubscriptionStatusInput {\n status: SubscriptionStatus!\n}",
57
- template: "Updates subscription status",
57
+ template: "Directly update the subscription status",
58
58
  reducer: "state.status = action.input.status;",
59
59
  errors: [],
60
60
  examples: [],
61
61
  scope: "global",
62
62
  },
63
63
  {
64
- id: "activate-subscription",
64
+ id: "op-activate-subscription",
65
65
  name: "ACTIVATE_SUBSCRIPTION",
66
- description: "Activates the subscription",
67
- schema: "input ActivateSubscriptionInput {\n activatedAt: DateTime!\n}",
68
- template: "Activates the subscription",
69
- reducer: 'state.status = "ACTIVE";\nstate.activatedSince = action.input.activatedAt;',
70
- errors: [],
66
+ description: "Activate a pending subscription",
67
+ schema: "input ActivateSubscriptionInput {\n activatedSince: DateTime!\n}",
68
+ template: "Activate a pending subscription",
69
+ reducer: 'if (state.status !== "PENDING") {\n throw new ActivateNotPendingError(`Cannot activate subscription with status ${state.status}`);\n}\nstate.status = "ACTIVE";\nstate.activatedSince = action.input.activatedSince;',
70
+ errors: [
71
+ {
72
+ id: "err-activate-not-pending",
73
+ name: "ActivateNotPendingError",
74
+ code: "ACTIVATE_NOT_PENDING",
75
+ description: "Subscription must be in PENDING status to activate",
76
+ template: "",
77
+ },
78
+ ],
71
79
  examples: [],
72
80
  scope: "global",
73
81
  },
74
82
  {
75
- id: "pause-subscription",
83
+ id: "op-pause-subscription",
76
84
  name: "PAUSE_SUBSCRIPTION",
77
- description: "Pauses the subscription",
78
- schema: "input PauseSubscriptionInput {\n pausedAt: DateTime!\n}",
79
- template: "Pauses the subscription",
80
- reducer: 'state.status = "PAUSED";\nstate.pausedSince = action.input.pausedAt;',
81
- errors: [],
85
+ description: "Pause an active subscription",
86
+ schema: "input PauseSubscriptionInput {\n pausedSince: DateTime!\n}",
87
+ template: "Pause an active subscription",
88
+ reducer: 'if (state.status !== "ACTIVE") {\n throw new PauseNotActiveError(`Cannot pause subscription with status ${state.status}`);\n}\nstate.status = "PAUSED";\nstate.pausedSince = action.input.pausedSince;',
89
+ errors: [
90
+ {
91
+ id: "err-pause-not-active",
92
+ name: "PauseNotActiveError",
93
+ code: "PAUSE_NOT_ACTIVE",
94
+ description: "Subscription must be in ACTIVE status to pause",
95
+ template: "",
96
+ },
97
+ ],
82
98
  examples: [],
83
99
  scope: "global",
84
100
  },
85
101
  {
86
- id: "set-expiring",
102
+ id: "op-set-expiring",
87
103
  name: "SET_EXPIRING",
88
- description: "Sets expiring status",
89
- schema: "input SetExpiringInput {\n expiringAt: DateTime!\n renewalDate: DateTime\n}",
90
- template: "Sets expiring status",
91
- reducer: 'state.status = "EXPIRING";\nstate.expiringSince = action.input.expiringAt;\nif (action.input.renewalDate) state.renewalDate = action.input.renewalDate;',
92
- errors: [],
104
+ description: "Mark subscription as expiring",
105
+ schema: "input SetExpiringInput {\n expiringSince: DateTime!\n}",
106
+ template: "Mark subscription as expiring",
107
+ reducer: 'if (state.status !== "ACTIVE") {\n throw new SetExpiringNotActiveError(`Cannot set expiring on subscription with status ${state.status}`);\n}\nstate.status = "EXPIRING";\nstate.expiringSince = action.input.expiringSince;',
108
+ errors: [
109
+ {
110
+ id: "err-set-expiring-not-active",
111
+ name: "SetExpiringNotActiveError",
112
+ code: "SET_EXPIRING_NOT_ACTIVE",
113
+ description: "",
114
+ template: "",
115
+ },
116
+ ],
93
117
  examples: [],
94
118
  scope: "global",
95
119
  },
96
120
  {
97
- id: "cancel-subscription",
121
+ id: "op-cancel-subscription",
98
122
  name: "CANCEL_SUBSCRIPTION",
99
- description: "Cancels the subscription",
100
- schema: "input CancelSubscriptionInput {\n cancelledAt: DateTime!\n reason: String\n}",
101
- template: "Cancels the subscription",
102
- reducer: 'state.status = "CANCELLED";\nstate.cancelledSince = action.input.cancelledAt;\nstate.cancellationReason = action.input.reason || null;',
103
- errors: [],
123
+ description: "Cancel a subscription with optional reason",
124
+ schema: "input CancelSubscriptionInput {\n cancelledSince: DateTime!\n cancellationReason: String\n}",
125
+ template: "Cancel a subscription with optional reason",
126
+ reducer: 'if (state.status === "CANCELLED") {\n throw new CancelAlreadyCancelledError("Subscription is already cancelled");\n}\nstate.status = "CANCELLED";\nstate.cancelledSince = action.input.cancelledSince;\nstate.cancellationReason = action.input.cancellationReason || null;',
127
+ errors: [
128
+ {
129
+ id: "err-cancel-already-cancelled",
130
+ name: "CancelAlreadyCancelledError",
131
+ code: "CANCEL_ALREADY_CANCELLED",
132
+ description: "",
133
+ template: "",
134
+ },
135
+ ],
104
136
  examples: [],
105
137
  scope: "global",
106
138
  },
107
139
  {
108
- id: "resume-subscription",
140
+ id: "op-resume-subscription",
109
141
  name: "RESUME_SUBSCRIPTION",
110
- description: "Resumes the subscription",
111
- schema: "input ResumeSubscriptionInput {\n resumedAt: DateTime!\n}",
112
- template: "Resumes the subscription",
113
- reducer: 'state.status = "ACTIVE";\nstate.activatedSince = action.input.resumedAt;\nstate.pausedSince = null;',
114
- errors: [],
142
+ description: "Resume a paused subscription",
143
+ schema: "input ResumeSubscriptionInput {\n timestamp: DateTime!\n}",
144
+ template: "Resume a paused subscription",
145
+ reducer: 'if (state.status !== "PAUSED") {\n throw new ResumeNotPausedError(`Cannot resume subscription with status ${state.status}`);\n}\nstate.status = "ACTIVE";\nstate.pausedSince = null;',
146
+ errors: [
147
+ {
148
+ id: "err-resume-not-paused",
149
+ name: "ResumeNotPausedError",
150
+ code: "RESUME_NOT_PAUSED",
151
+ description: "",
152
+ template: "",
153
+ },
154
+ ],
115
155
  examples: [],
116
156
  scope: "global",
117
157
  },
118
158
  {
119
- id: "renew-expiring-subscription",
159
+ id: "op-renew-expiring-subscription",
120
160
  name: "RENEW_EXPIRING_SUBSCRIPTION",
121
- description: "Renews an expiring subscription",
122
- schema: "input RenewExpiringSubscriptionInput {\n renewedAt: DateTime!\n newRenewalDate: DateTime\n}",
123
- template: "Renews an expiring subscription",
124
- reducer: 'state.status = "ACTIVE";\nstate.activatedSince = action.input.renewedAt;\nstate.expiringSince = null;\nif (action.input.newRenewalDate) state.renewalDate = action.input.newRenewalDate;',
125
- errors: [],
161
+ description: "Renew an expiring subscription",
162
+ schema: "input RenewExpiringSubscriptionInput {\n timestamp: DateTime!\n newRenewalDate: DateTime\n}",
163
+ template: "Renew an expiring subscription",
164
+ reducer: 'if (state.status !== "EXPIRING") {\n throw new RenewNotExpiringError(`Cannot renew subscription with status ${state.status}`);\n}\nstate.status = "ACTIVE";\nstate.expiringSince = null;\nstate.renewalDate = action.input.newRenewalDate || null;',
165
+ errors: [
166
+ {
167
+ id: "err-renew-not-expiring",
168
+ name: "RenewNotExpiringError",
169
+ code: "RENEW_NOT_EXPIRING",
170
+ description: "",
171
+ template: "",
172
+ },
173
+ ],
126
174
  examples: [],
127
175
  scope: "global",
128
176
  },
129
177
  {
130
- id: "set-budget-category",
178
+ id: "op-set-budget-category",
131
179
  name: "SET_BUDGET_CATEGORY",
132
- description: "Sets the budget category",
133
- schema: "input SetBudgetCategoryInput {\n id: OID!\n name: String!\n description: String\n}",
134
- template: "Sets the budget category",
135
- reducer: "state.budget = { id: action.input.id, name: action.input.name, description: action.input.description || null };",
180
+ description: "Assign a budget category",
181
+ schema: "input SetBudgetCategoryInput {\n budgetId: OID!\n budgetLabel: String!\n}",
182
+ template: "Assign a budget category",
183
+ reducer: "state.budget = {\n id: action.input.budgetId,\n label: action.input.budgetLabel,\n};",
136
184
  errors: [],
137
185
  examples: [],
138
186
  scope: "global",
139
187
  },
140
188
  {
141
- id: "remove-budget-category",
189
+ id: "op-remove-budget-category",
142
190
  name: "REMOVE_BUDGET_CATEGORY",
143
- description: "Removes the budget category",
144
- schema: "input RemoveBudgetCategoryInput {\n id: OID!\n}",
145
- template: "Removes the budget category",
146
- reducer: "state.budget = null;",
147
- errors: [],
191
+ description: "Remove budget category",
192
+ schema: "input RemoveBudgetCategoryInput {\n budgetId: OID!\n}",
193
+ template: "Remove budget category",
194
+ reducer: "if (!state.budget || state.budget.id !== action.input.budgetId) {\n throw new RemoveBudgetNotFoundError(`Budget category with ID ${action.input.budgetId} not found`);\n}\nstate.budget = null;",
195
+ errors: [
196
+ {
197
+ id: "err-remove-budget-not-found",
198
+ name: "RemoveBudgetNotFoundError",
199
+ code: "REMOVE_BUDGET_NOT_FOUND",
200
+ description: "",
201
+ template: "",
202
+ },
203
+ ],
148
204
  examples: [],
149
205
  scope: "global",
150
206
  },
151
207
  {
152
- id: "update-customer-info",
208
+ id: "op-update-customer-info",
153
209
  name: "UPDATE_CUSTOMER_INFO",
154
- description: "Updates customer info",
155
- schema: "input UpdateCustomerInfoInput {\n customerName: String\n customerEmail: String\n}",
156
- template: "Updates customer info",
157
- reducer: "if (action.input.customerName) state.customerName = action.input.customerName;\nif (action.input.customerEmail) state.customerEmail = action.input.customerEmail;",
210
+ description: "Update customer details",
211
+ schema: "input UpdateCustomerInfoInput {\n customerId: PHID\n customerName: String\n customerEmail: EmailAddress\n}",
212
+ template: "Update customer details",
213
+ reducer: "if (action.input.customerId !== undefined) state.customerId = action.input.customerId || null;\nif (action.input.customerName !== undefined) state.customerName = action.input.customerName || null;\nif (action.input.customerEmail !== undefined) state.customerEmail = action.input.customerEmail || null;",
158
214
  errors: [],
159
215
  examples: [],
160
216
  scope: "global",
161
217
  },
162
218
  {
163
- id: "update-tier-info",
219
+ id: "op-update-tier-info",
164
220
  name: "UPDATE_TIER_INFO",
165
- description: "Updates tier info",
166
- schema: "input UpdateTierInfoInput {\n tierName: String\n tierPricingOptionId: OID\n tierPrice: Amount_Money\n tierCurrency: Currency\n tierPricingMode: TierPricingMode\n selectedBillingCycle: BillingCycle\n}",
167
- template: "Updates tier info",
168
- reducer: "if (action.input.tierName) state.tierName = action.input.tierName;\nif (action.input.tierPricingOptionId) state.tierPricingOptionId = action.input.tierPricingOptionId;\nif (action.input.tierPrice) state.tierPrice = action.input.tierPrice;\nif (action.input.tierCurrency) state.tierCurrency = action.input.tierCurrency;\nif (action.input.tierPricingMode) state.tierPricingMode = action.input.tierPricingMode;\nif (action.input.selectedBillingCycle) state.selectedBillingCycle = action.input.selectedBillingCycle;",
221
+ description: "Update tier selection and pricing",
222
+ schema: "input UpdateTierInfoInput {\n tierName: String\n tierPricingOptionId: OID\n tierPrice: Amount_Money\n tierCurrency: Currency\n tierPricingMode: TierPricingMode\n}",
223
+ template: "Update tier selection and pricing",
224
+ reducer: "if (action.input.tierName !== undefined) state.tierName = action.input.tierName || null;\nif (action.input.tierPricingOptionId !== undefined) state.tierPricingOptionId = action.input.tierPricingOptionId || null;\nif (action.input.tierPrice !== undefined) state.tierPrice = action.input.tierPrice || null;\nif (action.input.tierCurrency !== undefined) state.tierCurrency = action.input.tierCurrency || null;\nif (action.input.tierPricingMode !== undefined) state.tierPricingMode = action.input.tierPricingMode || null;",
169
225
  errors: [],
170
226
  examples: [],
171
227
  scope: "global",
172
228
  },
173
229
  {
174
- id: "set-operator-notes",
230
+ id: "op-set-operator-notes",
175
231
  name: "SET_OPERATOR_NOTES",
176
- description: "Sets operator notes",
177
- schema: "input SetOperatorNotesInput {\n notes: String\n}",
178
- template: "Sets operator notes",
179
- reducer: "state.operatorNotes = action.input.notes || null;",
232
+ description: "Set operator notes",
233
+ schema: "input SetOperatorNotesInput {\n operatorNotes: String\n}",
234
+ template: "Set operator notes",
235
+ reducer: "state.operatorNotes = action.input.operatorNotes || null;",
180
236
  errors: [],
181
237
  examples: [],
182
238
  scope: "global",
183
239
  },
184
240
  {
185
- id: "set-auto-renew",
241
+ id: "op-set-auto-renew",
186
242
  name: "SET_AUTO_RENEW",
187
- description: "Sets auto-renew flag",
243
+ description: "Toggle auto-renewal",
188
244
  schema: "input SetAutoRenewInput {\n autoRenew: Boolean!\n}",
189
- template: "Sets auto-renew flag",
245
+ template: "Toggle auto-renewal",
190
246
  reducer: "state.autoRenew = action.input.autoRenew;",
191
247
  errors: [],
192
248
  examples: [],
193
249
  scope: "global",
194
250
  },
195
251
  {
196
- id: "set-renewal-date",
252
+ id: "op-set-renewal-date",
197
253
  name: "SET_RENEWAL_DATE",
198
- description: "Sets the renewal date",
254
+ description: "Set renewal date",
199
255
  schema: "input SetRenewalDateInput {\n renewalDate: DateTime!\n}",
200
- template: "Sets the renewal date",
256
+ template: "Set renewal date",
201
257
  reducer: "state.renewalDate = action.input.renewalDate;",
202
258
  errors: [],
203
259
  examples: [],
204
260
  scope: "global",
205
261
  },
206
262
  {
207
- id: "update-billing-projection",
263
+ id: "op-update-billing-projection",
208
264
  name: "UPDATE_BILLING_PROJECTION",
209
- description: "Updates billing projection",
265
+ description: "Update billing projections",
210
266
  schema: "input UpdateBillingProjectionInput {\n nextBillingDate: DateTime\n projectedBillAmount: Amount_Money\n projectedBillCurrency: Currency\n}",
211
- template: "Updates billing projection",
212
- reducer: "if (action.input.nextBillingDate) state.nextBillingDate = action.input.nextBillingDate;\nif (action.input.projectedBillAmount) state.projectedBillAmount = action.input.projectedBillAmount;\nif (action.input.projectedBillCurrency) state.projectedBillCurrency = action.input.projectedBillCurrency;",
267
+ template: "Update billing projections",
268
+ reducer: "if (action.input.nextBillingDate !== undefined) state.nextBillingDate = action.input.nextBillingDate || null;\nif (action.input.projectedBillAmount !== undefined) state.projectedBillAmount = action.input.projectedBillAmount || null;\nif (action.input.projectedBillCurrency !== undefined) state.projectedBillCurrency = action.input.projectedBillCurrency || null;",
213
269
  errors: [],
214
270
  examples: [],
215
271
  scope: "global",
@@ -217,268 +273,454 @@ export const documentModel = {
217
273
  ],
218
274
  },
219
275
  {
220
- id: "service",
221
- name: "Service",
222
- description: "Operations for managing subscription services",
276
+ id: "mod-service",
277
+ name: "service",
278
+ description: "Standalone service CRUD, cost management, facet selections, and payment tracking",
223
279
  operations: [
224
280
  {
225
- id: "add-service",
281
+ id: "op-add-service",
226
282
  name: "ADD_SERVICE",
227
- description: "Adds a service to the subscription",
228
- schema: "input AddServiceInput {\n id: OID!\n name: String!\n description: String\n customValue: String\n}",
229
- template: "Adds a service to the subscription",
230
- reducer: "state.services.push({ id: action.input.id, name: action.input.name, description: action.input.description || null, customValue: action.input.customValue || null, facetSelections: [], setupCost: null, recurringCost: null, metrics: [] });",
283
+ description: "Add a standalone service",
284
+ schema: "input DiscountServiceInfoInput {\n originalAmount: Amount_Money!\n discountType: DiscountType!\n discountValue: Float!\n source: DiscountSource!\n}\n\ninput AddServiceInput {\n serviceId: OID!\n name: String\n description: String\n customValue: String\n setupAmount: Amount_Money\n setupCurrency: Currency\n setupBillingDate: DateTime\n setupPaymentDate: DateTime\n recurringAmount: Amount_Money\n recurringCurrency: Currency\n recurringBillingCycle: BillingCycle\n recurringNextBillingDate: DateTime\n recurringLastPaymentDate: DateTime\n recurringDiscount: DiscountServiceInfoInput\n}",
285
+ template: "Add a standalone service",
286
+ reducer: "const service = {\n id: action.input.serviceId,\n name: action.input.name || null,\n description: action.input.description || null,\n customValue: action.input.customValue || null,\n facetSelections: [],\n setupCost: action.input.setupAmount && action.input.setupCurrency ? {\n amount: action.input.setupAmount,\n currency: action.input.setupCurrency,\n billingDate: action.input.setupBillingDate || null,\n paymentDate: action.input.setupPaymentDate || null,\n } : null,\n recurringCost: action.input.recurringAmount && action.input.recurringCurrency && action.input.recurringBillingCycle ? {\n amount: action.input.recurringAmount,\n currency: action.input.recurringCurrency,\n billingCycle: action.input.recurringBillingCycle,\n nextBillingDate: action.input.recurringNextBillingDate || null,\n lastPaymentDate: action.input.recurringLastPaymentDate || null,\n discount: action.input.recurringDiscount ? {\n originalAmount: action.input.recurringDiscount.originalAmount,\n discountType: action.input.recurringDiscount.discountType,\n discountValue: action.input.recurringDiscount.discountValue,\n source: action.input.recurringDiscount.source,\n } : null,\n } : null,\n metrics: [],\n};\nstate.services.push(service);",
231
287
  errors: [],
232
288
  examples: [],
233
289
  scope: "global",
234
290
  },
235
291
  {
236
- id: "remove-service",
292
+ id: "op-remove-service",
237
293
  name: "REMOVE_SERVICE",
238
- description: "Removes a service from the subscription",
239
- schema: "input RemoveServiceInput {\n id: OID!\n}",
240
- template: "Removes a service from the subscription",
241
- reducer: "const serviceIndex = state.services.findIndex(s => s.id === action.input.id);\nif (serviceIndex !== -1) { state.services.splice(serviceIndex, 1); }",
242
- errors: [],
294
+ description: "Remove a standalone service",
295
+ schema: "input RemoveServiceInput {\n serviceId: OID!\n}",
296
+ template: "Remove a standalone service",
297
+ reducer: "const index = state.services.findIndex((s) => s.id === action.input.serviceId);\nif (index === -1) {\n throw new RemoveServiceNotFoundError(`Service with ID ${action.input.serviceId} not found`);\n}\nstate.services.splice(index, 1);",
298
+ errors: [
299
+ {
300
+ id: "err-remove-service-not-found",
301
+ name: "RemoveServiceNotFoundError",
302
+ code: "REMOVE_SERVICE_NOT_FOUND",
303
+ description: "",
304
+ template: "",
305
+ },
306
+ ],
243
307
  examples: [],
244
308
  scope: "global",
245
309
  },
246
310
  {
247
- id: "update-service-setup-cost",
311
+ id: "op-update-service-setup-cost",
248
312
  name: "UPDATE_SERVICE_SETUP_COST",
249
- description: "Updates service setup cost",
250
- schema: "input UpdateServiceSetupCostInput {\n serviceId: OID!\n amount: Amount_Money!\n currency: Currency!\n}",
251
- template: "Updates service setup cost",
252
- reducer: "const service = state.services.find(s => s.id === action.input.serviceId);\nif (service) { service.setupCost = { amount: action.input.amount, currency: action.input.currency, paidAmount: null, paidAt: null }; }",
253
- errors: [],
313
+ description: "Update setup cost for a service",
314
+ schema: "input UpdateServiceSetupCostInput {\n serviceId: OID!\n amount: Amount_Money\n currency: Currency\n billingDate: DateTime\n paymentDate: DateTime\n}",
315
+ template: "Update setup cost for a service",
316
+ reducer: "const svc = state.services.find((s) => s.id === action.input.serviceId);\nif (!svc) {\n throw new UpdateServiceSetupCostNotFoundError(`Service with ID ${action.input.serviceId} not found`);\n}\nif (action.input.amount && action.input.currency) {\n svc.setupCost = {\n amount: action.input.amount,\n currency: action.input.currency,\n billingDate: action.input.billingDate || null,\n paymentDate: action.input.paymentDate || null,\n };\n} else if (svc.setupCost) {\n if (action.input.amount) svc.setupCost.amount = action.input.amount;\n if (action.input.currency) svc.setupCost.currency = action.input.currency;\n if (action.input.billingDate !== undefined) svc.setupCost.billingDate = action.input.billingDate || null;\n if (action.input.paymentDate !== undefined) svc.setupCost.paymentDate = action.input.paymentDate || null;\n}",
317
+ errors: [
318
+ {
319
+ id: "err-update-service-setup-cost-not-found",
320
+ name: "UpdateServiceSetupCostNotFoundError",
321
+ code: "UPDATE_SERVICE_SETUP_COST_NOT_FOUND",
322
+ description: "",
323
+ template: "",
324
+ },
325
+ ],
254
326
  examples: [],
255
327
  scope: "global",
256
328
  },
257
329
  {
258
- id: "update-service-recurring-cost",
330
+ id: "op-update-service-recurring-cost",
259
331
  name: "UPDATE_SERVICE_RECURRING_COST",
260
- description: "Updates service recurring cost",
261
- schema: "input UpdateServiceRecurringCostInput {\n serviceId: OID!\n amount: Amount_Money!\n currency: Currency!\n}",
262
- template: "Updates service recurring cost",
263
- reducer: "const service = state.services.find(s => s.id === action.input.serviceId);\nif (service) { service.recurringCost = { amount: action.input.amount, currency: action.input.currency, paidAmount: null, paidAt: null }; }",
264
- errors: [],
332
+ description: "Update recurring cost for a service",
333
+ schema: "input UpdateServiceRecurringCostInput {\n serviceId: OID!\n amount: Amount_Money\n currency: Currency\n billingCycle: BillingCycle\n nextBillingDate: DateTime\n lastPaymentDate: DateTime\n}",
334
+ template: "Update recurring cost for a service",
335
+ reducer: "const svc = state.services.find((s) => s.id === action.input.serviceId);\nif (!svc) {\n throw new UpdateServiceRecurringCostNotFoundError(`Service with ID ${action.input.serviceId} not found`);\n}\nif (action.input.amount && action.input.currency && action.input.billingCycle) {\n svc.recurringCost = {\n amount: action.input.amount,\n currency: action.input.currency,\n billingCycle: action.input.billingCycle,\n nextBillingDate: action.input.nextBillingDate || null,\n lastPaymentDate: action.input.lastPaymentDate || null,\n discount: svc.recurringCost?.discount || null,\n };\n} else if (svc.recurringCost) {\n if (action.input.amount) svc.recurringCost.amount = action.input.amount;\n if (action.input.currency) svc.recurringCost.currency = action.input.currency;\n if (action.input.billingCycle) svc.recurringCost.billingCycle = action.input.billingCycle;\n if (action.input.nextBillingDate !== undefined) svc.recurringCost.nextBillingDate = action.input.nextBillingDate || null;\n if (action.input.lastPaymentDate !== undefined) svc.recurringCost.lastPaymentDate = action.input.lastPaymentDate || null;\n}",
336
+ errors: [
337
+ {
338
+ id: "err-update-service-recurring-cost-not-found",
339
+ name: "UpdateServiceRecurringCostNotFoundError",
340
+ code: "UPDATE_SERVICE_RECURRING_COST_NOT_FOUND",
341
+ description: "",
342
+ template: "",
343
+ },
344
+ ],
265
345
  examples: [],
266
346
  scope: "global",
267
347
  },
268
348
  {
269
- id: "report-setup-payment",
349
+ id: "op-report-setup-payment",
270
350
  name: "REPORT_SETUP_PAYMENT",
271
- description: "Reports a setup payment",
272
- schema: "input ReportSetupPaymentInput {\n serviceId: OID!\n paidAmount: Amount_Money!\n paidAt: DateTime!\n}",
273
- template: "Reports a setup payment",
274
- reducer: "const service = state.services.find(s => s.id === action.input.serviceId);\nif (service && service.setupCost) { service.setupCost.paidAmount = action.input.paidAmount; service.setupCost.paidAt = action.input.paidAt; }",
275
- errors: [],
351
+ description: "Record a setup payment",
352
+ schema: "input ReportSetupPaymentInput {\n serviceId: OID!\n paymentDate: DateTime!\n}",
353
+ template: "Record a setup payment",
354
+ reducer: "const svc = state.services.find((s) => s.id === action.input.serviceId);\nif (!svc) {\n throw new ReportSetupPaymentServiceNotFoundError(`Service with ID ${action.input.serviceId} not found`);\n}\nif (svc.setupCost) {\n svc.setupCost.paymentDate = action.input.paymentDate;\n}",
355
+ errors: [
356
+ {
357
+ id: "err-report-setup-payment-not-found",
358
+ name: "ReportSetupPaymentServiceNotFoundError",
359
+ code: "REPORT_SETUP_PAYMENT_NOT_FOUND",
360
+ description: "",
361
+ template: "",
362
+ },
363
+ ],
276
364
  examples: [],
277
365
  scope: "global",
278
366
  },
279
367
  {
280
- id: "report-recurring-payment",
368
+ id: "op-report-recurring-payment",
281
369
  name: "REPORT_RECURRING_PAYMENT",
282
- description: "Reports a recurring payment",
283
- schema: "input ReportRecurringPaymentInput {\n serviceId: OID!\n paidAmount: Amount_Money!\n paidAt: DateTime!\n}",
284
- template: "Reports a recurring payment",
285
- reducer: "const service = state.services.find(s => s.id === action.input.serviceId);\nif (service && service.recurringCost) { service.recurringCost.paidAmount = action.input.paidAmount; service.recurringCost.paidAt = action.input.paidAt; }",
286
- errors: [],
370
+ description: "Record a recurring payment",
371
+ schema: "input ReportRecurringPaymentInput {\n serviceId: OID!\n paymentDate: DateTime!\n}",
372
+ template: "Record a recurring payment",
373
+ reducer: "const svc = state.services.find((s) => s.id === action.input.serviceId);\nif (!svc) {\n throw new ReportRecurringPaymentServiceNotFoundError(`Service with ID ${action.input.serviceId} not found`);\n}\nif (svc.recurringCost) {\n svc.recurringCost.lastPaymentDate = action.input.paymentDate;\n}",
374
+ errors: [
375
+ {
376
+ id: "err-report-recurring-payment-not-found",
377
+ name: "ReportRecurringPaymentServiceNotFoundError",
378
+ code: "REPORT_RECURRING_PAYMENT_NOT_FOUND",
379
+ description: "",
380
+ template: "",
381
+ },
382
+ ],
287
383
  examples: [],
288
384
  scope: "global",
289
385
  },
290
386
  {
291
- id: "update-service-info",
387
+ id: "op-update-service-info",
292
388
  name: "UPDATE_SERVICE_INFO",
293
- description: "Updates service info",
389
+ description: "Update service name, description, custom value",
294
390
  schema: "input UpdateServiceInfoInput {\n serviceId: OID!\n name: String\n description: String\n customValue: String\n}",
295
- template: "Updates service info",
296
- reducer: "const service = state.services.find(s => s.id === action.input.serviceId);\nif (service) {\n if (action.input.name) service.name = action.input.name;\n if (action.input.description !== undefined) service.description = action.input.description || null;\n if (action.input.customValue !== undefined) service.customValue = action.input.customValue || null;\n}",
297
- errors: [],
391
+ template: "Update service name, description, custom value",
392
+ reducer: "const svc = state.services.find((s) => s.id === action.input.serviceId);\nif (!svc) {\n throw new UpdateServiceInfoNotFoundError(`Service with ID ${action.input.serviceId} not found`);\n}\nif (action.input.name !== undefined) svc.name = action.input.name || null;\nif (action.input.description !== undefined) svc.description = action.input.description || null;\nif (action.input.customValue !== undefined) svc.customValue = action.input.customValue || null;",
393
+ errors: [
394
+ {
395
+ id: "err-update-service-info-not-found",
396
+ name: "UpdateServiceInfoNotFoundError",
397
+ code: "UPDATE_SERVICE_INFO_NOT_FOUND",
398
+ description: "",
399
+ template: "",
400
+ },
401
+ ],
298
402
  examples: [],
299
403
  scope: "global",
300
404
  },
301
405
  {
302
- id: "add-service-facet-selection",
406
+ id: "op-add-service-facet-selection",
303
407
  name: "ADD_SERVICE_FACET_SELECTION",
304
- description: "Adds a facet selection to a service",
305
- schema: "input AddServiceFacetSelectionInput {\n serviceId: OID!\n id: OID!\n facetName: String!\n selectedOption: String!\n}",
306
- template: "Adds a facet selection to a service",
307
- reducer: "const service = state.services.find(s => s.id === action.input.serviceId);\nif (service) { service.facetSelections.push({ id: action.input.id, facetName: action.input.facetName, selectedOption: action.input.selectedOption }); }",
308
- errors: [],
408
+ description: "Add facet selection to a service",
409
+ schema: "input AddServiceFacetSelectionInput {\n serviceId: OID!\n facetSelectionId: OID!\n facetName: String!\n selectedOption: String!\n}",
410
+ template: "Add facet selection to a service",
411
+ reducer: "const svc = state.services.find((s) => s.id === action.input.serviceId);\nif (!svc) {\n throw new AddServiceFacetSelectionServiceNotFoundError(`Service with ID ${action.input.serviceId} not found`);\n}\nsvc.facetSelections.push({\n id: action.input.facetSelectionId,\n facetName: action.input.facetName,\n selectedOption: action.input.selectedOption,\n});",
412
+ errors: [
413
+ {
414
+ id: "err-add-facet-service-not-found",
415
+ name: "AddServiceFacetSelectionServiceNotFoundError",
416
+ code: "ADD_FACET_SERVICE_NOT_FOUND",
417
+ description: "",
418
+ template: "",
419
+ },
420
+ ],
309
421
  examples: [],
310
422
  scope: "global",
311
423
  },
312
424
  {
313
- id: "remove-service-facet-selection",
425
+ id: "op-remove-service-facet-selection",
314
426
  name: "REMOVE_SERVICE_FACET_SELECTION",
315
- description: "Removes a facet selection",
427
+ description: "Remove facet selection from a service",
316
428
  schema: "input RemoveServiceFacetSelectionInput {\n serviceId: OID!\n facetSelectionId: OID!\n}",
317
- template: "Removes a facet selection",
318
- reducer: "const service = state.services.find(s => s.id === action.input.serviceId);\nif (service) {\n const fsIndex = service.facetSelections.findIndex(fs => fs.id === action.input.facetSelectionId);\n if (fsIndex !== -1) { service.facetSelections.splice(fsIndex, 1); }\n}",
319
- errors: [],
429
+ template: "Remove facet selection from a service",
430
+ reducer: "const svc = state.services.find((s) => s.id === action.input.serviceId);\nif (!svc) {\n throw new RemoveServiceFacetSelectionServiceNotFoundError(`Service with ID ${action.input.serviceId} not found`);\n}\nconst index = svc.facetSelections.findIndex((fs) => fs.id === action.input.facetSelectionId);\nif (index !== -1) {\n svc.facetSelections.splice(index, 1);\n}",
431
+ errors: [
432
+ {
433
+ id: "err-remove-facet-service-not-found",
434
+ name: "RemoveServiceFacetSelectionServiceNotFoundError",
435
+ code: "REMOVE_FACET_SERVICE_NOT_FOUND",
436
+ description: "",
437
+ template: "",
438
+ },
439
+ ],
320
440
  examples: [],
321
441
  scope: "global",
322
442
  },
323
443
  ],
324
444
  },
325
445
  {
326
- id: "service-group",
327
- name: "Service Group",
328
- description: "Operations for managing service groups",
446
+ id: "mod-service-group",
447
+ name: "service-group",
448
+ description: "Service group management and grouped service operations",
329
449
  operations: [
330
450
  {
331
- id: "add-service-group",
451
+ id: "op-add-service-group",
332
452
  name: "ADD_SERVICE_GROUP",
333
- description: "Adds a service group",
334
- schema: "input AddServiceGroupInput {\n id: OID!\n name: String!\n optional: Boolean!\n costType: GroupCostType\n}",
335
- template: "Adds a service group",
336
- reducer: "state.serviceGroups.push({ id: action.input.id, name: action.input.name, optional: action.input.optional, costType: action.input.costType || null, setupCost: null, recurringCost: null, services: [] });",
453
+ description: "Add a service group",
454
+ schema: "input AddServiceGroupInput {\n groupId: OID!\n name: String!\n optional: Boolean!\n costType: GroupCostType\n setupAmount: Amount_Money\n setupCurrency: Currency\n setupBillingDate: DateTime\n recurringAmount: Amount_Money\n recurringCurrency: Currency\n recurringBillingCycle: BillingCycle\n recurringDiscount: DiscountServiceInfoInput\n}",
455
+ template: "Add a service group",
456
+ reducer: "state.serviceGroups.push({\n id: action.input.groupId,\n name: action.input.name,\n optional: action.input.optional,\n costType: action.input.costType || null,\n setupCost: action.input.setupAmount && action.input.setupCurrency ? {\n amount: action.input.setupAmount,\n currency: action.input.setupCurrency,\n billingDate: action.input.setupBillingDate || null,\n paymentDate: null,\n } : null,\n recurringCost: action.input.recurringAmount && action.input.recurringCurrency && action.input.recurringBillingCycle ? {\n amount: action.input.recurringAmount,\n currency: action.input.recurringCurrency,\n billingCycle: action.input.recurringBillingCycle,\n nextBillingDate: null,\n lastPaymentDate: null,\n discount: action.input.recurringDiscount ? {\n originalAmount: action.input.recurringDiscount.originalAmount,\n discountType: action.input.recurringDiscount.discountType,\n discountValue: action.input.recurringDiscount.discountValue,\n source: action.input.recurringDiscount.source,\n } : null,\n } : null,\n services: [],\n});",
337
457
  errors: [],
338
458
  examples: [],
339
459
  scope: "global",
340
460
  },
341
461
  {
342
- id: "remove-service-group",
462
+ id: "op-remove-service-group",
343
463
  name: "REMOVE_SERVICE_GROUP",
344
- description: "Removes a service group",
345
- schema: "input RemoveServiceGroupInput {\n id: OID!\n}",
346
- template: "Removes a service group",
347
- reducer: "const groupIndex = state.serviceGroups.findIndex(g => g.id === action.input.id);\nif (groupIndex !== -1) { state.serviceGroups.splice(groupIndex, 1); }",
348
- errors: [],
464
+ description: "Remove a service group",
465
+ schema: "input RemoveServiceGroupInput {\n groupId: OID!\n}",
466
+ template: "Remove a service group",
467
+ reducer: "const index = state.serviceGroups.findIndex((g) => g.id === action.input.groupId);\nif (index === -1) {\n throw new RemoveServiceGroupNotFoundError(`Service group with ID ${action.input.groupId} not found`);\n}\nstate.serviceGroups.splice(index, 1);",
468
+ errors: [
469
+ {
470
+ id: "err-remove-group-not-found",
471
+ name: "RemoveServiceGroupNotFoundError",
472
+ code: "REMOVE_GROUP_NOT_FOUND",
473
+ description: "",
474
+ template: "",
475
+ },
476
+ ],
349
477
  examples: [],
350
478
  scope: "global",
351
479
  },
352
480
  {
353
- id: "add-service-to-group",
481
+ id: "op-add-service-to-group",
354
482
  name: "ADD_SERVICE_TO_GROUP",
355
- description: "Adds a service to a group",
356
- schema: "input AddServiceToGroupInput {\n groupId: OID!\n serviceId: OID!\n}",
357
- template: "Adds a service to a group",
358
- reducer: "const group = state.serviceGroups.find(g => g.id === action.input.groupId);\nif (group && !group.services.includes(action.input.serviceId)) { group.services.push(action.input.serviceId); }",
359
- errors: [],
483
+ description: "Add a service to a group",
484
+ schema: "input AddServiceToGroupInput {\n groupId: OID!\n serviceId: OID!\n name: String\n description: String\n customValue: String\n setupAmount: Amount_Money\n setupCurrency: Currency\n setupBillingDate: DateTime\n setupPaymentDate: DateTime\n recurringAmount: Amount_Money\n recurringCurrency: Currency\n recurringBillingCycle: BillingCycle\n recurringNextBillingDate: DateTime\n recurringLastPaymentDate: DateTime\n}",
485
+ template: "Add a service to a group",
486
+ reducer: "const group = state.serviceGroups.find((g) => g.id === action.input.groupId);\nif (!group) {\n throw new AddServiceToGroupGroupNotFoundError(`Service group with ID ${action.input.groupId} not found`);\n}\ngroup.services.push({\n id: action.input.serviceId,\n name: action.input.name || null,\n description: action.input.description || null,\n customValue: action.input.customValue || null,\n facetSelections: [],\n setupCost: action.input.setupAmount && action.input.setupCurrency ? {\n amount: action.input.setupAmount,\n currency: action.input.setupCurrency,\n billingDate: action.input.setupBillingDate || null,\n paymentDate: action.input.setupPaymentDate || null,\n } : null,\n recurringCost: action.input.recurringAmount && action.input.recurringCurrency && action.input.recurringBillingCycle ? {\n amount: action.input.recurringAmount,\n currency: action.input.recurringCurrency,\n billingCycle: action.input.recurringBillingCycle,\n nextBillingDate: action.input.recurringNextBillingDate || null,\n lastPaymentDate: action.input.recurringLastPaymentDate || null,\n discount: null,\n } : null,\n metrics: [],\n});",
487
+ errors: [
488
+ {
489
+ id: "err-add-to-group-not-found",
490
+ name: "AddServiceToGroupGroupNotFoundError",
491
+ code: "ADD_TO_GROUP_NOT_FOUND",
492
+ description: "",
493
+ template: "",
494
+ },
495
+ ],
360
496
  examples: [],
361
497
  scope: "global",
362
498
  },
363
499
  {
364
- id: "remove-service-from-group",
500
+ id: "op-remove-service-from-group",
365
501
  name: "REMOVE_SERVICE_FROM_GROUP",
366
- description: "Removes a service from a group",
502
+ description: "Remove a service from a group",
367
503
  schema: "input RemoveServiceFromGroupInput {\n groupId: OID!\n serviceId: OID!\n}",
368
- template: "Removes a service from a group",
369
- reducer: "const group = state.serviceGroups.find(g => g.id === action.input.groupId);\nif (group) {\n const sIndex = group.services.indexOf(action.input.serviceId);\n if (sIndex !== -1) { group.services.splice(sIndex, 1); }\n}",
370
- errors: [],
371
- examples: [],
372
- scope: "global",
373
- },
374
- {
375
- id: "update-service-group-cost",
504
+ template: "Remove a service from a group",
505
+ reducer: "const group = state.serviceGroups.find((g) => g.id === action.input.groupId);\nif (!group) {\n throw new RemoveServiceFromGroupGroupNotFoundError(`Service group with ID ${action.input.groupId} not found`);\n}\nconst index = group.services.findIndex((s) => s.id === action.input.serviceId);\nif (index === -1) {\n throw new RemoveServiceFromGroupServiceNotFoundError(`Service with ID ${action.input.serviceId} not found in group ${action.input.groupId}`);\n}\ngroup.services.splice(index, 1);",
506
+ errors: [
507
+ {
508
+ id: "err-remove-from-group-not-found",
509
+ name: "RemoveServiceFromGroupGroupNotFoundError",
510
+ code: "REMOVE_FROM_GROUP_NOT_FOUND",
511
+ description: "",
512
+ template: "",
513
+ },
514
+ {
515
+ id: "err-remove-from-group-service-not-found",
516
+ name: "RemoveServiceFromGroupServiceNotFoundError",
517
+ code: "REMOVE_FROM_GROUP_SERVICE_NOT_FOUND",
518
+ description: "",
519
+ template: "",
520
+ },
521
+ ],
522
+ examples: [],
523
+ scope: "global",
524
+ },
525
+ {
526
+ id: "op-update-service-group-cost",
376
527
  name: "UPDATE_SERVICE_GROUP_COST",
377
- description: "Updates service group cost",
378
- schema: "input UpdateServiceGroupCostInput {\n groupId: OID!\n costType: GroupCostType!\n amount: Amount_Money!\n currency: Currency!\n}",
379
- template: "Updates service group cost",
380
- reducer: 'const group = state.serviceGroups.find(g => g.id === action.input.groupId);\nif (group) {\n group.costType = action.input.costType;\n const cost = { amount: action.input.amount, currency: action.input.currency, paidAmount: null, paidAt: null };\n if (action.input.costType === "SETUP") { group.setupCost = cost; } else { group.recurringCost = cost; }\n}',
381
- errors: [],
528
+ description: "Update group setup and recurring costs",
529
+ schema: "input UpdateServiceGroupCostInput {\n groupId: OID!\n setupAmount: Amount_Money\n setupCurrency: Currency\n setupBillingDate: DateTime\n recurringAmount: Amount_Money\n recurringCurrency: Currency\n recurringBillingCycle: BillingCycle\n}",
530
+ template: "Update group setup and recurring costs",
531
+ reducer: "const group = state.serviceGroups.find((g) => g.id === action.input.groupId);\nif (!group) {\n throw new UpdateServiceGroupCostNotFoundError(`Service group with ID ${action.input.groupId} not found`);\n}\nif (action.input.setupAmount && action.input.setupCurrency) {\n group.setupCost = {\n amount: action.input.setupAmount,\n currency: action.input.setupCurrency,\n billingDate: action.input.setupBillingDate || null,\n paymentDate: group.setupCost?.paymentDate || null,\n };\n} else if (group.setupCost) {\n if (action.input.setupAmount) group.setupCost.amount = action.input.setupAmount;\n if (action.input.setupCurrency) group.setupCost.currency = action.input.setupCurrency;\n if (action.input.setupBillingDate !== undefined) group.setupCost.billingDate = action.input.setupBillingDate || null;\n}\nif (action.input.recurringAmount && action.input.recurringCurrency && action.input.recurringBillingCycle) {\n group.recurringCost = {\n amount: action.input.recurringAmount,\n currency: action.input.recurringCurrency,\n billingCycle: action.input.recurringBillingCycle,\n nextBillingDate: group.recurringCost?.nextBillingDate || null,\n lastPaymentDate: group.recurringCost?.lastPaymentDate || null,\n discount: group.recurringCost?.discount || null,\n };\n} else if (group.recurringCost) {\n if (action.input.recurringAmount) group.recurringCost.amount = action.input.recurringAmount;\n if (action.input.recurringCurrency) group.recurringCost.currency = action.input.recurringCurrency;\n if (action.input.recurringBillingCycle) group.recurringCost.billingCycle = action.input.recurringBillingCycle;\n}",
532
+ errors: [
533
+ {
534
+ id: "err-update-group-cost-not-found",
535
+ name: "UpdateServiceGroupCostNotFoundError",
536
+ code: "UPDATE_GROUP_COST_NOT_FOUND",
537
+ description: "",
538
+ template: "",
539
+ },
540
+ ],
382
541
  examples: [],
383
542
  scope: "global",
384
543
  },
385
544
  ],
386
545
  },
387
546
  {
388
- id: "metrics",
389
- name: "Metrics",
390
- description: "Operations for managing service metrics and usage",
547
+ id: "mod-metrics",
548
+ name: "metrics",
549
+ description: "Service metric tracking and usage management",
391
550
  operations: [
392
551
  {
393
- id: "add-service-metric",
552
+ id: "op-add-service-metric",
394
553
  name: "ADD_SERVICE_METRIC",
395
- description: "Adds a metric to a service",
396
- schema: "input AddServiceMetricInput {\n serviceId: OID!\n id: OID!\n name: String!\n unitName: String\n limit: Int\n freeLimit: Int\n paidLimit: Int\n unitCost: Amount_Money\n usageResetPeriod: ResetPeriod\n nextUsageReset: DateTime\n}",
397
- template: "Adds a metric to a service",
398
- reducer: "const service = state.services.find(s => s.id === action.input.serviceId);\nif (service) { service.metrics.push({ id: action.input.id, name: action.input.name, unitName: action.input.unitName || null, limit: action.input.limit || null, freeLimit: action.input.freeLimit || null, paidLimit: action.input.paidLimit || null, unitCost: action.input.unitCost || null, currentUsage: 0, usageResetPeriod: action.input.usageResetPeriod || null, nextUsageReset: action.input.nextUsageReset || null }); }",
399
- errors: [],
554
+ description: "Add a metric to a service",
555
+ schema: "input AddServiceMetricInput {\n serviceId: OID!\n metricId: OID!\n name: String!\n unitName: String!\n limit: Int\n freeLimit: Int\n paidLimit: Int\n currentUsage: Int!\n usageResetPeriod: ResetPeriod\n nextUsageReset: DateTime\n unitCostAmount: Amount_Money\n unitCostCurrency: Currency\n unitCostBillingCycle: BillingCycle\n unitCostNextBillingDate: DateTime\n unitCostLastPaymentDate: DateTime\n}",
556
+ template: "Add a metric to a service",
557
+ reducer: "const svc = state.services.find((s) => s.id === action.input.serviceId);\nif (!svc) {\n throw new AddServiceMetricServiceNotFoundError(`Service with ID ${action.input.serviceId} not found`);\n}\nsvc.metrics.push({\n id: action.input.metricId,\n name: action.input.name,\n unitName: action.input.unitName,\n limit: action.input.limit || null,\n freeLimit: action.input.freeLimit || null,\n paidLimit: action.input.paidLimit || null,\n unitCost: action.input.unitCostAmount && action.input.unitCostCurrency && action.input.unitCostBillingCycle ? {\n amount: action.input.unitCostAmount,\n currency: action.input.unitCostCurrency,\n billingCycle: action.input.unitCostBillingCycle,\n nextBillingDate: action.input.unitCostNextBillingDate || null,\n lastPaymentDate: action.input.unitCostLastPaymentDate || null,\n discount: null,\n } : null,\n currentUsage: action.input.currentUsage,\n usageResetPeriod: action.input.usageResetPeriod || null,\n nextUsageReset: action.input.nextUsageReset || null,\n});",
558
+ errors: [
559
+ {
560
+ id: "err-add-metric-service-not-found",
561
+ name: "AddServiceMetricServiceNotFoundError",
562
+ code: "ADD_METRIC_SERVICE_NOT_FOUND",
563
+ description: "",
564
+ template: "",
565
+ },
566
+ ],
400
567
  examples: [],
401
568
  scope: "global",
402
569
  },
403
570
  {
404
- id: "update-metric",
571
+ id: "op-update-metric",
405
572
  name: "UPDATE_METRIC",
406
- description: "Updates a metric",
407
- schema: "input UpdateMetricInput {\n serviceId: OID!\n metricId: OID!\n name: String\n unitName: String\n limit: Int\n freeLimit: Int\n paidLimit: Int\n unitCost: Amount_Money\n usageResetPeriod: ResetPeriod\n nextUsageReset: DateTime\n}",
408
- template: "Updates a metric",
409
- reducer: "const service = state.services.find(s => s.id === action.input.serviceId);\nif (service) {\n const metric = service.metrics.find(m => m.id === action.input.metricId);\n if (metric) {\n if (action.input.name) metric.name = action.input.name;\n if (action.input.unitName !== undefined) metric.unitName = action.input.unitName || null;\n if (action.input.limit !== undefined) metric.limit = action.input.limit;\n if (action.input.freeLimit !== undefined) metric.freeLimit = action.input.freeLimit;\n if (action.input.paidLimit !== undefined) metric.paidLimit = action.input.paidLimit;\n if (action.input.unitCost !== undefined) metric.unitCost = action.input.unitCost;\n if (action.input.usageResetPeriod !== undefined) metric.usageResetPeriod = action.input.usageResetPeriod;\n if (action.input.nextUsageReset !== undefined) metric.nextUsageReset = action.input.nextUsageReset;\n }\n}",
410
- errors: [],
411
- examples: [],
412
- scope: "global",
413
- },
414
- {
415
- id: "update-metric-usage",
573
+ description: "Update metric configuration",
574
+ schema: "input UpdateMetricInput {\n serviceId: OID!\n metricId: OID!\n name: String\n unitName: String\n limit: Int\n usageResetPeriod: ResetPeriod\n nextUsageReset: DateTime\n}",
575
+ template: "Update metric configuration",
576
+ reducer: "const svc = state.services.find((s) => s.id === action.input.serviceId);\nif (!svc) {\n throw new UpdateMetricServiceNotFoundError(`Service with ID ${action.input.serviceId} not found`);\n}\nconst metric = svc.metrics.find((m) => m.id === action.input.metricId);\nif (!metric) {\n throw new UpdateMetricNotFoundError(`Metric with ID ${action.input.metricId} not found`);\n}\nif (action.input.name) metric.name = action.input.name;\nif (action.input.unitName) metric.unitName = action.input.unitName;\nif (action.input.limit !== undefined) metric.limit = action.input.limit || null;\nif (action.input.usageResetPeriod !== undefined) metric.usageResetPeriod = action.input.usageResetPeriod || null;\nif (action.input.nextUsageReset !== undefined) metric.nextUsageReset = action.input.nextUsageReset || null;",
577
+ errors: [
578
+ {
579
+ id: "err-update-metric-service-not-found",
580
+ name: "UpdateMetricServiceNotFoundError",
581
+ code: "UPDATE_METRIC_SERVICE_NOT_FOUND",
582
+ description: "",
583
+ template: "",
584
+ },
585
+ {
586
+ id: "err-update-metric-not-found",
587
+ name: "UpdateMetricNotFoundError",
588
+ code: "UPDATE_METRIC_NOT_FOUND",
589
+ description: "",
590
+ template: "",
591
+ },
592
+ ],
593
+ examples: [],
594
+ scope: "global",
595
+ },
596
+ {
597
+ id: "op-update-metric-usage",
416
598
  name: "UPDATE_METRIC_USAGE",
417
- description: "Updates metric usage",
418
- schema: "input UpdateMetricUsageInput {\n serviceId: OID!\n metricId: OID!\n currentUsage: Int!\n}",
419
- template: "Updates metric usage",
420
- reducer: "const service = state.services.find(s => s.id === action.input.serviceId);\nif (service) {\n const metric = service.metrics.find(m => m.id === action.input.metricId);\n if (metric) { metric.currentUsage = action.input.currentUsage; }\n}",
421
- errors: [],
422
- examples: [],
423
- scope: "global",
424
- },
425
- {
426
- id: "remove-service-metric",
599
+ description: "Set metric usage directly",
600
+ schema: "input UpdateMetricUsageInput {\n serviceId: OID!\n metricId: OID!\n currentTime: DateTime!\n currentUsage: Int!\n}",
601
+ template: "Set metric usage directly",
602
+ reducer: "const svc = state.services.find((s) => s.id === action.input.serviceId);\nif (!svc) {\n throw new UpdateMetricUsageServiceNotFoundError(`Service with ID ${action.input.serviceId} not found`);\n}\nconst metric = svc.metrics.find((m) => m.id === action.input.metricId);\nif (!metric) {\n throw new UpdateMetricUsageNotFoundError(`Metric with ID ${action.input.metricId} not found`);\n}\nmetric.currentUsage = action.input.currentUsage;",
603
+ errors: [
604
+ {
605
+ id: "err-update-usage-service-not-found",
606
+ name: "UpdateMetricUsageServiceNotFoundError",
607
+ code: "UPDATE_USAGE_SERVICE_NOT_FOUND",
608
+ description: "",
609
+ template: "",
610
+ },
611
+ {
612
+ id: "err-update-usage-metric-not-found",
613
+ name: "UpdateMetricUsageNotFoundError",
614
+ code: "UPDATE_USAGE_METRIC_NOT_FOUND",
615
+ description: "",
616
+ template: "",
617
+ },
618
+ ],
619
+ examples: [],
620
+ scope: "global",
621
+ },
622
+ {
623
+ id: "op-remove-service-metric",
427
624
  name: "REMOVE_SERVICE_METRIC",
428
- description: "Removes a metric",
625
+ description: "Remove a metric from a service",
429
626
  schema: "input RemoveServiceMetricInput {\n serviceId: OID!\n metricId: OID!\n}",
430
- template: "Removes a metric",
431
- reducer: "const service = state.services.find(s => s.id === action.input.serviceId);\nif (service) {\n const mIndex = service.metrics.findIndex(m => m.id === action.input.metricId);\n if (mIndex !== -1) { service.metrics.splice(mIndex, 1); }\n}",
432
- errors: [],
433
- examples: [],
434
- scope: "global",
435
- },
436
- {
437
- id: "increment-metric-usage",
627
+ template: "Remove a metric from a service",
628
+ reducer: "const svc = state.services.find((s) => s.id === action.input.serviceId);\nif (!svc) {\n throw new RemoveServiceMetricServiceNotFoundError(`Service with ID ${action.input.serviceId} not found`);\n}\nconst index = svc.metrics.findIndex((m) => m.id === action.input.metricId);\nif (index === -1) {\n throw new RemoveServiceMetricNotFoundError(`Metric with ID ${action.input.metricId} not found`);\n}\nsvc.metrics.splice(index, 1);",
629
+ errors: [
630
+ {
631
+ id: "err-remove-metric-service-not-found",
632
+ name: "RemoveServiceMetricServiceNotFoundError",
633
+ code: "REMOVE_METRIC_SERVICE_NOT_FOUND",
634
+ description: "",
635
+ template: "",
636
+ },
637
+ {
638
+ id: "err-remove-metric-not-found",
639
+ name: "RemoveServiceMetricNotFoundError",
640
+ code: "REMOVE_METRIC_NOT_FOUND",
641
+ description: "",
642
+ template: "",
643
+ },
644
+ ],
645
+ examples: [],
646
+ scope: "global",
647
+ },
648
+ {
649
+ id: "op-increment-metric-usage",
438
650
  name: "INCREMENT_METRIC_USAGE",
439
- description: "Increments metric usage",
440
- schema: "input IncrementMetricUsageInput {\n serviceId: OID!\n metricId: OID!\n amount: Int!\n}",
441
- template: "Increments metric usage",
442
- reducer: "const service = state.services.find(s => s.id === action.input.serviceId);\nif (service) {\n const metric = service.metrics.find(m => m.id === action.input.metricId);\n if (metric) { metric.currentUsage += action.input.amount; }\n}",
443
- errors: [],
444
- examples: [],
445
- scope: "global",
446
- },
447
- {
448
- id: "decrement-metric-usage",
651
+ description: "Increment usage counter",
652
+ schema: "input IncrementMetricUsageInput {\n serviceId: OID!\n metricId: OID!\n currentTime: DateTime!\n incrementBy: Int!\n}",
653
+ template: "Increment usage counter",
654
+ reducer: "const svc = state.services.find((s) => s.id === action.input.serviceId);\nif (!svc) {\n throw new IncrementMetricUsageServiceNotFoundError(`Service with ID ${action.input.serviceId} not found`);\n}\nconst metric = svc.metrics.find((m) => m.id === action.input.metricId);\nif (!metric) {\n throw new IncrementMetricUsageNotFoundError(`Metric with ID ${action.input.metricId} not found`);\n}\nmetric.currentUsage += action.input.incrementBy;",
655
+ errors: [
656
+ {
657
+ id: "err-increment-service-not-found",
658
+ name: "IncrementMetricUsageServiceNotFoundError",
659
+ code: "INCREMENT_SERVICE_NOT_FOUND",
660
+ description: "",
661
+ template: "",
662
+ },
663
+ {
664
+ id: "err-increment-metric-not-found",
665
+ name: "IncrementMetricUsageNotFoundError",
666
+ code: "INCREMENT_METRIC_NOT_FOUND",
667
+ description: "",
668
+ template: "",
669
+ },
670
+ ],
671
+ examples: [],
672
+ scope: "global",
673
+ },
674
+ {
675
+ id: "op-decrement-metric-usage",
449
676
  name: "DECREMENT_METRIC_USAGE",
450
- description: "Decrements metric usage",
451
- schema: "input DecrementMetricUsageInput {\n serviceId: OID!\n metricId: OID!\n amount: Int!\n}",
452
- template: "Decrements metric usage",
453
- reducer: "const service = state.services.find(s => s.id === action.input.serviceId);\nif (service) {\n const metric = service.metrics.find(m => m.id === action.input.metricId);\n if (metric) { metric.currentUsage = Math.max(0, metric.currentUsage - action.input.amount); }\n}",
454
- errors: [],
677
+ description: "Decrement usage counter",
678
+ schema: "input DecrementMetricUsageInput {\n serviceId: OID!\n metricId: OID!\n currentTime: DateTime!\n decrementBy: Int!\n}",
679
+ template: "Decrement usage counter",
680
+ reducer: "const svc = state.services.find((s) => s.id === action.input.serviceId);\nif (!svc) {\n throw new DecrementMetricUsageServiceNotFoundError(`Service with ID ${action.input.serviceId} not found`);\n}\nconst metric = svc.metrics.find((m) => m.id === action.input.metricId);\nif (!metric) {\n throw new DecrementMetricUsageNotFoundError(`Metric with ID ${action.input.metricId} not found`);\n}\nmetric.currentUsage -= action.input.decrementBy;",
681
+ errors: [
682
+ {
683
+ id: "err-decrement-service-not-found",
684
+ name: "DecrementMetricUsageServiceNotFoundError",
685
+ code: "DECREMENT_SERVICE_NOT_FOUND",
686
+ description: "",
687
+ template: "",
688
+ },
689
+ {
690
+ id: "err-decrement-metric-not-found",
691
+ name: "DecrementMetricUsageNotFoundError",
692
+ code: "DECREMENT_METRIC_NOT_FOUND",
693
+ description: "",
694
+ template: "",
695
+ },
696
+ ],
455
697
  examples: [],
456
698
  scope: "global",
457
699
  },
458
700
  ],
459
701
  },
460
702
  {
461
- id: "customer",
462
- name: "Customer",
463
- description: "Operations for managing customer info",
703
+ id: "mod-customer",
704
+ name: "customer",
705
+ description: "Customer type and team member management",
464
706
  operations: [
465
707
  {
466
- id: "set-customer-type",
708
+ id: "op-set-customer-type",
467
709
  name: "SET_CUSTOMER_TYPE",
468
- description: "Sets the customer type",
469
- schema: "input SetCustomerTypeInput {\n customerType: CustomerType!\n}",
470
- template: "Sets the customer type",
471
- reducer: "state.customerType = action.input.customerType;",
710
+ description: "Set customer type (individual/team)",
711
+ schema: "input SetCustomerTypeInput {\n customerType: CustomerType!\n teamMemberCount: Int\n}",
712
+ template: "Set customer type (individual/team)",
713
+ reducer: "state.customerType = action.input.customerType;\nstate.teamMemberCount = action.input.teamMemberCount || null;",
472
714
  errors: [],
473
715
  examples: [],
474
716
  scope: "global",
475
717
  },
476
718
  {
477
- id: "update-team-member-count",
719
+ id: "op-update-team-member-count",
478
720
  name: "UPDATE_TEAM_MEMBER_COUNT",
479
- description: "Updates team member count",
721
+ description: "Update team member count",
480
722
  schema: "input UpdateTeamMemberCountInput {\n teamMemberCount: Int!\n}",
481
- template: "Updates team member count",
723
+ template: "Update team member count",
482
724
  reducer: "state.teamMemberCount = action.input.teamMemberCount;",
483
725
  errors: [],
484
726
  examples: [],