@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,260 @@
1
+ import { ERROR_CODES } from '@endora-commerce/contracts';
2
+ import { HttpError } from '@endora-commerce/platform/http';
3
+ import { recordAuditFromContext } from '@endora-commerce/platform/commands';
4
+ import { ReturnCase } from '../entities/return-case.entity.js';
5
+ import { ReturnCaseItem } from '../entities/return-case-item.entity.js';
6
+ import { ReturnCaseComment } from '../entities/return-case-comment.entity.js';
7
+ import { ReturnCaseAttachment } from '../entities/return-case-attachment.entity.js';
8
+ import { ReturnReason } from '../entities/return-reason.entity.js';
9
+ import { Refund } from '../entities/refund.entity.js';
10
+ import { RETURN_STATUS_CANCELLED } from '../domain/return-status-graph.js';
11
+ import { isWithinFreeWindow } from '../domain/free-return-window.js';
12
+ import { defaultRefundForQuantity } from '../domain/refund-math.js';
13
+ /** Statuses whose cases do NOT consume returnable quantity (FR-003). */
14
+ const VOID_STATUS_CODES = new Set(['rejected', 'cancelled']);
15
+ /**
16
+ * ReturnCaseService — feature 046 (US1).
17
+ *
18
+ * Eligibility evaluation, remaining-returnable computation, and case creation in
19
+ * the initial workflow status. Reads order facts only through the
20
+ * `OrderReturnContextPort` (Principle I).
21
+ */
22
+ export class ReturnCaseService {
23
+ deps;
24
+ constructor(deps) {
25
+ this.deps = deps;
26
+ }
27
+ now() {
28
+ return this.deps.now ? this.deps.now() : new Date();
29
+ }
30
+ /** Lines a customer may still return for an order, with the eligibility verdict. */
31
+ async getReturnable(orderId, customerAccountId) {
32
+ const ctx = await this.deps.orderContext.getReturnContext(orderId);
33
+ if (!ctx || ctx.customerAccountId !== customerAccountId) {
34
+ throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Order not found.');
35
+ }
36
+ const consumed = await this.consumedQuantities(orderId);
37
+ const lines = ctx.lines.map((l) => ({
38
+ orderItemId: l.orderItemId,
39
+ productId: l.productId,
40
+ name: l.name,
41
+ purchasedQty: l.purchasedQty,
42
+ remainingReturnableQty: Math.max(0, l.purchasedQty - (consumed.get(l.orderItemId) ?? 0)),
43
+ paidUnitAmount: l.paidUnitAmount,
44
+ currency: ctx.currency,
45
+ }));
46
+ const freeReturnEligible = await this.isWithinFreeWindow(ctx.salesChannelId, ctx.completingStatusEnteredAt);
47
+ if (ctx.completingStatusEnteredAt === null) {
48
+ return { eligible: false, reason: 'order_not_completing', freeReturnEligible: false, lines };
49
+ }
50
+ if (lines.every((l) => l.remainingReturnableQty === 0)) {
51
+ return { eligible: false, reason: 'fully_returned', freeReturnEligible, lines };
52
+ }
53
+ return { eligible: true, freeReturnEligible, lines };
54
+ }
55
+ /** Open a return/complaint case (FR-001/002/003/005). */
56
+ async createCase(input, customerAccountId) {
57
+ const ctx = await this.deps.orderContext.getReturnContext(input.orderId);
58
+ if (!ctx || ctx.customerAccountId !== customerAccountId) {
59
+ throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Order not found.');
60
+ }
61
+ if (ctx.completingStatusEnteredAt === null) {
62
+ throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, 'This order is not yet eligible for returns.', { code: 'order_not_completing' });
63
+ }
64
+ const byOrderItem = new Map(ctx.lines.map((l) => [l.orderItemId, l]));
65
+ const consumed = await this.consumedQuantities(input.orderId);
66
+ const reasons = new Map((await this.deps.emFactory().find(ReturnReason, { isActive: true })).map((r) => [r.id, r]));
67
+ for (const line of input.lines) {
68
+ const ctxLine = byOrderItem.get(line.orderItemId);
69
+ if (!ctxLine) {
70
+ throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, 'Unknown order line.', {
71
+ code: 'unknown_line',
72
+ });
73
+ }
74
+ const remaining = ctxLine.purchasedQty - (consumed.get(line.orderItemId) ?? 0);
75
+ if (line.quantity > remaining) {
76
+ throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, `Requested quantity (${line.quantity}) exceeds the returnable quantity (${remaining}).`, { code: 'qty_exceeds_returnable' });
77
+ }
78
+ if (!reasons.has(line.reasonId)) {
79
+ throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, 'Unknown or inactive reason.', {
80
+ code: 'unknown_reason',
81
+ });
82
+ }
83
+ }
84
+ const em = this.deps.emFactory();
85
+ const graph = await this.deps.graphService.loadGraph();
86
+ const now = this.now();
87
+ const freeReturnEligible = input.kind === 'return'
88
+ ? await this.isWithinFreeWindow(ctx.salesChannelId, ctx.completingStatusEnteredAt)
89
+ : false;
90
+ const rc = em.create(ReturnCase, {
91
+ kind: input.kind,
92
+ orderId: input.orderId,
93
+ salesChannelId: ctx.salesChannelId,
94
+ customerAccountId,
95
+ organizationId: ctx.organizationId,
96
+ statusCode: graph.initialCode(),
97
+ currency: ctx.currency,
98
+ freeReturnEligible,
99
+ submittedAt: now,
100
+ });
101
+ em.persist(rc);
102
+ for (const line of input.lines) {
103
+ const ctxLine = byOrderItem.get(line.orderItemId);
104
+ const defaultRefund = defaultRefundForQuantity(ctxLine.paidUnitAmount, line.quantity);
105
+ em.persist(em.create(ReturnCaseItem, {
106
+ returnCaseId: rc.id,
107
+ orderItemId: line.orderItemId,
108
+ productId: ctxLine.productId,
109
+ productName: ctxLine.name,
110
+ quantity: line.quantity,
111
+ reasonId: line.reasonId,
112
+ description: line.description ?? null,
113
+ defaultRefundAmount: defaultRefund.toFixed(2),
114
+ approvedRefundAmount: defaultRefund.toFixed(2),
115
+ }));
116
+ }
117
+ for (const assetId of input.attachmentAssetIds ?? []) {
118
+ em.persist(em.create(ReturnCaseAttachment, { returnCaseId: rc.id, assetId }));
119
+ }
120
+ if (input.comment) {
121
+ em.persist(em.create(ReturnCaseComment, {
122
+ returnCaseId: rc.id,
123
+ authorCustomerAccountId: customerAccountId,
124
+ body: input.comment,
125
+ isCustomerVisible: true,
126
+ notifyCustomer: false,
127
+ }));
128
+ }
129
+ if (this.deps.auditLog) {
130
+ recordAuditFromContext(this.deps.auditLog, em, {
131
+ action: 'return_case.create',
132
+ objectType: 'return_case',
133
+ objectId: rc.id,
134
+ stateBefore: null,
135
+ stateAfter: {
136
+ kind: rc.kind,
137
+ orderId: rc.orderId,
138
+ statusCode: rc.statusCode,
139
+ lineCount: input.lines.length,
140
+ },
141
+ });
142
+ }
143
+ await em.flush();
144
+ return this.mapDetail(em, rc.id, { customerView: true });
145
+ }
146
+ async listForCustomer(customerAccountId) {
147
+ const em = this.deps.emFactory();
148
+ const cases = await em.find(ReturnCase, { customerAccountId }, { orderBy: { submittedAt: 'desc' } });
149
+ const graph = await this.deps.graphService.loadGraph();
150
+ return cases.map((rc) => this.toSummary(rc, graph.get(rc.statusCode)?.defaultName ?? rc.statusCode));
151
+ }
152
+ async getForCustomer(id, customerAccountId) {
153
+ const em = this.deps.emFactory();
154
+ const rc = await em.findOne(ReturnCase, { id, customerAccountId });
155
+ if (!rc)
156
+ throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Return case not found.');
157
+ return this.mapDetail(em, rc.id, { customerView: true });
158
+ }
159
+ /** Customer withdraws their own case (if the workflow permits the transition). */
160
+ async cancelByCustomer(id, customerAccountId) {
161
+ const em = this.deps.emFactory();
162
+ const rc = await em.findOne(ReturnCase, { id, customerAccountId });
163
+ if (!rc)
164
+ throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Return case not found.');
165
+ await this.deps.transitions.apply(id, RETURN_STATUS_CANCELLED, { kind: 'customer', customerAccountId });
166
+ return this.mapDetail(this.deps.emFactory(), id, { customerView: true });
167
+ }
168
+ /** Detail for the admin surface (includes internal comments). */
169
+ async getByIdForAdmin(id) {
170
+ const em = this.deps.emFactory();
171
+ const rc = await em.findOne(ReturnCase, { id });
172
+ if (!rc)
173
+ throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Return case not found.');
174
+ return this.mapDetail(em, rc.id, { customerView: false });
175
+ }
176
+ // --- internals ----------------------------------------------------------
177
+ async consumedQuantities(orderId) {
178
+ const em = this.deps.emFactory();
179
+ const cases = await em.find(ReturnCase, { orderId });
180
+ const liveCaseIds = cases.filter((c) => !VOID_STATUS_CODES.has(c.statusCode)).map((c) => c.id);
181
+ const out = new Map();
182
+ if (liveCaseIds.length === 0)
183
+ return out;
184
+ const items = await em.find(ReturnCaseItem, { returnCaseId: { $in: liveCaseIds } });
185
+ for (const it of items) {
186
+ out.set(it.orderItemId, (out.get(it.orderItemId) ?? 0) + it.quantity);
187
+ }
188
+ return out;
189
+ }
190
+ async isWithinFreeWindow(salesChannelId, completingAt) {
191
+ const days = await this.deps.resolveFreeReturnDays(salesChannelId);
192
+ return isWithinFreeWindow(this.now(), completingAt, days);
193
+ }
194
+ toSummary(rc, statusLabel) {
195
+ return {
196
+ id: rc.id,
197
+ rmaNumber: rc.rmaNumber ?? null,
198
+ kind: rc.kind,
199
+ orderId: rc.orderId,
200
+ statusCode: rc.statusCode,
201
+ statusLabel,
202
+ totalRefundAmount: Number(rc.totalRefundAmount),
203
+ currency: rc.currency,
204
+ submittedAt: rc.submittedAt.toISOString(),
205
+ };
206
+ }
207
+ async mapDetail(em, id, opts) {
208
+ const rc = await em.findOneOrFail(ReturnCase, { id });
209
+ const [items, comments, graph, refund] = await Promise.all([
210
+ em.find(ReturnCaseItem, { returnCaseId: id }),
211
+ em.find(ReturnCaseComment, { returnCaseId: id }, { orderBy: { createdAt: 'asc' } }),
212
+ this.deps.graphService.loadGraph(),
213
+ // D-92 — the settled case's corrective-invoice answer, which used to live
214
+ // only in the settlement response and the audit entry. A reloaded screen
215
+ // reads it here.
216
+ em.findOne(Refund, { returnCaseId: id }),
217
+ ]);
218
+ const visibleComments = comments.filter((c) => !opts.customerView || c.isCustomerVisible);
219
+ return {
220
+ ...this.toSummary(rc, graph.get(rc.statusCode)?.defaultName ?? rc.statusCode),
221
+ salesChannelId: rc.salesChannelId,
222
+ customerAccountId: rc.customerAccountId,
223
+ organizationId: rc.organizationId ?? null,
224
+ returnDeliveryMethodId: rc.returnDeliveryMethodId ?? null,
225
+ appliedReturnCost: Number(rc.appliedReturnCost),
226
+ returnCostBearer: rc.returnCostBearer,
227
+ freeReturnEligible: rc.freeReturnEligible,
228
+ resolutionType: rc.resolutionType ?? null,
229
+ rejectionReason: rc.rejectionReason ?? null,
230
+ items: items.map((it) => ({
231
+ id: it.id,
232
+ orderItemId: it.orderItemId,
233
+ productId: it.productId,
234
+ productName: it.productName,
235
+ quantity: it.quantity,
236
+ reasonId: it.reasonId ?? null,
237
+ description: it.description ?? null,
238
+ inspectionOutcome: it.inspectionOutcome ?? null,
239
+ defaultRefundAmount: Number(it.defaultRefundAmount),
240
+ approvedRefundAmount: Number(it.approvedRefundAmount),
241
+ })),
242
+ comments: visibleComments.map((c) => ({
243
+ id: c.id,
244
+ authorKind: c.authorAdminUserId ? 'admin' : 'customer',
245
+ body: c.body,
246
+ isCustomerVisible: c.isCustomerVisible,
247
+ createdAt: c.createdAt.toISOString(),
248
+ })),
249
+ // Null while the case has no refund row: never settled, or settled as a
250
+ // replacement or repair, which corrects no document at all.
251
+ correctiveInvoice: refund
252
+ ? {
253
+ outcome: refund.correctiveInvoiceOutcome,
254
+ invoiceId: refund.correctiveInvoiceId ?? null,
255
+ }
256
+ : null,
257
+ };
258
+ }
259
+ }
260
+ //# sourceMappingURL=return-case-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"return-case-service.js","sourceRoot":"","sources":["../../../src/backend/services/return-case-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import type { ReturnCaseCommentDto } from '@endora-commerce/contracts';
3
+ import type { ReturnStatusGraphService } from './return-status-graph-service.js';
4
+ export interface ReturnCommentServiceDeps {
5
+ emFactory: () => EntityManager;
6
+ graphService: ReturnStatusGraphService;
7
+ }
8
+ /**
9
+ * ReturnCommentService — feature 046 (US4).
10
+ *
11
+ * Threaded comments on a case. Admin comments carry a visibility choice and a
12
+ * notify flag; customer comments are always visible. Commenting is blocked once
13
+ * the case reaches a terminal status. Mirrors `OrderCommentService`.
14
+ */
15
+ export declare class ReturnCommentService {
16
+ private readonly deps;
17
+ constructor(deps: ReturnCommentServiceDeps);
18
+ addByAdmin(id: string, adminUserId: string, input: {
19
+ body: string;
20
+ isCustomerVisible: boolean;
21
+ notifyCustomer: boolean;
22
+ }): Promise<ReturnCaseCommentDto>;
23
+ addByCustomer(id: string, customerAccountId: string, input: {
24
+ body: string;
25
+ }): Promise<ReturnCaseCommentDto>;
26
+ /**
27
+ * The whole thread, internal comments included, for the admin panel.
28
+ *
29
+ * The case read is the tenant boundary and not a formality: `ReturnCaseComment`
30
+ * is `@GlobalEntity`, so `em.find(ReturnCaseComment, { returnCaseId })` carries
31
+ * no filter and answers for every case on the platform, while `ReturnCase` is
32
+ * `@OrgScoped`. `listForCustomer` below has always keyed on the case *and* its
33
+ * customer; this one keyed on nothing, and every comment on this thread is one
34
+ * an operator wrote believing only their own colleagues would read it.
35
+ *
36
+ * `loadOpenCase` is the wrong helper here — it also refuses a closed case,
37
+ * which a reader is entitled to see. Same read, no lifecycle term.
38
+ */
39
+ listForAdmin(id: string): Promise<ReturnCaseCommentDto[]>;
40
+ listForCustomer(id: string, customerAccountId: string): Promise<ReturnCaseCommentDto[]>;
41
+ private loadOpenCase;
42
+ }
43
+ //# sourceMappingURL=return-comment-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"return-comment-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/return-comment-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAIvE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAEjF,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B,YAAY,EAAE,wBAAwB,CAAC;CACxC;AAED;;;;;;GAMG;AACH,qBAAa,oBAAoB;IACnB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,wBAAwB;IAErD,UAAU,CACd,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,OAAO,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,GAC3E,OAAO,CAAC,oBAAoB,CAAC;IA0B1B,aAAa,CACjB,EAAE,EAAE,MAAM,EACV,iBAAiB,EAAE,MAAM,EACzB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GACtB,OAAO,CAAC,oBAAoB,CAAC;IAiBhC;;;;;;;;;;;;OAYG;IACG,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAYzD,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;YAY/E,YAAY;CAc3B"}
@@ -0,0 +1,109 @@
1
+ import { ERROR_CODES } from '@endora-commerce/contracts';
2
+ import { HttpError } from '@endora-commerce/platform/http';
3
+ import { ReturnCase } from '../entities/return-case.entity.js';
4
+ import { ReturnCaseComment } from '../entities/return-case-comment.entity.js';
5
+ /**
6
+ * ReturnCommentService — feature 046 (US4).
7
+ *
8
+ * Threaded comments on a case. Admin comments carry a visibility choice and a
9
+ * notify flag; customer comments are always visible. Commenting is blocked once
10
+ * the case reaches a terminal status. Mirrors `OrderCommentService`.
11
+ */
12
+ export class ReturnCommentService {
13
+ deps;
14
+ constructor(deps) {
15
+ this.deps = deps;
16
+ }
17
+ async addByAdmin(id, adminUserId, input) {
18
+ // command-coverage-ignore: return-case comments are an append-only
19
+ // communication thread, not audited domain-state (no before-state, no undo).
20
+ const em = this.deps.emFactory();
21
+ const rc = await this.loadOpenCase(em, { id });
22
+ const comment = em.create(ReturnCaseComment, {
23
+ returnCaseId: rc.id,
24
+ authorAdminUserId: adminUserId,
25
+ body: input.body,
26
+ isCustomerVisible: input.isCustomerVisible,
27
+ notifyCustomer: input.notifyCustomer,
28
+ });
29
+ em.persist(comment);
30
+ await em.flush();
31
+ // `notifyCustomer` is recorded on the row and nothing sends on it. The
32
+ // optional `notifier` that used to stand here went with
33
+ // `specs/110-instance-repository/` T118c: it was declared, guarded and
34
+ // supplied by nobody — `plugin.ts` builds this service with `emFactory` and
35
+ // `graphService` and no third member — so its `catch` was a tolerance around
36
+ // an expression that could not run. This module's own `exposeServices` was
37
+ // deleted on the same argument in feature 072. Sending on a comment is a
38
+ // product decision; when it is taken, it takes a notifier the composition
39
+ // really supplies and an off-state answer of its own.
40
+ return toDto(comment);
41
+ }
42
+ async addByCustomer(id, customerAccountId, input) {
43
+ // command-coverage-ignore: return-case comments are an append-only
44
+ // communication thread, not audited domain-state (no before-state, no undo).
45
+ const em = this.deps.emFactory();
46
+ const rc = await this.loadOpenCase(em, { id, customerAccountId });
47
+ const comment = em.create(ReturnCaseComment, {
48
+ returnCaseId: rc.id,
49
+ authorCustomerAccountId: customerAccountId,
50
+ body: input.body,
51
+ isCustomerVisible: true,
52
+ notifyCustomer: false,
53
+ });
54
+ em.persist(comment);
55
+ await em.flush();
56
+ return toDto(comment);
57
+ }
58
+ /**
59
+ * The whole thread, internal comments included, for the admin panel.
60
+ *
61
+ * The case read is the tenant boundary and not a formality: `ReturnCaseComment`
62
+ * is `@GlobalEntity`, so `em.find(ReturnCaseComment, { returnCaseId })` carries
63
+ * no filter and answers for every case on the platform, while `ReturnCase` is
64
+ * `@OrgScoped`. `listForCustomer` below has always keyed on the case *and* its
65
+ * customer; this one keyed on nothing, and every comment on this thread is one
66
+ * an operator wrote believing only their own colleagues would read it.
67
+ *
68
+ * `loadOpenCase` is the wrong helper here — it also refuses a closed case,
69
+ * which a reader is entitled to see. Same read, no lifecycle term.
70
+ */
71
+ async listForAdmin(id) {
72
+ const em = this.deps.emFactory();
73
+ const rc = await em.findOne(ReturnCase, { id });
74
+ if (!rc)
75
+ throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Return case not found.');
76
+ const comments = await em.find(ReturnCaseComment, { returnCaseId: id }, { orderBy: { createdAt: 'asc' } });
77
+ return comments.map(toDto);
78
+ }
79
+ async listForCustomer(id, customerAccountId) {
80
+ const em = this.deps.emFactory();
81
+ const rc = await em.findOne(ReturnCase, { id, customerAccountId });
82
+ if (!rc)
83
+ throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Return case not found.');
84
+ const comments = await em.find(ReturnCaseComment, { returnCaseId: id, isCustomerVisible: true }, { orderBy: { createdAt: 'asc' } });
85
+ return comments.map(toDto);
86
+ }
87
+ async loadOpenCase(em, where) {
88
+ const rc = await em.findOne(ReturnCase, where);
89
+ if (!rc)
90
+ throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Return case not found.');
91
+ const graph = await this.deps.graphService.loadGraph();
92
+ if (graph.isTerminal(rc.statusCode)) {
93
+ throw new HttpError(409, ERROR_CODES.VALIDATION_FAILED, 'Commenting is closed for finished cases.', {
94
+ code: 'case_terminal',
95
+ });
96
+ }
97
+ return rc;
98
+ }
99
+ }
100
+ function toDto(c) {
101
+ return {
102
+ id: c.id,
103
+ authorKind: c.authorAdminUserId ? 'admin' : 'customer',
104
+ body: c.body,
105
+ isCustomerVisible: c.isCustomerVisible,
106
+ createdAt: c.createdAt.toISOString(),
107
+ };
108
+ }
109
+ //# sourceMappingURL=return-comment-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"return-comment-service.js","sourceRoot":"","sources":["../../../src/backend/services/return-comment-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import type { ReturnDeliveryMethodDto } from '@endora-commerce/contracts';
3
+ import type { AuditPort } from '@endora-commerce/platform/kernel';
4
+ import type { ReturnStatusGraphService } from './return-status-graph-service.js';
5
+ export interface ReturnDeliveryMethodServiceDeps {
6
+ emFactory: () => EntityManager;
7
+ graphService: ReturnStatusGraphService;
8
+ /** `'customer'` | `'shop'` default bearer once the free-return window passes. */
9
+ resolveDefaultCostBearer: (salesChannelId: string) => Promise<'customer' | 'shop'>;
10
+ /** Feature 054 — audits method writes co-transactionally when provided. */
11
+ auditLog?: AuditPort;
12
+ }
13
+ /**
14
+ * ReturnDeliveryMethodService — feature 046 (US6).
15
+ *
16
+ * CRUD for the allowed return delivery methods (with per-method return cost,
17
+ * 0 = free/shop-paid) and selection of a method on a case, which resolves the
18
+ * applied cost and the cost bearer from the free-return window (FR-021/022/023).
19
+ */
20
+ export declare class ReturnDeliveryMethodService {
21
+ #private;
22
+ private readonly deps;
23
+ constructor(deps: ReturnDeliveryMethodServiceDeps);
24
+ list(): Promise<ReturnDeliveryMethodDto[]>;
25
+ create(input: {
26
+ deliveryMethodId: string;
27
+ returnCost: number;
28
+ currency: string;
29
+ isActive?: boolean | undefined;
30
+ }): Promise<ReturnDeliveryMethodDto>;
31
+ update(id: string, patch: {
32
+ returnCost?: number | undefined;
33
+ currency?: string | undefined;
34
+ isActive?: boolean | undefined;
35
+ }): Promise<ReturnDeliveryMethodDto>;
36
+ remove(id: string): Promise<void>;
37
+ /** Apply an allowed return delivery method to a case and resolve cost + bearer. */
38
+ selectForCase(caseId: string, customerAccountId: string, returnDeliveryMethodId: string): Promise<{
39
+ appliedReturnCost: number;
40
+ returnCostBearer: 'customer' | 'shop';
41
+ currency: string;
42
+ }>;
43
+ }
44
+ //# sourceMappingURL=return-delivery-method-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"return-delivery-method-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/return-delivery-method-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAG1E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAGlE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAEjF,MAAM,WAAW,+BAA+B;IAC9C,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B,YAAY,EAAE,wBAAwB,CAAC;IACvC,iFAAiF;IACjF,wBAAwB,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,GAAG,MAAM,CAAC,CAAC;IACnF,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;GAMG;AACH,qBAAa,2BAA2B;;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,+BAA+B;IAe5D,IAAI,IAAI,OAAO,CAAC,uBAAuB,EAAE,CAAC;IAM1C,MAAM,CAAC,KAAK,EAAE;QAClB,gBAAgB,EAAE,MAAM,CAAC;QACzB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;KAChC,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAkB9B,MAAM,CACV,EAAE,EAAE,MAAM,EACV,KAAK,EAAE;QACL,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAChC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC9B,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;KAChC,GACA,OAAO,CAAC,uBAAuB,CAAC;IAgB7B,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUvC,mFAAmF;IAC7E,aAAa,CACjB,MAAM,EAAE,MAAM,EACd,iBAAiB,EAAE,MAAM,EACzB,sBAAsB,EAAE,MAAM,GAC7B,OAAO,CAAC;QAAE,iBAAiB,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,UAAU,GAAG,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CA6BnG"}
@@ -0,0 +1,114 @@
1
+ import { ERROR_CODES } from '@endora-commerce/contracts';
2
+ import { HttpError } from '@endora-commerce/platform/http';
3
+ import { recordAuditFromContext } from '@endora-commerce/platform/commands';
4
+ import { ReturnCase } from '../entities/return-case.entity.js';
5
+ import { ReturnDeliveryMethod } from '../entities/return-delivery-method.entity.js';
6
+ /**
7
+ * ReturnDeliveryMethodService — feature 046 (US6).
8
+ *
9
+ * CRUD for the allowed return delivery methods (with per-method return cost,
10
+ * 0 = free/shop-paid) and selection of a method on a case, which resolves the
11
+ * applied cost and the cost bearer from the free-return window (FR-021/022/023).
12
+ */
13
+ export class ReturnDeliveryMethodService {
14
+ deps;
15
+ constructor(deps) {
16
+ this.deps = deps;
17
+ }
18
+ #audit(em, action, objectType, objectId, stateBefore, stateAfter) {
19
+ if (this.deps.auditLog) {
20
+ recordAuditFromContext(this.deps.auditLog, em, { action, objectType, objectId, stateBefore, stateAfter });
21
+ }
22
+ }
23
+ async list() {
24
+ const em = this.deps.emFactory();
25
+ const rows = await em.find(ReturnDeliveryMethod, {}, { orderBy: { createdAt: 'asc' } });
26
+ return rows.map(toDto);
27
+ }
28
+ async create(input) {
29
+ const em = this.deps.emFactory();
30
+ const row = em.create(ReturnDeliveryMethod, {
31
+ deliveryMethodId: input.deliveryMethodId,
32
+ returnCost: input.returnCost.toFixed(2),
33
+ currency: input.currency,
34
+ isActive: input.isActive ?? true,
35
+ });
36
+ em.persist(row);
37
+ this.#audit(em, 'return_delivery_method.create', 'return_delivery_method', row.id, null, {
38
+ deliveryMethodId: row.deliveryMethodId,
39
+ returnCost: row.returnCost,
40
+ currency: row.currency,
41
+ });
42
+ await em.flush();
43
+ return toDto(row);
44
+ }
45
+ async update(id, patch) {
46
+ const em = this.deps.emFactory();
47
+ const row = await em.findOne(ReturnDeliveryMethod, { id });
48
+ if (!row)
49
+ throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Return delivery method not found.');
50
+ if (patch.returnCost !== undefined)
51
+ row.returnCost = patch.returnCost.toFixed(2);
52
+ if (patch.currency !== undefined)
53
+ row.currency = patch.currency;
54
+ if (patch.isActive !== undefined)
55
+ row.isActive = patch.isActive;
56
+ this.#audit(em, 'return_delivery_method.update', 'return_delivery_method', row.id, null, {
57
+ returnCost: row.returnCost,
58
+ currency: row.currency,
59
+ isActive: row.isActive,
60
+ });
61
+ await em.flush();
62
+ return toDto(row);
63
+ }
64
+ async remove(id) {
65
+ const em = this.deps.emFactory();
66
+ const row = await em.findOne(ReturnDeliveryMethod, { id });
67
+ if (!row)
68
+ throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Return delivery method not found.');
69
+ this.#audit(em, 'return_delivery_method.delete', 'return_delivery_method', row.id, {
70
+ deliveryMethodId: row.deliveryMethodId,
71
+ }, null);
72
+ await em.removeAndFlush(row);
73
+ }
74
+ /** Apply an allowed return delivery method to a case and resolve cost + bearer. */
75
+ async selectForCase(caseId, customerAccountId, returnDeliveryMethodId) {
76
+ const em = this.deps.emFactory();
77
+ const rc = await em.findOne(ReturnCase, { id: caseId, customerAccountId });
78
+ if (!rc)
79
+ throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Return case not found.');
80
+ const graph = await this.deps.graphService.loadGraph();
81
+ if (graph.isTerminal(rc.statusCode)) {
82
+ throw new HttpError(409, ERROR_CODES.VALIDATION_FAILED, 'The case is closed.', { code: 'case_terminal' });
83
+ }
84
+ const method = await em.findOne(ReturnDeliveryMethod, { id: returnDeliveryMethodId, isActive: true });
85
+ if (!method) {
86
+ throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, 'Return delivery method is not available.', {
87
+ code: 'method_not_allowed',
88
+ });
89
+ }
90
+ const bearer = rc.freeReturnEligible
91
+ ? 'shop'
92
+ : await this.deps.resolveDefaultCostBearer(rc.salesChannelId);
93
+ rc.returnDeliveryMethodId = method.id;
94
+ rc.appliedReturnCost = method.returnCost;
95
+ rc.returnCostBearer = bearer;
96
+ this.#audit(em, 'return_case.select_delivery_method', 'return_case', rc.id, null, {
97
+ returnDeliveryMethodId: method.id,
98
+ appliedReturnCost: method.returnCost,
99
+ returnCostBearer: bearer,
100
+ });
101
+ await em.flush();
102
+ return { appliedReturnCost: Number(method.returnCost), returnCostBearer: bearer, currency: method.currency };
103
+ }
104
+ }
105
+ function toDto(r) {
106
+ return {
107
+ id: r.id,
108
+ deliveryMethodId: r.deliveryMethodId,
109
+ returnCost: Number(r.returnCost),
110
+ currency: r.currency,
111
+ isActive: r.isActive,
112
+ };
113
+ }
114
+ //# sourceMappingURL=return-delivery-method-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"return-delivery-method-service.js","sourceRoot":"","sources":["../../../src/backend/services/return-delivery-method-service.ts"],"names":[],"mappings":"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"}