@endora-commerce/mod-returns 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.
- package/LICENSE +21 -0
- package/README.md +60 -0
- package/dist/admin/api/returns-client.d.ts +69 -0
- package/dist/admin/api/returns-client.d.ts.map +1 -0
- package/dist/admin/api/returns-client.js +36 -0
- package/dist/admin/api/returns-client.js.map +1 -0
- package/dist/admin/index.d.ts +37 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +57 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/ReturnDeliveryMethodsPage.d.ts +5 -0
- package/dist/admin/pages/ReturnDeliveryMethodsPage.d.ts.map +1 -0
- package/dist/admin/pages/ReturnDeliveryMethodsPage.js +44 -0
- package/dist/admin/pages/ReturnDeliveryMethodsPage.js.map +1 -0
- package/dist/admin/pages/ReturnDetail.d.ts +5 -0
- package/dist/admin/pages/ReturnDetail.d.ts.map +1 -0
- package/dist/admin/pages/ReturnDetail.js +115 -0
- package/dist/admin/pages/ReturnDetail.js.map +1 -0
- package/dist/admin/pages/ReturnReasonsPage.d.ts +5 -0
- package/dist/admin/pages/ReturnReasonsPage.d.ts.map +1 -0
- package/dist/admin/pages/ReturnReasonsPage.js +44 -0
- package/dist/admin/pages/ReturnReasonsPage.js.map +1 -0
- package/dist/admin/pages/ReturnStatusesConfigPage.d.ts +5 -0
- package/dist/admin/pages/ReturnStatusesConfigPage.d.ts.map +1 -0
- package/dist/admin/pages/ReturnStatusesConfigPage.js +71 -0
- package/dist/admin/pages/ReturnStatusesConfigPage.js.map +1 -0
- package/dist/admin/pages/ReturnsList.d.ts +5 -0
- package/dist/admin/pages/ReturnsList.d.ts.map +1 -0
- package/dist/admin/pages/ReturnsList.js +189 -0
- package/dist/admin/pages/ReturnsList.js.map +1 -0
- package/dist/backend/domain/default-reasons.d.ts +14 -0
- package/dist/backend/domain/default-reasons.d.ts.map +1 -0
- package/dist/backend/domain/default-reasons.js +9 -0
- package/dist/backend/domain/default-reasons.js.map +1 -0
- package/dist/backend/domain/free-return-window.d.ts +2 -0
- package/dist/backend/domain/free-return-window.d.ts.map +1 -0
- package/dist/backend/domain/free-return-window.js +19 -0
- package/dist/backend/domain/free-return-window.js.map +1 -0
- package/dist/backend/domain/refund-math.d.ts +19 -0
- package/dist/backend/domain/refund-math.d.ts.map +1 -0
- package/dist/backend/domain/refund-math.js +27 -0
- package/dist/backend/domain/refund-math.js.map +1 -0
- package/dist/backend/domain/return-status-graph.d.ts +91 -0
- package/dist/backend/domain/return-status-graph.d.ts.map +1 -0
- package/dist/backend/domain/return-status-graph.js +143 -0
- package/dist/backend/domain/return-status-graph.js.map +1 -0
- package/dist/backend/email-templates/return-authorized.d.ts +11 -0
- package/dist/backend/email-templates/return-authorized.d.ts.map +1 -0
- package/dist/backend/email-templates/return-authorized.js +18 -0
- package/dist/backend/email-templates/return-authorized.js.map +1 -0
- package/dist/backend/email-templates/return-rejected.d.ts +11 -0
- package/dist/backend/email-templates/return-rejected.d.ts.map +1 -0
- package/dist/backend/email-templates/return-rejected.js +20 -0
- package/dist/backend/email-templates/return-rejected.js.map +1 -0
- package/dist/backend/email-templates/transactional-defaults.d.ts +11 -0
- package/dist/backend/email-templates/transactional-defaults.d.ts.map +1 -0
- package/dist/backend/email-templates/transactional-defaults.js +64 -0
- package/dist/backend/email-templates/transactional-defaults.js.map +1 -0
- package/dist/backend/entities/refund.entity.d.ts +41 -0
- package/dist/backend/entities/refund.entity.d.ts.map +1 -0
- package/dist/backend/entities/refund.entity.js +122 -0
- package/dist/backend/entities/refund.entity.js.map +1 -0
- package/dist/backend/entities/return-case-attachment.entity.d.ts +16 -0
- package/dist/backend/entities/return-case-attachment.entity.d.ts.map +1 -0
- package/dist/backend/entities/return-case-attachment.entity.js +53 -0
- package/dist/backend/entities/return-case-attachment.entity.js.map +1 -0
- package/dist/backend/entities/return-case-comment.entity.d.ts +20 -0
- package/dist/backend/entities/return-case-comment.entity.d.ts.map +1 -0
- package/dist/backend/entities/return-case-comment.entity.js +70 -0
- package/dist/backend/entities/return-case-comment.entity.js.map +1 -0
- package/dist/backend/entities/return-case-item.entity.d.ts +25 -0
- package/dist/backend/entities/return-case-item.entity.d.ts.map +1 -0
- package/dist/backend/entities/return-case-item.entity.js +87 -0
- package/dist/backend/entities/return-case-item.entity.js.map +1 -0
- package/dist/backend/entities/return-case.entity.d.ts +41 -0
- package/dist/backend/entities/return-case.entity.d.ts.map +1 -0
- package/dist/backend/entities/return-case.entity.js +155 -0
- package/dist/backend/entities/return-case.entity.js.map +1 -0
- package/dist/backend/entities/return-delivery-method.entity.d.ts +18 -0
- package/dist/backend/entities/return-delivery-method.entity.d.ts.map +1 -0
- package/dist/backend/entities/return-delivery-method.entity.js +63 -0
- package/dist/backend/entities/return-delivery-method.entity.js.map +1 -0
- package/dist/backend/entities/return-list-saved-view.entity.d.ts +24 -0
- package/dist/backend/entities/return-list-saved-view.entity.d.ts.map +1 -0
- package/dist/backend/entities/return-list-saved-view.entity.js +74 -0
- package/dist/backend/entities/return-list-saved-view.entity.js.map +1 -0
- package/dist/backend/entities/return-reason.entity.d.ts +21 -0
- package/dist/backend/entities/return-reason.entity.d.ts.map +1 -0
- package/dist/backend/entities/return-reason.entity.js +65 -0
- package/dist/backend/entities/return-reason.entity.js.map +1 -0
- package/dist/backend/entities/return-shipment.entity.d.ts +22 -0
- package/dist/backend/entities/return-shipment.entity.d.ts.map +1 -0
- package/dist/backend/entities/return-shipment.entity.js +71 -0
- package/dist/backend/entities/return-shipment.entity.js.map +1 -0
- package/dist/backend/entities/return-status-transition.entity.d.ts +17 -0
- package/dist/backend/entities/return-status-transition.entity.d.ts.map +1 -0
- package/dist/backend/entities/return-status-transition.entity.js +55 -0
- package/dist/backend/entities/return-status-transition.entity.js.map +1 -0
- package/dist/backend/entities/return-status.entity.d.ts +29 -0
- package/dist/backend/entities/return-status.entity.d.ts.map +1 -0
- package/dist/backend/entities/return-status.entity.js +90 -0
- package/dist/backend/entities/return-status.entity.js.map +1 -0
- package/dist/backend/events/return-status-events.d.ts +44 -0
- package/dist/backend/events/return-status-events.d.ts.map +1 -0
- package/dist/backend/events/return-status-events.js +50 -0
- package/dist/backend/events/return-status-events.js.map +1 -0
- package/dist/backend/index.d.ts +106 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +116 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/plugin.d.ts +33 -0
- package/dist/backend/plugin.d.ts.map +1 -0
- package/dist/backend/plugin.js +124 -0
- package/dist/backend/plugin.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +38 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +181 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.customer.d.ts +22 -0
- package/dist/backend/routes.customer.d.ts.map +1 -0
- package/dist/backend/routes.customer.js +56 -0
- package/dist/backend/routes.customer.js.map +1 -0
- package/dist/backend/services/notification-context.d.ts +47 -0
- package/dist/backend/services/notification-context.d.ts.map +1 -0
- package/dist/backend/services/notification-context.js +49 -0
- package/dist/backend/services/notification-context.js.map +1 -0
- package/dist/backend/services/return-attachment-service.d.ts +20 -0
- package/dist/backend/services/return-attachment-service.d.ts.map +1 -0
- package/dist/backend/services/return-attachment-service.js +48 -0
- package/dist/backend/services/return-attachment-service.js.map +1 -0
- package/dist/backend/services/return-authorization-service.d.ts +53 -0
- package/dist/backend/services/return-authorization-service.d.ts.map +1 -0
- package/dist/backend/services/return-authorization-service.js +72 -0
- package/dist/backend/services/return-authorization-service.js.map +1 -0
- package/dist/backend/services/return-case-service.d.ts +44 -0
- package/dist/backend/services/return-case-service.d.ts.map +1 -0
- package/dist/backend/services/return-case-service.js +260 -0
- package/dist/backend/services/return-case-service.js.map +1 -0
- package/dist/backend/services/return-comment-service.d.ts +43 -0
- package/dist/backend/services/return-comment-service.d.ts.map +1 -0
- package/dist/backend/services/return-comment-service.js +109 -0
- package/dist/backend/services/return-comment-service.js.map +1 -0
- package/dist/backend/services/return-delivery-method-service.d.ts +44 -0
- package/dist/backend/services/return-delivery-method-service.d.ts.map +1 -0
- package/dist/backend/services/return-delivery-method-service.js +114 -0
- package/dist/backend/services/return-delivery-method-service.js.map +1 -0
- package/dist/backend/services/return-email-notifier.d.ts +89 -0
- package/dist/backend/services/return-email-notifier.d.ts.map +1 -0
- package/dist/backend/services/return-email-notifier.js +128 -0
- package/dist/backend/services/return-email-notifier.js.map +1 -0
- package/dist/backend/services/return-export-service.d.ts +16 -0
- package/dist/backend/services/return-export-service.d.ts.map +1 -0
- package/dist/backend/services/return-export-service.js +29 -0
- package/dist/backend/services/return-export-service.js.map +1 -0
- package/dist/backend/services/return-list-service.d.ts +27 -0
- package/dist/backend/services/return-list-service.d.ts.map +1 -0
- package/dist/backend/services/return-list-service.js +114 -0
- package/dist/backend/services/return-list-service.js.map +1 -0
- package/dist/backend/services/return-list-view-service.d.ts +36 -0
- package/dist/backend/services/return-list-view-service.d.ts.map +1 -0
- package/dist/backend/services/return-list-view-service.js +82 -0
- package/dist/backend/services/return-list-view-service.js.map +1 -0
- package/dist/backend/services/return-reason-service.d.ts +33 -0
- package/dist/backend/services/return-reason-service.d.ts.map +1 -0
- package/dist/backend/services/return-reason-service.js +90 -0
- package/dist/backend/services/return-reason-service.js.map +1 -0
- package/dist/backend/services/return-settlement-service.d.ts +67 -0
- package/dist/backend/services/return-settlement-service.d.ts.map +1 -0
- package/dist/backend/services/return-settlement-service.js +295 -0
- package/dist/backend/services/return-settlement-service.js.map +1 -0
- package/dist/backend/services/return-shipment-service.d.ts +40 -0
- package/dist/backend/services/return-shipment-service.d.ts.map +1 -0
- package/dist/backend/services/return-shipment-service.js +113 -0
- package/dist/backend/services/return-shipment-service.js.map +1 -0
- package/dist/backend/services/return-status-graph-service.d.ts +59 -0
- package/dist/backend/services/return-status-graph-service.d.ts.map +1 -0
- package/dist/backend/services/return-status-graph-service.js +197 -0
- package/dist/backend/services/return-status-graph-service.js.map +1 -0
- package/dist/backend/services/return-transition-service.d.ts +48 -0
- package/dist/backend/services/return-transition-service.d.ts.map +1 -0
- package/dist/backend/services/return-transition-service.js +116 -0
- package/dist/backend/services/return-transition-service.js.map +1 -0
- package/dist/backend/services/returns-seeder.d.ts +18 -0
- package/dist/backend/services/returns-seeder.d.ts.map +1 -0
- package/dist/backend/services/returns-seeder.js +63 -0
- package/dist/backend/services/returns-seeder.js.map +1 -0
- package/dist/backend/services/rma-number-generator.d.ts +35 -0
- package/dist/backend/services/rma-number-generator.d.ts.map +1 -0
- package/dist/backend/services/rma-number-generator.js +45 -0
- package/dist/backend/services/rma-number-generator.js.map +1 -0
- package/dist/manifest.d.ts +182 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +216 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260625T144227_returns_init.d.ts +15 -0
- package/dist/migrations/20260625T144227_returns_init.d.ts.map +1 -0
- package/dist/migrations/20260625T144227_returns_init.js +241 -0
- package/dist/migrations/20260625T144227_returns_init.js.map +1 -0
- package/dist/migrations/20260817T203206_returns_refund_corrective_invoice_outcome.d.ts +25 -0
- package/dist/migrations/20260817T203206_returns_refund_corrective_invoice_outcome.d.ts.map +1 -0
- package/dist/migrations/20260817T203206_returns_refund_corrective_invoice_outcome.js +46 -0
- package/dist/migrations/20260817T203206_returns_refund_corrective_invoice_outcome.js.map +1 -0
- package/dist/migrations/index.d.ts +28 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +31 -0
- package/dist/migrations/index.js.map +1 -0
- package/docs/returns.md +103 -0
- package/i18n/en.json +28 -0
- package/i18n/pl.json +28 -0
- package/package.json +101 -0
- package/tailwind.css +14 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { addReturnAttachmentRequestSchema, createReturnCaseRequestSchema, customerAddCommentRequestSchema, selectReturnDeliveryMethodRequestSchema, } from '@endora-commerce/contracts';
|
|
2
|
+
export async function registerReturnsCustomerRoutes(app, deps) {
|
|
3
|
+
const { caseService, commentService, deliveryMethodService, reasonService, attachmentService, requireCustomer, resolveCustomerAccountId, } = deps;
|
|
4
|
+
app.get('/api/v1/returns/reasons', { preHandler: requireCustomer }, async (request) => {
|
|
5
|
+
const data = await reasonService.listActive(request.query.kind);
|
|
6
|
+
return { data };
|
|
7
|
+
});
|
|
8
|
+
app.get('/api/v1/orders/:orderId/returnable', { preHandler: requireCustomer }, async (request) => {
|
|
9
|
+
const customerAccountId = resolveCustomerAccountId(request);
|
|
10
|
+
const data = await caseService.getReturnable(request.params.orderId, customerAccountId);
|
|
11
|
+
return { data };
|
|
12
|
+
});
|
|
13
|
+
app.get('/api/v1/returns', { preHandler: requireCustomer }, async (request) => {
|
|
14
|
+
const customerAccountId = resolveCustomerAccountId(request);
|
|
15
|
+
const data = await caseService.listForCustomer(customerAccountId);
|
|
16
|
+
return { data };
|
|
17
|
+
});
|
|
18
|
+
app.get('/api/v1/returns/:id', { preHandler: requireCustomer }, async (request) => {
|
|
19
|
+
const customerAccountId = resolveCustomerAccountId(request);
|
|
20
|
+
const data = await caseService.getForCustomer(request.params.id, customerAccountId);
|
|
21
|
+
return { data };
|
|
22
|
+
});
|
|
23
|
+
app.post('/api/v1/returns', { preHandler: requireCustomer, schema: { body: createReturnCaseRequestSchema } }, async (request, reply) => {
|
|
24
|
+
const customerAccountId = resolveCustomerAccountId(request);
|
|
25
|
+
const body = createReturnCaseRequestSchema.parse(request.body);
|
|
26
|
+
const data = await caseService.createCase(body, customerAccountId);
|
|
27
|
+
reply.status(201);
|
|
28
|
+
return { data };
|
|
29
|
+
});
|
|
30
|
+
app.post('/api/v1/returns/:id/comments', { preHandler: requireCustomer, schema: { body: customerAddCommentRequestSchema } }, async (request, reply) => {
|
|
31
|
+
const customerAccountId = resolveCustomerAccountId(request);
|
|
32
|
+
const body = customerAddCommentRequestSchema.parse(request.body);
|
|
33
|
+
const data = await commentService.addByCustomer(request.params.id, customerAccountId, body);
|
|
34
|
+
reply.status(201);
|
|
35
|
+
return { data };
|
|
36
|
+
});
|
|
37
|
+
app.post('/api/v1/returns/:id/select-delivery-method', { preHandler: requireCustomer, schema: { body: selectReturnDeliveryMethodRequestSchema } }, async (request) => {
|
|
38
|
+
const customerAccountId = resolveCustomerAccountId(request);
|
|
39
|
+
const body = selectReturnDeliveryMethodRequestSchema.parse(request.body);
|
|
40
|
+
const data = await deliveryMethodService.selectForCase(request.params.id, customerAccountId, body.returnDeliveryMethodId);
|
|
41
|
+
return { data };
|
|
42
|
+
});
|
|
43
|
+
app.post('/api/v1/returns/:id/cancel', { preHandler: requireCustomer }, async (request) => {
|
|
44
|
+
const customerAccountId = resolveCustomerAccountId(request);
|
|
45
|
+
const data = await caseService.cancelByCustomer(request.params.id, customerAccountId);
|
|
46
|
+
return { data };
|
|
47
|
+
});
|
|
48
|
+
app.post('/api/v1/returns/:id/attachments', { preHandler: requireCustomer, schema: { body: addReturnAttachmentRequestSchema } }, async (request, reply) => {
|
|
49
|
+
const customerAccountId = resolveCustomerAccountId(request);
|
|
50
|
+
const body = addReturnAttachmentRequestSchema.parse(request.body);
|
|
51
|
+
const data = await attachmentService.addToCase(request.params.id, customerAccountId, body);
|
|
52
|
+
reply.status(201);
|
|
53
|
+
return { data };
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=routes.customer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.customer.js","sourceRoot":"","sources":["../../src/backend/routes.customer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { CustomerAccountReadPort } from '@endora-commerce/contracts';
|
|
3
|
+
import type { CustomerEmailResolver } from './return-email-notifier.js';
|
|
4
|
+
/**
|
|
5
|
+
* The two facts a return notification needs from outside this module
|
|
6
|
+
* (`specs/110-instance-repository/` T118c).
|
|
7
|
+
*
|
|
8
|
+
* Both were `returnsBridge` members — closures a composition root wrote, once in
|
|
9
|
+
* `backend/src/composition.ts` and once in `backend/test/helpers/test-server.ts`,
|
|
10
|
+
* with the same body in each. They are functions of what they read rather than of
|
|
11
|
+
* a `ModuleContext`, so `backend/index.ts` supplies the real inputs and this file
|
|
12
|
+
* is testable over a stub with nothing composed.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Where the authorize / reject e-mail goes.
|
|
16
|
+
*
|
|
17
|
+
* `customer_accounts` publishes the record; this maps it to the one field the
|
|
18
|
+
* notifier needs and answers `null` when no account resolves, which the notifier
|
|
19
|
+
* reports as `no_recipient` rather than treating as sent.
|
|
20
|
+
*
|
|
21
|
+
* **No `catch`, deliberately.** `findById` on a `lazyPort` proxy throws
|
|
22
|
+
* `ModuleDisabledError` when its owner is absent, and reading that as "this buyer
|
|
23
|
+
* has no e-mail address" is the fail-open composition checklist item 7 refuses.
|
|
24
|
+
* `customer_accounts` declares `nonDeactivatable`, so it cannot happen today; the
|
|
25
|
+
* absence of the `catch` is what keeps it fail-closed if that ever changes.
|
|
26
|
+
*/
|
|
27
|
+
export declare function createCustomerEmailResolver(accounts: Pick<CustomerAccountReadPort, 'findById'>): CustomerEmailResolver;
|
|
28
|
+
/**
|
|
29
|
+
* The language a channel's e-mail is rendered in.
|
|
30
|
+
*
|
|
31
|
+
* A read of the platform's own `SalesChannel` entity through this module's
|
|
32
|
+
* `EntityManager`, which is what both composition roots did and what `orders`
|
|
33
|
+
* does at the identical site — resolving the language for a transactional e-mail
|
|
34
|
+
* (`order-service.ts`, `sendOrderConfirmation`).
|
|
35
|
+
*
|
|
36
|
+
* **Not `salesChannelResolutionPort`**, though the platform contributes it and it
|
|
37
|
+
* carries `defaultLanguage` on its `CachedChannel`. That port is the *cache*, and
|
|
38
|
+
* swapping a direct read for a cached one changes what this answers for a channel
|
|
39
|
+
* written outside the invalidation path — a design decision about channel reads,
|
|
40
|
+
* not a drain's, and T118c moves a value rather than redesigning it. Constitution
|
|
41
|
+
* XII is untouched either way: this resolves no request channel, it reads one
|
|
42
|
+
* attribute of a channel the return case already names.
|
|
43
|
+
*
|
|
44
|
+
* `en-US` for a channel that is not there is the fallback both roots carried.
|
|
45
|
+
*/
|
|
46
|
+
export declare function createChannelLanguageResolver(emFactory: () => EntityManager): (salesChannelId: string) => Promise<string>;
|
|
47
|
+
//# sourceMappingURL=notification-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-context.d.ts","sourceRoot":"","sources":["../../../src/backend/services/notification-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAE1E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAExE;;;;;;;;;GASG;AAEH;;;;;;;;;;;;GAYG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,IAAI,CAAC,uBAAuB,EAAE,UAAU,CAAC,GAClD,qBAAqB,CAEvB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,MAAM,aAAa,GAC7B,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAG7C"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { SalesChannel } from '@endora-commerce/platform/kernel';
|
|
2
|
+
/**
|
|
3
|
+
* The two facts a return notification needs from outside this module
|
|
4
|
+
* (`specs/110-instance-repository/` T118c).
|
|
5
|
+
*
|
|
6
|
+
* Both were `returnsBridge` members — closures a composition root wrote, once in
|
|
7
|
+
* `backend/src/composition.ts` and once in `backend/test/helpers/test-server.ts`,
|
|
8
|
+
* with the same body in each. They are functions of what they read rather than of
|
|
9
|
+
* a `ModuleContext`, so `backend/index.ts` supplies the real inputs and this file
|
|
10
|
+
* is testable over a stub with nothing composed.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Where the authorize / reject e-mail goes.
|
|
14
|
+
*
|
|
15
|
+
* `customer_accounts` publishes the record; this maps it to the one field the
|
|
16
|
+
* notifier needs and answers `null` when no account resolves, which the notifier
|
|
17
|
+
* reports as `no_recipient` rather than treating as sent.
|
|
18
|
+
*
|
|
19
|
+
* **No `catch`, deliberately.** `findById` on a `lazyPort` proxy throws
|
|
20
|
+
* `ModuleDisabledError` when its owner is absent, and reading that as "this buyer
|
|
21
|
+
* has no e-mail address" is the fail-open composition checklist item 7 refuses.
|
|
22
|
+
* `customer_accounts` declares `nonDeactivatable`, so it cannot happen today; the
|
|
23
|
+
* absence of the `catch` is what keeps it fail-closed if that ever changes.
|
|
24
|
+
*/
|
|
25
|
+
export function createCustomerEmailResolver(accounts) {
|
|
26
|
+
return async (customerAccountId) => (await accounts.findById(customerAccountId))?.email ?? null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The language a channel's e-mail is rendered in.
|
|
30
|
+
*
|
|
31
|
+
* A read of the platform's own `SalesChannel` entity through this module's
|
|
32
|
+
* `EntityManager`, which is what both composition roots did and what `orders`
|
|
33
|
+
* does at the identical site — resolving the language for a transactional e-mail
|
|
34
|
+
* (`order-service.ts`, `sendOrderConfirmation`).
|
|
35
|
+
*
|
|
36
|
+
* **Not `salesChannelResolutionPort`**, though the platform contributes it and it
|
|
37
|
+
* carries `defaultLanguage` on its `CachedChannel`. That port is the *cache*, and
|
|
38
|
+
* swapping a direct read for a cached one changes what this answers for a channel
|
|
39
|
+
* written outside the invalidation path — a design decision about channel reads,
|
|
40
|
+
* not a drain's, and T118c moves a value rather than redesigning it. Constitution
|
|
41
|
+
* XII is untouched either way: this resolves no request channel, it reads one
|
|
42
|
+
* attribute of a channel the return case already names.
|
|
43
|
+
*
|
|
44
|
+
* `en-US` for a channel that is not there is the fallback both roots carried.
|
|
45
|
+
*/
|
|
46
|
+
export function createChannelLanguageResolver(emFactory) {
|
|
47
|
+
return async (salesChannelId) => (await emFactory().findOne(SalesChannel, { id: salesChannelId }))?.defaultLanguage ?? 'en-US';
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=notification-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-context.js","sourceRoot":"","sources":["../../../src/backend/services/notification-context.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAGhE;;;;;;;;;GASG;AAEH;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,2BAA2B,CACzC,QAAmD;IAEnD,OAAO,KAAK,EAAE,iBAAiB,EAAE,EAAE,CAAC,CAAC,MAAM,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC;AAClG,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,6BAA6B,CAC3C,SAA8B;IAE9B,OAAO,KAAK,EAAE,cAAc,EAAE,EAAE,CAC9B,CAAC,MAAM,SAAS,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC,EAAE,eAAe,IAAI,OAAO,CAAC;AAClG,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { ReturnAttachmentDto } from '@endora-commerce/contracts';
|
|
3
|
+
/**
|
|
4
|
+
* ReturnAttachmentService — feature 046 (US1, T027).
|
|
5
|
+
*
|
|
6
|
+
* Links uploaded assets (e.g. defect photos) to a case or a specific case line.
|
|
7
|
+
* The binary upload itself goes through the assets library, which returns an
|
|
8
|
+
* `assetId`; this service persists the `return_case_attachments` link and
|
|
9
|
+
* enforces case ownership.
|
|
10
|
+
*/
|
|
11
|
+
export declare class ReturnAttachmentService {
|
|
12
|
+
private readonly emFactory;
|
|
13
|
+
constructor(emFactory: () => EntityManager);
|
|
14
|
+
addToCase(caseId: string, customerAccountId: string, input: {
|
|
15
|
+
assetId: string;
|
|
16
|
+
returnCaseItemId?: string | undefined;
|
|
17
|
+
}): Promise<ReturnAttachmentDto>;
|
|
18
|
+
listForCase(caseId: string): Promise<ReturnAttachmentDto[]>;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=return-attachment-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"return-attachment-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/return-attachment-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAKtE;;;;;;;GAOG;AACH,qBAAa,uBAAuB;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,aAAa;IAErD,SAAS,CACb,MAAM,EAAE,MAAM,EACd,iBAAiB,EAAE,MAAM,EACzB,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,GAChE,OAAO,CAAC,mBAAmB,CAAC;IAezB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;CASlE"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ERROR_CODES } from '@endora-commerce/contracts';
|
|
2
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
3
|
+
import { ReturnCase } from '../entities/return-case.entity.js';
|
|
4
|
+
import { ReturnCaseAttachment } from '../entities/return-case-attachment.entity.js';
|
|
5
|
+
/**
|
|
6
|
+
* ReturnAttachmentService — feature 046 (US1, T027).
|
|
7
|
+
*
|
|
8
|
+
* Links uploaded assets (e.g. defect photos) to a case or a specific case line.
|
|
9
|
+
* The binary upload itself goes through the assets library, which returns an
|
|
10
|
+
* `assetId`; this service persists the `return_case_attachments` link and
|
|
11
|
+
* enforces case ownership.
|
|
12
|
+
*/
|
|
13
|
+
export class ReturnAttachmentService {
|
|
14
|
+
emFactory;
|
|
15
|
+
constructor(emFactory) {
|
|
16
|
+
this.emFactory = emFactory;
|
|
17
|
+
}
|
|
18
|
+
async addToCase(caseId, customerAccountId, input) {
|
|
19
|
+
// command-coverage-ignore: append-only evidence attachment on a case, not an
|
|
20
|
+
// audited domain-state mutation (no before-state, no undo value).
|
|
21
|
+
const em = this.emFactory();
|
|
22
|
+
const rc = await em.findOne(ReturnCase, { id: caseId, customerAccountId });
|
|
23
|
+
if (!rc)
|
|
24
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Return case not found.');
|
|
25
|
+
const attachment = em.create(ReturnCaseAttachment, {
|
|
26
|
+
returnCaseId: caseId,
|
|
27
|
+
returnCaseItemId: input.returnCaseItemId ?? null,
|
|
28
|
+
assetId: input.assetId,
|
|
29
|
+
});
|
|
30
|
+
await em.persistAndFlush(attachment);
|
|
31
|
+
return toDto(attachment);
|
|
32
|
+
}
|
|
33
|
+
async listForCase(caseId) {
|
|
34
|
+
const em = this.emFactory();
|
|
35
|
+
const rows = await em.find(ReturnCaseAttachment, { returnCaseId: caseId }, { orderBy: { createdAt: 'asc' } });
|
|
36
|
+
return rows.map(toDto);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function toDto(a) {
|
|
40
|
+
return {
|
|
41
|
+
id: a.id,
|
|
42
|
+
returnCaseId: a.returnCaseId,
|
|
43
|
+
returnCaseItemId: a.returnCaseItemId ?? null,
|
|
44
|
+
assetId: a.assetId,
|
|
45
|
+
createdAt: a.createdAt.toISOString(),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=return-attachment-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"return-attachment-service.js","sourceRoot":"","sources":["../../../src/backend/services/return-attachment-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,8CAA8C,CAAC;AAEpF;;;;;;;GAOG;AACH,MAAM,OAAO,uBAAuB;IACL;IAA7B,YAA6B,SAA8B;QAA9B,cAAS,GAAT,SAAS,CAAqB;IAAG,CAAC;IAE/D,KAAK,CAAC,SAAS,CACb,MAAc,EACd,iBAAyB,EACzB,KAAiE;QAEjE,6EAA6E;QAC7E,kEAAkE;QAClE,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;QAC3E,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC,SAAS,EAAE,wBAAwB,CAAC,CAAC;QACnF,MAAM,UAAU,GAAG,EAAE,CAAC,MAAM,CAAC,oBAAoB,EAAE;YACjD,YAAY,EAAE,MAAM;YACpB,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,IAAI,IAAI;YAChD,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC,CAAC;QACH,MAAM,EAAE,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;QACrC,OAAO,KAAK,CAAC,UAAU,CAAC,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,MAAc;QAC9B,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CACxB,oBAAoB,EACpB,EAAE,YAAY,EAAE,MAAM,EAAE,EACxB,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,CAClC,CAAC;QACF,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;CACF;AAED,SAAS,KAAK,CAAC,CAAuB;IACpC,OAAO;QACL,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,YAAY,EAAE,CAAC,CAAC,YAAY;QAC5B,gBAAgB,EAAE,CAAC,CAAC,gBAAgB,IAAI,IAAI;QAC5C,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE;KACrC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { ReturnCase } from '../entities/return-case.entity.js';
|
|
3
|
+
import type { ReturnTransitionService } from './return-transition-service.js';
|
|
4
|
+
import type { RmaNumberGenerator } from './rma-number-generator.js';
|
|
5
|
+
import type { ReturnEmailResult } from './return-email-notifier.js';
|
|
6
|
+
/**
|
|
7
|
+
* Best-effort customer notifications on authorize/reject (FR-017).
|
|
8
|
+
*
|
|
9
|
+
* Each answers whether the message went out and, when it did not, why (issue
|
|
10
|
+
* #78). `Promise<void>` said nothing, and this service is the caller that could
|
|
11
|
+
* not tell a delivered notification from a deactivated template.
|
|
12
|
+
*/
|
|
13
|
+
export interface ReturnNotifier {
|
|
14
|
+
authorized(rc: ReturnCase): Promise<ReturnEmailResult>;
|
|
15
|
+
rejected(rc: ReturnCase): Promise<ReturnEmailResult>;
|
|
16
|
+
}
|
|
17
|
+
export interface ReturnAuthorizationServiceDeps {
|
|
18
|
+
emFactory: () => EntityManager;
|
|
19
|
+
transitions: ReturnTransitionService;
|
|
20
|
+
rmaGenerator: RmaNumberGenerator;
|
|
21
|
+
notifier?: ReturnNotifier;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* ReturnAuthorizationService — feature 046 (US2).
|
|
25
|
+
*
|
|
26
|
+
* Verification outcome handlers: `authorize` assigns a unique RMA number and
|
|
27
|
+
* advances the case to the authorized status; `reject` records a mandatory
|
|
28
|
+
* reason and moves the case to the rejected terminal. Both notify the customer
|
|
29
|
+
* best-effort.
|
|
30
|
+
*/
|
|
31
|
+
export declare class ReturnAuthorizationService {
|
|
32
|
+
private readonly deps;
|
|
33
|
+
constructor(deps: ReturnAuthorizationServiceDeps);
|
|
34
|
+
authorize(id: string, adminUserId: string): Promise<{
|
|
35
|
+
rmaNumber: string;
|
|
36
|
+
statusCode: string;
|
|
37
|
+
}>;
|
|
38
|
+
reject(id: string, adminUserId: string, reason: string): Promise<ReturnCase>;
|
|
39
|
+
/**
|
|
40
|
+
* The outer net for a notification that must not undo the transition it
|
|
41
|
+
* announces.
|
|
42
|
+
*
|
|
43
|
+
* The notifier itself now names every non-sent path in its result and writes
|
|
44
|
+
* it to the log (issue #78), so what reaches here is the residue: a throw the
|
|
45
|
+
* notifier deliberately let travel. That includes a `ModuleDisabledError`,
|
|
46
|
+
* which this `catch` still absorbs — re-throwing it would answer 503 to an
|
|
47
|
+
* authorize that has already committed, and the honest repair is an outbox
|
|
48
|
+
* the transition hands the message to, not a rethrow here. What it no longer
|
|
49
|
+
* does is absorb it in silence.
|
|
50
|
+
*/
|
|
51
|
+
private notifySafely;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=return-authorization-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"return-authorization-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/return-authorization-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAMpE;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACvD,QAAQ,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,8BAA8B;IAC7C,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B,WAAW,EAAE,uBAAuB,CAAC;IACrC,YAAY,EAAE,kBAAkB,CAAC;IACjC,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,qBAAa,0BAA0B;IACzB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,8BAA8B;IAE3D,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAkB9F,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAsBlF;;;;;;;;;;;OAWG;YACW,YAAY;CAiB3B"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { ERROR_CODES } from '@endora-commerce/contracts';
|
|
2
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
3
|
+
import { RETURN_STATUS_AUTHORIZED, RETURN_STATUS_REJECTED, } from '../domain/return-status-graph.js';
|
|
4
|
+
/**
|
|
5
|
+
* ReturnAuthorizationService — feature 046 (US2).
|
|
6
|
+
*
|
|
7
|
+
* Verification outcome handlers: `authorize` assigns a unique RMA number and
|
|
8
|
+
* advances the case to the authorized status; `reject` records a mandatory
|
|
9
|
+
* reason and moves the case to the rejected terminal. Both notify the customer
|
|
10
|
+
* best-effort.
|
|
11
|
+
*/
|
|
12
|
+
export class ReturnAuthorizationService {
|
|
13
|
+
deps;
|
|
14
|
+
constructor(deps) {
|
|
15
|
+
this.deps = deps;
|
|
16
|
+
}
|
|
17
|
+
async authorize(id, adminUserId) {
|
|
18
|
+
const rc = await this.deps.transitions.apply(id, RETURN_STATUS_AUTHORIZED, { kind: 'admin', adminUserId }, {
|
|
19
|
+
mutate: async (target, em) => {
|
|
20
|
+
if (!target.rmaNumber) {
|
|
21
|
+
target.rmaNumber = await this.deps.rmaGenerator.generate(em, target.salesChannelId);
|
|
22
|
+
}
|
|
23
|
+
target.authorizedAt = new Date();
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
await this.notifySafely(rc, 'return_authorized', () => this.deps.notifier?.authorized(rc));
|
|
27
|
+
return { rmaNumber: rc.rmaNumber, statusCode: rc.statusCode };
|
|
28
|
+
}
|
|
29
|
+
async reject(id, adminUserId, reason) {
|
|
30
|
+
const trimmed = reason.trim();
|
|
31
|
+
if (!trimmed) {
|
|
32
|
+
throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, 'A rejection reason is required.', {
|
|
33
|
+
code: 'reason_required',
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
const rc = await this.deps.transitions.apply(id, RETURN_STATUS_REJECTED, { kind: 'admin', adminUserId }, {
|
|
37
|
+
reason: trimmed,
|
|
38
|
+
mutate: (target) => {
|
|
39
|
+
target.rejectionReason = trimmed;
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
await this.notifySafely(rc, 'return_rejected', () => this.deps.notifier?.rejected(rc));
|
|
43
|
+
return rc;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The outer net for a notification that must not undo the transition it
|
|
47
|
+
* announces.
|
|
48
|
+
*
|
|
49
|
+
* The notifier itself now names every non-sent path in its result and writes
|
|
50
|
+
* it to the log (issue #78), so what reaches here is the residue: a throw the
|
|
51
|
+
* notifier deliberately let travel. That includes a `ModuleDisabledError`,
|
|
52
|
+
* which this `catch` still absorbs — re-throwing it would answer 503 to an
|
|
53
|
+
* authorize that has already committed, and the honest repair is an outbox
|
|
54
|
+
* the transition hands the message to, not a rethrow here. What it no longer
|
|
55
|
+
* does is absorb it in silence.
|
|
56
|
+
*/
|
|
57
|
+
async notifySafely(rc, kind, fn) {
|
|
58
|
+
try {
|
|
59
|
+
await fn();
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
// Notifications are best-effort; never block the workflow transition.
|
|
63
|
+
console.warn('[returns] the return e-mail was not sent', {
|
|
64
|
+
returnCaseId: rc.id,
|
|
65
|
+
kind,
|
|
66
|
+
reason: 'failed',
|
|
67
|
+
error: error instanceof Error ? error.message : error,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=return-authorization-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"return-authorization-service.js","sourceRoot":"","sources":["../../../src/backend/services/return-authorization-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAK3D,OAAO,EACL,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,kCAAkC,CAAC;AAqB1C;;;;;;;GAOG;AACH,MAAM,OAAO,0BAA0B;IACR;IAA7B,YAA6B,IAAoC;QAApC,SAAI,GAAJ,IAAI,CAAgC;IAAG,CAAC;IAErE,KAAK,CAAC,SAAS,CAAC,EAAU,EAAE,WAAmB;QAC7C,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAC1C,EAAE,EACF,wBAAwB,EACxB,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,EAC9B;YACE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE;gBAC3B,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;oBACtB,MAAM,CAAC,SAAS,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;gBACtF,CAAC;gBACD,MAAM,CAAC,YAAY,GAAG,IAAI,IAAI,EAAE,CAAC;YACnC,CAAC;SACF,CACF,CAAC;QACF,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,mBAAmB,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3F,OAAO,EAAE,SAAS,EAAE,EAAE,CAAC,SAAU,EAAE,UAAU,EAAE,EAAE,CAAC,UAAU,EAAE,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,WAAmB,EAAE,MAAc;QAC1D,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;QAC9B,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC,iBAAiB,EAAE,iCAAiC,EAAE;gBACzF,IAAI,EAAE,iBAAiB;aACxB,CAAC,CAAC;QACL,CAAC;QACD,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAC1C,EAAE,EACF,sBAAsB,EACtB,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,EAC9B;YACE,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE;gBACjB,MAAM,CAAC,eAAe,GAAG,OAAO,CAAC;YACnC,CAAC;SACF,CACF,CAAC;QACF,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,iBAAiB,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;QACvF,OAAO,EAAE,CAAC;IACZ,CAAC;IAED;;;;;;;;;;;OAWG;IACK,KAAK,CAAC,YAAY,CACxB,EAAc,EACd,IAAY,EACZ,EAAgD;QAEhD,IAAI,CAAC;YACH,MAAM,EAAE,EAAE,CAAC;QACb,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,sEAAsE;YACtE,OAAO,CAAC,IAAI,CAAC,0CAA0C,EAAE;gBACvD,YAAY,EAAE,EAAE,CAAC,EAAE;gBACnB,IAAI;gBACJ,MAAM,EAAE,QAAQ;gBAChB,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;aACtD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { CreateReturnCaseRequest, OrderReturnContextPort, ReturnableResponse, ReturnCaseDetail, ReturnCaseSummary } from '@endora-commerce/contracts';
|
|
3
|
+
import type { AuditPort } from '@endora-commerce/platform/kernel';
|
|
4
|
+
import type { ReturnStatusGraphService } from './return-status-graph-service.js';
|
|
5
|
+
import type { ReturnTransitionService } from './return-transition-service.js';
|
|
6
|
+
export interface ReturnCaseServiceDeps {
|
|
7
|
+
emFactory: () => EntityManager;
|
|
8
|
+
graphService: ReturnStatusGraphService;
|
|
9
|
+
transitions: ReturnTransitionService;
|
|
10
|
+
orderContext: OrderReturnContextPort;
|
|
11
|
+
/** Free-return window in days for a sales channel (0 = no free-return option). */
|
|
12
|
+
resolveFreeReturnDays: (salesChannelId: string) => Promise<number>;
|
|
13
|
+
/** Injected for deterministic tests; defaults to `new Date()`. */
|
|
14
|
+
now?: () => Date;
|
|
15
|
+
/** Feature 054 — audits case creation co-transactionally when provided. */
|
|
16
|
+
auditLog?: AuditPort;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* ReturnCaseService — feature 046 (US1).
|
|
20
|
+
*
|
|
21
|
+
* Eligibility evaluation, remaining-returnable computation, and case creation in
|
|
22
|
+
* the initial workflow status. Reads order facts only through the
|
|
23
|
+
* `OrderReturnContextPort` (Principle I).
|
|
24
|
+
*/
|
|
25
|
+
export declare class ReturnCaseService {
|
|
26
|
+
private readonly deps;
|
|
27
|
+
constructor(deps: ReturnCaseServiceDeps);
|
|
28
|
+
private now;
|
|
29
|
+
/** Lines a customer may still return for an order, with the eligibility verdict. */
|
|
30
|
+
getReturnable(orderId: string, customerAccountId: string): Promise<ReturnableResponse>;
|
|
31
|
+
/** Open a return/complaint case (FR-001/002/003/005). */
|
|
32
|
+
createCase(input: CreateReturnCaseRequest, customerAccountId: string): Promise<ReturnCaseDetail>;
|
|
33
|
+
listForCustomer(customerAccountId: string): Promise<ReturnCaseSummary[]>;
|
|
34
|
+
getForCustomer(id: string, customerAccountId: string): Promise<ReturnCaseDetail>;
|
|
35
|
+
/** Customer withdraws their own case (if the workflow permits the transition). */
|
|
36
|
+
cancelByCustomer(id: string, customerAccountId: string): Promise<ReturnCaseDetail>;
|
|
37
|
+
/** Detail for the admin surface (includes internal comments). */
|
|
38
|
+
getByIdForAdmin(id: string): Promise<ReturnCaseDetail>;
|
|
39
|
+
private consumedQuantities;
|
|
40
|
+
private isWithinFreeWindow;
|
|
41
|
+
private toSummary;
|
|
42
|
+
private mapDetail;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=return-case-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"return-case-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/return-case-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,KAAK,EACV,uBAAuB,EACvB,sBAAsB,EACtB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAUlE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AACjF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAK9E,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B,YAAY,EAAE,wBAAwB,CAAC;IACvC,WAAW,EAAE,uBAAuB,CAAC;IACrC,YAAY,EAAE,sBAAsB,CAAC;IACrC,kFAAkF;IAClF,qBAAqB,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACnE,kEAAkE;IAClE,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;GAMG;AACH,qBAAa,iBAAiB;IAChB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,qBAAqB;IAExD,OAAO,CAAC,GAAG;IAIX,oFAAoF;IAC9E,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA0B5F,yDAAyD;IACnD,UAAU,CACd,KAAK,EAAE,uBAAuB,EAC9B,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC,gBAAgB,CAAC;IAmHtB,eAAe,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAWxE,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAOtF,kFAAkF;IAC5E,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAQxF,iEAAiE;IAC3D,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;YAS9C,kBAAkB;YAalB,kBAAkB;IAQhC,OAAO,CAAC,SAAS;YAcH,SAAS;CAwDxB"}
|