@endora-commerce/mod-customers 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 (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +60 -0
  3. package/dist/admin/index.d.ts +26 -0
  4. package/dist/admin/index.d.ts.map +1 -0
  5. package/dist/admin/index.js +68 -0
  6. package/dist/admin/index.js.map +1 -0
  7. package/dist/admin/pages/CustomerDetail.d.ts +9 -0
  8. package/dist/admin/pages/CustomerDetail.d.ts.map +1 -0
  9. package/dist/admin/pages/CustomerDetail.js +72 -0
  10. package/dist/admin/pages/CustomerDetail.js.map +1 -0
  11. package/dist/admin/pages/CustomersList.d.ts +9 -0
  12. package/dist/admin/pages/CustomersList.d.ts.map +1 -0
  13. package/dist/admin/pages/CustomersList.js +115 -0
  14. package/dist/admin/pages/CustomersList.js.map +1 -0
  15. package/dist/admin/pages/OnlineCustomers.d.ts +9 -0
  16. package/dist/admin/pages/OnlineCustomers.d.ts.map +1 -0
  17. package/dist/admin/pages/OnlineCustomers.js +77 -0
  18. package/dist/admin/pages/OnlineCustomers.js.map +1 -0
  19. package/dist/admin/panels/HistoryPanels.d.ts +11 -0
  20. package/dist/admin/panels/HistoryPanels.d.ts.map +1 -0
  21. package/dist/admin/panels/HistoryPanels.js +64 -0
  22. package/dist/admin/panels/HistoryPanels.js.map +1 -0
  23. package/dist/admin/panels/ManagementPanels.d.ts +14 -0
  24. package/dist/admin/panels/ManagementPanels.d.ts.map +1 -0
  25. package/dist/admin/panels/ManagementPanels.js +93 -0
  26. package/dist/admin/panels/ManagementPanels.js.map +1 -0
  27. package/dist/backend/entities/customer-address.entity.d.ts +25 -0
  28. package/dist/backend/entities/customer-address.entity.d.ts.map +1 -0
  29. package/dist/backend/entities/customer-address.entity.js +95 -0
  30. package/dist/backend/entities/customer-address.entity.js.map +1 -0
  31. package/dist/backend/index.d.ts +91 -0
  32. package/dist/backend/index.d.ts.map +1 -0
  33. package/dist/backend/index.js +201 -0
  34. package/dist/backend/index.js.map +1 -0
  35. package/dist/backend/plugin.d.ts +168 -0
  36. package/dist/backend/plugin.d.ts.map +1 -0
  37. package/dist/backend/plugin.js +83 -0
  38. package/dist/backend/plugin.js.map +1 -0
  39. package/dist/backend/routes.admin.d.ts +70 -0
  40. package/dist/backend/routes.admin.d.ts.map +1 -0
  41. package/dist/backend/routes.admin.js +313 -0
  42. package/dist/backend/routes.admin.js.map +1 -0
  43. package/dist/backend/routes.register.d.ts +12 -0
  44. package/dist/backend/routes.register.d.ts.map +1 -0
  45. package/dist/backend/routes.register.js +40 -0
  46. package/dist/backend/routes.register.js.map +1 -0
  47. package/dist/backend/routes.self.d.ts +46 -0
  48. package/dist/backend/routes.self.d.ts.map +1 -0
  49. package/dist/backend/routes.self.js +145 -0
  50. package/dist/backend/routes.self.js.map +1 -0
  51. package/dist/backend/serializers.d.ts +13 -0
  52. package/dist/backend/serializers.d.ts.map +1 -0
  53. package/dist/backend/serializers.js +39 -0
  54. package/dist/backend/serializers.js.map +1 -0
  55. package/dist/backend/services/customer-address-read-port.d.ts +29 -0
  56. package/dist/backend/services/customer-address-read-port.d.ts.map +1 -0
  57. package/dist/backend/services/customer-address-read-port.js +53 -0
  58. package/dist/backend/services/customer-address-read-port.js.map +1 -0
  59. package/dist/backend/services/customer-address-service.d.ts +94 -0
  60. package/dist/backend/services/customer-address-service.d.ts.map +1 -0
  61. package/dist/backend/services/customer-address-service.js +176 -0
  62. package/dist/backend/services/customer-address-service.js.map +1 -0
  63. package/dist/backend/services/customer-admin-query-service.d.ts +57 -0
  64. package/dist/backend/services/customer-admin-query-service.d.ts.map +1 -0
  65. package/dist/backend/services/customer-admin-query-service.js +97 -0
  66. package/dist/backend/services/customer-admin-query-service.js.map +1 -0
  67. package/dist/backend/services/customer-authority-service.d.ts +46 -0
  68. package/dist/backend/services/customer-authority-service.d.ts.map +1 -0
  69. package/dist/backend/services/customer-authority-service.js +42 -0
  70. package/dist/backend/services/customer-authority-service.js.map +1 -0
  71. package/dist/backend/services/customer-defaults-service.d.ts +48 -0
  72. package/dist/backend/services/customer-defaults-service.d.ts.map +1 -0
  73. package/dist/backend/services/customer-defaults-service.js +71 -0
  74. package/dist/backend/services/customer-defaults-service.js.map +1 -0
  75. package/dist/backend/services/customer-deletion-service.d.ts +39 -0
  76. package/dist/backend/services/customer-deletion-service.d.ts.map +1 -0
  77. package/dist/backend/services/customer-deletion-service.js +94 -0
  78. package/dist/backend/services/customer-deletion-service.js.map +1 -0
  79. package/dist/backend/services/customer-moderation-service.d.ts +58 -0
  80. package/dist/backend/services/customer-moderation-service.d.ts.map +1 -0
  81. package/dist/backend/services/customer-moderation-service.js +70 -0
  82. package/dist/backend/services/customer-moderation-service.js.map +1 -0
  83. package/dist/backend/services/customer-org-assignment-service.d.ts +52 -0
  84. package/dist/backend/services/customer-org-assignment-service.d.ts.map +1 -0
  85. package/dist/backend/services/customer-org-assignment-service.js +106 -0
  86. package/dist/backend/services/customer-org-assignment-service.js.map +1 -0
  87. package/dist/backend/services/customer-presence-service.d.ts +44 -0
  88. package/dist/backend/services/customer-presence-service.d.ts.map +1 -0
  89. package/dist/backend/services/customer-presence-service.js +47 -0
  90. package/dist/backend/services/customer-presence-service.js.map +1 -0
  91. package/dist/backend/services/customer-registration-service.d.ts +56 -0
  92. package/dist/backend/services/customer-registration-service.d.ts.map +1 -0
  93. package/dist/backend/services/customer-registration-service.js +43 -0
  94. package/dist/backend/services/customer-registration-service.js.map +1 -0
  95. package/dist/backend/workers/anonymization-sweep-worker.d.ts +15 -0
  96. package/dist/backend/workers/anonymization-sweep-worker.d.ts.map +1 -0
  97. package/dist/backend/workers/anonymization-sweep-worker.js +20 -0
  98. package/dist/backend/workers/anonymization-sweep-worker.js.map +1 -0
  99. package/dist/manifest.d.ts +181 -0
  100. package/dist/manifest.d.ts.map +1 -0
  101. package/dist/manifest.js +192 -0
  102. package/dist/manifest.js.map +1 -0
  103. package/dist/migrations/20260611T140404_customers_customer_addresses_init.d.ts +14 -0
  104. package/dist/migrations/20260611T140404_customers_customer_addresses_init.d.ts.map +1 -0
  105. package/dist/migrations/20260611T140404_customers_customer_addresses_init.js +40 -0
  106. package/dist/migrations/20260611T140404_customers_customer_addresses_init.js.map +1 -0
  107. package/dist/migrations/index.d.ts +27 -0
  108. package/dist/migrations/index.d.ts.map +1 -0
  109. package/dist/migrations/index.js +29 -0
  110. package/dist/migrations/index.js.map +1 -0
  111. package/docs/customers.md +108 -0
  112. package/i18n/en.json +94 -0
  113. package/i18n/pl.json +94 -0
  114. package/package.json +101 -0
  115. package/tailwind.css +14 -0
@@ -0,0 +1,201 @@
1
+ import { z } from 'zod';
2
+ import { ERROR_CODES } from '@endora-commerce/contracts';
3
+ import { HttpError } from '@endora-commerce/platform/http';
4
+ import { CustomerAddressReadService } from './services/customer-address-read-port.js';
5
+ import { lazyPort } from '@endora-commerce/platform/kernel';
6
+ import { CUSTOMERS_SETTING_CODES } from '../manifest.js';
7
+ import { customersModule } from './plugin.js';
8
+ import { CustomerAddress } from './entities/customer-address.entity.js';
9
+ /**
10
+ * Who is moderating, and which organizations they may act on — defaulted here
11
+ * (`specs/117-instance-bring-up/` Phase 6, FR-033).
12
+ *
13
+ * It was contributed by `backend/src/composition.ts` and by
14
+ * `backend/test/helpers/test-server.ts` and by nothing else, so a composition
15
+ * that is neither — which is every client instance — could not resolve it, and
16
+ * met that fact as a 500 on the first admin customer screen rather than at
17
+ * boot.
18
+ *
19
+ * The production root wrote the role lookup as `knex.raw` over `admin_users`
20
+ * and `admin_roles`, which is two modules' tables read on this module's
21
+ * `EntityManager`, outside either owner's gate. The two ports say the same
22
+ * thing and are already this manifest's declared `dependencies`; the test
23
+ * harness had been written this way since T140, so the port shape is the one
24
+ * with the prior art and the SQL is what goes.
25
+ */
26
+ function registerModerationActor(ctx) {
27
+ ctx.di.register({
28
+ customerModerationActorResolver: ctx
29
+ .asFunction(() => {
30
+ const admins = lazyPort(ctx, 'adminUserReadPort');
31
+ const roles = lazyPort(ctx, 'adminRolePort');
32
+ const salesRepScope = lazyPort(ctx, 'organizationSalesRepScopePort');
33
+ return async (request) => {
34
+ const actor = request.actor;
35
+ if (actor?.kind !== 'admin') {
36
+ throw new HttpError(401, ERROR_CODES.UNAUTHORIZED, 'Admin session required.');
37
+ }
38
+ const adminUser = await admins.findById(actor.adminUserId);
39
+ // `getById` cannot 404 here: `admin_users_admin_role_fk` is
40
+ // `on delete restrict`, so a non-null `adminRoleId` names a row.
41
+ const role = adminUser?.adminRoleId ? await roles.getById(adminUser.adminRoleId) : null;
42
+ const isPlatformAdmin = role?.code !== 'sales_representative';
43
+ // Feature 056 — subtree-expanded when the rep holds
44
+ // `organizations:rollup`, which is `organizations`' decision and the
45
+ // reason this reads an assembled port rather than the tree.
46
+ const allowedOrganizationIds = isPlatformAdmin
47
+ ? []
48
+ : await salesRepScope.listAssignedOrganizationIds(actor.adminUserId);
49
+ return { adminUserId: actor.adminUserId, isPlatformAdmin, allowedOrganizationIds };
50
+ };
51
+ })
52
+ .singleton(),
53
+ });
54
+ }
55
+ export function registerModule(ctx) {
56
+ const cradle = () => ctx.cradle();
57
+ registerModerationActor(ctx);
58
+ /**
59
+ * This module's own settings, read against the system-default channel. Each
60
+ * degrades to the manifest default rather than failing the request — the
61
+ * identical try/catch stood in the production root, three times over.
62
+ *
63
+ * D-48 removed an `if (!channel) return fallback;` from between the two: on
64
+ * the impossible no-channel branch it returned the compiled-in constant
65
+ * without reading the setting at all, which is worse than switching tier.
66
+ */
67
+ const readChannelSetting = async (code, schema, fallback) => {
68
+ try {
69
+ const channel = await cradle().salesChannelResolutionPort.getSystemDefault();
70
+ return (await cradle().settingsReadPort.get(code, channel.id, schema));
71
+ }
72
+ catch {
73
+ return fallback;
74
+ }
75
+ };
76
+ /**
77
+ * Feature 075, Phase P — the customer's own saved addresses.
78
+ *
79
+ * `quick_order` reads one when it fills in a buyer's one-click defaults. The
80
+ * table is distinct from `addresses`' on purpose and the port keeps them
81
+ * distinct: this one hangs off a customer account, that one off an
82
+ * organisation, and a B2C buyer keeps addresses that are theirs rather than
83
+ * their personal organisation's.
84
+ */
85
+ ctx.di.providePort('customerAddressReadPort', ctx
86
+ .asFunction(({ emFactory }) => new CustomerAddressReadService(emFactory))
87
+ .singleton());
88
+ ctx.di.register({
89
+ customers: ctx
90
+ .asFunction(({ emFactory, auditLogService }) => customersModule({
91
+ emFactory,
92
+ auditLogService,
93
+ // Every one of these is another module's gated port and is stored by
94
+ // the constructor, so a singleton may not hold one directly.
95
+ // Feature 075, Phase C (issue #195) — `customerAuthPort` rather
96
+ // than the `customerAuthService` class registration. Unlike the
97
+ // address pair this was never a record type over a class: the
98
+ // cradle field resolved to a direct class-type import, an ordinary
99
+ // FR-011 edge that happened to share the container name.
100
+ customerAuthService: lazyPort(ctx, 'customerAuthPort'),
101
+ passwordResetService: lazyPort(ctx, 'passwordResetService'),
102
+ // D-98.2 / issue #196 — `authSessionPort` is the name the contract
103
+ // publishes; `sessionService` is `auth`'s class registration, and
104
+ // resolving it here was the leak D-98.1 repaired for `addressService`.
105
+ sessionService: lazyPort(ctx, 'authSessionPort'),
106
+ customerAccountReadPort: lazyPort(ctx, 'customerAccountReadPort'),
107
+ // Feature 075, Phase C — the rest of `customer_accounts`' surface
108
+ // this module's screens run on. Every one of these replaced a
109
+ // direct load or mutation of that module's `CustomerAccount`
110
+ // entity: the block/unblock/delete/restore/assign writes, the
111
+ // retention scrub, the admin list's paginated query, and the
112
+ // customer-group names beside it.
113
+ customerAccountLifecycleWritePort: lazyPort(ctx, 'customerAccountLifecycleWritePort'),
114
+ customerAccountAdminSearchPort: lazyPort(ctx, 'customerAccountAdminSearchPort'),
115
+ customerGroupReadPort: lazyPort(ctx, 'customerGroupReadPort'),
116
+ // `organizations`' row-level read (names on the admin list, the
117
+ // existence check on the assignment screen) and its personal-org
118
+ // provisioner, which the B2C registration and the retention sweep
119
+ // both go through. Both replaced an `em.findOne(Organization, …)`
120
+ // — and the sweep's three-field scrub of that row — here.
121
+ organizationDetailsPort: lazyPort(ctx, 'organizationDetailsPort'),
122
+ personalOrganizationPort: lazyPort(ctx, 'personalOrganizationPort'),
123
+ // `addresses`' read: the org-shared book a customer may pick a
124
+ // delivery address from, which this module used to query directly.
125
+ addressReadPort: lazyPort(ctx, 'addressReadPort'),
126
+ customFieldValues: lazyPort(ctx, 'customFieldValueService'),
127
+ rfqService: lazyPort(ctx, 'rfqService'),
128
+ // Issue #108 — `organizations`' scope, not a private copy of its
129
+ // wiring. The copy this replaces omitted the optional subtree deps,
130
+ // so feature 056's roll-up was skipped for every block / unblock /
131
+ // delete / org-assign decision, and `tsc` had nothing to say about
132
+ // it.
133
+ salesRepVisibility: lazyPort(ctx, 'organizationSalesRepScopePort'),
134
+ mailer: lazyPort(ctx, 'emailMailer'),
135
+ // Feature 075 — `carts`' reporting read and `admin_users`'
136
+ // impersonation seam, where this module built a second instance of
137
+ // each from an import of the owner's directory. Both are gated
138
+ // ports, so a singleton may not hold one directly.
139
+ cartQueryPort: lazyPort(ctx, 'cartQueryPort'),
140
+ impersonationPort: lazyPort(ctx, 'impersonationPort'),
141
+ // `auth`'s read over the session table, for the online-customers
142
+ // panel — the last thing in this module that named `Session`.
143
+ authSessionReadPort: lazyPort(ctx, 'authSessionReadPort'),
144
+ // `orders`' published list, replacing the late-bound accessor and
145
+ // the `Pick<OrderListService, 'list'>` that typed it. `orders` is
146
+ // non-deactivatable, so the edge is an ordinary binding dependency
147
+ // and the panels have no absent state to degrade into: the port's
148
+ // own 503 covers the window before `orders` registers its routes.
149
+ orderList: lazyPort(ctx, 'orderListPort'),
150
+ // Feature 080, T048 (D-169) — `orders`' row-level read, for the
151
+ // customer-detail header's sales-channel list. That list was an
152
+ // `em.find(Order, …)` inside this module: a plain read of another
153
+ // module's table, which kept answering out of an `orders` the
154
+ // platform was not serving. A read takes a read-port method, never
155
+ // an `EntityManager`-taking apply port. Same binding edge as
156
+ // `orderListPort` above and the same manifest `dependencies` entry.
157
+ orderReadPort: lazyPort(ctx, 'orderReadPort'),
158
+ // Feature 076 (D-86) — `organizations`' port, resolved lazily: the
159
+ // factory below is a singleton and stores what it is handed, and a
160
+ // captured gate keeps answering after its owner is switched off.
161
+ vatValidator: lazyPort(ctx, 'vatValidatorPort'),
162
+ // Issue #216 — the ordering defaults this module renders and edits
163
+ // belong to `quick_order`. Until now it built a **second instance**
164
+ // of that module's `DefaultPreferenceService` from an import of its
165
+ // directory, so the two holders had to be converted together and
166
+ // the port published for this consumer went unreached. Binding, per
167
+ // the port's own contract: a switched-off `quick_order` answers 503
168
+ // `MODULE_DISABLED` at the seam rather than half a set of defaults.
169
+ defaultPreferencePort: lazyPort(ctx, 'defaultPreferencePort'),
170
+ storefrontBaseUrl: cradle().storefrontBaseUrl,
171
+ requireAdmin: (permission) => async (req, reply) => cradle().requireAdmin(permission)(req, reply),
172
+ requireCustomer: (req, reply) => cradle().requireCustomer(req, reply),
173
+ resolveCustomerActor: (req) => cradle().customerActorResolver(req),
174
+ resolveModerationActor: (req) => cradle().customerModerationActorResolver(req),
175
+ // The three reads that used to be a root's, one of them exercised
176
+ // by no test at all — see the note above.
177
+ resolveAllowRegistrationWithoutOrganization: () => readChannelSetting(CUSTOMERS_SETTING_CODES.ALLOW_REGISTRATION_WITHOUT_ORGANIZATION, z.boolean(), false),
178
+ resolveDeletionRetentionDays: () => readChannelSetting(CUSTOMERS_SETTING_CODES.DELETION_RETENTION_DAYS, z.number(), 365),
179
+ resolvePresenceFreshnessMinutes: () => readChannelSetting(CUSTOMERS_SETTING_CODES.PRESENCE_FRESHNESS_MINUTES, z.number(), 10),
180
+ }))
181
+ .singleton(),
182
+ });
183
+ ctx.routes(async (app) => {
184
+ await cradle().customers.plugin(app);
185
+ });
186
+ }
187
+ /**
188
+ * The module's persisted entity classes, on the `./backend` subpath, as one
189
+ * array and **no named class export** (D-168).
190
+ *
191
+ * This is the shape the platform reads when the package is *installed*: the
192
+ * boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
193
+ * and the static declaration reader (`scripts/lib/package-declarations.ts`),
194
+ * which is the third source of `check:module-boundary`'s `table→owner` map and
195
+ * the package pass of `check-entity-tenant-classification`. A missing array is
196
+ * answered with `[]` — zero entities registered, no error anywhere.
197
+ */
198
+ export const entities = [
199
+ CustomerAddress,
200
+ ];
201
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,168 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { EntityManager } from '@mikro-orm/postgresql';
3
+ import type { AuditPort } from '@endora-commerce/platform/kernel';
4
+ import { CustomerRegistrationService } from './services/customer-registration-service.js';
5
+ import { CustomerAuthorityService, type SalesRepVisibility } from './services/customer-authority-service.js';
6
+ import { AnonymizationSweepWorker } from './workers/anonymization-sweep-worker.js';
7
+ import type { AddressReadPort, AuthSessionPort, AuthSessionReadPort, CartQueryPort, CustomFieldValuePort, CustomerAccountAdminSearchPort, CustomerAccountLifecycleWritePort, CustomerAccountReadPort, CustomerAuthPort, CustomerGroupReadPort, CustomerPasswordResetPort, DefaultPreferencePort, EmailMailerPort, ImpersonationPort, OrderListPort, OrderReadPort, OrganizationDetailsPort, PersonalOrganizationPort, RfqCustomerPort, VatValidator } from '@endora-commerce/contracts';
8
+ import { type RequireCustomerGuard, type ResolveCustomerActor } from './routes.self.js';
9
+ import { type RequireAdminGuard, type ResolveModerationActor } from './routes.admin.js';
10
+ /**
11
+ * The attach function this module hands its composition root.
12
+ *
13
+ * Typed on `fastify`'s own `FastifyInstance` rather than on the platform's
14
+ * `ModulePlugin`, which `contracts/host-package.md` §1.4g classifies **A**: the
15
+ * host does not publish it, so a packaged module cannot name it. The
16
+ * already-packaged `quote_requests` types its attach function the same way.
17
+ */
18
+ type ModuleAttach = (app: FastifyInstance) => Promise<void>;
19
+ /**
20
+ * Customers module composition root (feature 040).
21
+ *
22
+ * Orchestrates the customer-lifecycle surfaces over the existing data
23
+ * modules. Sibling services are injected from `composition.ts` so the module
24
+ * never imports another module's internals (Constitution Principle I).
25
+ */
26
+ export interface CustomersModuleOptions {
27
+ /**
28
+ * Feature 072 (T094) — `customer_accounts` owns these now. Injected rather
29
+ * than built here, because this host and `organizations` each built their own and the
30
+ * MFA argument differed between them.
31
+ */
32
+ customerAuthService: CustomerAuthPort;
33
+ passwordResetService: CustomerPasswordResetPort;
34
+ emFactory: () => EntityManager;
35
+ /**
36
+ * `auth`'s published session port, resolved under the name the contract
37
+ * publishes (D-98.2 / issue #196). It used to be the `SessionService`
38
+ * class, resolved as `sessionService`: the three calls this host makes —
39
+ * `createSession`, `destroyAllForCustomer`, `listRecentlyActiveCustomers`
40
+ * — are all on {@link AuthSessionPort}, so nothing needed the class.
41
+ *
42
+ * It was one of two options over the same port until this module's Phase-C
43
+ * cut: the duplicate existed only to feed the `ImpersonationService` this
44
+ * host built itself, and that instance is now `admin_users`' port.
45
+ */
46
+ sessionService: AuthSessionPort;
47
+ /**
48
+ * `admin_users`' published impersonation seam (feature 075). This host used
49
+ * to construct a **second** `ImpersonationService` from an import of that
50
+ * module's directory, over the port `admin_users`' own Phase-C cut published
51
+ * for it — so the machinery ran here whether its owner was present or not.
52
+ * The port is gated: a switched-off `admin_users` answers 503
53
+ * `MODULE_DISABLED` at the seam instead of minting a session.
54
+ */
55
+ impersonationPort: ImpersonationPort;
56
+ /**
57
+ * `auth`'s published read over the session table, for the online-customers
58
+ * panel. The panel *reports* on sessions rather than managing them, which is
59
+ * why it is a separate port from {@link AuthSessionPort} — and why this
60
+ * module no longer needs `Session` to answer "when was this account last
61
+ * seen".
62
+ */
63
+ authSessionReadPort: AuthSessionReadPort;
64
+ customerAccountReadPort: CustomerAccountReadPort;
65
+ /**
66
+ * Feature 075, Phase C — the rest of `customer_accounts`' published surface
67
+ * this module's own screens run on: the lifecycle writes it used to perform
68
+ * by mutating that module's entity, the admin list's query, and the group
69
+ * names beside it.
70
+ */
71
+ customerAccountLifecycleWritePort: CustomerAccountLifecycleWritePort;
72
+ customerAccountAdminSearchPort: CustomerAccountAdminSearchPort;
73
+ customerGroupReadPort: CustomerGroupReadPort;
74
+ /**
75
+ * `organizations`' published surface: the row-level read the admin list and
76
+ * the assignment screen ask for names and existence with, and the personal
77
+ * organisation a B2C registration provisions — which is also what the
78
+ * retention sweep cascades onto.
79
+ */
80
+ organizationDetailsPort: OrganizationDetailsPort;
81
+ personalOrganizationPort: PersonalOrganizationPort;
82
+ /** `addresses`' published read: the org-shared book a customer may pick from. */
83
+ addressReadPort: AddressReadPort;
84
+ /**
85
+ * Which organizations the acting staff member may see —
86
+ * `organizations`' `organizationSalesRepScopePort` (issue #108).
87
+ *
88
+ * Injected rather than built here. This module used to construct its own
89
+ * `SalesRepAssignmentService` from an entity-manager factory and an audit log,
90
+ * and that class's third argument — the feature-056 subtree deps — is
91
+ * optional, so omitting it compiled and quietly reverted every
92
+ * staff-authority decision to the flat pre-056 rule: a rep holding
93
+ * `organizations:rollup` could not act on a customer belonging to a
94
+ * descendant of an organization assigned to them.
95
+ */
96
+ salesRepVisibility: SalesRepVisibility;
97
+ requireCustomer: RequireCustomerGuard;
98
+ resolveCustomerActor: ResolveCustomerActor;
99
+ /** Reads `customers.allow_registration_without_organization`. */
100
+ resolveAllowRegistrationWithoutOrganization: () => Promise<boolean>;
101
+ /**
102
+ * `orders`' published list, which the self-service history panel and the
103
+ * admin customer-detail orders panel both read (feature 075). It replaces
104
+ * `Pick<OrderListService, 'list'>` over a late-bound accessor: the port keeps
105
+ * the accessor's timing, because `orders` builds the service inside its
106
+ * plugin body, and answers a call made before that with a 503 rather than a
107
+ * `null` two route files had to remember to check.
108
+ */
109
+ orderList: OrderListPort;
110
+ /**
111
+ * `orders`' row-level read. Only the customer-detail header's sales-channel
112
+ * list uses it, and that list was an `em.find(Order, …)` in this module until
113
+ * feature 080's T048 (D-169) — a plain read of another module's table, so a
114
+ * read-port method rather than an `EntityManager`-taking apply port.
115
+ */
116
+ orderReadPort: OrderReadPort;
117
+ rfqService: RfqCustomerPort;
118
+ auditLogService: AuditPort;
119
+ /**
120
+ * `quick_order`'s ordering defaults, which the customer-detail screen and
121
+ * self-service profile render and edit (issue #216).
122
+ *
123
+ * A **binding** dependency: the port's own contract says the seam fails
124
+ * closed when `quick_order` is off, and this module used to answer the same
125
+ * question out of a second instance it constructed itself — which read the
126
+ * table whether the owner was present or not.
127
+ */
128
+ defaultPreferencePort: DefaultPreferencePort;
129
+ requireAdmin: RequireAdminGuard;
130
+ resolveModerationActor: ResolveModerationActor;
131
+ /** VAT/NIP validator port (VIES / Biała lista in production). */
132
+ vatValidator: VatValidator;
133
+ mailer: EmailMailerPort;
134
+ /**
135
+ * `carts`' reporting read: the account's current cart and its abandoned ones,
136
+ * for the customer-detail panel. This host used to build a second
137
+ * `CartQueryService` from an import of that module's directory (feature 075).
138
+ */
139
+ cartQueryPort: CartQueryPort;
140
+ /** Base URL for the storefront set-password link in reset emails. */
141
+ storefrontBaseUrl: string;
142
+ /** Reads `customers.deletion_retention_days`. */
143
+ resolveDeletionRetentionDays: () => Promise<number>;
144
+ /** Reads `customers.presence_freshness_minutes`. */
145
+ resolvePresenceFreshnessMinutes: () => Promise<number>;
146
+ /**
147
+ * Feature 055 — validates Customer custom-field values on the admin edit
148
+ * path. The persistence is `customer_accounts`': its lifecycle port runs the
149
+ * Command and takes this validator as the merge (feature 075).
150
+ */
151
+ customFieldValues?: CustomFieldValuePort;
152
+ }
153
+ export interface CustomersModuleHandle {
154
+ registrationService: CustomerRegistrationService;
155
+ anonymizationSweepWorker: AnonymizationSweepWorker;
156
+ /**
157
+ * Exposed so the sales-rep scope this module *uses* can be asserted (issue
158
+ * #108). The defect it closes was invisible to `tsc` and to every route test,
159
+ * because a flat scope answers plausibly — just not with the roll-up.
160
+ */
161
+ authorityService: CustomerAuthorityService;
162
+ }
163
+ export declare function customersModule(options: CustomersModuleOptions): {
164
+ plugin: ModuleAttach;
165
+ handle: () => CustomersModuleHandle;
166
+ };
167
+ export {};
168
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import { CustomerRegistrationService } from './services/customer-registration-service.js';
2
+ import { CustomerAddressService } from './services/customer-address-service.js';
3
+ import { CustomerDefaultsService } from './services/customer-defaults-service.js';
4
+ import { CustomerAuthorityService, } from './services/customer-authority-service.js';
5
+ import { CustomerModerationService } from './services/customer-moderation-service.js';
6
+ import { CustomerAdminQueryService } from './services/customer-admin-query-service.js';
7
+ import { CustomerOrgAssignmentService } from './services/customer-org-assignment-service.js';
8
+ import { CustomerDeletionService } from './services/customer-deletion-service.js';
9
+ import { CustomerPresenceService } from './services/customer-presence-service.js';
10
+ import { AnonymizationSweepWorker } from './workers/anonymization-sweep-worker.js';
11
+ import { registerCustomersRegisterRoutes } from './routes.register.js';
12
+ import { registerCustomersSelfRoutes, } from './routes.self.js';
13
+ import { registerCustomersAdminRoutes, } from './routes.admin.js';
14
+ export function customersModule(options) {
15
+ const customerAuthService = options.customerAuthService;
16
+ const registrationService = new CustomerRegistrationService({
17
+ accounts: options.customerAccountLifecycleWritePort,
18
+ sessionService: options.sessionService,
19
+ resolveAllowRegistrationWithoutOrganization: options.resolveAllowRegistrationWithoutOrganization,
20
+ });
21
+ const customerAddressService = new CustomerAddressService(options.emFactory, options.addressReadPort, options.customerAccountReadPort, options.auditLogService);
22
+ const customerDefaultsService = new CustomerDefaultsService(options.emFactory, options.defaultPreferencePort, customerAddressService);
23
+ const authorityService = new CustomerAuthorityService(options.salesRepVisibility);
24
+ const moderationService = new CustomerModerationService(options.customerAccountReadPort, options.customerAccountLifecycleWritePort, authorityService, {
25
+ destroyAllForCustomer: (customerAccountId) => options.sessionService.destroyAllForCustomer(customerAccountId),
26
+ });
27
+ const queryService = new CustomerAdminQueryService(customerDefaultsService, {
28
+ accounts: options.customerAccountReadPort,
29
+ accountSearch: options.customerAccountAdminSearchPort,
30
+ organizations: options.organizationDetailsPort,
31
+ customerGroups: options.customerGroupReadPort,
32
+ orders: options.orderReadPort,
33
+ });
34
+ const orgAssignmentService = new CustomerOrgAssignmentService(options.customerAccountReadPort, options.customerAccountLifecycleWritePort, options.organizationDetailsPort, authorityService, options.personalOrganizationPort);
35
+ const deletionService = new CustomerDeletionService(options.customerAccountReadPort, options.customerAccountLifecycleWritePort, options.personalOrganizationPort, authorityService, {
36
+ destroyAllForCustomer: (customerAccountId) => options.sessionService.destroyAllForCustomer(customerAccountId),
37
+ });
38
+ const presenceService = new CustomerPresenceService({
39
+ listRecentlyActiveCustomers: (windowMinutes) => options.sessionService.listRecentlyActiveCustomers(windowMinutes),
40
+ }, options.authSessionReadPort, options.resolvePresenceFreshnessMinutes, options.customerAccountReadPort);
41
+ const passwordResetService = options.passwordResetService;
42
+ const anonymizationSweepWorker = new AnonymizationSweepWorker(deletionService, options.resolveDeletionRetentionDays);
43
+ const plugin = async (app) => {
44
+ await registerCustomersRegisterRoutes(app, { registrationService });
45
+ await registerCustomersSelfRoutes(app, {
46
+ accounts: options.customerAccountReadPort,
47
+ requireCustomer: options.requireCustomer,
48
+ resolveCustomerActor: options.resolveCustomerActor,
49
+ customerAuthService,
50
+ orderList: options.orderList,
51
+ rfqService: options.rfqService,
52
+ customerAddressService,
53
+ customerDefaultsService,
54
+ });
55
+ await registerCustomersAdminRoutes(app, {
56
+ accounts: options.customerAccountReadPort,
57
+ accountWrites: options.customerAccountLifecycleWritePort,
58
+ requireAdmin: options.requireAdmin,
59
+ resolveModerationActor: options.resolveModerationActor,
60
+ moderationService,
61
+ queryService,
62
+ orgAssignmentService,
63
+ addressService: customerAddressService,
64
+ cartQueryService: options.cartQueryPort,
65
+ orderList: options.orderList,
66
+ rfqService: options.rfqService,
67
+ vatValidator: options.vatValidator,
68
+ impersonationService: options.impersonationPort,
69
+ deletionService,
70
+ presenceService,
71
+ passwordResetService,
72
+ mailer: options.mailer,
73
+ auditLogService: options.auditLogService,
74
+ storefrontBaseUrl: options.storefrontBaseUrl,
75
+ ...(options.customFieldValues ? { customFieldValues: options.customFieldValues } : {}),
76
+ });
77
+ };
78
+ return {
79
+ plugin,
80
+ handle: () => ({ registrationService, anonymizationSweepWorker, authorityService }),
81
+ };
82
+ }
83
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
2
+ import { type CartQueryPort, type CustomerAccountLifecycleWritePort, type CustomerAccountReadPort, type CustomFieldValuePort, type EmailMailerPort, type ImpersonationPort, type OrderListPort, type RfqCustomerPort, type VatValidator } from '@endora-commerce/contracts';
3
+ import type { CustomerModerationService } from './services/customer-moderation-service.js';
4
+ import type { CustomerAdminQueryService } from './services/customer-admin-query-service.js';
5
+ import type { CustomerOrgAssignmentService } from './services/customer-org-assignment-service.js';
6
+ import type { CustomerDeletionService } from './services/customer-deletion-service.js';
7
+ import type { CustomerPresenceService } from './services/customer-presence-service.js';
8
+ import type { CustomerAddressService } from './services/customer-address-service.js';
9
+ import type { CustomerPasswordResetPort } from '@endora-commerce/contracts';
10
+ import type { AuditPort } from '@endora-commerce/platform/kernel';
11
+ /**
12
+ * Admin customer-management routes (feature 040). Block/unblock land here in
13
+ * US3; later user stories extend this file (impersonate, detail, group,
14
+ * delete/restore, presence). Every route is gated by `requireAdmin`.
15
+ */
16
+ export type RequireAdminGuard = (permission?: string) => (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
17
+ /**
18
+ * Resolves the acting admin's id, whether they are a Platform Administrator,
19
+ * and (for a scoped Salesperson) the Organizations they may act on.
20
+ */
21
+ export type ResolveModerationActor = (request: FastifyRequest) => Promise<{
22
+ adminUserId: string;
23
+ isPlatformAdmin: boolean;
24
+ allowedOrganizationIds: string[];
25
+ }>;
26
+ export interface CustomersAdminDeps {
27
+ /**
28
+ * Feature 075 — `customer_accounts`' published read and lifecycle write,
29
+ * where this file used to load and patch that module's entity through an
30
+ * `EntityManager` it no longer needs at all.
31
+ */
32
+ accounts: CustomerAccountReadPort;
33
+ accountWrites: CustomerAccountLifecycleWritePort;
34
+ requireAdmin: RequireAdminGuard;
35
+ resolveModerationActor: ResolveModerationActor;
36
+ moderationService: CustomerModerationService;
37
+ queryService: CustomerAdminQueryService;
38
+ orgAssignmentService: CustomerOrgAssignmentService;
39
+ addressService: CustomerAddressService;
40
+ cartQueryService: CartQueryPort;
41
+ /**
42
+ * `orders`' published list (feature 075), where this surface used to hold
43
+ * `Pick<OrderListService, 'list'>` — a type operator in front of a
44
+ * cross-module import of the class.
45
+ */
46
+ orderList: OrderListPort;
47
+ rfqService: RfqCustomerPort;
48
+ vatValidator: VatValidator;
49
+ /**
50
+ * `admin_users`' published impersonation seam. The "view as this customer"
51
+ * control lives on this screen and the machinery lives in that module by
52
+ * design, so the surface holds the port rather than a second instance of the
53
+ * service (feature 075).
54
+ */
55
+ impersonationService: ImpersonationPort;
56
+ deletionService: CustomerDeletionService;
57
+ presenceService: CustomerPresenceService;
58
+ passwordResetService: CustomerPasswordResetPort;
59
+ mailer: EmailMailerPort;
60
+ auditLogService: AuditPort;
61
+ storefrontBaseUrl: string;
62
+ /**
63
+ * Feature 055 — validates Customer custom-field values. It no longer
64
+ * persists them: `customer_accounts` owns the row, so its lifecycle port
65
+ * runs the Command and this validator is the merge that port is handed.
66
+ */
67
+ customFieldValues?: CustomFieldValuePort;
68
+ }
69
+ export declare function registerCustomersAdminRoutes(app: FastifyInstance, deps: CustomersAdminDeps): Promise<void>;
70
+ //# sourceMappingURL=routes.admin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.admin.d.ts","sourceRoot":"","sources":["../../src/backend/routes.admin.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAYL,KAAK,aAAa,EAClB,KAAK,iCAAiC,EACtC,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,YAAY,EAClB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AAC3F,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4CAA4C,CAAC;AAC5F,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,+CAA+C,CAAC;AAClG,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AACvF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AACvF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AACrF,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAC5E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAmBlE;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC9B,UAAU,CAAC,EAAE,MAAM,KAChB,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAErE;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG,CACnC,OAAO,EAAE,cAAc,KACpB,OAAO,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;IACzB,sBAAsB,EAAE,MAAM,EAAE,CAAC;CAClC,CAAC,CAAC;AAEH,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,QAAQ,EAAE,uBAAuB,CAAC;IAClC,aAAa,EAAE,iCAAiC,CAAC;IACjD,YAAY,EAAE,iBAAiB,CAAC;IAChC,sBAAsB,EAAE,sBAAsB,CAAC;IAC/C,iBAAiB,EAAE,yBAAyB,CAAC;IAC7C,YAAY,EAAE,yBAAyB,CAAC;IACxC,oBAAoB,EAAE,4BAA4B,CAAC;IACnD,cAAc,EAAE,sBAAsB,CAAC;IACvC,gBAAgB,EAAE,aAAa,CAAC;IAChC;;;;OAIG;IACH,SAAS,EAAE,aAAa,CAAC;IACzB,UAAU,EAAE,eAAe,CAAC;IAC5B,YAAY,EAAE,YAAY,CAAC;IAC3B;;;;;OAKG;IACH,oBAAoB,EAAE,iBAAiB,CAAC;IACxC,eAAe,EAAE,uBAAuB,CAAC;IACzC,eAAe,EAAE,uBAAuB,CAAC;IACzC,oBAAoB,EAAE,yBAAyB,CAAC;IAChD,MAAM,EAAE,eAAe,CAAC;IACxB,eAAe,EAAE,SAAS,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,oBAAoB,CAAC;CAC1C;AAED,wBAAsB,4BAA4B,CAChD,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,IAAI,CAAC,CAgbf"}