@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,89 @@
|
|
|
1
|
+
import type { EmailMailerPort, TransactionalEmailSender } from '@endora-commerce/contracts';
|
|
2
|
+
import type { ReturnCase } from '../entities/return-case.entity.js';
|
|
3
|
+
import type { ReturnNotifier } from './return-authorization-service.js';
|
|
4
|
+
/** Resolves a customer account's email address (injected to avoid importing
|
|
5
|
+
* the customer_accounts module's internals into returns). */
|
|
6
|
+
export type CustomerEmailResolver = (customerAccountId: string) => Promise<string | null>;
|
|
7
|
+
/**
|
|
8
|
+
* Why the authorize / reject e-mail did — or did not — go out (issue #78).
|
|
9
|
+
*
|
|
10
|
+
* Both methods answered `void`, and reaching the transactional sender counted
|
|
11
|
+
* as done: they returned right after the send whatever it reported, so a
|
|
12
|
+
* deactivated template, a transport-less composition and a code with no
|
|
13
|
+
* definition were indistinguishable from a delivered message — and from the
|
|
14
|
+
* legacy-builder path, which is the only one that put an e-mail anywhere in
|
|
15
|
+
* those cases.
|
|
16
|
+
*/
|
|
17
|
+
export type ReturnEmailNotSentReason =
|
|
18
|
+
/** The case carries no RMA number, so there is no authorization to announce. */
|
|
19
|
+
'no_rma'
|
|
20
|
+
/** No address resolves for the case's customer account. */
|
|
21
|
+
| 'no_recipient'
|
|
22
|
+
/** The operator switched this return e-mail off. */
|
|
23
|
+
| 'deactivated'
|
|
24
|
+
/** No mailer is wired behind the sender. */
|
|
25
|
+
| 'no_transport'
|
|
26
|
+
/** No template exists for this code yet. */
|
|
27
|
+
| 'no_definition'
|
|
28
|
+
/**
|
|
29
|
+
* The transport itself declined to send (D-59) — today, an already-accepted
|
|
30
|
+
* `messageId`, so an earlier call delivered the message and this one did not.
|
|
31
|
+
*/
|
|
32
|
+
| 'suppressed'
|
|
33
|
+
/** The send raised, and the workflow transition stays applied. */
|
|
34
|
+
| 'failed';
|
|
35
|
+
export type ReturnEmailResult = {
|
|
36
|
+
sent: true;
|
|
37
|
+
} | {
|
|
38
|
+
sent: false;
|
|
39
|
+
reason: ReturnEmailNotSentReason;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Where a send that did not happen is reported. Injectable so a test can read
|
|
43
|
+
* it; defaults to `console.warn`, which is what the rest of this layer uses.
|
|
44
|
+
*/
|
|
45
|
+
export type ReturnEmailLog = (message: string, context: Record<string, unknown>) => void;
|
|
46
|
+
/** Feature 047 — optional transactional-email wiring (admin-editable templates). */
|
|
47
|
+
export interface ReturnNotifierEmailOptions {
|
|
48
|
+
getTransactionalEmailSender?: () => TransactionalEmailSender | undefined;
|
|
49
|
+
resolveLanguage?: (salesChannelId: string) => Promise<string>;
|
|
50
|
+
log?: ReturnEmailLog;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* ReturnEmailNotifier — feature 046 (US2, FR-017).
|
|
54
|
+
*
|
|
55
|
+
* Sends the authorize / reject notifications through the platform Mailer.
|
|
56
|
+
* Best-effort: the caller (`ReturnAuthorizationService`) already wraps these in
|
|
57
|
+
* a try/catch so a mail failure never blocks the workflow transition — which is
|
|
58
|
+
* exactly why every non-sent path is written to the log here as well as named
|
|
59
|
+
* in the result. That caller discards the result, so the log is what an
|
|
60
|
+
* operator has; a direct caller and the tests read the result.
|
|
61
|
+
*
|
|
62
|
+
* **Which message goes out is unchanged.** The legacy in-code builder still
|
|
63
|
+
* runs only when no sender is wired at all, including when the sender answers
|
|
64
|
+
* `no_definition` — where `templateEmailPort` would fall back. Making returns
|
|
65
|
+
* fall back too is a decision about what mail a client receives, not a
|
|
66
|
+
* reporting repair, so it is named here and in the result rather than slipped
|
|
67
|
+
* in: the reason reaches the log, and nothing is sent silently.
|
|
68
|
+
*/
|
|
69
|
+
export declare class ReturnEmailNotifier implements ReturnNotifier {
|
|
70
|
+
private readonly mailer;
|
|
71
|
+
private readonly resolveCustomerEmail;
|
|
72
|
+
private readonly emailOptions;
|
|
73
|
+
private readonly log;
|
|
74
|
+
constructor(mailer: EmailMailerPort, resolveCustomerEmail: CustomerEmailResolver, emailOptions?: ReturnNotifierEmailOptions);
|
|
75
|
+
private language;
|
|
76
|
+
authorized(rc: ReturnCase): Promise<ReturnEmailResult>;
|
|
77
|
+
rejected(rc: ReturnCase): Promise<ReturnEmailResult>;
|
|
78
|
+
/**
|
|
79
|
+
* The tolerance the workflow transition needs, narrowed to what it is for.
|
|
80
|
+
*
|
|
81
|
+
* A switched-off module is a presence answer about the whole operation rather
|
|
82
|
+
* than one message that failed to render, so it travels on. Everything else
|
|
83
|
+
* is contained: the return case has already moved to its new status and must
|
|
84
|
+
* not move back because the customer could not be told.
|
|
85
|
+
*/
|
|
86
|
+
private contained;
|
|
87
|
+
private notSent;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=return-email-notifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"return-email-notifier.d.ts","sourceRoot":"","sources":["../../../src/backend/services/return-email-notifier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAE5F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAIxE;8DAC8D;AAC9D,MAAM,MAAM,qBAAqB,GAAG,CAAC,iBAAiB,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAE1F;;;;;;;;;GASG;AACH,MAAM,MAAM,wBAAwB;AAClC,gFAAgF;AAC9E,QAAQ;AACV,2DAA2D;GACzD,cAAc;AAChB,oDAAoD;GAClD,aAAa;AACf,4CAA4C;GAC1C,cAAc;AAChB,4CAA4C;GAC1C,eAAe;AACjB;;;GAGG;GACD,YAAY;AACd,kEAAkE;GAChE,QAAQ,CAAC;AAEb,MAAM,MAAM,iBAAiB,GAAG;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,wBAAwB,CAAA;CAAE,CAAC;AAEnG;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;AAEzF,oFAAoF;AACpF,MAAM,WAAW,0BAA0B;IACzC,2BAA2B,CAAC,EAAE,MAAM,wBAAwB,GAAG,SAAS,CAAC;IACzE,eAAe,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9D,GAAG,CAAC,EAAE,cAAc,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,mBAAoB,YAAW,cAAc;IAItD,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY;IAL/B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAiB;gBAGlB,MAAM,EAAE,eAAe,EACvB,oBAAoB,EAAE,qBAAqB,EAC3C,YAAY,GAAE,0BAA+B;YAKlD,QAAQ;IAShB,UAAU,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA+BtD,QAAQ,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA8B1D;;;;;;;OAOG;IACH,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,OAAO;CAchB"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { rethrowIfModuleDisabled } from '@endora-commerce/platform/kernel';
|
|
2
|
+
import { buildReturnAuthorizedEmail } from '../email-templates/return-authorized.js';
|
|
3
|
+
import { buildReturnRejectedEmail } from '../email-templates/return-rejected.js';
|
|
4
|
+
/**
|
|
5
|
+
* ReturnEmailNotifier — feature 046 (US2, FR-017).
|
|
6
|
+
*
|
|
7
|
+
* Sends the authorize / reject notifications through the platform Mailer.
|
|
8
|
+
* Best-effort: the caller (`ReturnAuthorizationService`) already wraps these in
|
|
9
|
+
* a try/catch so a mail failure never blocks the workflow transition — which is
|
|
10
|
+
* exactly why every non-sent path is written to the log here as well as named
|
|
11
|
+
* in the result. That caller discards the result, so the log is what an
|
|
12
|
+
* operator has; a direct caller and the tests read the result.
|
|
13
|
+
*
|
|
14
|
+
* **Which message goes out is unchanged.** The legacy in-code builder still
|
|
15
|
+
* runs only when no sender is wired at all, including when the sender answers
|
|
16
|
+
* `no_definition` — where `templateEmailPort` would fall back. Making returns
|
|
17
|
+
* fall back too is a decision about what mail a client receives, not a
|
|
18
|
+
* reporting repair, so it is named here and in the result rather than slipped
|
|
19
|
+
* in: the reason reaches the log, and nothing is sent silently.
|
|
20
|
+
*/
|
|
21
|
+
export class ReturnEmailNotifier {
|
|
22
|
+
mailer;
|
|
23
|
+
resolveCustomerEmail;
|
|
24
|
+
emailOptions;
|
|
25
|
+
log;
|
|
26
|
+
constructor(mailer, resolveCustomerEmail, emailOptions = {}) {
|
|
27
|
+
this.mailer = mailer;
|
|
28
|
+
this.resolveCustomerEmail = resolveCustomerEmail;
|
|
29
|
+
this.emailOptions = emailOptions;
|
|
30
|
+
this.log = emailOptions.log ?? ((message, context) => console.warn(message, context));
|
|
31
|
+
}
|
|
32
|
+
async language(salesChannelId) {
|
|
33
|
+
if (!this.emailOptions.resolveLanguage)
|
|
34
|
+
return 'en-US';
|
|
35
|
+
try {
|
|
36
|
+
return await this.emailOptions.resolveLanguage(salesChannelId);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return 'en-US';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async authorized(rc) {
|
|
43
|
+
const kind = 'return_authorized';
|
|
44
|
+
if (!rc.rmaNumber)
|
|
45
|
+
return this.notSent(rc, kind, 'no_rma');
|
|
46
|
+
const to = await this.resolveCustomerEmail(rc.customerAccountId);
|
|
47
|
+
if (!to)
|
|
48
|
+
return this.notSent(rc, kind, 'no_recipient');
|
|
49
|
+
const rmaNumber = rc.rmaNumber;
|
|
50
|
+
const sender = this.emailOptions.getTransactionalEmailSender?.();
|
|
51
|
+
try {
|
|
52
|
+
if (sender) {
|
|
53
|
+
const outcome = await sender.send({
|
|
54
|
+
code: kind,
|
|
55
|
+
salesChannelId: rc.salesChannelId,
|
|
56
|
+
language: await this.language(rc.salesChannelId),
|
|
57
|
+
to,
|
|
58
|
+
messageId: `${kind}:${rc.id}`,
|
|
59
|
+
variables: { rmaNumber, returnCaseId: rc.id },
|
|
60
|
+
meta: { returnCaseId: rc.id, kind },
|
|
61
|
+
});
|
|
62
|
+
if (outcome.status !== 'sent')
|
|
63
|
+
return this.notSent(rc, kind, outcome.status);
|
|
64
|
+
return { sent: true };
|
|
65
|
+
}
|
|
66
|
+
const outcome = await this.mailer.send(buildReturnAuthorizedEmail({ to, rmaNumber, returnCaseId: rc.id }));
|
|
67
|
+
if (outcome.status !== 'sent')
|
|
68
|
+
return this.notSent(rc, kind, 'suppressed');
|
|
69
|
+
return { sent: true };
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
return this.contained(rc, kind, error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async rejected(rc) {
|
|
76
|
+
const kind = 'return_rejected';
|
|
77
|
+
const to = await this.resolveCustomerEmail(rc.customerAccountId);
|
|
78
|
+
if (!to)
|
|
79
|
+
return this.notSent(rc, kind, 'no_recipient');
|
|
80
|
+
const reason = rc.rejectionReason ?? '';
|
|
81
|
+
const sender = this.emailOptions.getTransactionalEmailSender?.();
|
|
82
|
+
try {
|
|
83
|
+
if (sender) {
|
|
84
|
+
const outcome = await sender.send({
|
|
85
|
+
code: kind,
|
|
86
|
+
salesChannelId: rc.salesChannelId,
|
|
87
|
+
language: await this.language(rc.salesChannelId),
|
|
88
|
+
to,
|
|
89
|
+
messageId: `${kind}:${rc.id}`,
|
|
90
|
+
variables: { reason, returnCaseId: rc.id },
|
|
91
|
+
meta: { returnCaseId: rc.id, kind },
|
|
92
|
+
});
|
|
93
|
+
if (outcome.status !== 'sent')
|
|
94
|
+
return this.notSent(rc, kind, outcome.status);
|
|
95
|
+
return { sent: true };
|
|
96
|
+
}
|
|
97
|
+
const outcome = await this.mailer.send(buildReturnRejectedEmail({ to, reason, returnCaseId: rc.id }));
|
|
98
|
+
if (outcome.status !== 'sent')
|
|
99
|
+
return this.notSent(rc, kind, 'suppressed');
|
|
100
|
+
return { sent: true };
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
return this.contained(rc, kind, error);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The tolerance the workflow transition needs, narrowed to what it is for.
|
|
108
|
+
*
|
|
109
|
+
* A switched-off module is a presence answer about the whole operation rather
|
|
110
|
+
* than one message that failed to render, so it travels on. Everything else
|
|
111
|
+
* is contained: the return case has already moved to its new status and must
|
|
112
|
+
* not move back because the customer could not be told.
|
|
113
|
+
*/
|
|
114
|
+
contained(rc, kind, error) {
|
|
115
|
+
rethrowIfModuleDisabled(error);
|
|
116
|
+
return this.notSent(rc, kind, 'failed', error);
|
|
117
|
+
}
|
|
118
|
+
notSent(rc, kind, reason, error) {
|
|
119
|
+
this.log('[returns] the return e-mail was not sent', {
|
|
120
|
+
returnCaseId: rc.id,
|
|
121
|
+
kind,
|
|
122
|
+
reason,
|
|
123
|
+
...(error === undefined ? {} : { error: error instanceof Error ? error.message : error }),
|
|
124
|
+
});
|
|
125
|
+
return { sent: false, reason };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=return-email-notifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"return-email-notifier.js","sourceRoot":"","sources":["../../../src/backend/services/return-email-notifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AdminReturnsListQuery } from '@endora-commerce/contracts';
|
|
2
|
+
import type { ReturnListService } from './return-list-service.js';
|
|
3
|
+
/**
|
|
4
|
+
* ReturnExportService — feature 046 (US8). Exports the current returns-list
|
|
5
|
+
* view to CSV (RFC-4180 quoting, hard row cap). Mirrors `OrderExportService`.
|
|
6
|
+
*/
|
|
7
|
+
export declare class ReturnExportService {
|
|
8
|
+
private readonly listService;
|
|
9
|
+
constructor(listService: ReturnListService);
|
|
10
|
+
exportCsv(query: AdminReturnsListQuery): Promise<{
|
|
11
|
+
csv: string;
|
|
12
|
+
rowCount: number;
|
|
13
|
+
truncated: boolean;
|
|
14
|
+
}>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=return-export-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"return-export-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/return-export-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAKlE;;;GAGG;AACH,qBAAa,mBAAmB;IAClB,OAAO,CAAC,QAAQ,CAAC,WAAW;gBAAX,WAAW,EAAE,iBAAiB;IAErD,SAAS,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;CAY9G"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const MAX_EXPORT_ROWS = 10_000;
|
|
2
|
+
const COLUMNS = ['rmaNumber', 'kind', 'orderId', 'statusCode', 'totalRefundAmount', 'currency', 'submittedAt'];
|
|
3
|
+
/**
|
|
4
|
+
* ReturnExportService — feature 046 (US8). Exports the current returns-list
|
|
5
|
+
* view to CSV (RFC-4180 quoting, hard row cap). Mirrors `OrderExportService`.
|
|
6
|
+
*/
|
|
7
|
+
export class ReturnExportService {
|
|
8
|
+
listService;
|
|
9
|
+
constructor(listService) {
|
|
10
|
+
this.listService = listService;
|
|
11
|
+
}
|
|
12
|
+
async exportCsv(query) {
|
|
13
|
+
const result = await this.listService.list({ ...query, page: 1, pageSize: MAX_EXPORT_ROWS });
|
|
14
|
+
const header = COLUMNS.join(',');
|
|
15
|
+
const lines = result.rows.map((r) => COLUMNS.map((c) => csvCell(String(r[c] ?? ''))).join(','));
|
|
16
|
+
return {
|
|
17
|
+
csv: [header, ...lines].join('\r\n'),
|
|
18
|
+
rowCount: result.rows.length,
|
|
19
|
+
truncated: result.total > result.rows.length,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** RFC-4180: quote when the cell contains a quote, comma, or newline. */
|
|
24
|
+
function csvCell(value) {
|
|
25
|
+
if (/[",\r\n]/.test(value))
|
|
26
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=return-export-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"return-export-service.js","sourceRoot":"","sources":["../../../src/backend/services/return-export-service.ts"],"names":[],"mappings":"AAGA,MAAM,eAAe,GAAG,MAAM,CAAC;AAC/B,MAAM,OAAO,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,mBAAmB,EAAE,UAAU,EAAE,aAAa,CAAU,CAAC;AAExH;;;GAGG;AACH,MAAM,OAAO,mBAAmB;IACD;IAA7B,YAA6B,WAA8B;QAA9B,gBAAW,GAAX,WAAW,CAAmB;IAAG,CAAC;IAE/D,KAAK,CAAC,SAAS,CAAC,KAA4B;QAC1C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC,CAAC;QAC7F,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAClC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAE,CAA6B,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CACvF,CAAC;QACF,OAAO;YACL,GAAG,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;YACpC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM;YAC5B,SAAS,EAAE,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM;SAC7C,CAAC;IACJ,CAAC;CACF;AAED,yEAAyE;AACzE,SAAS,OAAO,CAAC,KAAa;IAC5B,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;IACpE,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { FilterQuery } from '@mikro-orm/core';
|
|
3
|
+
import type { AdminReturnsListQuery, AdminReturnsListResponse, BulkTransitionResult } from '@endora-commerce/contracts';
|
|
4
|
+
import { ReturnCase } from '../entities/return-case.entity.js';
|
|
5
|
+
import type { ReturnStatusGraphService } from './return-status-graph-service.js';
|
|
6
|
+
import type { ReturnTransitionService } from './return-transition-service.js';
|
|
7
|
+
export interface ReturnListServiceDeps {
|
|
8
|
+
emFactory: () => EntityManager;
|
|
9
|
+
graphService: ReturnStatusGraphService;
|
|
10
|
+
transitions: ReturnTransitionService;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* ReturnListService — feature 046 (US8).
|
|
14
|
+
*
|
|
15
|
+
* Filter/search/sort the admin returns list with per-status counts, plus a
|
|
16
|
+
* bulk status change that skips cases whose current status does not permit the
|
|
17
|
+
* target transition. Mirrors `OrderListService`.
|
|
18
|
+
*/
|
|
19
|
+
export declare class ReturnListService {
|
|
20
|
+
private readonly deps;
|
|
21
|
+
constructor(deps: ReturnListServiceDeps);
|
|
22
|
+
list(query: AdminReturnsListQuery): Promise<AdminReturnsListResponse>;
|
|
23
|
+
bulkTransition(ids: string[], to: string, adminUserId: string, reason?: string): Promise<BulkTransitionResult>;
|
|
24
|
+
buildWhere(query: AdminReturnsListQuery): FilterQuery<ReturnCase>;
|
|
25
|
+
private statusCounts;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=return-list-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"return-list-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/return-list-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,KAAK,EAEV,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,EACrB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAE/D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AACjF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAE9E,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B,YAAY,EAAE,wBAAwB,CAAC;IACvC,WAAW,EAAE,uBAAuB,CAAC;CACtC;AAID;;;;;;GAMG;AACH,qBAAa,iBAAiB;IAChB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,qBAAqB;IAElD,IAAI,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAgCrE,cAAc,CAClB,GAAG,EAAE,MAAM,EAAE,EACb,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,oBAAoB,CAAC;IA0BhC,UAAU,CAAC,KAAK,EAAE,qBAAqB,GAAG,WAAW,CAAC,UAAU,CAAC;YAiBnD,YAAY;CA0B3B"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { ReturnCase } from '../entities/return-case.entity.js';
|
|
2
|
+
import { orgConstraintFor } from '@endora-commerce/platform/tenancy';
|
|
3
|
+
const DEFAULT_PAGE_SIZE = 25;
|
|
4
|
+
/**
|
|
5
|
+
* ReturnListService — feature 046 (US8).
|
|
6
|
+
*
|
|
7
|
+
* Filter/search/sort the admin returns list with per-status counts, plus a
|
|
8
|
+
* bulk status change that skips cases whose current status does not permit the
|
|
9
|
+
* target transition. Mirrors `OrderListService`.
|
|
10
|
+
*/
|
|
11
|
+
export class ReturnListService {
|
|
12
|
+
deps;
|
|
13
|
+
constructor(deps) {
|
|
14
|
+
this.deps = deps;
|
|
15
|
+
}
|
|
16
|
+
async list(query) {
|
|
17
|
+
const em = this.deps.emFactory();
|
|
18
|
+
const where = this.buildWhere(query);
|
|
19
|
+
const page = query.page ?? 1;
|
|
20
|
+
const pageSize = query.pageSize ?? DEFAULT_PAGE_SIZE;
|
|
21
|
+
const [field, dir] = (query.sort ?? 'submittedAt:desc').split(':');
|
|
22
|
+
const orderBy = field === 'rmaNumber' ? { rmaNumber: dir } : { submittedAt: dir };
|
|
23
|
+
const [cases, total] = await em.findAndCount(ReturnCase, where, {
|
|
24
|
+
orderBy,
|
|
25
|
+
limit: pageSize,
|
|
26
|
+
offset: (page - 1) * pageSize,
|
|
27
|
+
});
|
|
28
|
+
const graph = await this.deps.graphService.loadGraph();
|
|
29
|
+
const rows = cases.map((rc) => ({
|
|
30
|
+
id: rc.id,
|
|
31
|
+
rmaNumber: rc.rmaNumber ?? null,
|
|
32
|
+
kind: rc.kind,
|
|
33
|
+
orderId: rc.orderId,
|
|
34
|
+
statusCode: rc.statusCode,
|
|
35
|
+
statusLabel: graph.get(rc.statusCode)?.defaultName ?? rc.statusCode,
|
|
36
|
+
totalRefundAmount: Number(rc.totalRefundAmount),
|
|
37
|
+
currency: rc.currency,
|
|
38
|
+
submittedAt: rc.submittedAt.toISOString(),
|
|
39
|
+
customerName: null,
|
|
40
|
+
organizationName: null,
|
|
41
|
+
}));
|
|
42
|
+
return { rows, total, counts: await this.statusCounts() };
|
|
43
|
+
}
|
|
44
|
+
async bulkTransition(ids, to, adminUserId, reason) {
|
|
45
|
+
const em = this.deps.emFactory();
|
|
46
|
+
const graph = await this.deps.graphService.loadGraph();
|
|
47
|
+
const moved = [];
|
|
48
|
+
const skipped = [];
|
|
49
|
+
for (const id of ids) {
|
|
50
|
+
const rc = await em.findOne(ReturnCase, { id });
|
|
51
|
+
if (!rc) {
|
|
52
|
+
skipped.push({ id, reason: 'not_found' });
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (!graph.canTransition(rc.statusCode, to)) {
|
|
56
|
+
skipped.push({ id, reason: `cannot transition from "${rc.statusCode}"` });
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
await this.deps.transitions.apply(id, to, { kind: 'admin', adminUserId }, reason !== undefined ? { reason } : undefined);
|
|
60
|
+
moved.push(id);
|
|
61
|
+
}
|
|
62
|
+
return { moved, skipped };
|
|
63
|
+
}
|
|
64
|
+
buildWhere(query) {
|
|
65
|
+
const where = {};
|
|
66
|
+
const statuses = (query.status ?? '')
|
|
67
|
+
.split(',')
|
|
68
|
+
.map((s) => s.trim())
|
|
69
|
+
.filter(Boolean);
|
|
70
|
+
if (statuses.length > 0)
|
|
71
|
+
Object.assign(where, { statusCode: { $in: statuses } });
|
|
72
|
+
if (query.kind)
|
|
73
|
+
Object.assign(where, { kind: query.kind });
|
|
74
|
+
if (query.rmaNumber)
|
|
75
|
+
Object.assign(where, { rmaNumber: { $ilike: `%${query.rmaNumber}%` } });
|
|
76
|
+
if (query.q) {
|
|
77
|
+
Object.assign(where, {
|
|
78
|
+
$or: [{ rmaNumber: { $ilike: `%${query.q}%` } }, { orderId: query.q }],
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return where;
|
|
82
|
+
}
|
|
83
|
+
async statusCounts() {
|
|
84
|
+
const em = this.deps.emFactory();
|
|
85
|
+
// The main list uses em.findAndCount (auto-filtered), but this aggregate is
|
|
86
|
+
// raw knex, so apply the tenant org constraint explicitly (feature 050).
|
|
87
|
+
const qb = em
|
|
88
|
+
.getKnex()
|
|
89
|
+
.from('return_cases')
|
|
90
|
+
.select('status_code')
|
|
91
|
+
.count('* as count')
|
|
92
|
+
.groupBy('status_code');
|
|
93
|
+
const constraint = orgConstraintFor();
|
|
94
|
+
if (constraint.kind === 'single') {
|
|
95
|
+
// `whereNull` matches nothing since D-178: the only actor that reached it
|
|
96
|
+
// was a signed-in customer with no Organization, and
|
|
97
|
+
// `customer_accounts.organization_id` is `NOT NULL` now. Kept because the
|
|
98
|
+
// constraint type still admits a null.
|
|
99
|
+
if (constraint.organizationId === null)
|
|
100
|
+
void qb.whereNull('organization_id');
|
|
101
|
+
else
|
|
102
|
+
void qb.where('organization_id', constraint.organizationId);
|
|
103
|
+
}
|
|
104
|
+
else if (constraint.kind === 'set') {
|
|
105
|
+
void qb.whereIn('organization_id', [...constraint.organizationIds]);
|
|
106
|
+
}
|
|
107
|
+
const rows = await qb;
|
|
108
|
+
const out = {};
|
|
109
|
+
for (const r of rows)
|
|
110
|
+
out[r.status_code] = Number(r.count);
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=return-list-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"return-list-service.js","sourceRoot":"","sources":["../../../src/backend/services/return-list-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { ReturnSavedViewDto } from '@endora-commerce/contracts';
|
|
3
|
+
/**
|
|
4
|
+
* ReturnListViewService — feature 046 (US8).
|
|
5
|
+
*
|
|
6
|
+
* Saved admin list presets (filters + sort + visible columns), private to the
|
|
7
|
+
* owner or shared with everyone with list access. Mirrors
|
|
8
|
+
* `OrderListViewService`.
|
|
9
|
+
*/
|
|
10
|
+
export declare class ReturnListViewService {
|
|
11
|
+
private readonly emFactory;
|
|
12
|
+
constructor(emFactory: () => EntityManager);
|
|
13
|
+
listFor(adminUserId: string): Promise<ReturnSavedViewDto[]>;
|
|
14
|
+
create(adminUserId: string, input: {
|
|
15
|
+
name: string;
|
|
16
|
+
shared?: boolean | undefined;
|
|
17
|
+
filters: Record<string, unknown>;
|
|
18
|
+
sort: {
|
|
19
|
+
field: string;
|
|
20
|
+
dir: 'asc' | 'desc';
|
|
21
|
+
};
|
|
22
|
+
visibleColumns?: string[] | undefined;
|
|
23
|
+
}): Promise<ReturnSavedViewDto>;
|
|
24
|
+
update(id: string, adminUserId: string, patch: {
|
|
25
|
+
name?: string | undefined;
|
|
26
|
+
shared?: boolean | undefined;
|
|
27
|
+
filters?: Record<string, unknown> | undefined;
|
|
28
|
+
sort?: {
|
|
29
|
+
field: string;
|
|
30
|
+
dir: 'asc' | 'desc';
|
|
31
|
+
} | undefined;
|
|
32
|
+
visibleColumns?: string[] | null | undefined;
|
|
33
|
+
}): Promise<ReturnSavedViewDto>;
|
|
34
|
+
remove(id: string, adminUserId: string): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=return-list-view-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"return-list-view-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/return-list-view-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAIrE;;;;;;GAMG;AACH,qBAAa,qBAAqB;IACpB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,aAAa;IAErD,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAU3D,MAAM,CACV,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACjC,IAAI,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,KAAK,GAAG,MAAM,CAAA;SAAE,CAAC;QAC7C,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;KACvC,GACA,OAAO,CAAC,kBAAkB,CAAC;IAgBxB,MAAM,CACV,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE;QACL,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAC7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;QAC9C,IAAI,CAAC,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,KAAK,GAAG,MAAM,CAAA;SAAE,GAAG,SAAS,CAAC;QAC1D,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;KAC9C,GACA,OAAO,CAAC,kBAAkB,CAAC;IAkBxB,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAW7D"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { ERROR_CODES } from '@endora-commerce/contracts';
|
|
2
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
3
|
+
import { ReturnListSavedView } from '../entities/return-list-saved-view.entity.js';
|
|
4
|
+
/**
|
|
5
|
+
* ReturnListViewService — feature 046 (US8).
|
|
6
|
+
*
|
|
7
|
+
* Saved admin list presets (filters + sort + visible columns), private to the
|
|
8
|
+
* owner or shared with everyone with list access. Mirrors
|
|
9
|
+
* `OrderListViewService`.
|
|
10
|
+
*/
|
|
11
|
+
export class ReturnListViewService {
|
|
12
|
+
emFactory;
|
|
13
|
+
constructor(emFactory) {
|
|
14
|
+
this.emFactory = emFactory;
|
|
15
|
+
}
|
|
16
|
+
async listFor(adminUserId) {
|
|
17
|
+
const em = this.emFactory();
|
|
18
|
+
const views = await em.find(ReturnListSavedView, { $or: [{ ownerAdminUserId: adminUserId }, { shared: true }] }, { orderBy: { createdAt: 'asc' } });
|
|
19
|
+
return views.map(toDto);
|
|
20
|
+
}
|
|
21
|
+
async create(adminUserId, input) {
|
|
22
|
+
// command-coverage-ignore: saved list views are per-admin UI presets
|
|
23
|
+
// (filters/sort/columns), not audited domain-state.
|
|
24
|
+
const em = this.emFactory();
|
|
25
|
+
const view = em.create(ReturnListSavedView, {
|
|
26
|
+
ownerAdminUserId: adminUserId,
|
|
27
|
+
name: input.name,
|
|
28
|
+
shared: input.shared ?? false,
|
|
29
|
+
filters: input.filters,
|
|
30
|
+
sort: input.sort,
|
|
31
|
+
visibleColumns: input.visibleColumns ?? null,
|
|
32
|
+
});
|
|
33
|
+
await em.persistAndFlush(view);
|
|
34
|
+
return toDto(view);
|
|
35
|
+
}
|
|
36
|
+
async update(id, adminUserId, patch) {
|
|
37
|
+
// command-coverage-ignore: saved list views are per-admin UI presets, not
|
|
38
|
+
// audited domain-state.
|
|
39
|
+
const em = this.emFactory();
|
|
40
|
+
const view = await em.findOne(ReturnListSavedView, { id });
|
|
41
|
+
if (!view)
|
|
42
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Saved view not found.');
|
|
43
|
+
if (view.ownerAdminUserId !== adminUserId) {
|
|
44
|
+
throw new HttpError(403, ERROR_CODES.FORBIDDEN, 'Only the owner can edit this view.');
|
|
45
|
+
}
|
|
46
|
+
if (patch.name !== undefined)
|
|
47
|
+
view.name = patch.name;
|
|
48
|
+
if (patch.shared !== undefined)
|
|
49
|
+
view.shared = patch.shared;
|
|
50
|
+
if (patch.filters !== undefined)
|
|
51
|
+
view.filters = patch.filters;
|
|
52
|
+
if (patch.sort !== undefined)
|
|
53
|
+
view.sort = patch.sort;
|
|
54
|
+
if (patch.visibleColumns !== undefined)
|
|
55
|
+
view.visibleColumns = patch.visibleColumns;
|
|
56
|
+
await em.flush();
|
|
57
|
+
return toDto(view);
|
|
58
|
+
}
|
|
59
|
+
async remove(id, adminUserId) {
|
|
60
|
+
// command-coverage-ignore: saved list views are per-admin UI presets, not
|
|
61
|
+
// audited domain-state.
|
|
62
|
+
const em = this.emFactory();
|
|
63
|
+
const view = await em.findOne(ReturnListSavedView, { id });
|
|
64
|
+
if (!view)
|
|
65
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Saved view not found.');
|
|
66
|
+
if (view.ownerAdminUserId !== adminUserId) {
|
|
67
|
+
throw new HttpError(403, ERROR_CODES.FORBIDDEN, 'Only the owner can delete this view.');
|
|
68
|
+
}
|
|
69
|
+
await em.removeAndFlush(view);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function toDto(v) {
|
|
73
|
+
return {
|
|
74
|
+
id: v.id,
|
|
75
|
+
name: v.name,
|
|
76
|
+
shared: v.shared,
|
|
77
|
+
filters: v.filters,
|
|
78
|
+
sort: v.sort,
|
|
79
|
+
visibleColumns: v.visibleColumns ?? null,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=return-list-view-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"return-list-view-service.js","sourceRoot":"","sources":["../../../src/backend/services/return-list-view-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAC;AAEnF;;;;;;GAMG;AACH,MAAM,OAAO,qBAAqB;IACH;IAA7B,YAA6B,SAA8B;QAA9B,cAAS,GAAT,SAAS,CAAqB;IAAG,CAAC;IAE/D,KAAK,CAAC,OAAO,CAAC,WAAmB;QAC/B,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,IAAI,CACzB,mBAAmB,EACnB,EAAE,GAAG,EAAE,CAAC,EAAE,gBAAgB,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,EAC9D,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,CAClC,CAAC;QACF,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,MAAM,CACV,WAAmB,EACnB,KAMC;QAED,qEAAqE;QACrE,oDAAoD;QACpD,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,EAAE;YAC1C,gBAAgB,EAAE,WAAW;YAC7B,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,KAAK;YAC7B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,IAAI;SAC7C,CAAC,CAAC;QACH,MAAM,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC/B,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,MAAM,CACV,EAAU,EACV,WAAmB,EACnB,KAMC;QAED,0EAA0E;QAC1E,wBAAwB;QACxB,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,mBAAmB,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAC3D,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC,SAAS,EAAE,uBAAuB,CAAC,CAAC;QACpF,IAAI,IAAI,CAAC,gBAAgB,KAAK,WAAW,EAAE,CAAC;YAC1C,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC,SAAS,EAAE,oCAAoC,CAAC,CAAC;QACxF,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;YAAE,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACrD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;YAAE,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC3D,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;YAAE,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QAC9D,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;YAAE,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACrD,IAAI,KAAK,CAAC,cAAc,KAAK,SAAS;YAAE,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC,cAAc,CAAC;QACnF,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;QACjB,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,WAAmB;QAC1C,0EAA0E;QAC1E,wBAAwB;QACxB,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,mBAAmB,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAC3D,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC,SAAS,EAAE,uBAAuB,CAAC,CAAC;QACpF,IAAI,IAAI,CAAC,gBAAgB,KAAK,WAAW,EAAE,CAAC;YAC1C,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC,SAAS,EAAE,sCAAsC,CAAC,CAAC;QAC1F,CAAC;QACD,MAAM,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;CACF;AAED,SAAS,KAAK,CAAC,CAAsB;IACnC,OAAO;QACL,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,cAAc,EAAE,CAAC,CAAC,cAAc,IAAI,IAAI;KACzC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { ReturnReasonDto } from '@endora-commerce/contracts';
|
|
3
|
+
import type { AuditPort } from '@endora-commerce/platform/kernel';
|
|
4
|
+
/**
|
|
5
|
+
* ReturnReasonService — feature 046 (US7, FR-030).
|
|
6
|
+
*
|
|
7
|
+
* Manages the return/complaint reason list. The storefront form reads the active
|
|
8
|
+
* reasons (optionally filtered by case kind) in `weight` order.
|
|
9
|
+
*/
|
|
10
|
+
export declare class ReturnReasonService {
|
|
11
|
+
#private;
|
|
12
|
+
private readonly emFactory;
|
|
13
|
+
private readonly auditLog?;
|
|
14
|
+
constructor(emFactory: () => EntityManager, auditLog?: AuditPort | undefined);
|
|
15
|
+
/** All reasons (admin view), ordered by weight. */
|
|
16
|
+
listAll(): Promise<ReturnReasonDto[]>;
|
|
17
|
+
/** Active reasons for a case kind (storefront), ordered by weight. */
|
|
18
|
+
listActive(kind?: 'return' | 'complaint'): Promise<ReturnReasonDto[]>;
|
|
19
|
+
create(input: {
|
|
20
|
+
label: Record<string, string>;
|
|
21
|
+
appliesTo: 'return' | 'complaint' | 'both';
|
|
22
|
+
isActive?: boolean | undefined;
|
|
23
|
+
weight?: number | undefined;
|
|
24
|
+
}): Promise<ReturnReasonDto>;
|
|
25
|
+
update(id: string, patch: {
|
|
26
|
+
label?: Record<string, string> | undefined;
|
|
27
|
+
appliesTo?: 'return' | 'complaint' | 'both' | undefined;
|
|
28
|
+
isActive?: boolean | undefined;
|
|
29
|
+
weight?: number | undefined;
|
|
30
|
+
}): Promise<ReturnReasonDto>;
|
|
31
|
+
remove(id: string): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=return-reason-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"return-reason-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/return-reason-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAGlE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAGlE;;;;;GAKG;AACH,qBAAa,mBAAmB;;IAE5B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBADT,SAAS,EAAE,MAAM,aAAa,EAC9B,QAAQ,CAAC,EAAE,SAAS,YAAA;IAqBvC,mDAAmD;IAC7C,OAAO,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAM3C,sEAAsE;IAChE,UAAU,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,WAAW,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IASrE,MAAM,CAAC,KAAK,EAAE;QAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC9B,SAAS,EAAE,QAAQ,GAAG,WAAW,GAAG,MAAM,CAAC;QAC3C,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAC/B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC7B,GAAG,OAAO,CAAC,eAAe,CAAC;IActB,MAAM,CACV,EAAE,EAAE,MAAM,EACV,KAAK,EAAE;QACL,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;QAC3C,SAAS,CAAC,EAAE,QAAQ,GAAG,WAAW,GAAG,MAAM,GAAG,SAAS,CAAC;QACxD,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAC/B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC7B,GACA,OAAO,CAAC,eAAe,CAAC;IAiBrB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAOxC"}
|