@devx-retailos/cms 0.0.1

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 (56) hide show
  1. package/.medusa/server/src/admin/index.js +23 -0
  2. package/.medusa/server/src/admin/index.mjs +24 -0
  3. package/.medusa/server/src/api/admin/retailos/cms/accumulation/[storeId]/route.js +34 -0
  4. package/.medusa/server/src/api/admin/retailos/cms/export/route.js +52 -0
  5. package/.medusa/server/src/api/admin/retailos/cms/handovers/[id]/route.js +35 -0
  6. package/.medusa/server/src/api/admin/retailos/cms/handovers/route.js +78 -0
  7. package/.medusa/server/src/api/admin/retailos/cms/operation/route.js +124 -0
  8. package/.medusa/server/src/api/admin/retailos/cms/petty-cash/route.js +31 -0
  9. package/.medusa/server/src/api/admin/retailos/cms/shift-logs/route.js +36 -0
  10. package/.medusa/server/src/links/employee-to-cms-handover.js +13 -0
  11. package/.medusa/server/src/links/store-to-cms-handover.js +13 -0
  12. package/.medusa/server/src/modules/cms/constants.js +5 -0
  13. package/.medusa/server/src/modules/cms/index.js +53 -0
  14. package/.medusa/server/src/modules/cms/migrations/Migration20260622000000.js +30 -0
  15. package/.medusa/server/src/modules/cms/migrations/Migration20260622000001.js +16 -0
  16. package/.medusa/server/src/modules/cms/models/cms-accumulation.js +11 -0
  17. package/.medusa/server/src/modules/cms/models/cms-handover.js +17 -0
  18. package/.medusa/server/src/modules/cms/models/index.js +15 -0
  19. package/.medusa/server/src/modules/cms/models/petty-cash-transaction.js +24 -0
  20. package/.medusa/server/src/modules/cms/models/petty-cash.js +11 -0
  21. package/.medusa/server/src/modules/cms/permissions.js +36 -0
  22. package/.medusa/server/src/modules/cms/services/cms-module-service.js +210 -0
  23. package/.medusa/server/src/modules/cms/services/index.js +9 -0
  24. package/README.md +155 -0
  25. package/package.json +60 -0
  26. package/src/admin/.gitkeep +0 -0
  27. package/src/api/admin/retailos/cms/accumulation/[storeId]/__tests__/route.test.ts +68 -0
  28. package/src/api/admin/retailos/cms/accumulation/[storeId]/route.ts +35 -0
  29. package/src/api/admin/retailos/cms/export/__tests__/route.test.ts +126 -0
  30. package/src/api/admin/retailos/cms/export/route.ts +58 -0
  31. package/src/api/admin/retailos/cms/handovers/[id]/__tests__/route.test.ts +68 -0
  32. package/src/api/admin/retailos/cms/handovers/[id]/route.ts +36 -0
  33. package/src/api/admin/retailos/cms/handovers/__tests__/route.test.ts +104 -0
  34. package/src/api/admin/retailos/cms/handovers/route.ts +79 -0
  35. package/src/api/admin/retailos/cms/operation/__tests__/route.test.ts +169 -0
  36. package/src/api/admin/retailos/cms/operation/route.ts +130 -0
  37. package/src/api/admin/retailos/cms/petty-cash/__tests__/route.test.ts +73 -0
  38. package/src/api/admin/retailos/cms/petty-cash/route.ts +35 -0
  39. package/src/api/admin/retailos/cms/shift-logs/__tests__/route.test.ts +77 -0
  40. package/src/api/admin/retailos/cms/shift-logs/route.ts +38 -0
  41. package/src/links/employee-to-cms-handover.ts +11 -0
  42. package/src/links/store-to-cms-handover.ts +11 -0
  43. package/src/modules/cms/__tests__/cms-module-service.test.ts +333 -0
  44. package/src/modules/cms/__tests__/permissions.test.ts +36 -0
  45. package/src/modules/cms/constants.ts +1 -0
  46. package/src/modules/cms/index.ts +24 -0
  47. package/src/modules/cms/migrations/Migration20260622000000.ts +58 -0
  48. package/src/modules/cms/migrations/Migration20260622000001.ts +17 -0
  49. package/src/modules/cms/models/cms-accumulation.ts +10 -0
  50. package/src/modules/cms/models/cms-handover.ts +16 -0
  51. package/src/modules/cms/models/index.ts +4 -0
  52. package/src/modules/cms/models/petty-cash-transaction.ts +23 -0
  53. package/src/modules/cms/models/petty-cash.ts +10 -0
  54. package/src/modules/cms/permissions.ts +34 -0
  55. package/src/modules/cms/services/cms-module-service.ts +284 -0
  56. package/src/modules/cms/services/index.ts +13 -0
