@devx-retailos/cms 0.0.1 → 0.1.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 (30) hide show
  1. package/.medusa/server/src/api/admin/retailos/cms/accumulation/[storeId]/route.js +3 -5
  2. package/.medusa/server/src/api/admin/retailos/cms/export/route.js +2 -2
  3. package/.medusa/server/src/api/admin/retailos/cms/handovers/[id]/route.js +2 -2
  4. package/.medusa/server/src/api/admin/retailos/cms/handovers/route.js +72 -7
  5. package/.medusa/server/src/api/admin/retailos/cms/petty-cash/route.js +35 -4
  6. package/.medusa/server/src/api/admin/retailos/cms/reconciliation/route.js +42 -0
  7. package/.medusa/server/src/modules/cms/migrations/Migration20260626000000.js +16 -0
  8. package/.medusa/server/src/modules/cms/models/cms-handover.js +3 -1
  9. package/.medusa/server/src/modules/cms/permissions.js +6 -1
  10. package/.medusa/server/src/modules/cms/services/cms-module-service.js +137 -42
  11. package/README.md +116 -0
  12. package/package.json +3 -3
  13. package/src/api/admin/retailos/cms/accumulation/[storeId]/__tests__/route.test.ts +27 -9
  14. package/src/api/admin/retailos/cms/accumulation/[storeId]/route.ts +2 -4
  15. package/src/api/admin/retailos/cms/export/__tests__/route.test.ts +9 -9
  16. package/src/api/admin/retailos/cms/export/route.ts +1 -1
  17. package/src/api/admin/retailos/cms/handovers/[id]/__tests__/route.test.ts +5 -5
  18. package/src/api/admin/retailos/cms/handovers/[id]/route.ts +1 -1
  19. package/src/api/admin/retailos/cms/handovers/__tests__/route.test.ts +22 -11
  20. package/src/api/admin/retailos/cms/handovers/route.ts +78 -6
  21. package/src/api/admin/retailos/cms/petty-cash/__tests__/route.test.ts +18 -12
  22. package/src/api/admin/retailos/cms/petty-cash/route.ts +46 -4
  23. package/src/api/admin/retailos/cms/reconciliation/__tests__/route.test.ts +124 -0
  24. package/src/api/admin/retailos/cms/reconciliation/route.ts +47 -0
  25. package/src/modules/cms/__tests__/cms-module-service.test.ts +219 -78
  26. package/src/modules/cms/__tests__/permissions.test.ts +3 -2
  27. package/src/modules/cms/migrations/Migration20260626000000.ts +17 -0
  28. package/src/modules/cms/models/cms-handover.ts +2 -0
  29. package/src/modules/cms/permissions.ts +5 -0
  30. package/src/modules/cms/services/cms-module-service.ts +217 -41
