@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.
Files changed (211) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +60 -0
  3. package/dist/admin/api/returns-client.d.ts +69 -0
  4. package/dist/admin/api/returns-client.d.ts.map +1 -0
  5. package/dist/admin/api/returns-client.js +36 -0
  6. package/dist/admin/api/returns-client.js.map +1 -0
  7. package/dist/admin/index.d.ts +37 -0
  8. package/dist/admin/index.d.ts.map +1 -0
  9. package/dist/admin/index.js +57 -0
  10. package/dist/admin/index.js.map +1 -0
  11. package/dist/admin/pages/ReturnDeliveryMethodsPage.d.ts +5 -0
  12. package/dist/admin/pages/ReturnDeliveryMethodsPage.d.ts.map +1 -0
  13. package/dist/admin/pages/ReturnDeliveryMethodsPage.js +44 -0
  14. package/dist/admin/pages/ReturnDeliveryMethodsPage.js.map +1 -0
  15. package/dist/admin/pages/ReturnDetail.d.ts +5 -0
  16. package/dist/admin/pages/ReturnDetail.d.ts.map +1 -0
  17. package/dist/admin/pages/ReturnDetail.js +115 -0
  18. package/dist/admin/pages/ReturnDetail.js.map +1 -0
  19. package/dist/admin/pages/ReturnReasonsPage.d.ts +5 -0
  20. package/dist/admin/pages/ReturnReasonsPage.d.ts.map +1 -0
  21. package/dist/admin/pages/ReturnReasonsPage.js +44 -0
  22. package/dist/admin/pages/ReturnReasonsPage.js.map +1 -0
  23. package/dist/admin/pages/ReturnStatusesConfigPage.d.ts +5 -0
  24. package/dist/admin/pages/ReturnStatusesConfigPage.d.ts.map +1 -0
  25. package/dist/admin/pages/ReturnStatusesConfigPage.js +71 -0
  26. package/dist/admin/pages/ReturnStatusesConfigPage.js.map +1 -0
  27. package/dist/admin/pages/ReturnsList.d.ts +5 -0
  28. package/dist/admin/pages/ReturnsList.d.ts.map +1 -0
  29. package/dist/admin/pages/ReturnsList.js +189 -0
  30. package/dist/admin/pages/ReturnsList.js.map +1 -0
  31. package/dist/backend/domain/default-reasons.d.ts +14 -0
  32. package/dist/backend/domain/default-reasons.d.ts.map +1 -0
  33. package/dist/backend/domain/default-reasons.js +9 -0
  34. package/dist/backend/domain/default-reasons.js.map +1 -0
  35. package/dist/backend/domain/free-return-window.d.ts +2 -0
  36. package/dist/backend/domain/free-return-window.d.ts.map +1 -0
  37. package/dist/backend/domain/free-return-window.js +19 -0
  38. package/dist/backend/domain/free-return-window.js.map +1 -0
  39. package/dist/backend/domain/refund-math.d.ts +19 -0
  40. package/dist/backend/domain/refund-math.d.ts.map +1 -0
  41. package/dist/backend/domain/refund-math.js +27 -0
  42. package/dist/backend/domain/refund-math.js.map +1 -0
  43. package/dist/backend/domain/return-status-graph.d.ts +91 -0
  44. package/dist/backend/domain/return-status-graph.d.ts.map +1 -0
  45. package/dist/backend/domain/return-status-graph.js +143 -0
  46. package/dist/backend/domain/return-status-graph.js.map +1 -0
  47. package/dist/backend/email-templates/return-authorized.d.ts +11 -0
  48. package/dist/backend/email-templates/return-authorized.d.ts.map +1 -0
  49. package/dist/backend/email-templates/return-authorized.js +18 -0
  50. package/dist/backend/email-templates/return-authorized.js.map +1 -0
  51. package/dist/backend/email-templates/return-rejected.d.ts +11 -0
  52. package/dist/backend/email-templates/return-rejected.d.ts.map +1 -0
  53. package/dist/backend/email-templates/return-rejected.js +20 -0
  54. package/dist/backend/email-templates/return-rejected.js.map +1 -0
  55. package/dist/backend/email-templates/transactional-defaults.d.ts +11 -0
  56. package/dist/backend/email-templates/transactional-defaults.d.ts.map +1 -0
  57. package/dist/backend/email-templates/transactional-defaults.js +64 -0
  58. package/dist/backend/email-templates/transactional-defaults.js.map +1 -0
  59. package/dist/backend/entities/refund.entity.d.ts +41 -0
  60. package/dist/backend/entities/refund.entity.d.ts.map +1 -0
  61. package/dist/backend/entities/refund.entity.js +122 -0
  62. package/dist/backend/entities/refund.entity.js.map +1 -0
  63. package/dist/backend/entities/return-case-attachment.entity.d.ts +16 -0
  64. package/dist/backend/entities/return-case-attachment.entity.d.ts.map +1 -0
  65. package/dist/backend/entities/return-case-attachment.entity.js +53 -0
  66. package/dist/backend/entities/return-case-attachment.entity.js.map +1 -0
  67. package/dist/backend/entities/return-case-comment.entity.d.ts +20 -0
  68. package/dist/backend/entities/return-case-comment.entity.d.ts.map +1 -0
  69. package/dist/backend/entities/return-case-comment.entity.js +70 -0
  70. package/dist/backend/entities/return-case-comment.entity.js.map +1 -0
  71. package/dist/backend/entities/return-case-item.entity.d.ts +25 -0
  72. package/dist/backend/entities/return-case-item.entity.d.ts.map +1 -0
  73. package/dist/backend/entities/return-case-item.entity.js +87 -0
  74. package/dist/backend/entities/return-case-item.entity.js.map +1 -0
  75. package/dist/backend/entities/return-case.entity.d.ts +41 -0
  76. package/dist/backend/entities/return-case.entity.d.ts.map +1 -0
  77. package/dist/backend/entities/return-case.entity.js +155 -0
  78. package/dist/backend/entities/return-case.entity.js.map +1 -0
  79. package/dist/backend/entities/return-delivery-method.entity.d.ts +18 -0
  80. package/dist/backend/entities/return-delivery-method.entity.d.ts.map +1 -0
  81. package/dist/backend/entities/return-delivery-method.entity.js +63 -0
  82. package/dist/backend/entities/return-delivery-method.entity.js.map +1 -0
  83. package/dist/backend/entities/return-list-saved-view.entity.d.ts +24 -0
  84. package/dist/backend/entities/return-list-saved-view.entity.d.ts.map +1 -0
  85. package/dist/backend/entities/return-list-saved-view.entity.js +74 -0
  86. package/dist/backend/entities/return-list-saved-view.entity.js.map +1 -0
  87. package/dist/backend/entities/return-reason.entity.d.ts +21 -0
  88. package/dist/backend/entities/return-reason.entity.d.ts.map +1 -0
  89. package/dist/backend/entities/return-reason.entity.js +65 -0
  90. package/dist/backend/entities/return-reason.entity.js.map +1 -0
  91. package/dist/backend/entities/return-shipment.entity.d.ts +22 -0
  92. package/dist/backend/entities/return-shipment.entity.d.ts.map +1 -0
  93. package/dist/backend/entities/return-shipment.entity.js +71 -0
  94. package/dist/backend/entities/return-shipment.entity.js.map +1 -0
  95. package/dist/backend/entities/return-status-transition.entity.d.ts +17 -0
  96. package/dist/backend/entities/return-status-transition.entity.d.ts.map +1 -0
  97. package/dist/backend/entities/return-status-transition.entity.js +55 -0
  98. package/dist/backend/entities/return-status-transition.entity.js.map +1 -0
  99. package/dist/backend/entities/return-status.entity.d.ts +29 -0
  100. package/dist/backend/entities/return-status.entity.d.ts.map +1 -0
  101. package/dist/backend/entities/return-status.entity.js +90 -0
  102. package/dist/backend/entities/return-status.entity.js.map +1 -0
  103. package/dist/backend/events/return-status-events.d.ts +44 -0
  104. package/dist/backend/events/return-status-events.d.ts.map +1 -0
  105. package/dist/backend/events/return-status-events.js +50 -0
  106. package/dist/backend/events/return-status-events.js.map +1 -0
  107. package/dist/backend/index.d.ts +106 -0
  108. package/dist/backend/index.d.ts.map +1 -0
  109. package/dist/backend/index.js +116 -0
  110. package/dist/backend/index.js.map +1 -0
  111. package/dist/backend/plugin.d.ts +33 -0
  112. package/dist/backend/plugin.d.ts.map +1 -0
  113. package/dist/backend/plugin.js +124 -0
  114. package/dist/backend/plugin.js.map +1 -0
  115. package/dist/backend/routes.admin.d.ts +38 -0
  116. package/dist/backend/routes.admin.d.ts.map +1 -0
  117. package/dist/backend/routes.admin.js +181 -0
  118. package/dist/backend/routes.admin.js.map +1 -0
  119. package/dist/backend/routes.customer.d.ts +22 -0
  120. package/dist/backend/routes.customer.d.ts.map +1 -0
  121. package/dist/backend/routes.customer.js +56 -0
  122. package/dist/backend/routes.customer.js.map +1 -0
  123. package/dist/backend/services/notification-context.d.ts +47 -0
  124. package/dist/backend/services/notification-context.d.ts.map +1 -0
  125. package/dist/backend/services/notification-context.js +49 -0
  126. package/dist/backend/services/notification-context.js.map +1 -0
  127. package/dist/backend/services/return-attachment-service.d.ts +20 -0
  128. package/dist/backend/services/return-attachment-service.d.ts.map +1 -0
  129. package/dist/backend/services/return-attachment-service.js +48 -0
  130. package/dist/backend/services/return-attachment-service.js.map +1 -0
  131. package/dist/backend/services/return-authorization-service.d.ts +53 -0
  132. package/dist/backend/services/return-authorization-service.d.ts.map +1 -0
  133. package/dist/backend/services/return-authorization-service.js +72 -0
  134. package/dist/backend/services/return-authorization-service.js.map +1 -0
  135. package/dist/backend/services/return-case-service.d.ts +44 -0
  136. package/dist/backend/services/return-case-service.d.ts.map +1 -0
  137. package/dist/backend/services/return-case-service.js +260 -0
  138. package/dist/backend/services/return-case-service.js.map +1 -0
  139. package/dist/backend/services/return-comment-service.d.ts +43 -0
  140. package/dist/backend/services/return-comment-service.d.ts.map +1 -0
  141. package/dist/backend/services/return-comment-service.js +109 -0
  142. package/dist/backend/services/return-comment-service.js.map +1 -0
  143. package/dist/backend/services/return-delivery-method-service.d.ts +44 -0
  144. package/dist/backend/services/return-delivery-method-service.d.ts.map +1 -0
  145. package/dist/backend/services/return-delivery-method-service.js +114 -0
  146. package/dist/backend/services/return-delivery-method-service.js.map +1 -0
  147. package/dist/backend/services/return-email-notifier.d.ts +89 -0
  148. package/dist/backend/services/return-email-notifier.d.ts.map +1 -0
  149. package/dist/backend/services/return-email-notifier.js +128 -0
  150. package/dist/backend/services/return-email-notifier.js.map +1 -0
  151. package/dist/backend/services/return-export-service.d.ts +16 -0
  152. package/dist/backend/services/return-export-service.d.ts.map +1 -0
  153. package/dist/backend/services/return-export-service.js +29 -0
  154. package/dist/backend/services/return-export-service.js.map +1 -0
  155. package/dist/backend/services/return-list-service.d.ts +27 -0
  156. package/dist/backend/services/return-list-service.d.ts.map +1 -0
  157. package/dist/backend/services/return-list-service.js +114 -0
  158. package/dist/backend/services/return-list-service.js.map +1 -0
  159. package/dist/backend/services/return-list-view-service.d.ts +36 -0
  160. package/dist/backend/services/return-list-view-service.d.ts.map +1 -0
  161. package/dist/backend/services/return-list-view-service.js +82 -0
  162. package/dist/backend/services/return-list-view-service.js.map +1 -0
  163. package/dist/backend/services/return-reason-service.d.ts +33 -0
  164. package/dist/backend/services/return-reason-service.d.ts.map +1 -0
  165. package/dist/backend/services/return-reason-service.js +90 -0
  166. package/dist/backend/services/return-reason-service.js.map +1 -0
  167. package/dist/backend/services/return-settlement-service.d.ts +67 -0
  168. package/dist/backend/services/return-settlement-service.d.ts.map +1 -0
  169. package/dist/backend/services/return-settlement-service.js +295 -0
  170. package/dist/backend/services/return-settlement-service.js.map +1 -0
  171. package/dist/backend/services/return-shipment-service.d.ts +40 -0
  172. package/dist/backend/services/return-shipment-service.d.ts.map +1 -0
  173. package/dist/backend/services/return-shipment-service.js +113 -0
  174. package/dist/backend/services/return-shipment-service.js.map +1 -0
  175. package/dist/backend/services/return-status-graph-service.d.ts +59 -0
  176. package/dist/backend/services/return-status-graph-service.d.ts.map +1 -0
  177. package/dist/backend/services/return-status-graph-service.js +197 -0
  178. package/dist/backend/services/return-status-graph-service.js.map +1 -0
  179. package/dist/backend/services/return-transition-service.d.ts +48 -0
  180. package/dist/backend/services/return-transition-service.d.ts.map +1 -0
  181. package/dist/backend/services/return-transition-service.js +116 -0
  182. package/dist/backend/services/return-transition-service.js.map +1 -0
  183. package/dist/backend/services/returns-seeder.d.ts +18 -0
  184. package/dist/backend/services/returns-seeder.d.ts.map +1 -0
  185. package/dist/backend/services/returns-seeder.js +63 -0
  186. package/dist/backend/services/returns-seeder.js.map +1 -0
  187. package/dist/backend/services/rma-number-generator.d.ts +35 -0
  188. package/dist/backend/services/rma-number-generator.d.ts.map +1 -0
  189. package/dist/backend/services/rma-number-generator.js +45 -0
  190. package/dist/backend/services/rma-number-generator.js.map +1 -0
  191. package/dist/manifest.d.ts +182 -0
  192. package/dist/manifest.d.ts.map +1 -0
  193. package/dist/manifest.js +216 -0
  194. package/dist/manifest.js.map +1 -0
  195. package/dist/migrations/20260625T144227_returns_init.d.ts +15 -0
  196. package/dist/migrations/20260625T144227_returns_init.d.ts.map +1 -0
  197. package/dist/migrations/20260625T144227_returns_init.js +241 -0
  198. package/dist/migrations/20260625T144227_returns_init.js.map +1 -0
  199. package/dist/migrations/20260817T203206_returns_refund_corrective_invoice_outcome.d.ts +25 -0
  200. package/dist/migrations/20260817T203206_returns_refund_corrective_invoice_outcome.d.ts.map +1 -0
  201. package/dist/migrations/20260817T203206_returns_refund_corrective_invoice_outcome.js +46 -0
  202. package/dist/migrations/20260817T203206_returns_refund_corrective_invoice_outcome.js.map +1 -0
  203. package/dist/migrations/index.d.ts +28 -0
  204. package/dist/migrations/index.d.ts.map +1 -0
  205. package/dist/migrations/index.js +31 -0
  206. package/dist/migrations/index.js.map +1 -0
  207. package/docs/returns.md +103 -0
  208. package/i18n/en.json +28 -0
  209. package/i18n/pl.json +28 -0
  210. package/package.json +101 -0
  211. 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":"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"}
@@ -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"}