@@ -0,0 +1,284 @@
1
+ import { MedusaService } from "@medusajs/framework/utils"
2
+ import { createNoopLogger, type Logger } from "@devx-retailos/core"
3
+ import CmsHandover from "../models/cms-handover"
4
+ import CmsAccumulation from "../models/cms-accumulation"
5
+ import PettyCash from "../models/petty-cash"
6
+ import PettyCashTransaction from "../models/petty-cash-transaction"
7
+
8
+ export type HandoverType = "DB" | "CR"
9
+ export type TransactionType = "open" | "close" | "petty_cash"
10
+ export type EntryType = "DB" | "CR"
11
+ export type PettyCashSource = "self" | "from_petty_cash"
12
+
13
+ export interface DayStartInput {
14
+ store_id: string
15
+ employee_id: string
16
+ opening_amount: number
17
+ }
18
+
19
+ export interface DayEndInput {
20
+ store_id: string
21
+ employee_id: string
22
+ closing_amount: number
23
+ }
24
+
25
+ export interface HandoverInput {
26
+ store_id: string
27
+ employee_id: string
28
+ handover_id?: string | null
29
+ handover_amount: number
30
+ total_cash?: number | null
31
+ type?: HandoverType | null
32
+ image_url?: string | null
33
+ remark?: string | null
34
+ metadata?: Record<string, unknown> | null
35
+ }
36
+
37
+ export interface AddPettyCashInput {
38
+ store_id: string
39
+ employee_id: string
40
+ amount: number
41
+ source?: PettyCashSource | null
42
+ }
43
+
44
+ export interface AddExpenseInput {
45
+ store_id: string
46
+ employee_id: string
47
+ amount: number
48
+ category?: string | null
49
+ sub_category?: string | null
50
+ reason?: string | null
51
+ image_url?: string | null
52
+ metadata?: Record<string, unknown> | null
53
+ }
54
+
55
+ export interface ShiftLogFilters {
56
+ store_id?: string
57
+ from?: Date
58
+ to?: Date
59
+ limit?: number
60
+ }
61
+
62
+ class CmsModuleService extends MedusaService({
63
+ CmsHandover,
64
+ CmsAccumulation,
65
+ PettyCash,
66
+ PettyCashTransaction,
67
+ }) {
68
+ protected logger_: Logger
69
+
70
+ constructor(...args: any[]) {
71
+ super(...args)
72
+ const container = (args[0] ?? {}) as { logger?: Logger }
73
+ this.logger_ = container.logger ?? createNoopLogger()
74
+ this.logger_.debug("[retailos/cms] service initialized")
75
+ }
76
+
77
+ async dayStart(input: DayStartInput): Promise<void> {
78
+ const { store_id, employee_id, opening_amount } = input
79
+ if (opening_amount < 0) {
80
+ throw new Error("[retailos/cms] opening_amount must be >= 0")
81
+ }
82
+
83
+ const existing = await this.listCmsAccumulations({ store_id: [store_id] })
84
+ if (existing.length === 0) {
85
+ await this.createCmsAccumulations([{ store_id, cash_in_store: opening_amount }])
86
+ } else {
87
+ await this.updateCmsAccumulations([{ id: (existing[0] as any).id, cash_in_store: opening_amount }])
88
+ }
89
+
90
+ await this.createPettyCashTransactions([{
91
+ store_id,
92
+ employee_id,
93
+ transaction_type: "open" as TransactionType,
94
+ entry_type: "CR" as EntryType,
95
+ source: null,
96
+ amount: opening_amount,
97
+ opening_balance: opening_amount,
98
+ closing_balance: null,
99
+ expense_amount: 0,
100
+ difference_amount: 0,
101
+ category: null,
102
+ sub_category: null,
103
+ reason: null,
104
+ image_url: null,
105
+ date: new Date(),
106
+ metadata: null,
107
+ }])
108
+
109
+ this.logger_.info("cms.day_started", { store_id, employee_id, opening_amount })
110
+ }
111
+
112
+ async dayEnd(input: DayEndInput): Promise<void> {
113
+ const { store_id, employee_id, closing_amount } = input
114
+ if (closing_amount < 0) {
115
+ throw new Error("[retailos/cms] closing_amount must be >= 0")
116
+ }
117
+
118
+ const accumulations = await this.listCmsAccumulations({ store_id: [store_id] })
119
+ const current_cash = accumulations[0] ? (accumulations[0] as any).cash_in_store : 0
120
+ const difference = closing_amount - current_cash
121
+
122
+ await this.createPettyCashTransactions([{
123
+ store_id,
124
+ employee_id,
125
+ transaction_type: "close" as TransactionType,
126
+ entry_type: "DB" as EntryType,
127
+ source: null,
128
+ amount: closing_amount,
129
+ opening_balance: current_cash,
130
+ closing_balance: closing_amount,
131
+ expense_amount: 0,
132
+ difference_amount: difference,
133
+ category: null,
134
+ sub_category: null,
135
+ reason: null,
136
+ image_url: null,
137
+ date: new Date(),
138
+ metadata: null,
139
+ }])
140
+
141
+ this.logger_.info("cms.day_ended", { store_id, employee_id, closing_amount, difference })
142
+ }
143
+
144
+ async handover(input: HandoverInput) {
145
+ const { store_id, handover_amount, type } = input
146
+ if (handover_amount < 0) {
147
+ throw new Error("[retailos/cms] handover_amount must be >= 0")
148
+ }
149
+
150
+ const [created] = await this.createCmsHandovers([{
151
+ store_id,
152
+ employee_id: input.employee_id,
153
+ handover_id: input.handover_id ?? null,
154
+ handover_amount,
155
+ total_cash: input.total_cash ?? null,
156
+ type: type ?? null,
157
+ image_url: input.image_url ?? null,
158
+ remark: input.remark ?? null,
159
+ metadata: input.metadata ?? null,
160
+ }])
161
+
162
+ const accumulations = await this.listCmsAccumulations({ store_id: [store_id] })
163
+ const current = accumulations[0] ? (accumulations[0] as any).cash_in_store : 0
164
+
165
+ let updated_cash = current
166
+ if (type === "CR") {
167
+ updated_cash = current + handover_amount
168
+ } else if (type === "DB") {
169
+ updated_cash = current - handover_amount
170
+ }
171
+
172
+ if (accumulations.length === 0) {
173
+ await this.createCmsAccumulations([{ store_id, cash_in_store: updated_cash }])
174
+ } else {
175
+ await this.updateCmsAccumulations([{ id: (accumulations[0] as any).id, cash_in_store: updated_cash }])
176
+ }
177
+
178
+ this.logger_.info("[retailos/cms] handover created", {
179
+ id: (created as any)?.id,
180
+ store_id,
181
+ handover_amount,
182
+ type,
183
+ })
184
+
185
+ return created
186
+ }
187
+
188
+ async addPettyCash(input: AddPettyCashInput) {
189
+ const { store_id, employee_id, amount, source } = input
190
+ if (amount <= 0) {
191
+ throw new Error("[retailos/cms] amount must be > 0")
192
+ }
193
+
194
+ const existing = await this.listPettyCashes({ store_id: [store_id] })
195
+ const current_balance = existing[0] ? (existing[0] as any).balance : 0
196
+ const new_balance = current_balance + amount
197
+
198
+ if (existing.length === 0) {
199
+ await this.createPettyCashes([{ store_id, balance: new_balance }])
200
+ } else {
201
+ await this.updatePettyCashes([{ id: (existing[0] as any).id, balance: new_balance }])
202
+ }
203
+
204
+ const [tx] = await this.createPettyCashTransactions([{
205
+ store_id,
206
+ employee_id,
207
+ transaction_type: "petty_cash" as TransactionType,
208
+ entry_type: "CR" as EntryType,
209
+ source: source ?? null,
210
+ amount,
211
+ opening_balance: current_balance,
212
+ closing_balance: new_balance,
213
+ expense_amount: 0,
214
+ difference_amount: 0,
215
+ category: null,
216
+ sub_category: null,
217
+ reason: null,
218
+ image_url: null,
219
+ date: new Date(),
220
+ metadata: null,
221
+ }])
222
+
223
+ this.logger_.info("[retailos/cms] petty cash added", { store_id, amount, new_balance })
224
+ return tx
225
+ }
226
+
227
+ async addExpense(input: AddExpenseInput) {
228
+ const { store_id, employee_id, amount } = input
229
+ if (amount <= 0) {
230
+ throw new Error("[retailos/cms] expense amount must be > 0")
231
+ }
232
+
233
+ const existing = await this.listPettyCashes({ store_id: [store_id] })
234
+ const current_balance = existing[0] ? (existing[0] as any).balance : 0
235
+ const new_balance = current_balance - amount
236
+
237
+ if (existing.length === 0) {
238
+ await this.createPettyCashes([{ store_id, balance: new_balance }])
239
+ } else {
240
+ await this.updatePettyCashes([{ id: (existing[0] as any).id, balance: new_balance }])
241
+ }
242
+
243
+ const [tx] = await this.createPettyCashTransactions([{
244
+ store_id,
245
+ employee_id,
246
+ transaction_type: "petty_cash" as TransactionType,
247
+ entry_type: "DB" as EntryType,
248
+ source: null,
249
+ amount,
250
+ opening_balance: current_balance,
251
+ closing_balance: new_balance,
252
+ expense_amount: amount,
253
+ difference_amount: 0,
254
+ category: input.category ?? null,
255
+ sub_category: input.sub_category ?? null,
256
+ reason: input.reason ?? null,
257
+ image_url: input.image_url ?? null,
258
+ date: new Date(),
259
+ metadata: input.metadata ?? null,
260
+ }])
261
+
262
+ this.logger_.info("[retailos/cms] expense recorded", { store_id, amount, category: input.category })
263
+ return tx
264
+ }
265
+
266
+ async getAccumulation(store_id: string) {
267
+ const rows = await this.listCmsAccumulations({ store_id: [store_id] })
268
+ return rows[0] ?? null
269
+ }
270
+
271
+ async getShiftLogs(filters: ShiftLogFilters) {
272
+ const dbFilters: Record<string, unknown> = {}
273
+ if (filters.store_id) dbFilters.store_id = [filters.store_id]
274
+ dbFilters.transaction_type = ["open", "close"]
275
+
276
+ const rows = await this.listPettyCashTransactions(dbFilters, {
277
+ take: filters.limit ?? 100,
278
+ order: { created_at: "DESC" } as any,
279
+ })
280
+ return rows
281
+ }
282
+ }
283
+
284
+ export default CmsModuleService
@@ -0,0 +1,13 @@
1
+ export { default as CmsModuleService } from "./cms-module-service"
2
+ export type {
3
+ DayStartInput,
4
+ DayEndInput,
5
+ HandoverInput,
6
+ AddPettyCashInput,
7
+ AddExpenseInput,
8
+ ShiftLogFilters,
9
+ HandoverType,
10
+ TransactionType,
11
+ EntryType,
12
+ PettyCashSource,
13
+ } from "./cms-module-service"