@@ -10,10 +10,10 @@ const cms_accumulation_1 = __importDefault(require("../models/cms-accumulation")
10
10
  const petty_cash_1 = __importDefault(require("../models/petty-cash"));
11
11
  const petty_cash_transaction_1 = __importDefault(require("../models/petty-cash-transaction"));
12
12
  class CmsModuleService extends (0, utils_1.MedusaService)({
13
- CmsHandover: cms_handover_1.default,
14
- CmsAccumulation: cms_accumulation_1.default,
15
- PettyCash: petty_cash_1.default,
16
- PettyCashTransaction: petty_cash_transaction_1.default,
13
+ RetailosCmsHandover: cms_handover_1.default,
14
+ RetailosCmsAccumulation: cms_accumulation_1.default,
15
+ RetailosPettyCash: petty_cash_1.default,
16
+ RetailosPettyCashTransaction: petty_cash_transaction_1.default,
17
17
  }) {
18
18
  constructor(...args) {
19
19
  super(...args);
@@ -26,14 +26,16 @@ class CmsModuleService extends (0, utils_1.MedusaService)({
26
26
  if (opening_amount < 0) {
27
27
  throw new Error("[retailos/cms] opening_amount must be >= 0");
28
28
  }
29
- const existing = await this.listCmsAccumulations({ store_id: [store_id] });
29
+ const existing = await this.listRetailosCmsAccumulations({ store_id: [store_id] });
30
30
  if (existing.length === 0) {
31
- await this.createCmsAccumulations([{ store_id, cash_in_store: opening_amount }]);
31
+ await this.createRetailosCmsAccumulations([{ store_id, cash_in_store: opening_amount }]);
32
32
  }
33
33
  else {
34
- await this.updateCmsAccumulations([{ id: existing[0].id, cash_in_store: opening_amount }]);
34
+ await this.updateRetailosCmsAccumulations([{ id: existing[0].id, cash_in_store: opening_amount }]);
35
35
  }
36
- await this.createPettyCashTransactions([{
36
+ const pettyCashes = await this.listRetailosPettyCashes({ store_id: [store_id] });
37
+ const petty_cash_balance = pettyCashes[0] ? pettyCashes[0].balance : 0;
38
+ await this.createRetailosPettyCashTransactions([{
37
39
  store_id,
38
40
  employee_id,
39
41
  transaction_type: "open",
@@ -49,19 +51,21 @@ class CmsModuleService extends (0, utils_1.MedusaService)({
49
51
  reason: null,
50
52
  image_url: null,
51
53
  date: new Date(),
52
- metadata: null,
54
+ metadata: { petty_cash_balance },
53
55
  }]);
54
- this.logger_.info("cms.day_started", { store_id, employee_id, opening_amount });
56
+ this.logger_.info("cms.day_started", { store_id, employee_id, opening_amount, petty_cash_balance });
55
57
  }
56
58
  async dayEnd(input) {
57
59
  const { store_id, employee_id, closing_amount } = input;
58
60
  if (closing_amount < 0) {
59
61
  throw new Error("[retailos/cms] closing_amount must be >= 0");
60
62
  }
61
- const accumulations = await this.listCmsAccumulations({ store_id: [store_id] });
63
+ const accumulations = await this.listRetailosCmsAccumulations({ store_id: [store_id] });
62
64
  const current_cash = accumulations[0] ? accumulations[0].cash_in_store : 0;
63
65
  const difference = closing_amount - current_cash;
64
- await this.createPettyCashTransactions([{
66
+ const pettyCashes = await this.listRetailosPettyCashes({ store_id: [store_id] });
67
+ const petty_cash_balance = pettyCashes[0] ? pettyCashes[0].balance : 0;
68
+ await this.createRetailosPettyCashTransactions([{
65
69
  store_id,
66
70
  employee_id,
67
71
  transaction_type: "close",
@@ -77,46 +81,51 @@ class CmsModuleService extends (0, utils_1.MedusaService)({
77
81
  reason: null,
78
82
  image_url: null,
79
83
  date: new Date(),
80
- metadata: null,
84
+ metadata: { petty_cash_balance },
81
85
  }]);
82
- this.logger_.info("cms.day_ended", { store_id, employee_id, closing_amount, difference });
86
+ this.logger_.info("cms.day_ended", { store_id, employee_id, closing_amount, difference, petty_cash_balance });
83
87
  }
84
88
  async handover(input) {
85
89
  const { store_id, handover_amount, type } = input;
86
90
  if (handover_amount < 0) {
87
91
  throw new Error("[retailos/cms] handover_amount must be >= 0");
88
92
  }
89
- const [created] = await this.createCmsHandovers([{
93
+ const accumulations = await this.listRetailosCmsAccumulations({ store_id: [store_id] });
94
+ if (accumulations.length === 0) {
95
+ throw new Error("[retailos/cms] Day Start must be completed before creating a handover");
96
+ }
97
+ const opening_balance = accumulations[0].cash_in_store;
98
+ let closing_balance = opening_balance;
99
+ if (type === "CR") {
100
+ closing_balance = opening_balance + handover_amount;
101
+ }
102
+ else if (type === "DB") {
103
+ if (handover_amount > opening_balance) {
104
+ throw new Error(`[retailos/cms] Insufficient cash in store: cannot debit ${handover_amount}, current balance is ${opening_balance}`);
105
+ }
106
+ closing_balance = opening_balance - handover_amount;
107
+ }
108
+ const [created] = await this.createRetailosCmsHandovers([{
90
109
  store_id,
91
110
  employee_id: input.employee_id,
92
111
  handover_id: input.handover_id ?? null,
93
112
  handover_amount,
94
- total_cash: input.total_cash ?? null,
113
+ total_cash: closing_balance,
114
+ opening_balance,
115
+ closing_balance,
95
116
  type: type ?? null,
96
117
  image_url: input.image_url ?? null,
97
118
  remark: input.remark ?? null,
98
119
  metadata: input.metadata ?? null,
99
120
  }]);
100
- const accumulations = await this.listCmsAccumulations({ store_id: [store_id] });
101
- const current = accumulations[0] ? accumulations[0].cash_in_store : 0;
102
- let updated_cash = current;
103
- if (type === "CR") {
104
- updated_cash = current + handover_amount;
105
- }
106
- else if (type === "DB") {
107
- updated_cash = current - handover_amount;
108
- }
109
- if (accumulations.length === 0) {
110
- await this.createCmsAccumulations([{ store_id, cash_in_store: updated_cash }]);
111
- }
112
- else {
113
- await this.updateCmsAccumulations([{ id: accumulations[0].id, cash_in_store: updated_cash }]);
114
- }
121
+ await this.updateRetailosCmsAccumulations([{ id: accumulations[0].id, cash_in_store: closing_balance }]);
115
122
  this.logger_.info("[retailos/cms] handover created", {
116
123
  id: created?.id,
117
124
  store_id,
118
125
  handover_amount,
119
126
  type,
127
+ opening_balance,
128
+ closing_balance,
120
129
  });
121
130
  return created;
122
131
  }
@@ -125,16 +134,31 @@ class CmsModuleService extends (0, utils_1.MedusaService)({
125
134
  if (amount <= 0) {
126
135
  throw new Error("[retailos/cms] amount must be > 0");
127
136
  }
128
- const existing = await this.listPettyCashes({ store_id: [store_id] });
137
+ const existing = await this.listRetailosPettyCashes({ store_id: [store_id] });
129
138
  const current_balance = existing[0] ? existing[0].balance : 0;
130
139
  const new_balance = current_balance + amount;
131
140
  if (existing.length === 0) {
132
- await this.createPettyCashes([{ store_id, balance: new_balance }]);
141
+ await this.createRetailosPettyCashes([{ store_id, balance: new_balance }]);
133
142
  }
134
143
  else {
135
- await this.updatePettyCashes([{ id: existing[0].id, balance: new_balance }]);
144
+ await this.updateRetailosPettyCashes([{ id: existing[0].id, balance: new_balance }]);
145
+ }
146
+ // When funded from store cash, deduct the amount from cms_accumulation
147
+ if (source === "from_petty_cash") {
148
+ const accumulations = await this.listRetailosCmsAccumulations({ store_id: [store_id] });
149
+ const current_store_cash = accumulations[0] ? accumulations[0].cash_in_store : 0;
150
+ const new_store_cash = current_store_cash - amount;
151
+ if (accumulations.length === 0) {
152
+ await this.createRetailosCmsAccumulations([{ store_id, cash_in_store: new_store_cash }]);
153
+ }
154
+ else {
155
+ await this.updateRetailosCmsAccumulations([{ id: accumulations[0].id, cash_in_store: new_store_cash }]);
156
+ }
157
+ this.logger_.info("[retailos/cms] store cash deducted for petty cash transfer", {
158
+ store_id, amount, new_store_cash,
159
+ });
136
160
  }
137
- const [tx] = await this.createPettyCashTransactions([{
161
+ const [tx] = await this.createRetailosPettyCashTransactions([{
138
162
  store_id,
139
163
  employee_id,
140
164
  transaction_type: "petty_cash",
@@ -160,16 +184,16 @@ class CmsModuleService extends (0, utils_1.MedusaService)({
160
184
  if (amount <= 0) {
161
185
  throw new Error("[retailos/cms] expense amount must be > 0");
162
186
  }
163
- const existing = await this.listPettyCashes({ store_id: [store_id] });
187
+ const existing = await this.listRetailosPettyCashes({ store_id: [store_id] });
164
188
  const current_balance = existing[0] ? existing[0].balance : 0;
165
189
  const new_balance = current_balance - amount;
166
190
  if (existing.length === 0) {
167
- await this.createPettyCashes([{ store_id, balance: new_balance }]);
191
+ await this.createRetailosPettyCashes([{ store_id, balance: new_balance }]);
168
192
  }
169
193
  else {
170
- await this.updatePettyCashes([{ id: existing[0].id, balance: new_balance }]);
194
+ await this.updateRetailosPettyCashes([{ id: existing[0].id, balance: new_balance }]);
171
195
  }
172
- const [tx] = await this.createPettyCashTransactions([{
196
+ const [tx] = await this.createRetailosPettyCashTransactions([{
173
197
  store_id,
174
198
  employee_id,
175
199
  transaction_type: "petty_cash",
@@ -191,15 +215,86 @@ class CmsModuleService extends (0, utils_1.MedusaService)({
191
215
  return tx;
192
216
  }
193
217
  async getAccumulation(store_id) {
194
- const rows = await this.listCmsAccumulations({ store_id: [store_id] });
218
+ const rows = await this.listRetailosCmsAccumulations({ store_id: [store_id] });
195
219
  return rows[0] ?? null;
196
220
  }
221
+ async getEnrichedAccumulation(store_id) {
222
+ const accumulation = await this.getAccumulation(store_id);
223
+ const pettyCashes = await this.listRetailosPettyCashes({ store_id: [store_id] });
224
+ const petty_cash = pettyCashes[0] ? pettyCashes[0].balance : 0;
225
+ const now = new Date();
226
+ const dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
227
+ const start = new Date(`${dateStr}T00:00:00.000Z`);
228
+ const end = new Date(`${dateStr}T23:59:59.999Z`);
229
+ const todayTxns = await this.listRetailosPettyCashTransactions({ store_id: [store_id], created_at: { $gte: start, $lte: end } }, { take: 10, order: { created_at: "ASC" } });
230
+ const txnList = todayTxns;
231
+ const openTx = txnList.find((t) => t.transaction_type === "open");
232
+ const closeTx = txnList.find((t) => t.transaction_type === "close");
233
+ return {
234
+ ...(accumulation ? accumulation : { store_id, cash_in_store: 0 }),
235
+ petty_cash,
236
+ opening_store_cash: openTx ? openTx.amount : null,
237
+ opening_petty_cash: openTx?.metadata?.petty_cash_balance ?? null,
238
+ closing_store_cash: closeTx ? closeTx.amount : null,
239
+ closing_petty_cash: closeTx?.metadata?.petty_cash_balance ?? null,
240
+ };
241
+ }
242
+ async getReconciliationReport(input) {
243
+ const { store_id, date, net_cash_sales = 0 } = input;
244
+ const start = new Date(`${date}T00:00:00.000Z`);
245
+ const end = new Date(`${date}T23:59:59.999Z`);
246
+ const dateFilter = { $gte: start, $lte: end };
247
+ const txns = await this.listRetailosPettyCashTransactions({ store_id: [store_id], created_at: dateFilter }, { take: 1000, order: { created_at: "ASC" } });
248
+ const handovers = await this.listRetailosCmsHandovers({ store_id: [store_id], created_at: dateFilter }, { take: 1000, order: { created_at: "ASC" } });
249
+ const accumulation = await this.getAccumulation(store_id);
250
+ const txnList = txns;
251
+ const handoverList = handovers;
252
+ const openTx = txnList.find((t) => t.transaction_type === "open");
253
+ const closeTx = txnList.find((t) => t.transaction_type === "close");
254
+ const opening_balance = openTx ? openTx.amount : 0;
255
+ const closing_balance = closeTx ? closeTx.amount : null;
256
+ const pettyCashTxns = txnList.filter((t) => t.transaction_type === "petty_cash");
257
+ const petty_cash_in = pettyCashTxns
258
+ .filter((t) => t.entry_type === "CR")
259
+ .reduce((sum, t) => sum + t.amount, 0);
260
+ const petty_cash_out = pettyCashTxns
261
+ .filter((t) => t.entry_type === "DB")
262
+ .reduce((sum, t) => sum + t.amount, 0);
263
+ const total_handovers_in = handoverList
264
+ .filter((h) => h.type === "CR")
265
+ .reduce((sum, h) => sum + h.handover_amount, 0);
266
+ const total_handovers_out = handoverList
267
+ .filter((h) => h.type === "DB")
268
+ .reduce((sum, h) => sum + h.handover_amount, 0);
269
+ const expected_closing_balance = opening_balance + net_cash_sales + petty_cash_in - petty_cash_out + total_handovers_in - total_handovers_out;
270
+ const variance = closing_balance !== null ? closing_balance - expected_closing_balance : null;
271
+ const is_balanced = variance !== null ? variance === 0 : null;
272
+ const shift_logs = txnList.filter((t) => t.transaction_type === "open" || t.transaction_type === "close");
273
+ this.logger_.info("[retailos/cms] reconciliation report generated", { store_id, date });
274
+ return {
275
+ date,
276
+ store_id,
277
+ opening_balance,
278
+ net_cash_sales,
279
+ total_handovers_in,
280
+ total_handovers_out,
281
+ petty_cash_in,
282
+ petty_cash_out,
283
+ expected_closing_balance,
284
+ closing_balance,
285
+ cms_balance: accumulation ? accumulation.cash_in_store : null,
286
+ variance,
287
+ is_balanced,
288
+ handovers: handoverList,
289
+ shift_logs,
290
+ };
291
+ }
197
292
  async getShiftLogs(filters) {
198
293
  const dbFilters = {};
199
294
  if (filters.store_id)
200
295
  dbFilters.store_id = [filters.store_id];
201
296
  dbFilters.transaction_type = ["open", "close"];
202
- const rows = await this.listPettyCashTransactions(dbFilters, {
297
+ const rows = await this.listRetailosPettyCashTransactions(dbFilters, {
203
298
  take: filters.limit ?? 100,
204
299
  order: { created_at: "DESC" },
205
300
  });
@@ -207,4 +302,4 @@ class CmsModuleService extends (0, utils_1.MedusaService)({
207
302
  }
208
303
  }
209
304
  exports.default = CmsModuleService;
210
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY21zLW1vZHVsZS1zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL21vZHVsZXMvY21zL3NlcnZpY2VzL2Ntcy1tb2R1bGUtc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLHFEQUF5RDtBQUN6RCw4Q0FBbUU7QUFDbkUsMEVBQWdEO0FBQ2hELGtGQUF3RDtBQUN4RCxzRUFBNEM7QUFDNUMsOEZBQW1FO0FBd0RuRSxNQUFNLGdCQUFpQixTQUFRLElBQUEscUJBQWEsRUFBQztJQUMzQyxXQUFXLEVBQVgsc0JBQVc7SUFDWCxlQUFlLEVBQWYsMEJBQWU7SUFDZixTQUFTLEVBQVQsb0JBQVM7SUFDVCxvQkFBb0IsRUFBcEIsZ0NBQW9CO0NBQ3JCLENBQUM7SUFHQSxZQUFZLEdBQUcsSUFBVztRQUN4QixLQUFLLENBQUMsR0FBRyxJQUFJLENBQUMsQ0FBQTtRQUNkLE1BQU0sU0FBUyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBd0IsQ0FBQTtRQUN4RCxJQUFJLENBQUMsT0FBTyxHQUFHLFNBQVMsQ0FBQyxNQUFNLElBQUksSUFBQSx1QkFBZ0IsR0FBRSxDQUFBO1FBQ3JELElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLG9DQUFvQyxDQUFDLENBQUE7SUFDMUQsQ0FBQztJQUVELEtBQUssQ0FBQyxRQUFRLENBQUMsS0FBb0I7UUFDakMsTUFBTSxFQUFFLFFBQVEsRUFBRSxXQUFXLEVBQUUsY0FBYyxFQUFFLEdBQUcsS0FBSyxDQUFBO1FBQ3ZELElBQUksY0FBYyxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ3ZCLE1BQU0sSUFBSSxLQUFLLENBQUMsNENBQTRDLENBQUMsQ0FBQTtRQUMvRCxDQUFDO1FBRUQsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMsb0JBQW9CLENBQUMsRUFBRSxRQUFRLEVBQUUsQ0FBQyxRQUFRLENBQUMsRUFB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
305
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY21zLW1vZHVsZS1zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL21vZHVsZXMvY21zL3NlcnZpY2VzL2Ntcy1tb2R1bGUtc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLHFEQUF5RDtBQUN6RCw4Q0FBbUU7QUFDbkUsMEVBQWdEO0FBQ2hELGtGQUF3RDtBQUN4RCxzRUFBNEM7QUFDNUMsOEZBQW1FO0FBZ0duRSxNQUFNLGdCQUFpQixTQUFRLElBQUEscUJBQWEsRUFBQztJQUMzQyxtQkFBbUIsRUFBRSxzQkFBVztJQUNoQyx1QkFBdUIsRUFBRSwwQkFBZTtJQUN4QyxpQkFBaUIsRUFBRSxvQkFBUztJQUM1Qiw0QkFBNEIsRUFBRSxnQ0FBb0I7Q0FDbkQsQ0FBQztJQUdBLFlBQVksR0FBRyxJQUFXO1FBQ3hCLEtBQUssQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFBO1FBQ2QsTUFBTSxTQUFTLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUF3QixDQUFBO1FBQ3hELElBQUksQ0FBQyxPQUFPLEdBQUcsU0FBUyxDQUFDLE1BQU0sSUFBSSxJQUFBLHVCQUFnQixHQUFFLENBQUE7UUFDckQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsb0NBQW9DLENBQUMsQ0FBQTtJQUMxRCxDQUFDO0lBRUQsS0FBSyxDQUFDLFFBQVEsQ0FBQyxLQUFvQjtRQUNqQyxNQUFNLEVBQUUsUUFBUSxFQUFFLFdBQVcsRUFBRSxjQUFjLEVBQUUsR0FBRyxLQUFLLENBQUE7UUFDdkQsSUFBSSxjQUFjLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDdkIsTUFBTSxJQUFJLEtBQUssQ0FBQyw0Q0FBNEMsQ0FBQyxDQUFBO1FBQy9ELENBQUM7UUFFRCxNQUFNLFFBQVEsR0FBRyxNQUFNLElBQUksQ0FBQyw0QkFBNEIsQ0FBQyxFQUFFLFFBQVEsRUFBRSxDQUFDLFFBQVEs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
package/README.md CHANGED
@@ -126,8 +126,21 @@ const logs = await cmsService.getShiftLogs({
126
126
  to: new Date("2024-01-31"),
127
127
  limit: 50,
128
128
  })
129
+
130
+ // Daily cash reconciliation report
131
+ const report = await cmsService.getReconciliationReport({
132
+ store_id: "store_01",
133
+ date: "2026-06-23", // YYYY-MM-DD (UTC)
134
+ net_cash_sales: 28450, // optional — cash collected from orders (default 0)
135
+ })
136
+ // report.expected_closing_balance = opening + net_cash_sales
137
+ // + petty_cash_in - petty_cash_out + total_handovers_in - total_handovers_out
138
+ // report.variance = closing_balance - expected (null until day_end runs)
139
+ // report.is_balanced = variance === 0
129
140
  ```
130
141
 
142
+ > **`net_cash_sales`** comes from your order module. Pass it from the brand's order query or via `sdk-client`. When omitted the report uses 0 and computes everything from CMS data only.
143
+
131
144
  ---
132
145
 
133
146
  ## Permissions
@@ -140,6 +153,109 @@ const logs = await cmsService.getShiftLogs({
140
153
  | `cms.petty_cash.read` | View petty cash balance and transactions |
141
154
  | `cms.petty_cash.operate` | Add petty cash or record expenses |
142
155
  | `cms.accumulation.read` | View current cash accumulation for a store |
156
+ | `cms.reconciliation.read` | View daily cash reconciliation report with variance analysis |
157
+
158
+ ---
159
+
160
+ ## HTTP API
161
+
162
+ All routes are mounted under `/admin/retailos/cms` and require a valid Medusa admin session.
163
+
164
+ ### Handovers
165
+
166
+ | Method | Path | Description |
167
+ |--------|------|-------------|
168
+ | `GET` | `/admin/retailos/cms/handovers` | List handovers with filtering, pagination, and linked employee/store data |
169
+ | `POST` | `/admin/retailos/cms/handovers` | Create a cash handover |
170
+ | `GET` | `/admin/retailos/cms/handovers/:id` | Retrieve a single handover by ID |
171
+
172
+ **GET /admin/retailos/cms/handovers** query params:
173
+
174
+ | Param | Type | Description |
175
+ |-------|------|-------------|
176
+ | `store_id` | string | Filter by store |
177
+ | `employee_id` | string | Filter by employee |
178
+ | `type` | `CR` \| `DB` | Filter by direction |
179
+ | `handover_id` | string | Exact match on handover_id |
180
+ | `q` | string | Full-text search on handover_id (`ILIKE`) |
181
+ | `created_at` | string / object | Date range filter |
182
+ | `limit` | number | Max 500, default 20 |
183
+ | `offset` | number | Default 0 |
184
+ | `order` | string | Field name; prefix `-` for DESC (e.g. `-created_at`) |
185
+
186
+ Response includes `handovers[]` and `count`. Each handover carries an embedded `employee` (`id`, `first_name`, `last_name`) and `store` (`id`, `name`, `store_code`) when module links resolve.
187
+
188
+ **POST /admin/retailos/cms/handovers** body (Zod-validated):
189
+
190
+ ```json
191
+ {
192
+ "store_id": "store_01",
193
+ "employee_id": "emp_01",
194
+ "handover_amount": 2000,
195
+ "type": "CR",
196
+ "remark": "shift change",
197
+ "handover_id": null,
198
+ "image_url": null,
199
+ "metadata": null
200
+ }
201
+ ```
202
+
203
+ ---
204
+
205
+ ### Petty cash
206
+
207
+ | Method | Path | Description |
208
+ |--------|------|-------------|
209
+ | `GET` | `/admin/retailos/cms/petty-cash` | List petty cash transactions with employee enrichment |
210
+
211
+ Query params: `store_id`, `limit` (max 500, default 100), `offset`.
212
+
213
+ Each transaction includes an embedded `employee` object when the `employee` module is available.
214
+
215
+ ---
216
+
217
+ ### Reconciliation
218
+
219
+ | Method | Path | Description |
220
+ |--------|------|-------------|
221
+ | `GET` | `/admin/retailos/cms/reconciliation` | Daily cash reconciliation report |
222
+
223
+ Required params: `store_id`, `date` (YYYY-MM-DD). Optional: `net_cash_sales` (number).
224
+
225
+ ---
226
+
227
+ ### Export
228
+
229
+ | Method | Path | Description |
230
+ |--------|------|-------------|
231
+ | `GET` | `/admin/retailos/cms/export` | Download all handovers as a CSV file |
232
+
233
+ Query params: `store_id` (optional). Returns `text/csv` with `Content-Disposition: attachment`.
234
+
235
+ ---
236
+
237
+ ## sdk-client
238
+
239
+ The `cms` namespace on `RetailOSClient` exposes typed wrappers for all routes above. Key methods:
240
+
241
+ ```ts
242
+ // Reconciliation report
243
+ const report = await client.cms.getReconciliationReport({
244
+ store_id: "store_01",
245
+ date: "2026-06-26",
246
+ net_cash_sales: 28450,
247
+ })
248
+
249
+ // List handovers (paginated)
250
+ const { handovers, count } = await client.cms.listHandovers({
251
+ store_id: "store_01",
252
+ limit: 20,
253
+ offset: 0,
254
+ })
255
+
256
+ // Export CSV (returns raw text)
257
+ const csv = await client.cms.exportHandovers({ store_id: "store_01" })
258
+ ```
143
259
 
144
260
  ---
145
261
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devx-retailos/cms",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "Cash Management System for retailOS: shift ops, handovers, petty cash, accumulation.",
5
5
  "license": "MIT",
6
6
  "main": "./.medusa/server/src/modules/cms/index.js",
@@ -25,9 +25,9 @@
25
25
  "src"
26
26
  ],
27
27
  "dependencies": {
28
- "@devx-retailos/core": "0.0.2",
28
+ "@devx-retailos/rbac": "0.0.2",
29
29
  "@devx-retailos/employee": "0.0.3",
30
- "@devx-retailos/rbac": "0.0.2"
30
+ "@devx-retailos/core": "0.0.2"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "@medusajs/framework": "^2.15.0",
@@ -3,7 +3,7 @@ import { GET } from "../route"
3
3
 
4
4
  function makeService() {
5
5
  return {
6
- getAccumulation: vi.fn(),
6
+ getEnrichedAccumulation: vi.fn(),
7
7
  }
8
8
  }
9
9
 
@@ -28,35 +28,53 @@ function makeRes() {
28
28
  }
29
29
 
30
30
  describe("GET /accumulation/[storeId]", () => {
31
- it("returns accumulation when it exists", async () => {
31
+ it("returns enriched accumulation when it exists", async () => {
32
32
  const service = makeService()
33
- const accumulation = { id: "cmsa_1", store_id: "s1", cash_in_store: 750 }
34
- service.getAccumulation.mockResolvedValue(accumulation)
33
+ const accumulation = {
34
+ id: "cmsa_1",
35
+ store_id: "s1",
36
+ cash_in_store: 750,
37
+ petty_cash: 200,
38
+ opening_store_cash: 500,
39
+ opening_petty_cash: 150,
40
+ closing_store_cash: null,
41
+ closing_petty_cash: null,
42
+ }
43
+ service.getEnrichedAccumulation.mockResolvedValue(accumulation)
35
44
  const req = makeReq({ storeId: "s1" }, service)
36
45
  const res = makeRes()
37
46
 
38
47
  await GET(req as any, res as any)
39
48
 
40
- expect(service.getAccumulation).toHaveBeenCalledWith("s1")
49
+ expect(service.getEnrichedAccumulation).toHaveBeenCalledWith("s1")
41
50
  expect(res.status).toHaveBeenCalledWith(200)
42
51
  expect(res.json).toHaveBeenCalledWith({ accumulation })
43
52
  })
44
53
 
45
- it("returns default zero accumulation when none exists", async () => {
54
+ it("returns default zero accumulation when no day has started", async () => {
46
55
  const service = makeService()
47
- service.getAccumulation.mockResolvedValue(null)
56
+ const accumulation = {
57
+ store_id: "s1",
58
+ cash_in_store: 0,
59
+ petty_cash: 0,
60
+ opening_store_cash: null,
61
+ opening_petty_cash: null,
62
+ closing_store_cash: null,
63
+ closing_petty_cash: null,
64
+ }
65
+ service.getEnrichedAccumulation.mockResolvedValue(accumulation)
48
66
  const req = makeReq({ storeId: "s1" }, service)
49
67
  const res = makeRes()
50
68
 
51
69
  await GET(req as any, res as any)
52
70
 
53
71
  expect(res.status).toHaveBeenCalledWith(200)
54
- expect(res.json).toHaveBeenCalledWith({ accumulation: { store_id: "s1", cash_in_store: 0 } })
72
+ expect(res.json).toHaveBeenCalledWith({ accumulation })
55
73
  })
56
74
 
57
75
  it("returns 500 when service throws", async () => {
58
76
  const service = makeService()
59
- service.getAccumulation.mockRejectedValue(new Error("db error"))
77
+ service.getEnrichedAccumulation.mockRejectedValue(new Error("db error"))
60
78
  const req = makeReq({ storeId: "s1" }, service)
61
79
  const res = makeRes()
62
80
 
@@ -21,10 +21,8 @@ export const GET = async (req: AuthenticatedMedusaRequest, res: MedusaResponse)
21
21
  const { storeId } = req.params
22
22
  try {
23
23
  const service = getService(req)
24
- const accumulation = await service.getAccumulation(storeId)
25
- return res.status(200).json({
26
- accumulation: accumulation ?? { store_id: storeId, cash_in_store: 0 },
27
- })
24
+ const accumulation = await service.getEnrichedAccumulation(storeId)
25
+ return res.status(200).json({ accumulation })
28
26
  } catch (err) {
29
27
  getLogger(req).error("[retailos/cms] get accumulation failed", {
30
28
  store_id: storeId,
@@ -3,7 +3,7 @@ import { GET } from "../route"
3
3
 
4
4
  function makeService() {
5
5
  return {
6
- listAndCountCmsHandovers: vi.fn().mockResolvedValue([[], 0]),
6
+ listAndCountRetailosCmsHandovers: vi.fn().mockResolvedValue([[], 0]),
7
7
  }
8
8
  }
9
9
 
@@ -34,7 +34,7 @@ function makeRes() {
34
34
  describe("GET /export", () => {
35
35
  it("returns CSV with correct headers row", async () => {
36
36
  const service = makeService()
37
- service.listAndCountCmsHandovers.mockResolvedValue([[], 0])
37
+ service.listAndCountRetailosCmsHandovers.mockResolvedValue([[], 0])
38
38
  const req = makeReq({}, service)
39
39
  const res = makeRes()
40
40
 
@@ -55,7 +55,7 @@ describe("GET /export", () => {
55
55
  const handovers = [
56
56
  { id: "cmsh_1", store_id: "s1", employee_id: "e1", handover_id: null, handover_amount: 500, total_cash: null, type: "CR", remark: null, created_at: "2026-06-22T00:00:00Z" },
57
57
  ]
58
- service.listAndCountCmsHandovers.mockResolvedValue([handovers, 1])
58
+ service.listAndCountRetailosCmsHandovers.mockResolvedValue([handovers, 1])
59
59
  const req = makeReq({}, service)
60
60
  const res = makeRes()
61
61
 
@@ -74,7 +74,7 @@ describe("GET /export", () => {
74
74
  const handovers = [
75
75
  { id: "cmsh_2", store_id: "s1", employee_id: "e1", handover_id: null, handover_amount: 100, total_cash: null, type: "DB", remark: 'cash, "extra"', created_at: "2026-06-22T00:00:00Z" },
76
76
  ]
77
- service.listAndCountCmsHandovers.mockResolvedValue([handovers, 1])
77
+ service.listAndCountRetailosCmsHandovers.mockResolvedValue([handovers, 1])
78
78
  const req = makeReq({}, service)
79
79
  const res = makeRes()
80
80
 
@@ -86,13 +86,13 @@ describe("GET /export", () => {
86
86
 
87
87
  it("filters by store_id when provided in query", async () => {
88
88
  const service = makeService()
89
- service.listAndCountCmsHandovers.mockResolvedValue([[], 0])
89
+ service.listAndCountRetailosCmsHandovers.mockResolvedValue([[], 0])
90
90
  const req = makeReq({ store_id: "s1" }, service)
91
91
  const res = makeRes()
92
92
 
93
93
  await GET(req as any, res as any)
94
94
 
95
- expect(service.listAndCountCmsHandovers).toHaveBeenCalledWith(
95
+ expect(service.listAndCountRetailosCmsHandovers).toHaveBeenCalledWith(
96
96
  expect.objectContaining({ store_id: ["s1"] }),
97
97
  expect.any(Object)
98
98
  )
@@ -100,13 +100,13 @@ describe("GET /export", () => {
100
100
 
101
101
  it("does not filter by store_id when not in query", async () => {
102
102
  const service = makeService()
103
- service.listAndCountCmsHandovers.mockResolvedValue([[], 0])
103
+ service.listAndCountRetailosCmsHandovers.mockResolvedValue([[], 0])
104
104
  const req = makeReq({}, service)
105
105
  const res = makeRes()
106
106
 
107
107
  await GET(req as any, res as any)
108
108
 
109
- expect(service.listAndCountCmsHandovers).toHaveBeenCalledWith(
109
+ expect(service.listAndCountRetailosCmsHandovers).toHaveBeenCalledWith(
110
110
  expect.not.objectContaining({ store_id: expect.anything() }),
111
111
  expect.any(Object)
112
112
  )
@@ -114,7 +114,7 @@ describe("GET /export", () => {
114
114
 
115
115
  it("returns 500 when service throws", async () => {
116
116
  const service = makeService()
117
- service.listAndCountCmsHandovers.mockRejectedValue(new Error("db error"))
117
+ service.listAndCountRetailosCmsHandovers.mockRejectedValue(new Error("db error"))
118
118
  const req = makeReq({}, service)
119
119
  const res = makeRes()
120
120
 
@@ -35,7 +35,7 @@ export const GET = async (req: AuthenticatedMedusaRequest, res: MedusaResponse)
35
35
  const filters: Record<string, unknown> = {}
36
36
  if (store_id) filters.store_id = [store_id]
37
37
 
38
- const [handovers] = await service.listAndCountCmsHandovers(filters, {
38
+ const [handovers] = await service.listAndCountRetailosCmsHandovers(filters, {
39
39
  take: 10000,
40
40
  order: { created_at: "DESC" },
41
41
  })