@endora-commerce/mod-organizations 0.100.0

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 (288) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +62 -0
  3. package/dist/admin/components/OrganizationSalesRepsTab.d.ts +27 -0
  4. package/dist/admin/components/OrganizationSalesRepsTab.d.ts.map +1 -0
  5. package/dist/admin/components/OrganizationSalesRepsTab.js +170 -0
  6. package/dist/admin/components/OrganizationSalesRepsTab.js.map +1 -0
  7. package/dist/admin/index.d.ts +30 -0
  8. package/dist/admin/index.d.ts.map +1 -0
  9. package/dist/admin/index.js +59 -0
  10. package/dist/admin/index.js.map +1 -0
  11. package/dist/admin/pages/OrganizationDetail.d.ts +9 -0
  12. package/dist/admin/pages/OrganizationDetail.d.ts.map +1 -0
  13. package/dist/admin/pages/OrganizationDetail.js +247 -0
  14. package/dist/admin/pages/OrganizationDetail.js.map +1 -0
  15. package/dist/admin/pages/OrganizationsList.d.ts +9 -0
  16. package/dist/admin/pages/OrganizationsList.d.ts.map +1 -0
  17. package/dist/admin/pages/OrganizationsList.js +73 -0
  18. package/dist/admin/pages/OrganizationsList.js.map +1 -0
  19. package/dist/admin/panels/ApplicablePriceListsPanel.d.ts +12 -0
  20. package/dist/admin/panels/ApplicablePriceListsPanel.d.ts.map +1 -0
  21. package/dist/admin/panels/ApplicablePriceListsPanel.js +49 -0
  22. package/dist/admin/panels/ApplicablePriceListsPanel.js.map +1 -0
  23. package/dist/admin/panels/FulfilmentStrategyPanel.d.ts +16 -0
  24. package/dist/admin/panels/FulfilmentStrategyPanel.d.ts.map +1 -0
  25. package/dist/admin/panels/FulfilmentStrategyPanel.js +29 -0
  26. package/dist/admin/panels/FulfilmentStrategyPanel.js.map +1 -0
  27. package/dist/admin/panels/HierarchyPanel.d.ts +15 -0
  28. package/dist/admin/panels/HierarchyPanel.d.ts.map +1 -0
  29. package/dist/admin/panels/HierarchyPanel.js +65 -0
  30. package/dist/admin/panels/HierarchyPanel.js.map +1 -0
  31. package/dist/admin/panels/ModerationActionsPanel.d.ts +28 -0
  32. package/dist/admin/panels/ModerationActionsPanel.d.ts.map +1 -0
  33. package/dist/admin/panels/ModerationActionsPanel.js +111 -0
  34. package/dist/admin/panels/ModerationActionsPanel.js.map +1 -0
  35. package/dist/admin/panels/RestrictionsPanel.d.ts +24 -0
  36. package/dist/admin/panels/RestrictionsPanel.d.ts.map +1 -0
  37. package/dist/admin/panels/RestrictionsPanel.js +121 -0
  38. package/dist/admin/panels/RestrictionsPanel.js.map +1 -0
  39. package/dist/admin/panels/VatValidationPanel.d.ts +28 -0
  40. package/dist/admin/panels/VatValidationPanel.d.ts.map +1 -0
  41. package/dist/admin/panels/VatValidationPanel.js +79 -0
  42. package/dist/admin/panels/VatValidationPanel.js.map +1 -0
  43. package/dist/backend/commands/move.command.d.ts +11 -0
  44. package/dist/backend/commands/move.command.d.ts.map +1 -0
  45. package/dist/backend/commands/move.command.js +11 -0
  46. package/dist/backend/commands/move.command.js.map +1 -0
  47. package/dist/backend/commands/set-cart-approval-policy.command.d.ts +31 -0
  48. package/dist/backend/commands/set-cart-approval-policy.command.d.ts.map +1 -0
  49. package/dist/backend/commands/set-cart-approval-policy.command.js +56 -0
  50. package/dist/backend/commands/set-cart-approval-policy.command.js.map +1 -0
  51. package/dist/backend/commands/set-credit-mode.command.d.ts +16 -0
  52. package/dist/backend/commands/set-credit-mode.command.d.ts.map +1 -0
  53. package/dist/backend/commands/set-credit-mode.command.js +25 -0
  54. package/dist/backend/commands/set-credit-mode.command.js.map +1 -0
  55. package/dist/backend/commands/set-parent.command.d.ts +26 -0
  56. package/dist/backend/commands/set-parent.command.d.ts.map +1 -0
  57. package/dist/backend/commands/set-parent.command.js +51 -0
  58. package/dist/backend/commands/set-parent.command.js.map +1 -0
  59. package/dist/backend/demo/reset.d.ts +20 -0
  60. package/dist/backend/demo/reset.d.ts.map +1 -0
  61. package/dist/backend/demo/reset.js +11 -0
  62. package/dist/backend/demo/reset.js.map +1 -0
  63. package/dist/backend/demo/rows.d.ts +44 -0
  64. package/dist/backend/demo/rows.d.ts.map +1 -0
  65. package/dist/backend/demo/rows.js +44 -0
  66. package/dist/backend/demo/rows.js.map +1 -0
  67. package/dist/backend/demo/seed.d.ts +20 -0
  68. package/dist/backend/demo/seed.d.ts.map +1 -0
  69. package/dist/backend/demo/seed.js +28 -0
  70. package/dist/backend/demo/seed.js.map +1 -0
  71. package/dist/backend/email-templates/invitation.d.ts +23 -0
  72. package/dist/backend/email-templates/invitation.d.ts.map +1 -0
  73. package/dist/backend/email-templates/invitation.js +35 -0
  74. package/dist/backend/email-templates/invitation.js.map +1 -0
  75. package/dist/backend/email-templates/transactional-defaults.d.ts +54 -0
  76. package/dist/backend/email-templates/transactional-defaults.d.ts.map +1 -0
  77. package/dist/backend/email-templates/transactional-defaults.js +242 -0
  78. package/dist/backend/email-templates/transactional-defaults.js.map +1 -0
  79. package/dist/backend/email-templates/verification.d.ts +14 -0
  80. package/dist/backend/email-templates/verification.d.ts.map +1 -0
  81. package/dist/backend/email-templates/verification.js +34 -0
  82. package/dist/backend/email-templates/verification.js.map +1 -0
  83. package/dist/backend/entities/email-verification-token.entity.d.ts +17 -0
  84. package/dist/backend/entities/email-verification-token.entity.d.ts.map +1 -0
  85. package/dist/backend/entities/email-verification-token.entity.js +60 -0
  86. package/dist/backend/entities/email-verification-token.entity.js.map +1 -0
  87. package/dist/backend/entities/organization-delivery-method-link.entity.d.ts +14 -0
  88. package/dist/backend/entities/organization-delivery-method-link.entity.d.ts.map +1 -0
  89. package/dist/backend/entities/organization-delivery-method-link.entity.js +41 -0
  90. package/dist/backend/entities/organization-delivery-method-link.entity.js.map +1 -0
  91. package/dist/backend/entities/organization-invitation.entity.d.ts +21 -0
  92. package/dist/backend/entities/organization-invitation.entity.d.ts.map +1 -0
  93. package/dist/backend/entities/organization-invitation.entity.js +85 -0
  94. package/dist/backend/entities/organization-invitation.entity.js.map +1 -0
  95. package/dist/backend/entities/organization-payment-method-link.entity.d.ts +14 -0
  96. package/dist/backend/entities/organization-payment-method-link.entity.d.ts.map +1 -0
  97. package/dist/backend/entities/organization-payment-method-link.entity.js +41 -0
  98. package/dist/backend/entities/organization-payment-method-link.entity.js.map +1 -0
  99. package/dist/backend/entities/organization-sales-rep-assignment.entity.d.ts +17 -0
  100. package/dist/backend/entities/organization-sales-rep-assignment.entity.d.ts.map +1 -0
  101. package/dist/backend/entities/organization-sales-rep-assignment.entity.js +56 -0
  102. package/dist/backend/entities/organization-sales-rep-assignment.entity.js.map +1 -0
  103. package/dist/backend/entities/organization-tax-id-validation.entity.d.ts +29 -0
  104. package/dist/backend/entities/organization-tax-id-validation.entity.d.ts.map +1 -0
  105. package/dist/backend/entities/organization-tax-id-validation.entity.js +78 -0
  106. package/dist/backend/entities/organization-tax-id-validation.entity.js.map +1 -0
  107. package/dist/backend/entities/organization-warehouse-link.entity.d.ts +15 -0
  108. package/dist/backend/entities/organization-warehouse-link.entity.d.ts.map +1 -0
  109. package/dist/backend/entities/organization-warehouse-link.entity.js +42 -0
  110. package/dist/backend/entities/organization-warehouse-link.entity.js.map +1 -0
  111. package/dist/backend/entities/organization.entity.d.ts +125 -0
  112. package/dist/backend/entities/organization.entity.d.ts.map +1 -0
  113. package/dist/backend/entities/organization.entity.js +282 -0
  114. package/dist/backend/entities/organization.entity.js.map +1 -0
  115. package/dist/backend/index.d.ts +250 -0
  116. package/dist/backend/index.d.ts.map +1 -0
  117. package/dist/backend/index.js +506 -0
  118. package/dist/backend/index.js.map +1 -0
  119. package/dist/backend/integrations/ministerstwo-finansow-client.d.ts +22 -0
  120. package/dist/backend/integrations/ministerstwo-finansow-client.d.ts.map +1 -0
  121. package/dist/backend/integrations/ministerstwo-finansow-client.js +172 -0
  122. package/dist/backend/integrations/ministerstwo-finansow-client.js.map +1 -0
  123. package/dist/backend/integrations/vies-client.d.ts +18 -0
  124. package/dist/backend/integrations/vies-client.d.ts.map +1 -0
  125. package/dist/backend/integrations/vies-client.js +133 -0
  126. package/dist/backend/integrations/vies-client.js.map +1 -0
  127. package/dist/backend/plugin.d.ts +126 -0
  128. package/dist/backend/plugin.d.ts.map +1 -0
  129. package/dist/backend/plugin.js +139 -0
  130. package/dist/backend/plugin.js.map +1 -0
  131. package/dist/backend/routes.admin.d.ts +66 -0
  132. package/dist/backend/routes.admin.d.ts.map +1 -0
  133. package/dist/backend/routes.admin.js +711 -0
  134. package/dist/backend/routes.admin.js.map +1 -0
  135. package/dist/backend/routes.customer.d.ts +33 -0
  136. package/dist/backend/routes.customer.d.ts.map +1 -0
  137. package/dist/backend/routes.customer.js +139 -0
  138. package/dist/backend/routes.customer.js.map +1 -0
  139. package/dist/backend/routes.members.d.ts +31 -0
  140. package/dist/backend/routes.members.d.ts.map +1 -0
  141. package/dist/backend/routes.members.js +109 -0
  142. package/dist/backend/routes.members.js.map +1 -0
  143. package/dist/backend/routes.public.d.ts +49 -0
  144. package/dist/backend/routes.public.d.ts.map +1 -0
  145. package/dist/backend/routes.public.js +220 -0
  146. package/dist/backend/routes.public.js.map +1 -0
  147. package/dist/backend/routes.sales-reps.d.ts +46 -0
  148. package/dist/backend/routes.sales-reps.d.ts.map +1 -0
  149. package/dist/backend/routes.sales-reps.js +114 -0
  150. package/dist/backend/routes.sales-reps.js.map +1 -0
  151. package/dist/backend/routes.storefront.d.ts +25 -0
  152. package/dist/backend/routes.storefront.d.ts.map +1 -0
  153. package/dist/backend/routes.storefront.js +65 -0
  154. package/dist/backend/routes.storefront.js.map +1 -0
  155. package/dist/backend/schemas/organization.d.ts +59 -0
  156. package/dist/backend/schemas/organization.d.ts.map +1 -0
  157. package/dist/backend/schemas/organization.js +55 -0
  158. package/dist/backend/schemas/organization.js.map +1 -0
  159. package/dist/backend/schemas/settings.d.ts +30 -0
  160. package/dist/backend/schemas/settings.d.ts.map +1 -0
  161. package/dist/backend/schemas/settings.js +35 -0
  162. package/dist/backend/schemas/settings.js.map +1 -0
  163. package/dist/backend/services/email-verification-service.d.ts +45 -0
  164. package/dist/backend/services/email-verification-service.d.ts.map +1 -0
  165. package/dist/backend/services/email-verification-service.js +95 -0
  166. package/dist/backend/services/email-verification-service.js.map +1 -0
  167. package/dist/backend/services/invitation-service.d.ts +85 -0
  168. package/dist/backend/services/invitation-service.d.ts.map +1 -0
  169. package/dist/backend/services/invitation-service.js +266 -0
  170. package/dist/backend/services/invitation-service.js.map +1 -0
  171. package/dist/backend/services/normalize-name.d.ts +14 -0
  172. package/dist/backend/services/normalize-name.d.ts.map +1 -0
  173. package/dist/backend/services/normalize-name.js +14 -0
  174. package/dist/backend/services/normalize-name.js.map +1 -0
  175. package/dist/backend/services/org-registration-notifier.d.ts +36 -0
  176. package/dist/backend/services/org-registration-notifier.d.ts.map +1 -0
  177. package/dist/backend/services/org-registration-notifier.js +109 -0
  178. package/dist/backend/services/org-registration-notifier.js.map +1 -0
  179. package/dist/backend/services/org-template-email.d.ts +18 -0
  180. package/dist/backend/services/org-template-email.d.ts.map +1 -0
  181. package/dist/backend/services/org-template-email.js +16 -0
  182. package/dist/backend/services/org-template-email.js.map +1 -0
  183. package/dist/backend/services/organization-context-service.d.ts +52 -0
  184. package/dist/backend/services/organization-context-service.d.ts.map +1 -0
  185. package/dist/backend/services/organization-context-service.js +70 -0
  186. package/dist/backend/services/organization-context-service.js.map +1 -0
  187. package/dist/backend/services/organization-country-reference.d.ts +11 -0
  188. package/dist/backend/services/organization-country-reference.d.ts.map +1 -0
  189. package/dist/backend/services/organization-country-reference.js +24 -0
  190. package/dist/backend/services/organization-country-reference.js.map +1 -0
  191. package/dist/backend/services/organization-details-port.d.ts +33 -0
  192. package/dist/backend/services/organization-details-port.d.ts.map +1 -0
  193. package/dist/backend/services/organization-details-port.js +95 -0
  194. package/dist/backend/services/organization-details-port.js.map +1 -0
  195. package/dist/backend/services/organization-effective-pricelists-service.d.ts +50 -0
  196. package/dist/backend/services/organization-effective-pricelists-service.d.ts.map +1 -0
  197. package/dist/backend/services/organization-effective-pricelists-service.js +68 -0
  198. package/dist/backend/services/organization-effective-pricelists-service.js.map +1 -0
  199. package/dist/backend/services/organization-inheritance-service.d.ts +73 -0
  200. package/dist/backend/services/organization-inheritance-service.d.ts.map +1 -0
  201. package/dist/backend/services/organization-inheritance-service.js +63 -0
  202. package/dist/backend/services/organization-inheritance-service.js.map +1 -0
  203. package/dist/backend/services/organization-moderation-service.d.ts +124 -0
  204. package/dist/backend/services/organization-moderation-service.d.ts.map +1 -0
  205. package/dist/backend/services/organization-moderation-service.js +337 -0
  206. package/dist/backend/services/organization-moderation-service.js.map +1 -0
  207. package/dist/backend/services/organization-restriction-service.d.ts +70 -0
  208. package/dist/backend/services/organization-restriction-service.d.ts.map +1 -0
  209. package/dist/backend/services/organization-restriction-service.js +210 -0
  210. package/dist/backend/services/organization-restriction-service.js.map +1 -0
  211. package/dist/backend/services/organization-tax-id-validation-service.d.ts +59 -0
  212. package/dist/backend/services/organization-tax-id-validation-service.d.ts.map +1 -0
  213. package/dist/backend/services/organization-tax-id-validation-service.js +115 -0
  214. package/dist/backend/services/organization-tax-id-validation-service.js.map +1 -0
  215. package/dist/backend/services/organization-tree-service.d.ts +102 -0
  216. package/dist/backend/services/organization-tree-service.d.ts.map +1 -0
  217. package/dist/backend/services/organization-tree-service.js +184 -0
  218. package/dist/backend/services/organization-tree-service.js.map +1 -0
  219. package/dist/backend/services/personal-organization-service.d.ts +130 -0
  220. package/dist/backend/services/personal-organization-service.d.ts.map +1 -0
  221. package/dist/backend/services/personal-organization-service.js +207 -0
  222. package/dist/backend/services/personal-organization-service.js.map +1 -0
  223. package/dist/backend/services/registration-service.d.ts +80 -0
  224. package/dist/backend/services/registration-service.d.ts.map +1 -0
  225. package/dist/backend/services/registration-service.js +126 -0
  226. package/dist/backend/services/registration-service.js.map +1 -0
  227. package/dist/backend/services/sales-rep-assignment-service.d.ts +89 -0
  228. package/dist/backend/services/sales-rep-assignment-service.d.ts.map +1 -0
  229. package/dist/backend/services/sales-rep-assignment-service.js +176 -0
  230. package/dist/backend/services/sales-rep-assignment-service.js.map +1 -0
  231. package/dist/backend/services/vat-validator-port.d.ts +20 -0
  232. package/dist/backend/services/vat-validator-port.d.ts.map +1 -0
  233. package/dist/backend/services/vat-validator-port.js +2 -0
  234. package/dist/backend/services/vat-validator-port.js.map +1 -0
  235. package/dist/manifest.d.ts +176 -0
  236. package/dist/manifest.d.ts.map +1 -0
  237. package/dist/manifest.js +503 -0
  238. package/dist/manifest.js.map +1 -0
  239. package/dist/migrations/20260424T205317_organizations_init.d.ts +13 -0
  240. package/dist/migrations/20260424T205317_organizations_init.d.ts.map +1 -0
  241. package/dist/migrations/20260424T205317_organizations_init.js +100 -0
  242. package/dist/migrations/20260424T205317_organizations_init.js.map +1 -0
  243. package/dist/migrations/20260425T051700_organizations_invitations_init.d.ts +13 -0
  244. package/dist/migrations/20260425T051700_organizations_invitations_init.d.ts.map +1 -0
  245. package/dist/migrations/20260425T051700_organizations_invitations_init.js +39 -0
  246. package/dist/migrations/20260425T051700_organizations_invitations_init.js.map +1 -0
  247. package/dist/migrations/20260611T140349_organizations_consolidation.d.ts +35 -0
  248. package/dist/migrations/20260611T140349_organizations_consolidation.d.ts.map +1 -0
  249. package/dist/migrations/20260611T140349_organizations_consolidation.js +180 -0
  250. package/dist/migrations/20260611T140349_organizations_consolidation.js.map +1 -0
  251. package/dist/migrations/20260611T140358_organizations_org_order_confirmation_emails.d.ts +10 -0
  252. package/dist/migrations/20260611T140358_organizations_org_order_confirmation_emails.d.ts.map +1 -0
  253. package/dist/migrations/20260611T140358_organizations_org_order_confirmation_emails.js +14 -0
  254. package/dist/migrations/20260611T140358_organizations_org_order_confirmation_emails.js.map +1 -0
  255. package/dist/migrations/20260611T140402_organizations_org_fulfilment_strategy.d.ts +14 -0
  256. package/dist/migrations/20260611T140402_organizations_org_fulfilment_strategy.d.ts.map +1 -0
  257. package/dist/migrations/20260611T140402_organizations_org_fulfilment_strategy.js +20 -0
  258. package/dist/migrations/20260611T140402_organizations_org_fulfilment_strategy.js.map +1 -0
  259. package/dist/migrations/20260717T151403_organizations_personal_organizations.d.ts +24 -0
  260. package/dist/migrations/20260717T151403_organizations_personal_organizations.d.ts.map +1 -0
  261. package/dist/migrations/20260717T151403_organizations_personal_organizations.js +63 -0
  262. package/dist/migrations/20260717T151403_organizations_personal_organizations.js.map +1 -0
  263. package/dist/migrations/20260718T200340_organizations_organization_custom_field_values.d.ts +12 -0
  264. package/dist/migrations/20260718T200340_organizations_organization_custom_field_values.d.ts.map +1 -0
  265. package/dist/migrations/20260718T200340_organizations_organization_custom_field_values.js +16 -0
  266. package/dist/migrations/20260718T200340_organizations_organization_custom_field_values.js.map +1 -0
  267. package/dist/migrations/20260720T044254_organizations_org_hierarchy.d.ts +23 -0
  268. package/dist/migrations/20260720T044254_organizations_org_hierarchy.d.ts.map +1 -0
  269. package/dist/migrations/20260720T044254_organizations_org_hierarchy.js +37 -0
  270. package/dist/migrations/20260720T044254_organizations_org_hierarchy.js.map +1 -0
  271. package/dist/migrations/20260912T094646_organizations_sales_channel_organizations.d.ts +27 -0
  272. package/dist/migrations/20260912T094646_organizations_sales_channel_organizations.d.ts.map +1 -0
  273. package/dist/migrations/20260912T094646_organizations_sales_channel_organizations.js +44 -0
  274. package/dist/migrations/20260912T094646_organizations_sales_channel_organizations.js.map +1 -0
  275. package/dist/migrations/index.d.ts +35 -0
  276. package/dist/migrations/index.d.ts.map +1 -0
  277. package/dist/migrations/index.js +45 -0
  278. package/dist/migrations/index.js.map +1 -0
  279. package/dist/ports/index.d.ts +53 -0
  280. package/dist/ports/index.d.ts.map +1 -0
  281. package/dist/ports/index.js +2 -0
  282. package/dist/ports/index.js.map +1 -0
  283. package/docs/organization-hierarchy.md +117 -0
  284. package/docs/organizations.md +312 -0
  285. package/i18n/en.json +17 -0
  286. package/i18n/pl.json +17 -0
  287. package/package.json +107 -0
  288. package/tailwind.css +14 -0
@@ -0,0 +1,44 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+ /**
3
+ * The `sales_channel_organizations` sales-channel bridge — the organizations a channel is visible to.
4
+ *
5
+ * It was created by the platform's frozen `Migration20260430T170044CoreSalesChannelsPromote`
6
+ * until `specs/120-migration-closure-bridge-ownership/` Phase 2. Under D-226 a
7
+ * bridge between an always-present near side (`sales_channels`, a kernel table)
8
+ * and a switchable far side belongs to the far side, so this module creates it:
9
+ * an instance that does not install `organizations` no longer has a migration corpus
10
+ * naming a table nothing builds.
11
+ *
12
+ * `if not exists`, because every database that has already applied the frozen
13
+ * migration has this table. The storage keys on the class name and holds no
14
+ * checksum, so nothing is re-offered there and this migration is the no-op it
15
+ * reads as; on a fresh database it is the creation.
16
+ *
17
+ * The statements are the frozen ones verbatim — same columns, same primary key,
18
+ * same two foreign keys, same index — so the two paths reach one schema. Its
19
+ * position needs nothing declared: `sales_channels` is the kernel's and
20
+ * `organizations` is created inside the frozen prefix, which every
21
+ * above-watermark migration runs after.
22
+ */
23
+ export class Migration20260912T094646OrganizationsSalesChannelOrganizations extends Migration {
24
+ async up() {
25
+ this.addSql(`
26
+ create table if not exists "sales_channel_organizations" (
27
+ "sales_channel_id" uuid not null,
28
+ "organization_id" uuid not null,
29
+ constraint "sales_channel_organizations_pkey"
30
+ primary key ("sales_channel_id", "organization_id"),
31
+ constraint "sales_channel_organizations_channel_fk"
32
+ foreign key ("sales_channel_id") references "sales_channels" ("id") on delete cascade,
33
+ constraint "sales_channel_organizations_organization_fk"
34
+ foreign key ("organization_id") references "organizations" ("id") on delete cascade
35
+ );
36
+ `);
37
+ this.addSql('create index if not exists "sales_channel_organizations_organization_id_index" ' +
38
+ 'on "sales_channel_organizations" ("organization_id");');
39
+ }
40
+ async down() {
41
+ this.addSql('drop table if exists "sales_channel_organizations" cascade;');
42
+ }
43
+ }
44
+ //# sourceMappingURL=20260912T094646_organizations_sales_channel_organizations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20260912T094646_organizations_sales_channel_organizations.js","sourceRoot":"","sources":["../../src/migrations/20260912T094646_organizations_sales_channel_organizations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,OAAO,8DAA+D,SAAQ,SAAS;IAClF,KAAK,CAAC,EAAE;QACf,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;;KAWX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CACT,iFAAiF;YAC/E,uDAAuD,CAC1D,CAAC;IACJ,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CAAC,6DAA6D,CAAC,CAAC;IAC7E,CAAC;CACF"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The `./migrations` subpath — every migration class this module owns, as one
3
+ * ordered `migrations` array.
4
+ *
5
+ * The array is what the platform reads when this module is **installed**:
6
+ * `src/packages/package-runtime.ts` takes `exported['migrations']` and refuses
7
+ * the package outright when it is absent (D-168).
8
+ *
9
+ * Listed in ascending timestamp, which orders **this module's own** migrations
10
+ * and nothing else (feature 081). Where the block sits relative to every other
11
+ * module's is decided by the manifest `dependencies` graph.
12
+ *
13
+ * The **named** exports stay, and the asymmetry with `./backend` — which
14
+ * publishes an array and no entity class by name (D-168) — is deliberate.
15
+ * `db/migrations-registry.generated.ts` imports each class by name from this
16
+ * specifier, and a migration class name is contract in a way an entity class
17
+ * name is not: `mikro_orm_migrations` persists it, so it is a string every
18
+ * already-migrated database holds.
19
+ *
20
+ * A class that is in neither the array nor the barrel is a migration that does
21
+ * not run: `migration:pending` reports nothing pending and the first symptom
22
+ * is a query against a table nobody created.
23
+ */
24
+ import { Migration20260424T205317OrganizationsInit } from './20260424T205317_organizations_init.js';
25
+ import { Migration20260425T051700OrganizationsInvitationsInit } from './20260425T051700_organizations_invitations_init.js';
26
+ import { Migration20260611T140349OrganizationsConsolidation } from './20260611T140349_organizations_consolidation.js';
27
+ import { Migration20260611T140358OrganizationsOrgOrderConfirmationEmails } from './20260611T140358_organizations_org_order_confirmation_emails.js';
28
+ import { Migration20260611T140402OrganizationsOrgFulfilmentStrategy } from './20260611T140402_organizations_org_fulfilment_strategy.js';
29
+ import { Migration20260717T151403OrganizationsPersonalOrganizations } from './20260717T151403_organizations_personal_organizations.js';
30
+ import { Migration20260718T200340OrganizationsOrganizationCustomFieldValues } from './20260718T200340_organizations_organization_custom_field_values.js';
31
+ import { Migration20260720T044254OrganizationsOrgHierarchy } from './20260720T044254_organizations_org_hierarchy.js';
32
+ import { Migration20260912T094646OrganizationsSalesChannelOrganizations } from './20260912T094646_organizations_sales_channel_organizations.js';
33
+ export declare const migrations: (typeof Migration20260424T205317OrganizationsInit)[];
34
+ export { Migration20260424T205317OrganizationsInit, Migration20260425T051700OrganizationsInvitationsInit, Migration20260611T140349OrganizationsConsolidation, Migration20260611T140358OrganizationsOrgOrderConfirmationEmails, Migration20260611T140402OrganizationsOrgFulfilmentStrategy, Migration20260717T151403OrganizationsPersonalOrganizations, Migration20260718T200340OrganizationsOrganizationCustomFieldValues, Migration20260720T044254OrganizationsOrgHierarchy, Migration20260912T094646OrganizationsSalesChannelOrganizations, };
35
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,yCAAyC,EAAE,MAAM,yCAAyC,CAAC;AACpG,OAAO,EAAE,oDAAoD,EAAE,MAAM,qDAAqD,CAAC;AAC3H,OAAO,EAAE,kDAAkD,EAAE,MAAM,kDAAkD,CAAC;AACtH,OAAO,EAAE,+DAA+D,EAAE,MAAM,kEAAkE,CAAC;AACnJ,OAAO,EAAE,0DAA0D,EAAE,MAAM,4DAA4D,CAAC;AACxI,OAAO,EAAE,0DAA0D,EAAE,MAAM,2DAA2D,CAAC;AACvI,OAAO,EAAE,kEAAkE,EAAE,MAAM,qEAAqE,CAAC;AACzJ,OAAO,EAAE,iDAAiD,EAAE,MAAM,kDAAkD,CAAC;AACrH,OAAO,EAAE,8DAA8D,EAAE,MAAM,gEAAgE,CAAC;AAEhJ,eAAO,MAAM,UAAU,sDAUtB,CAAC;AAEF,OAAO,EACL,yCAAyC,EACzC,oDAAoD,EACpD,kDAAkD,EAClD,+DAA+D,EAC/D,0DAA0D,EAC1D,0DAA0D,EAC1D,kEAAkE,EAClE,iDAAiD,EACjD,8DAA8D,GAC/D,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The `./migrations` subpath — every migration class this module owns, as one
3
+ * ordered `migrations` array.
4
+ *
5
+ * The array is what the platform reads when this module is **installed**:
6
+ * `src/packages/package-runtime.ts` takes `exported['migrations']` and refuses
7
+ * the package outright when it is absent (D-168).
8
+ *
9
+ * Listed in ascending timestamp, which orders **this module's own** migrations
10
+ * and nothing else (feature 081). Where the block sits relative to every other
11
+ * module's is decided by the manifest `dependencies` graph.
12
+ *
13
+ * The **named** exports stay, and the asymmetry with `./backend` — which
14
+ * publishes an array and no entity class by name (D-168) — is deliberate.
15
+ * `db/migrations-registry.generated.ts` imports each class by name from this
16
+ * specifier, and a migration class name is contract in a way an entity class
17
+ * name is not: `mikro_orm_migrations` persists it, so it is a string every
18
+ * already-migrated database holds.
19
+ *
20
+ * A class that is in neither the array nor the barrel is a migration that does
21
+ * not run: `migration:pending` reports nothing pending and the first symptom
22
+ * is a query against a table nobody created.
23
+ */
24
+ import { Migration20260424T205317OrganizationsInit } from './20260424T205317_organizations_init.js';
25
+ import { Migration20260425T051700OrganizationsInvitationsInit } from './20260425T051700_organizations_invitations_init.js';
26
+ import { Migration20260611T140349OrganizationsConsolidation } from './20260611T140349_organizations_consolidation.js';
27
+ import { Migration20260611T140358OrganizationsOrgOrderConfirmationEmails } from './20260611T140358_organizations_org_order_confirmation_emails.js';
28
+ import { Migration20260611T140402OrganizationsOrgFulfilmentStrategy } from './20260611T140402_organizations_org_fulfilment_strategy.js';
29
+ import { Migration20260717T151403OrganizationsPersonalOrganizations } from './20260717T151403_organizations_personal_organizations.js';
30
+ import { Migration20260718T200340OrganizationsOrganizationCustomFieldValues } from './20260718T200340_organizations_organization_custom_field_values.js';
31
+ import { Migration20260720T044254OrganizationsOrgHierarchy } from './20260720T044254_organizations_org_hierarchy.js';
32
+ import { Migration20260912T094646OrganizationsSalesChannelOrganizations } from './20260912T094646_organizations_sales_channel_organizations.js';
33
+ export const migrations = [
34
+ Migration20260424T205317OrganizationsInit,
35
+ Migration20260425T051700OrganizationsInvitationsInit,
36
+ Migration20260611T140349OrganizationsConsolidation,
37
+ Migration20260611T140358OrganizationsOrgOrderConfirmationEmails,
38
+ Migration20260611T140402OrganizationsOrgFulfilmentStrategy,
39
+ Migration20260717T151403OrganizationsPersonalOrganizations,
40
+ Migration20260718T200340OrganizationsOrganizationCustomFieldValues,
41
+ Migration20260720T044254OrganizationsOrgHierarchy,
42
+ Migration20260912T094646OrganizationsSalesChannelOrganizations,
43
+ ];
44
+ export { Migration20260424T205317OrganizationsInit, Migration20260425T051700OrganizationsInvitationsInit, Migration20260611T140349OrganizationsConsolidation, Migration20260611T140358OrganizationsOrgOrderConfirmationEmails, Migration20260611T140402OrganizationsOrgFulfilmentStrategy, Migration20260717T151403OrganizationsPersonalOrganizations, Migration20260718T200340OrganizationsOrganizationCustomFieldValues, Migration20260720T044254OrganizationsOrgHierarchy, Migration20260912T094646OrganizationsSalesChannelOrganizations, };
45
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,yCAAyC,EAAE,MAAM,yCAAyC,CAAC;AACpG,OAAO,EAAE,oDAAoD,EAAE,MAAM,qDAAqD,CAAC;AAC3H,OAAO,EAAE,kDAAkD,EAAE,MAAM,kDAAkD,CAAC;AACtH,OAAO,EAAE,+DAA+D,EAAE,MAAM,kEAAkE,CAAC;AACnJ,OAAO,EAAE,0DAA0D,EAAE,MAAM,4DAA4D,CAAC;AACxI,OAAO,EAAE,0DAA0D,EAAE,MAAM,2DAA2D,CAAC;AACvI,OAAO,EAAE,kEAAkE,EAAE,MAAM,qEAAqE,CAAC;AACzJ,OAAO,EAAE,iDAAiD,EAAE,MAAM,kDAAkD,CAAC;AACrH,OAAO,EAAE,8DAA8D,EAAE,MAAM,gEAAgE,CAAC;AAEhJ,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,yCAAyC;IACzC,oDAAoD;IACpD,kDAAkD;IAClD,+DAA+D;IAC/D,0DAA0D;IAC1D,0DAA0D;IAC1D,kEAAkE;IAClE,iDAAiD;IACjD,8DAA8D;CAC/D,CAAC;AAEF,OAAO,EACL,yCAAyC,EACzC,oDAAoD,EACpD,kDAAkD,EAClD,+DAA+D,EAC/D,0DAA0D,EAC1D,0DAA0D,EAC1D,kEAAkE,EAClE,iDAAiD,EACjD,8DAA8D,GAC/D,CAAC"}
@@ -0,0 +1,53 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ /**
3
+ * The one co-transactional seam this module publishes (D-178, under D-78
4
+ * point 2).
5
+ *
6
+ * It is declared here rather than in `@endora-commerce/contracts` for the same
7
+ * reason `credit_limits`' `CreditLimitPort` and `promotions`'
8
+ * `PromotionUsageFinalizer` are: its signature carries the caller's MikroORM
9
+ * `EntityManager`, and FR-034 keeps a MikroORM type out of that package. A
10
+ * branded stand-in would publish the coupling instead of removing it, which
11
+ * D-78 refuses in writing.
12
+ *
13
+ * **What holds it co-transactional is a foreign key, not a convention.**
14
+ * `customer_accounts_organization_fk` (`on delete restrict`) points
15
+ * `customer_accounts.organization_id` at `organizations.id`, and since D-178
16
+ * that column is `NOT NULL`. So the account row cannot be inserted before the
17
+ * organisation exists, and a second transaction cannot satisfy a foreign key
18
+ * against a row it cannot see. The two writes are one unit of work or the
19
+ * invariant has a window in it — which is precisely the window D-178 was taken
20
+ * to close: `createStandalone` used to commit the account with a null
21
+ * organisation and provision the organisation afterwards, and nothing retried
22
+ * the second half.
23
+ *
24
+ * `customer_accounts` is the caller and it already declares `organizations` in
25
+ * its manifest `dependencies` — the tenancy direction feature 051 established —
26
+ * so the edge this seam needs is the one the foreign key already required.
27
+ */
28
+ export interface PersonalOrganizationProvisionInput {
29
+ /**
30
+ * The id the account **will** carry. It is generated by the caller before the
31
+ * account row exists, because the personal organisation's `taxId` is derived
32
+ * from it and that derivation is what makes the provisioning re-runnable.
33
+ */
34
+ readonly customerAccountId: string;
35
+ readonly email: string;
36
+ readonly firstName?: string | null;
37
+ readonly lastName?: string | null;
38
+ }
39
+ export interface PersonalOrganizationProvisionApi {
40
+ /**
41
+ * Find-or-create the personal organisation for an account that is about to be
42
+ * written, on the caller's `EntityManager` and inside the caller's
43
+ * transaction. Writes nothing on `customer_accounts`: the membership column is
44
+ * the caller's own, and it sets it on the row it is inserting.
45
+ *
46
+ * Idempotent by `taxId`, so a retry after a rolled-back attempt re-finds
47
+ * rather than duplicating.
48
+ */
49
+ provisionFor(em: EntityManager, input: PersonalOrganizationProvisionInput): Promise<{
50
+ id: string;
51
+ }>;
52
+ }
53
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ports/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,WAAW,kCAAkC;IACjD;;;;OAIG;IACH,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,gCAAgC;IAC/C;;;;;;;;OAQG;IACH,YAAY,CACV,EAAE,EAAE,aAAa,EACjB,KAAK,EAAE,kCAAkC,GACxC,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC5B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ports/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,117 @@
1
+ ---
2
+ title: Organization hierarchy
3
+ ---
4
+
5
+ # Organization hierarchy
6
+
7
+ Organizations can be nested into a **tree**: a head-office organization owns
8
+ branch organizations, which may own sub-branches. Two things flow along the
9
+ tree — **scope** (a roll-up-enabled parent-org user or scoped sales-rep sees the
10
+ whole subtree) and **commercial terms** (a descendant with no own price list or
11
+ credit limit resolves the nearest ancestor's).
12
+
13
+ This is an additive layer over the flat organization model. Every organization
14
+ starts as a **root** (no parent), and a flat installation behaves byte-for-byte
15
+ as it did before the hierarchy existed.
16
+
17
+ ## The tree
18
+
19
+ Each organization records a nullable self-referential `parent_id` (NULL ⇒ root)
20
+ plus a server-maintained materialized `path` (`/<rootId>/…/<thisId>/`). A single
21
+ indexed prefix scan answers "descendants of X" and "ancestors of X" — there is
22
+ no per-node walk.
23
+
24
+ - **Assign / move a parent** — `POST /api/v1/admin/organizations/:id/parent`
25
+ with `{ "parentId": "<uuid>" | null }`. `null` detaches the organization back
26
+ to a root. The move is validated against **cycles** (a node may not become a
27
+ child of its own descendant) and a **maximum depth of 10 levels**, and is
28
+ audited through the Command Bus (`organization.set_parent` / `organization.move`,
29
+ both reversible).
30
+ - **Read the subtree** — `GET /api/v1/admin/organizations/:id/subtree` returns
31
+ the descendants (including the node itself) pre-order, each as
32
+ `{ id, name, parentId, depth, status }`.
33
+ - **Read the ancestor chain** — `GET /api/v1/admin/organizations/:id/ancestors`
34
+ returns the parent → … → root chain, nearest-first.
35
+ - **Deletion is blocked** — an organization with any child cannot be deleted
36
+ (`409 has_children`), backed by the `parent_id … ON DELETE RESTRICT` foreign
37
+ key. Reassign or remove the children first.
38
+
39
+ Re-parenting is **retroactive**: subtree membership, roll-up visibility, and
40
+ term inheritance are always computed against the *current* tree, so after a move
41
+ a branch's history becomes visible to the new parent's roll-up users and it
42
+ inherits the new parent's terms.
43
+
44
+ The admin UI exposes a parent picker plus a subtree/ancestor view on the
45
+ organization detail page.
46
+
47
+ ## Roll-up scope
48
+
49
+ Visibility across descendants is **permission-gated** by the
50
+ `organizations:rollup` capability ("Act across organization descendants").
51
+
52
+ - A **scoped sales-rep** holding the capability has each assignment expanded to
53
+ the assigned node's subtree. A descendant that carries its **own** assignment
54
+ overrides the inherited one for its subtree — the **nearest assignment on the
55
+ ancestor chain wins**. Without the capability the rep stays confined to the
56
+ organizations directly assigned to them.
57
+ - A branch-only user (no roll-up) sees only their own organization.
58
+
59
+ Expansion is derived **server-side** and widens the existing tenant-scope guard
60
+ (`allowedOrganizationIds`) to exactly the subtree — never a sibling, cousin, or
61
+ an ancestor outside the granted node. An out-of-subtree record is
62
+ indistinguishable from "does not exist". The three admin read surfaces — orders,
63
+ quotes, and customers — read the already-widened scope and need no change of
64
+ their own.
65
+
66
+ ## Term inheritance
67
+
68
+ A descendant with no own commercial term resolves the nearest ancestor's.
69
+
70
+ ### Price lists
71
+
72
+ Price lists target organizations through their application rule (there is no
73
+ organization foreign key). The resolver builds the org candidate set as
74
+ `[thisOrg, …ancestors]` (nearest-first) and keeps the existing priority order
75
+ (`organization` > `customerGroup` > `category` > `salesChannel`):
76
+
77
+ - a list naming a **nearer** organization outranks one naming a farther
78
+ ancestor, so a **branch override always wins**;
79
+ - an inherited ancestor org-named list still outranks a customer-group,
80
+ category, or sales-channel list;
81
+ - a branch with no own list falls through to the nearest ancestor's org-named
82
+ list before dropping to the non-org priority levels.
83
+
84
+ Only that branch diverges — siblings are unaffected. Resolution stays a single
85
+ pass (no per-ancestor re-run).
86
+
87
+ ### Credit limits
88
+
89
+ A descendant with no own credit limit transacts against the **nearest
90
+ ancestor** that has one. How the ancestor's limit is consumed is a
91
+ **per-organization mode**, set only by a platform administrator:
92
+
93
+ - **`shared_pool`** — every organization in the subtree draws against one shared
94
+ pool on the owning ancestor's row. Concurrent draws serialize on that row, so
95
+ the pool can never be over-spent (no double-spend).
96
+ - **`independent_default`** — the inherited amount is each branch's own limit;
97
+ every branch may draw the full inherited amount independently of its siblings.
98
+
99
+ A branch with its **own** credit limit overrides the inherited one.
100
+
101
+ #### The credit-inheritance-mode setting
102
+
103
+ The factory default is the platform-wide Settings value
104
+ `organizations.hierarchy.credit_inheritance_mode` (default **`shared_pool`**). A
105
+ per-organization override is stored on the organization and set via
106
+ `PUT /api/v1/admin/organizations/:id/credit-inheritance-mode` with
107
+ `{ "mode": "shared_pool" | "independent_default" | null }` (`null` falls back to
108
+ the Settings default). This endpoint is **platform-admin only** — a scoped or
109
+ roll-up actor is rejected with `403` — and the change is audited
110
+ (`organization.set_credit_mode`).
111
+
112
+ ## Flat-behavior guarantee
113
+
114
+ For a root organization the subtree is `{itself}`, the price-list org chain is
115
+ `[itself]`, and the credit owner is the organization itself (or none). All
116
+ scope, pricing, and credit resolution therefore collapse to the pre-feature
117
+ single-organization behavior, byte-for-byte.
@@ -0,0 +1,312 @@
1
+ ---
2
+ title: organizations
3
+ description: Customer Organizations, registration, invitations
4
+ ---
5
+
6
+ # `organizations`
7
+
8
+ Customer Organizations — registration, email verification, member management,
9
+ invitations, and the suspension state. The first user of a registering
10
+ Organization becomes its `organization_admin`.
11
+
12
+ ## Public surface
13
+
14
+ Org-Admin-only routes are enforced server-side via the
15
+ `assertOrganizationAdmin` helper. Admin routes (`/api/v1/admin/*`) are
16
+ gated by `customers:manage`.
17
+
18
+ | Verb + Path | Audience | Purpose |
19
+ | --- | --- | --- |
20
+ | `POST /api/v1/organizations/register` | anon | Register Org + first member, email verification dispatched |
21
+ | `POST /api/v1/auth/email-verification/verify` | anon | Redeem verification token |
22
+ | `POST /api/v1/auth/customer/login` | anon | Customer login → sets `b2b_session` cookie; merges anonymous cart |
23
+ | `POST /api/v1/auth/customer/logout` | customer | Destroy session |
24
+ | `POST /api/v1/auth/password-reset/request` | anon | Always 202 (defends against account enumeration) |
25
+ | `POST /api/v1/auth/password-reset/confirm` | anon | Redeem the emailed reset token |
26
+ | `GET /api/v1/me` | customer | Current customer + their organization, plus `impersonation: { impersonatorAdminUserId }` when an admin is acting as the buyer |
27
+ | `POST /api/v1/me/password` | customer | Change password (rejects wrong `currentPassword`) |
28
+ | `GET /api/v1/organizations/mine/members` | org admin | List members |
29
+ | `DELETE /api/v1/organizations/mine/members/:id` | org admin | Remove member (last-admin guard) |
30
+ | `PATCH /api/v1/organizations/mine/members/:id/role` | org admin | Promote / demote (last-admin guard) |
31
+ | `GET /api/v1/organizations/mine/invitations` | org admin | List pending invitations |
32
+ | `POST /api/v1/organizations/mine/invitations` | org admin | Invite a new user; emails the redemption link via the injected Mailer |
33
+ | `DELETE /api/v1/organizations/mine/invitations/:id` | org admin | Revoke a pending invitation |
34
+ | `POST /api/v1/organizations/invitations/:token/accept` | anon | Redeem invitation, mint Customer Account |
35
+ | `GET /api/v1/organizations/mine/addresses` | customer | List delivery / billing addresses |
36
+ | `POST /api/v1/organizations/mine/addresses` | customer | Create address |
37
+ | `PATCH /api/v1/organizations/mine/addresses/:id` | customer | Update |
38
+ | `DELETE /api/v1/organizations/mine/addresses/:id` | customer | Remove |
39
+ | `GET /api/v1/admin/organizations` | admin | List with `filter[status]` / `filter[vatStatus]` / `q` |
40
+ | `GET /api/v1/admin/organizations/:id` | admin | Org + member roster (`updatedAt`, members include `lastLoginAt`) |
41
+ | `PATCH /api/v1/admin/organizations/:id` | admin | Update name / `status` / `vatStatus`; optional `expectedUpdatedAt` → `409 VERSION_CONFLICT` when stale |
42
+ | `POST /api/v1/admin/organizations/:id/members/invite` | admin | Invite by email + role (platform-scope) |
43
+ | `POST /api/v1/admin/organizations/:id/members` | admin | Direct-create member with password |
44
+ | `PATCH /api/v1/admin/organizations/:id/members/:customerAccountId/role` | admin | Change role; optional `expectedUpdatedAt` per member |
45
+ | `DELETE /api/v1/admin/organizations/:id/members/:customerAccountId` | admin | Soft-remove member (last-admin guard) |
46
+ | `POST /api/v1/admin/organizations/:id/recover-admin-access` | admin | Break-glass — promote existing member to `organization_admin` |
47
+
48
+ Configure **`SMTP_URL`** in the backend environment so outbound mail uses SMTP instead of the console logger.
49
+
50
+ ## Entities
51
+
52
+ `Organization`, `OrganizationInvitation`, `EmailVerificationToken`. Tax-ID
53
+ uniqueness is enforced at the DB level; duplicate registrations return
54
+ `409 ORGANIZATION_TAX_ID_EXISTS`.
55
+
56
+ ## Events emitted
57
+
58
+ `organization.registered.v1`, `organization.verified.v1`,
59
+ `organization.suspended.v1`, `organization.member_invited.v1`,
60
+ `organization.member_role_changed.v1`.
61
+
62
+ ## Extension points
63
+
64
+ - **Verification dispatch** — `email-verification-service.ts` exposes a
65
+ pluggable mailer interface; swap the dev-mode console mailer for a real
66
+ SMTP/SendGrid driver in production composition.
67
+ - **Last-admin guard** — codified in `role-service.ts#changeRole` and
68
+ `invitation-service.ts#revoke`; add new "must keep at least one admin"
69
+ call sites here.
70
+
71
+ ## Commercial party and moderation
72
+
73
+ Organization is a first-class commercial party. This section describes
74
+ the runtime surface.
75
+
76
+ ### Lifecycle status (`pending_verification` → `active` → `blocked` / `rejected`)
77
+
78
+ Every newly-registered Organization starts in `pending_verification`. The
79
+ platform-wide setting `organizations.moderation.mode` (`manual` /
80
+ `auto`) controls whether an admin must approve manually before the
81
+ Organization can transact. While the status is anything other than
82
+ `active`, the platform refuses Order placement, RFQ submission, and
83
+ cart-line addition with HTTP 423.
84
+
85
+ The legacy `suspended` status was renamed to `blocked` by migration 047
86
+ with an audit-log breadcrumb on every remapped row.
87
+
88
+ Admin endpoints:
89
+
90
+ | Verb + Path | Purpose |
91
+ | --- | --- |
92
+ | `POST /api/v1/admin/organizations/:id/approve` | Transition `pending_verification` → `active` |
93
+ | `POST /api/v1/admin/organizations/:id/reject` | Transition `pending_verification` → `rejected` (terminal) |
94
+ | `POST /api/v1/admin/organizations/:id/block` | Transition `active` → `blocked` (operator lever) |
95
+ | `POST /api/v1/admin/organizations/:id/unblock` | Transition `blocked` → `active` |
96
+
97
+ Every body carries `expectedVersion: number` (optimistic-lock token from
98
+ the `organizations.version` column) and is wrapped in
99
+ `em.transactional`. A stale `expectedVersion` returns `409
100
+ VERSION_CONFLICT` with the `currentVersion` in the body. A status guard
101
+ violation (e.g. approving an already-active org) returns `422
102
+ VALIDATION_FAILED`.
103
+
104
+ Customer-side gate: the storefront receives the localized
105
+ "why-you-can't-transact" message via `GET /api/v1/me`'s
106
+ `organization.canTransact` + `organization.moderationMessage` fields.
107
+ The cart and checkout pages render `<OrganizationModerationBanner>`
108
+ above the form when `canTransact === false`.
109
+
110
+ ### Admin notifications
111
+
112
+ A small `admin_notifications` module owns the bell surface. On every
113
+ new Organization registration, `OrgRegistrationNotifier` writes one
114
+ broadcast notification (`audience='all_admins'`,
115
+ `kind='organization.registered'`) and dispatches one e-mail per entry
116
+ in the `organizations.notifications.new_registration_recipients`
117
+ setting. The bell polls every 30 s via
118
+ `GET /api/v1/admin/notifications`.
119
+
120
+ | Verb + Path | Purpose |
121
+ | --- | --- |
122
+ | `GET /api/v1/admin/notifications` | Paged feed, per-admin `isRead` resolution |
123
+ | `POST /api/v1/admin/notifications/:id/read` | Mark one entry read |
124
+ | `POST /api/v1/admin/notifications/mark-all-read` | Mark every visible entry read |
125
+
126
+ ### Per-organization commercial scoping
127
+
128
+ Three allow-list bridges control what an Organization may use at
129
+ checkout:
130
+
131
+ - `organization_payment_methods` (pivot: `(organization_id, payment_method_id)`)
132
+ - `organization_delivery_methods` (pivot: `(organization_id, delivery_method_id)`)
133
+ - `organization_warehouses` (pivot: `(organization_id, warehouse_id)`)
134
+
135
+ **Empty list ⇒ platform defaults apply.** A non-empty list filters the
136
+ storefront `GET /api/v1/payment-methods`, `GET /api/v1/delivery-methods`,
137
+ and inventory stock-figure endpoints intersected with the caller's
138
+ Organization assignment.
139
+
140
+ Admin endpoints:
141
+
142
+ | Verb + Path | Purpose |
143
+ | --- | --- |
144
+ | `GET /api/v1/admin/organizations/:id/restrictions` | Read all three allow-lists + the org's `version` |
145
+ | `PUT /api/v1/admin/organizations/:id/restrictions` | Atomic replace of all three |
146
+ | `PATCH .../restrictions/payment-methods` | Surgical `{ add?, remove? }` |
147
+ | `PATCH .../restrictions/delivery-methods` | Same |
148
+ | `PATCH .../restrictions/warehouses` | Same |
149
+
150
+ Storefront preflight:
151
+
152
+ | Verb + Path | Purpose |
153
+ | --- | --- |
154
+ | `POST /api/v1/storefront/checkout/preflight` | Returns `{ canTransact, allowedPaymentMethodIds, allowedDeliveryMethodIds, assignedWarehouseIds }` or 423 when the org cannot transact |
155
+
156
+ ### Applicable price lists + promotion targeting
157
+
158
+ `OrganizationEffectivePriceListsService.listApplicable(orgId)` reuses
159
+ the existing `application-rule-evaluator` from the `price_lists` module
160
+ to compute every Price List that currently applies to the Organization,
161
+ each tagged with a `reasons[]` array
162
+ (`direct_organization_match` / `customer_group_match` /
163
+ `sales_channel_inheritance` / `segment_rule_match`). Surfaced at
164
+ `GET /api/v1/admin/organizations/:id/applicable-price-lists` and
165
+ rendered as a read-only table in the admin Organization detail page.
166
+
167
+ Promotions: when a promotion targets a specific Organization
168
+ (`promotions.organization_id` is set), the platform applies the rule
169
+ only when the cart's Organization is `active`. The check is wired
170
+ through `PromotionService`'s optional `resolveOrganizationStatus`
171
+ constructor argument; composition.ts passes a raw SQL lookup.
172
+
173
+ ### Sales-rep ownership
174
+
175
+ `organization_sales_rep_assignments` (pivot: `(organization_id,
176
+ admin_user_id)`) binds sales reps to organizations. When the
177
+ caller's admin role is `sales_representative`, the admin Orders list
178
+ and RFQ list are filtered to the orgs the rep owns. Platform admins
179
+ see everything.
180
+
181
+ Three endpoints maintain the relation, and this module owns and
182
+ registers all three:
183
+
184
+ | Verb + Path | Purpose |
185
+ | --- | --- |
186
+ | `GET /api/v1/admin/organizations/:id/sales-reps` | List the reps assigned to an organization. |
187
+ | `POST /api/v1/admin/organizations/:id/sales-reps` | Assign a rep. |
188
+ | `DELETE /api/v1/admin/organizations/:id/sales-reps/:adminUserId` | Remove an assignment. |
189
+
190
+ They are gated by `organizations:assign-sales-rep`. Until 2026-08 they
191
+ were registered by the quote-requests module and gated by
192
+ `rfqs:handle`, which meant switching quote requests off also removed
193
+ the ability to assign a sales representative — and the code gating
194
+ the screen disappeared from the roles matrix with it. Assigning a rep
195
+ qualifies an organization, so it belongs here, with a code this
196
+ module declares. The one endpoint that stayed behind is the reverse
197
+ listing, `GET /api/v1/admin/sales-reps/:adminUserId/organizations`:
198
+ it reports how many quote requests are open per organization, which
199
+ is that module's fact.
200
+
201
+ Other modules read the relation through this module's
202
+ `organizationSalesRepScopePort`, never by querying the pivot.
203
+
204
+ ### VAT-ID / NIP validation
205
+
206
+ Two production HTTP clients implement the `VatValidator` port:
207
+
208
+ - `ViesClient` → `POST` against the VIES REST endpoint
209
+ (`/check-vat-number`). 5-second timeout; single abort on network
210
+ error.
211
+ - `MinisterstwoFinansowClient` → `GET` against `wl-api.mf.gov.pl/api/search/nip/{nip}`.
212
+ In-process 10-rps throttle; 7-day cache key on `(nip, today)` is
213
+ baked into the service-side `OrganizationTaxIdValidation` history
214
+ (one row per attempt).
215
+
216
+ `OrganizationTaxIdValidationService` auto-picks the provider per
217
+ tax-id prefix (Polish 10-digit → MF; other ISO-2 prefix → VIES;
218
+ anything else → format-only). When `applyAutoFill=true` AND the
219
+ result is `validated`, the org's `legalName` is updated and `version`
220
+ bumps so the next admin edit honors the optimistic-lock.
221
+
222
+ All adapters degrade safely on provider outage:
223
+ `outcome: 'deferred'`. The org save never fails because of a
224
+ third-party hiccup.
225
+
226
+ | Verb + Path | Purpose |
227
+ | --- | --- |
228
+ | `POST /api/v1/admin/organizations/:id/vat-validations` | Trigger one validation attempt (`providerHint`, `applyAutoFill`) |
229
+ | `GET /api/v1/admin/organizations/:id/vat-validations` | History list, newest first |
230
+
231
+ ### Picker primitive + diacritic-insensitive search
232
+
233
+ The admin app ships a reusable `<OrganizationPicker>` (single-select)
234
+ and `<OrganizationPickerMulti>` (multi-select) on top of the existing
235
+ `<Combobox>`. They consume `GET /api/v1/admin/organizations?q=` whose
236
+ `q` parameter is diacritic-insensitive: a query of `lodz` finds
237
+ "Bauhaus Łódź" via the denormalized `name_search` column populated
238
+ by the Organization entity's `@BeforeCreate` / `@BeforeUpdate` hooks.
239
+ `normalizeOrganizationName` is that fold plus the whitespace policy the
240
+ column needs. The fold itself is `foldDiacritics`
241
+ (`packages/contracts/src/text-normalization.ts`), shared with the admin
242
+ panel: NFD decomposition, a strip of the combining
243
+ marks, then an explicit table for the precomposed Latin letters NFD
244
+ doesn't split (`ł`/`Ł`, `ø`/`Ø`, `đ`/`Đ`, `ð`/`Ð`, `þ`/`Þ`, `ß`, `æ`,
245
+ `œ`). Changing that table re-folds new rows differently from old ones,
246
+ so it is a migration of `name_search`, not an edit.
247
+
248
+ ### New settings (declared on the manifest)
249
+
250
+ | Code | Type | Default | Purpose |
251
+ | --- | --- | --- | --- |
252
+ | `organizations.moderation.mode` | `string` enum | `'manual'` | `manual` ⇒ pending_verification; `auto` ⇒ active on registration |
253
+ | `organizations.notifications.new_registration_recipients` | `json` array | `[]` | E-mail recipients for new-Organization notifications |
254
+
255
+ ### Migrations
256
+
257
+ - `047_organizations_consolidation.ts` — adds `legal_name`, VAT-validation
258
+ columns, blocked / rejected / approved audit columns, `version`
259
+ optimistic-lock, `name_search` denormalized column, three allow-list
260
+ bridges, the validation-history table, the `organizations_name_search_idx`
261
+ B-Tree index, and remaps every `suspended` row to `blocked`.
262
+ - `048_admin_notifications_init.ts` — adds the `admin_notifications`
263
+ table + the per-admin `admin_notification_reads` bridge.
264
+ - `049_customer_accounts_organization_optional.ts` — relaxed
265
+ `customer_accounts.organization_id` to nullable so guest-style
266
+ Customer accounts were representable. **That design is
267
+ dead**: personal organizations replaced it and the column was re-tightened — see
268
+ `customer_accounts`' `20260825T141659_customer_accounts_organization_required`.
269
+ - `089_personal_organizations.ts` — adds `organizations.is_personal`
270
+ and backfills a personal organization for every pre-existing no-org
271
+ customer account (see "Personal organizations" below).
272
+
273
+ ### Personal organizations (B2C)
274
+
275
+ The Organization is the platform's single tenant concept. A B2C /
276
+ individual customer is **not** a null-org special case: every standalone
277
+ customer registration provisions a single-member **personal
278
+ organization** (`is_personal = true`). The organization and the
279
+ account are written **in one transaction**, by the module that owns the account
280
+ row, on both paths that create one — self-registration and federated sign-in.
281
+ This means:
282
+
283
+ - **Transacting works unchanged.** `organization_id` is `NOT NULL`,
284
+ so ordering, RFQs, credit, invoices and addresses need no null-org path — and
285
+ the column, not a guard, is what refuses one: MikroORM applies its tenant
286
+ filter to `SELECT` / `UPDATE` / `DELETE` and not to `INSERT`.
287
+ - **Isolation is structural.** The tenant guard isolates each
288
+ personal org as its own tenant — two B2C customers can never see each
289
+ other's data, with zero null-org special-casing.
290
+ - **Individual defaults.** `status = active`, `vat_status = vat_exempt`,
291
+ `name` from the customer's name (falling back to the email local-part),
292
+ and a synthetic 32-hex `tax_id` derived from the account id (the column
293
+ is globally `UNIQUE`; an individual has no company tax id).
294
+ - **Invisible in B2B admin.** Personal orgs are excluded by default from
295
+ the admin org list/pickers, cannot receive a sales rep, and never enter
296
+ the moderation queue (they are created `active`). The admin org list
297
+ accepts `?includePersonal=true` to surface them when needed.
298
+ - **Per-channel gate.** Standalone (B2C) registration is controlled per
299
+ sales channel by the `customers.allow_registration_without_organization`
300
+ setting; a B2B-only channel refuses the registration and provisions
301
+ nothing. The setting's name is a leftover from an earlier design — what it
302
+ gates is registration outside a *company* organization, not registration
303
+ without one.
304
+ - **Detaching a member from a company moves them here.** The admin
305
+ `DELETE /api/v1/admin/customers/:id/organization` used to write
306
+ `organization_id = NULL`; it now provisions (or re-finds) the
307
+ customer's own personal organization and moves them into it, keeping the
308
+ `customer_account.organization_unassigned` audit verb.
309
+
310
+ Company (B2B) organizations are unaffected — the single-member invariant
311
+ (`assertMembershipAllowed`) only rejects adding a second member to a
312
+ personal org.