@goweekdays/core 2.15.5 → 2.15.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/index.d.ts +299 -1
- package/dist/index.js +2638 -48
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2870 -278
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -72,6 +72,14 @@ __export(src_exports, {
|
|
|
72
72
|
VERIFICATION_USER_INVITE_DURATION: () => VERIFICATION_USER_INVITE_DURATION,
|
|
73
73
|
XENDIT_BASE_URL: () => XENDIT_BASE_URL,
|
|
74
74
|
XENDIT_SECRET_KEY: () => XENDIT_SECRET_KEY,
|
|
75
|
+
assetItemCategories: () => assetItemCategories,
|
|
76
|
+
assetItemClasses: () => assetItemClasses,
|
|
77
|
+
assetItemPurposes: () => assetItemPurposes,
|
|
78
|
+
assetItemStatuses: () => assetItemStatuses,
|
|
79
|
+
assetItemTrackingTypes: () => assetItemTrackingTypes,
|
|
80
|
+
assetUnitStatuses: () => assetUnitStatuses,
|
|
81
|
+
buildCategoryPath: () => buildCategoryPath,
|
|
82
|
+
categoryLevels: () => categoryLevels,
|
|
75
83
|
chartOfAccountControlTypes: () => chartOfAccountControlTypes,
|
|
76
84
|
chartOfAccountNormalBalances: () => chartOfAccountNormalBalances,
|
|
77
85
|
chartOfAccountStatuses: () => chartOfAccountStatuses,
|
|
@@ -86,7 +94,10 @@ __export(src_exports, {
|
|
|
86
94
|
ledgerBillTypes: () => ledgerBillTypes,
|
|
87
95
|
modelAccountBalance: () => modelAccountBalance,
|
|
88
96
|
modelApp: () => modelApp,
|
|
97
|
+
modelAssetItem: () => modelAssetItem,
|
|
98
|
+
modelAssetUnit: () => modelAssetUnit,
|
|
89
99
|
modelBusinessProfile: () => modelBusinessProfile,
|
|
100
|
+
modelCategoryNode: () => modelCategoryNode,
|
|
90
101
|
modelChartOfAccount: () => modelChartOfAccount,
|
|
91
102
|
modelCustomer: () => modelCustomer,
|
|
92
103
|
modelJobApplication: () => modelJobApplication,
|
|
@@ -112,14 +123,22 @@ __export(src_exports, {
|
|
|
112
123
|
modelPlan: () => modelPlan,
|
|
113
124
|
modelPromo: () => modelPromo,
|
|
114
125
|
modelRole: () => modelRole,
|
|
126
|
+
modelStockMovement: () => modelStockMovement,
|
|
115
127
|
modelSubscription: () => modelSubscription,
|
|
116
128
|
modelSubscriptionTransaction: () => modelSubscriptionTransaction,
|
|
129
|
+
modelTag: () => modelTag,
|
|
117
130
|
modelTax: () => modelTax,
|
|
118
131
|
modelUser: () => modelUser,
|
|
119
132
|
modelVerification: () => modelVerification,
|
|
133
|
+
normalizeName: () => normalizeName,
|
|
120
134
|
schemaAccountBalance: () => schemaAccountBalance,
|
|
121
135
|
schemaApp: () => schemaApp,
|
|
122
136
|
schemaAppUpdate: () => schemaAppUpdate,
|
|
137
|
+
schemaAssetItem: () => schemaAssetItem,
|
|
138
|
+
schemaAssetItemCreate: () => schemaAssetItemCreate,
|
|
139
|
+
schemaAssetItemUpdate: () => schemaAssetItemUpdate,
|
|
140
|
+
schemaAssetUnit: () => schemaAssetUnit,
|
|
141
|
+
schemaAssetUnitUpdate: () => schemaAssetUnitUpdate,
|
|
123
142
|
schemaAward: () => schemaAward,
|
|
124
143
|
schemaBuilding: () => schemaBuilding,
|
|
125
144
|
schemaBuildingUnit: () => schemaBuildingUnit,
|
|
@@ -131,6 +150,10 @@ __export(src_exports, {
|
|
|
131
150
|
schemaBusinessProfileRegisteredAddress: () => schemaBusinessProfileRegisteredAddress,
|
|
132
151
|
schemaBusinessProfileTIN: () => schemaBusinessProfileTIN,
|
|
133
152
|
schemaBusinessProfileTradeName: () => schemaBusinessProfileTradeName,
|
|
153
|
+
schemaCategoryGetAll: () => schemaCategoryGetAll,
|
|
154
|
+
schemaCategoryNodeCreate: () => schemaCategoryNodeCreate,
|
|
155
|
+
schemaCategoryNodeStd: () => schemaCategoryNodeStd,
|
|
156
|
+
schemaCategoryNodeUpdate: () => schemaCategoryNodeUpdate,
|
|
134
157
|
schemaCertification: () => schemaCertification,
|
|
135
158
|
schemaChartOfAccountBase: () => schemaChartOfAccountBase,
|
|
136
159
|
schemaChartOfAccountStd: () => schemaChartOfAccountStd,
|
|
@@ -200,6 +223,7 @@ __export(src_exports, {
|
|
|
200
223
|
schemaRole: () => schemaRole,
|
|
201
224
|
schemaRoleUpdate: () => schemaRoleUpdate,
|
|
202
225
|
schemaSkill: () => schemaSkill,
|
|
226
|
+
schemaStockMovement: () => schemaStockMovement,
|
|
203
227
|
schemaSubscribe: () => schemaSubscribe,
|
|
204
228
|
schemaSubscription: () => schemaSubscription,
|
|
205
229
|
schemaSubscriptionCompute: () => schemaSubscriptionCompute,
|
|
@@ -207,6 +231,9 @@ __export(src_exports, {
|
|
|
207
231
|
schemaSubscriptionSeats: () => schemaSubscriptionSeats,
|
|
208
232
|
schemaSubscriptionTransaction: () => schemaSubscriptionTransaction,
|
|
209
233
|
schemaSubscriptionUpdate: () => schemaSubscriptionUpdate,
|
|
234
|
+
schemaTagCreate: () => schemaTagCreate,
|
|
235
|
+
schemaTagStd: () => schemaTagStd,
|
|
236
|
+
schemaTagUpdate: () => schemaTagUpdate,
|
|
210
237
|
schemaTax: () => schemaTax,
|
|
211
238
|
schemaTaxUpdate: () => schemaTaxUpdate,
|
|
212
239
|
schemaUpdateOptions: () => schemaUpdateOptions,
|
|
@@ -214,6 +241,8 @@ __export(src_exports, {
|
|
|
214
241
|
schemaVerification: () => schemaVerification,
|
|
215
242
|
schemaVerificationOrgInvite: () => schemaVerificationOrgInvite,
|
|
216
243
|
schemaWorkExp: () => schemaWorkExp,
|
|
244
|
+
stockMovementReferenceTypes: () => stockMovementReferenceTypes,
|
|
245
|
+
stockMovementTypes: () => stockMovementTypes,
|
|
217
246
|
taxDirections: () => taxDirections,
|
|
218
247
|
taxTypes: () => taxTypes,
|
|
219
248
|
transactionSchema: () => transactionSchema,
|
|
@@ -221,6 +250,11 @@ __export(src_exports, {
|
|
|
221
250
|
useAppController: () => useAppController,
|
|
222
251
|
useAppRepo: () => useAppRepo,
|
|
223
252
|
useAppService: () => useAppService,
|
|
253
|
+
useAssetItemController: () => useAssetItemController,
|
|
254
|
+
useAssetItemRepo: () => useAssetItemRepo,
|
|
255
|
+
useAssetItemService: () => useAssetItemService,
|
|
256
|
+
useAssetUnitController: () => useAssetUnitController,
|
|
257
|
+
useAssetUnitRepo: () => useAssetUnitRepo,
|
|
224
258
|
useAuthController: () => useAuthController,
|
|
225
259
|
useAuthService: () => useAuthService,
|
|
226
260
|
useBuildingController: () => useBuildingController,
|
|
@@ -231,6 +265,9 @@ __export(src_exports, {
|
|
|
231
265
|
useBuildingUnitService: () => useBuildingUnitService,
|
|
232
266
|
useBusinessProfileCtrl: () => useBusinessProfileCtrl,
|
|
233
267
|
useBusinessProfileRepo: () => useBusinessProfileRepo,
|
|
268
|
+
useCategoryController: () => useCategoryController,
|
|
269
|
+
useCategoryRepo: () => useCategoryRepo,
|
|
270
|
+
useCategoryService: () => useCategoryService,
|
|
234
271
|
useChartOfAccountController: () => useChartOfAccountController,
|
|
235
272
|
useChartOfAccountRepo: () => useChartOfAccountRepo,
|
|
236
273
|
useCounterModel: () => useCounterModel,
|
|
@@ -286,11 +323,17 @@ __export(src_exports, {
|
|
|
286
323
|
useRoleController: () => useRoleController,
|
|
287
324
|
useRoleRepo: () => useRoleRepo,
|
|
288
325
|
useRoleService: () => useRoleService,
|
|
326
|
+
useStockMovementController: () => useStockMovementController,
|
|
327
|
+
useStockMovementRepo: () => useStockMovementRepo,
|
|
328
|
+
useStockMovementService: () => useStockMovementService,
|
|
289
329
|
useSubscriptionController: () => useSubscriptionController,
|
|
290
330
|
useSubscriptionRepo: () => useSubscriptionRepo,
|
|
291
331
|
useSubscriptionService: () => useSubscriptionService,
|
|
292
332
|
useSubscriptionTransactionController: () => useSubscriptionTransactionController,
|
|
293
333
|
useSubscriptionTransactionRepo: () => useSubscriptionTransactionRepo,
|
|
334
|
+
useTagController: () => useTagController,
|
|
335
|
+
useTagRepo: () => useTagRepo,
|
|
336
|
+
useTagService: () => useTagService,
|
|
294
337
|
useTaxController: () => useTaxController,
|
|
295
338
|
useTaxRepo: () => useTaxRepo,
|
|
296
339
|
useUserController: () => useUserController,
|
|
@@ -3671,6 +3714,11 @@ function useAppService() {
|
|
|
3671
3714
|
name: "Finance",
|
|
3672
3715
|
description: "Finance and billing services."
|
|
3673
3716
|
},
|
|
3717
|
+
{
|
|
3718
|
+
code: "assets",
|
|
3719
|
+
name: "Assets",
|
|
3720
|
+
description: "Asset management and tracking."
|
|
3721
|
+
},
|
|
3674
3722
|
{
|
|
3675
3723
|
code: "marketplace",
|
|
3676
3724
|
name: "Marketplace",
|
|
@@ -11100,22 +11148,23 @@ function useOrgService() {
|
|
|
11100
11148
|
},
|
|
11101
11149
|
filePath
|
|
11102
11150
|
});
|
|
11103
|
-
mailer.sendMail({
|
|
11151
|
+
await mailer.sendMail({
|
|
11104
11152
|
to: verification.email,
|
|
11105
11153
|
subject: "Welcome to GoWeekdays - Your Organization is Ready",
|
|
11106
11154
|
from: `"GoWeekdays" <support@goweekdays.com>`,
|
|
11107
11155
|
html: emailContent
|
|
11108
|
-
}).catch((error2) => {
|
|
11109
|
-
import_utils58.logger.log({
|
|
11110
|
-
level: "error",
|
|
11111
|
-
message: `Error sending user invite email: ${error2}`
|
|
11112
|
-
});
|
|
11113
11156
|
});
|
|
11114
11157
|
await session?.commitTransaction();
|
|
11115
11158
|
return "Successfully created organization with verification.";
|
|
11116
11159
|
} catch (error2) {
|
|
11117
11160
|
await session?.abortTransaction();
|
|
11118
|
-
|
|
11161
|
+
if (error2 instanceof import_utils58.AppError) {
|
|
11162
|
+
throw error2;
|
|
11163
|
+
} else {
|
|
11164
|
+
throw new import_utils58.InternalServerError(
|
|
11165
|
+
"Failed to create organization with verification."
|
|
11166
|
+
);
|
|
11167
|
+
}
|
|
11119
11168
|
} finally {
|
|
11120
11169
|
await session?.endSession();
|
|
11121
11170
|
}
|
|
@@ -11224,22 +11273,23 @@ function useOrgService() {
|
|
|
11224
11273
|
},
|
|
11225
11274
|
filePath
|
|
11226
11275
|
});
|
|
11227
|
-
mailer.sendMail({
|
|
11276
|
+
await mailer.sendMail({
|
|
11228
11277
|
to: verification.email,
|
|
11229
11278
|
subject: "Welcome to GoWeekdays - Your Organization is Ready",
|
|
11230
11279
|
from: `"GoWeekdays" <support@goweekdays.com>`,
|
|
11231
11280
|
html: emailContent
|
|
11232
|
-
}).catch((error2) => {
|
|
11233
|
-
import_utils58.logger.log({
|
|
11234
|
-
level: "error",
|
|
11235
|
-
message: `Error sending user invite email: ${error2}`
|
|
11236
|
-
});
|
|
11237
11281
|
});
|
|
11238
11282
|
await session?.commitTransaction();
|
|
11239
11283
|
return "Successfully created organization with verification.";
|
|
11240
11284
|
} catch (error2) {
|
|
11241
11285
|
await session?.abortTransaction();
|
|
11242
|
-
|
|
11286
|
+
if (error2 instanceof import_utils58.AppError) {
|
|
11287
|
+
throw error2;
|
|
11288
|
+
} else {
|
|
11289
|
+
throw new import_utils58.InternalServerError(
|
|
11290
|
+
"Failed to create organization with verification."
|
|
11291
|
+
);
|
|
11292
|
+
}
|
|
11243
11293
|
} finally {
|
|
11244
11294
|
await session?.endSession();
|
|
11245
11295
|
}
|
|
@@ -12133,16 +12183,11 @@ function useVerificationService() {
|
|
|
12133
12183
|
},
|
|
12134
12184
|
filePath: filePath2
|
|
12135
12185
|
});
|
|
12136
|
-
mailer.sendMail({
|
|
12186
|
+
await mailer.sendMail({
|
|
12137
12187
|
to: email,
|
|
12138
12188
|
subject: "Member Invite",
|
|
12139
12189
|
html: emailContent2,
|
|
12140
12190
|
from: `"GoWeekdays" <support@goweekdays.com>`
|
|
12141
|
-
}).catch((error) => {
|
|
12142
|
-
import_utils62.logger.log({
|
|
12143
|
-
level: "error",
|
|
12144
|
-
message: `Error sending user invite email: ${error}`
|
|
12145
|
-
});
|
|
12146
12191
|
});
|
|
12147
12192
|
return res2;
|
|
12148
12193
|
}
|
|
@@ -12155,20 +12200,19 @@ function useVerificationService() {
|
|
|
12155
12200
|
},
|
|
12156
12201
|
filePath
|
|
12157
12202
|
});
|
|
12158
|
-
mailer.sendMail({
|
|
12203
|
+
await mailer.sendMail({
|
|
12159
12204
|
to: email,
|
|
12160
12205
|
subject: "User Invite",
|
|
12161
12206
|
html: emailContent,
|
|
12162
12207
|
from: `"GoWeekdays" <support@goweekdays.com>`
|
|
12163
|
-
}).catch((error) => {
|
|
12164
|
-
import_utils62.logger.log({
|
|
12165
|
-
level: "error",
|
|
12166
|
-
message: `Error sending user invite email: ${error}`
|
|
12167
|
-
});
|
|
12168
12208
|
});
|
|
12169
12209
|
return res;
|
|
12170
12210
|
} catch (error) {
|
|
12171
|
-
|
|
12211
|
+
if (error instanceof import_utils62.AppError) {
|
|
12212
|
+
throw error;
|
|
12213
|
+
} else {
|
|
12214
|
+
throw new import_utils62.InternalServerError("Failed to create user invitation.");
|
|
12215
|
+
}
|
|
12172
12216
|
}
|
|
12173
12217
|
}
|
|
12174
12218
|
async function createForgetPassword(email) {
|
|
@@ -12190,20 +12234,19 @@ function useVerificationService() {
|
|
|
12190
12234
|
},
|
|
12191
12235
|
filePath
|
|
12192
12236
|
});
|
|
12193
|
-
mailer.sendMail({
|
|
12237
|
+
await mailer.sendMail({
|
|
12194
12238
|
to: email,
|
|
12195
12239
|
subject: "Forget Password",
|
|
12196
12240
|
from: `"GoWeekdays" <support@goweekdays.com>`,
|
|
12197
12241
|
html: emailContent
|
|
12198
|
-
}).catch((error) => {
|
|
12199
|
-
import_utils62.logger.log({
|
|
12200
|
-
level: "error",
|
|
12201
|
-
message: `Error sending forget password email: ${error}`
|
|
12202
|
-
});
|
|
12203
12242
|
});
|
|
12204
12243
|
return "Successfully created a link to reset password. Please check your email.";
|
|
12205
12244
|
} catch (error) {
|
|
12206
|
-
|
|
12245
|
+
if (error instanceof import_utils62.AppError) {
|
|
12246
|
+
throw error;
|
|
12247
|
+
} else {
|
|
12248
|
+
throw new import_utils62.InternalServerError("Failed to create forget password link.");
|
|
12249
|
+
}
|
|
12207
12250
|
}
|
|
12208
12251
|
}
|
|
12209
12252
|
async function getById(id) {
|
|
@@ -12380,20 +12423,19 @@ function useVerificationService() {
|
|
|
12380
12423
|
},
|
|
12381
12424
|
filePath
|
|
12382
12425
|
});
|
|
12383
|
-
mailer.sendMail({
|
|
12426
|
+
await mailer.sendMail({
|
|
12384
12427
|
to: email,
|
|
12385
12428
|
subject: "Sign Up Verification",
|
|
12386
12429
|
html: emailContent,
|
|
12387
12430
|
from: `"GoWeekdays" <support@goweekdays.com>`
|
|
12388
|
-
}).catch((error) => {
|
|
12389
|
-
import_utils62.logger.log({
|
|
12390
|
-
level: "error",
|
|
12391
|
-
message: `Error sending user invite email: ${error}`
|
|
12392
|
-
});
|
|
12393
12431
|
});
|
|
12394
12432
|
return res;
|
|
12395
12433
|
} catch (error) {
|
|
12396
|
-
|
|
12434
|
+
if (error instanceof import_utils62.AppError) {
|
|
12435
|
+
throw error;
|
|
12436
|
+
} else {
|
|
12437
|
+
throw new import_utils62.InternalServerError("Failed to create sign up verification.");
|
|
12438
|
+
}
|
|
12397
12439
|
}
|
|
12398
12440
|
}
|
|
12399
12441
|
const { getByOrg } = useSubscriptionRepo();
|
|
@@ -12472,16 +12514,11 @@ function useVerificationService() {
|
|
|
12472
12514
|
},
|
|
12473
12515
|
filePath: filePath2
|
|
12474
12516
|
});
|
|
12475
|
-
mailer.sendMail({
|
|
12517
|
+
await mailer.sendMail({
|
|
12476
12518
|
to: value.email,
|
|
12477
12519
|
subject: "Member Invite",
|
|
12478
12520
|
html: emailContent2,
|
|
12479
12521
|
from: `"GoWeekdays" <support@goweekdays.com>`
|
|
12480
|
-
}).catch((error2) => {
|
|
12481
|
-
import_utils62.logger.log({
|
|
12482
|
-
level: "error",
|
|
12483
|
-
message: `Error sending user invite email: ${error2}`
|
|
12484
|
-
});
|
|
12485
12522
|
});
|
|
12486
12523
|
return verificationId2;
|
|
12487
12524
|
}
|
|
@@ -12494,7 +12531,7 @@ function useVerificationService() {
|
|
|
12494
12531
|
},
|
|
12495
12532
|
filePath
|
|
12496
12533
|
});
|
|
12497
|
-
mailer.sendMail({
|
|
12534
|
+
await mailer.sendMail({
|
|
12498
12535
|
to: value.email,
|
|
12499
12536
|
subject: "User Invite",
|
|
12500
12537
|
html: emailContent,
|
|
@@ -22470,6 +22507,2516 @@ function useJournalLineController() {
|
|
|
22470
22507
|
getById
|
|
22471
22508
|
};
|
|
22472
22509
|
}
|
|
22510
|
+
|
|
22511
|
+
// src/resources/asset-item/asset.item.model.ts
|
|
22512
|
+
var import_utils115 = require("@goweekdays/utils");
|
|
22513
|
+
var import_joi96 = __toESM(require("joi"));
|
|
22514
|
+
var import_mongodb58 = require("mongodb");
|
|
22515
|
+
var assetItemCategories = [
|
|
22516
|
+
"supply",
|
|
22517
|
+
"tool",
|
|
22518
|
+
"vehicle",
|
|
22519
|
+
"facility",
|
|
22520
|
+
"equipment"
|
|
22521
|
+
];
|
|
22522
|
+
var assetItemClasses = [
|
|
22523
|
+
"consumable",
|
|
22524
|
+
"semi_expendable",
|
|
22525
|
+
"ppe"
|
|
22526
|
+
];
|
|
22527
|
+
var assetItemPurposes = ["internal", "for-sale", "for-rent"];
|
|
22528
|
+
var assetItemTrackingTypes = ["quantity", "individual"];
|
|
22529
|
+
var assetItemStatuses = ["active", "archived"];
|
|
22530
|
+
var schemaAssetItemBase = {
|
|
22531
|
+
orgId: import_joi96.default.string().hex().length(24).required(),
|
|
22532
|
+
name: import_joi96.default.string().required(),
|
|
22533
|
+
sku: import_joi96.default.string().optional().allow("", null).default(""),
|
|
22534
|
+
price: import_joi96.default.number().min(0).optional().allow("", null).default(0),
|
|
22535
|
+
unit: import_joi96.default.string().required(),
|
|
22536
|
+
description: import_joi96.default.string().optional().allow("", null),
|
|
22537
|
+
assetCategory: import_joi96.default.string().valid(...assetItemCategories).required(),
|
|
22538
|
+
assetClass: import_joi96.default.string().valid(...assetItemClasses).optional().allow("", null),
|
|
22539
|
+
trackingType: import_joi96.default.string().valid(...assetItemTrackingTypes).required(),
|
|
22540
|
+
purpose: import_joi96.default.string().valid(...assetItemPurposes).required(),
|
|
22541
|
+
itemRefId: import_joi96.default.string().hex().length(24).optional().allow("", null),
|
|
22542
|
+
remarks: import_joi96.default.string().optional().allow("", null),
|
|
22543
|
+
departmentId: import_joi96.default.string().hex().length(24).required(),
|
|
22544
|
+
categoryId: import_joi96.default.string().hex().length(24).optional().allow("", null),
|
|
22545
|
+
subcategoryId: import_joi96.default.string().hex().length(24).optional().allow("", null),
|
|
22546
|
+
categoryPath: import_joi96.default.string().optional().allow("", null),
|
|
22547
|
+
brand: import_joi96.default.string().optional().allow("", null),
|
|
22548
|
+
tags: import_joi96.default.array().items(
|
|
22549
|
+
import_joi96.default.object({
|
|
22550
|
+
id: import_joi96.default.string().required(),
|
|
22551
|
+
name: import_joi96.default.string().required()
|
|
22552
|
+
})
|
|
22553
|
+
).optional().default([]),
|
|
22554
|
+
quantityOnHand: import_joi96.default.number().min(0).optional().default(0)
|
|
22555
|
+
};
|
|
22556
|
+
var schemaAssetItem = import_joi96.default.object({
|
|
22557
|
+
...schemaAssetItemBase,
|
|
22558
|
+
departmentName: import_joi96.default.string().required(),
|
|
22559
|
+
categoryName: import_joi96.default.string().optional().allow("", null),
|
|
22560
|
+
subcategoryName: import_joi96.default.string().optional().allow("", null),
|
|
22561
|
+
status: import_joi96.default.string().valid(...assetItemStatuses).optional().allow("", null)
|
|
22562
|
+
});
|
|
22563
|
+
var schemaAssetItemCreate = import_joi96.default.object({
|
|
22564
|
+
...schemaAssetItemBase,
|
|
22565
|
+
tags: import_joi96.default.array().items(
|
|
22566
|
+
import_joi96.default.object({
|
|
22567
|
+
id: import_joi96.default.string().required(),
|
|
22568
|
+
name: import_joi96.default.string().required()
|
|
22569
|
+
})
|
|
22570
|
+
).optional().default([])
|
|
22571
|
+
}).custom((value, helpers) => {
|
|
22572
|
+
if (value.subcategoryId && !value.categoryId) {
|
|
22573
|
+
return helpers.error("any.custom", {
|
|
22574
|
+
message: "categoryId is required when subcategoryId is provided."
|
|
22575
|
+
});
|
|
22576
|
+
}
|
|
22577
|
+
return value;
|
|
22578
|
+
}, "subcategory-requires-category");
|
|
22579
|
+
var schemaAssetItemUpdate = import_joi96.default.object({
|
|
22580
|
+
name: import_joi96.default.string().optional(),
|
|
22581
|
+
description: import_joi96.default.string().optional().allow("", null),
|
|
22582
|
+
assetCategory: import_joi96.default.string().valid(...assetItemCategories).optional(),
|
|
22583
|
+
assetClass: import_joi96.default.string().valid(...assetItemClasses).optional(),
|
|
22584
|
+
brand: import_joi96.default.string().optional().allow("", null),
|
|
22585
|
+
sku: import_joi96.default.string().optional().allow("", null),
|
|
22586
|
+
price: import_joi96.default.number().min(0).optional().allow("", null),
|
|
22587
|
+
tags: import_joi96.default.array().items(
|
|
22588
|
+
import_joi96.default.object({
|
|
22589
|
+
id: import_joi96.default.string().required(),
|
|
22590
|
+
name: import_joi96.default.string().required()
|
|
22591
|
+
})
|
|
22592
|
+
).optional(),
|
|
22593
|
+
quantityOnHand: import_joi96.default.number().min(0).optional()
|
|
22594
|
+
});
|
|
22595
|
+
function modelAssetItem(data) {
|
|
22596
|
+
const { error } = schemaAssetItem.validate(data);
|
|
22597
|
+
if (error) {
|
|
22598
|
+
throw new import_utils115.BadRequestError(`Invalid asset item data: ${error.message}`);
|
|
22599
|
+
}
|
|
22600
|
+
try {
|
|
22601
|
+
data.orgId = new import_mongodb58.ObjectId(data.orgId);
|
|
22602
|
+
} catch (error2) {
|
|
22603
|
+
throw new import_utils115.BadRequestError("Invalid orgId format.");
|
|
22604
|
+
}
|
|
22605
|
+
try {
|
|
22606
|
+
data.departmentId = new import_mongodb58.ObjectId(data.departmentId);
|
|
22607
|
+
} catch (error2) {
|
|
22608
|
+
throw new import_utils115.BadRequestError("Invalid departmentId format.");
|
|
22609
|
+
}
|
|
22610
|
+
if (data.categoryId) {
|
|
22611
|
+
try {
|
|
22612
|
+
data.categoryId = new import_mongodb58.ObjectId(data.categoryId);
|
|
22613
|
+
} catch (error2) {
|
|
22614
|
+
throw new import_utils115.BadRequestError("Invalid categoryId format.");
|
|
22615
|
+
}
|
|
22616
|
+
}
|
|
22617
|
+
if (data.subcategoryId) {
|
|
22618
|
+
try {
|
|
22619
|
+
data.subcategoryId = new import_mongodb58.ObjectId(data.subcategoryId);
|
|
22620
|
+
} catch (error2) {
|
|
22621
|
+
throw new import_utils115.BadRequestError("Invalid subcategoryId format.");
|
|
22622
|
+
}
|
|
22623
|
+
}
|
|
22624
|
+
if (data.itemRefId) {
|
|
22625
|
+
try {
|
|
22626
|
+
data.itemRefId = new import_mongodb58.ObjectId(data.itemRefId);
|
|
22627
|
+
} catch (error2) {
|
|
22628
|
+
throw new import_utils115.BadRequestError("Invalid itemRefId format.");
|
|
22629
|
+
}
|
|
22630
|
+
}
|
|
22631
|
+
if (data.purpose === "for-sale" && (data.price === void 0 || data.price === null || data.price === 0)) {
|
|
22632
|
+
throw new import_utils115.BadRequestError(
|
|
22633
|
+
"Price must be greater than 0 for items marked as 'for-sale'."
|
|
22634
|
+
);
|
|
22635
|
+
}
|
|
22636
|
+
return {
|
|
22637
|
+
_id: data._id,
|
|
22638
|
+
orgId: data.orgId,
|
|
22639
|
+
name: data.name,
|
|
22640
|
+
description: data.description ?? "",
|
|
22641
|
+
assetCategory: data.assetCategory,
|
|
22642
|
+
assetClass: data.assetClass,
|
|
22643
|
+
unit: data.unit ?? "",
|
|
22644
|
+
price: data.price ?? 0,
|
|
22645
|
+
sku: data.sku ?? "",
|
|
22646
|
+
brand: data.brand ?? "",
|
|
22647
|
+
trackingType: data.trackingType,
|
|
22648
|
+
purpose: data.purpose,
|
|
22649
|
+
itemRefId: data.itemRefId ?? "",
|
|
22650
|
+
remarks: data.remarks ?? "",
|
|
22651
|
+
departmentId: data.departmentId,
|
|
22652
|
+
departmentName: data.departmentName,
|
|
22653
|
+
categoryId: data.categoryId ?? "",
|
|
22654
|
+
categoryName: data.categoryName ?? "",
|
|
22655
|
+
subcategoryId: data.subcategoryId ?? "",
|
|
22656
|
+
subcategoryName: data.subcategoryName ?? "",
|
|
22657
|
+
categoryPath: data.categoryPath ?? "",
|
|
22658
|
+
tags: data.tags ?? [],
|
|
22659
|
+
quantityOnHand: data.quantityOnHand ?? 0,
|
|
22660
|
+
status: data.status ?? "active",
|
|
22661
|
+
createdAt: data.createdAt ?? /* @__PURE__ */ new Date(),
|
|
22662
|
+
updatedAt: data.updatedAt ?? "",
|
|
22663
|
+
deletedAt: data.deletedAt ?? ""
|
|
22664
|
+
};
|
|
22665
|
+
}
|
|
22666
|
+
|
|
22667
|
+
// src/resources/asset-item/asset.item.repository.ts
|
|
22668
|
+
var import_utils116 = require("@goweekdays/utils");
|
|
22669
|
+
var import_mongodb59 = require("mongodb");
|
|
22670
|
+
function useAssetItemRepo() {
|
|
22671
|
+
const namespace_collection = "asset.items";
|
|
22672
|
+
const repo = (0, import_utils116.useRepo)(namespace_collection);
|
|
22673
|
+
async function createIndexes() {
|
|
22674
|
+
try {
|
|
22675
|
+
await repo.collection.createIndexes([
|
|
22676
|
+
{ key: { categoryPath: 1 } },
|
|
22677
|
+
{ key: { departmentId: 1 } },
|
|
22678
|
+
{ key: { categoryId: 1 } },
|
|
22679
|
+
{ key: { subcategoryId: 1 } },
|
|
22680
|
+
{ key: { brand: 1 } },
|
|
22681
|
+
{ key: { "tags.name": 1 } },
|
|
22682
|
+
{ key: { assetCategory: 1 } },
|
|
22683
|
+
{ key: { purpose: 1 } },
|
|
22684
|
+
{ key: { trackingType: 1 } },
|
|
22685
|
+
{ key: { status: 1 } },
|
|
22686
|
+
{ key: { name: "text" } }
|
|
22687
|
+
]);
|
|
22688
|
+
} catch (error) {
|
|
22689
|
+
throw new import_utils116.BadRequestError("Failed to create asset item indexes.");
|
|
22690
|
+
}
|
|
22691
|
+
}
|
|
22692
|
+
async function add(value, session) {
|
|
22693
|
+
try {
|
|
22694
|
+
value = modelAssetItem(value);
|
|
22695
|
+
const res = await repo.collection.insertOne(value, { session });
|
|
22696
|
+
repo.delCachedData();
|
|
22697
|
+
return res.insertedId;
|
|
22698
|
+
} catch (error) {
|
|
22699
|
+
if (error instanceof import_utils116.AppError) {
|
|
22700
|
+
throw error;
|
|
22701
|
+
}
|
|
22702
|
+
import_utils116.logger.log({ level: "error", message: error.message });
|
|
22703
|
+
const isDuplicated = error.message.includes("duplicate");
|
|
22704
|
+
if (isDuplicated) {
|
|
22705
|
+
throw new import_utils116.BadRequestError("Asset item name already exists.");
|
|
22706
|
+
}
|
|
22707
|
+
throw new import_utils116.InternalServerError("Failed to create asset item.");
|
|
22708
|
+
}
|
|
22709
|
+
}
|
|
22710
|
+
async function getAll({
|
|
22711
|
+
search = "",
|
|
22712
|
+
page = 1,
|
|
22713
|
+
limit = 10,
|
|
22714
|
+
status: status2 = "active",
|
|
22715
|
+
assetCategory = "",
|
|
22716
|
+
trackingType = "",
|
|
22717
|
+
purpose = "",
|
|
22718
|
+
departmentId = "",
|
|
22719
|
+
categoryId = "",
|
|
22720
|
+
subcategoryId = "",
|
|
22721
|
+
brand = "",
|
|
22722
|
+
tags = []
|
|
22723
|
+
} = {}) {
|
|
22724
|
+
page = page > 0 ? page - 1 : 0;
|
|
22725
|
+
const query = { status: status2 };
|
|
22726
|
+
if (search) {
|
|
22727
|
+
query.$text = { $search: search };
|
|
22728
|
+
}
|
|
22729
|
+
if (assetCategory) {
|
|
22730
|
+
query.assetCategory = assetCategory;
|
|
22731
|
+
}
|
|
22732
|
+
if (trackingType) {
|
|
22733
|
+
query.trackingType = trackingType;
|
|
22734
|
+
}
|
|
22735
|
+
if (purpose) {
|
|
22736
|
+
query.purpose = purpose;
|
|
22737
|
+
}
|
|
22738
|
+
if (departmentId) {
|
|
22739
|
+
query.departmentId = departmentId;
|
|
22740
|
+
}
|
|
22741
|
+
if (categoryId) {
|
|
22742
|
+
query.categoryId = categoryId;
|
|
22743
|
+
}
|
|
22744
|
+
if (subcategoryId) {
|
|
22745
|
+
query.subcategoryId = subcategoryId;
|
|
22746
|
+
}
|
|
22747
|
+
if (brand) {
|
|
22748
|
+
query.brand = brand;
|
|
22749
|
+
}
|
|
22750
|
+
if (tags.length > 0) {
|
|
22751
|
+
query["tags.name"] = { $all: tags };
|
|
22752
|
+
}
|
|
22753
|
+
const cacheKey = (0, import_utils116.makeCacheKey)(namespace_collection, {
|
|
22754
|
+
search,
|
|
22755
|
+
page,
|
|
22756
|
+
limit,
|
|
22757
|
+
status: status2,
|
|
22758
|
+
assetCategory,
|
|
22759
|
+
trackingType,
|
|
22760
|
+
purpose,
|
|
22761
|
+
departmentId,
|
|
22762
|
+
categoryId,
|
|
22763
|
+
subcategoryId,
|
|
22764
|
+
brand,
|
|
22765
|
+
tags: tags.join(","),
|
|
22766
|
+
tag: "getAll"
|
|
22767
|
+
});
|
|
22768
|
+
try {
|
|
22769
|
+
const cached = await repo.getCache(cacheKey);
|
|
22770
|
+
if (cached)
|
|
22771
|
+
return cached;
|
|
22772
|
+
const items = await repo.collection.aggregate([
|
|
22773
|
+
{ $match: query },
|
|
22774
|
+
{ $sort: { _id: -1 } },
|
|
22775
|
+
{ $skip: page * limit },
|
|
22776
|
+
{ $limit: limit }
|
|
22777
|
+
]).toArray();
|
|
22778
|
+
const length = await repo.collection.countDocuments(query);
|
|
22779
|
+
const data = (0, import_utils116.paginate)(items, page, limit, length);
|
|
22780
|
+
repo.setCache(cacheKey, data, 600);
|
|
22781
|
+
return data;
|
|
22782
|
+
} catch (error) {
|
|
22783
|
+
import_utils116.logger.log({ level: "error", message: `${error}` });
|
|
22784
|
+
throw error;
|
|
22785
|
+
}
|
|
22786
|
+
}
|
|
22787
|
+
async function getById(id) {
|
|
22788
|
+
const cacheKey = (0, import_utils116.makeCacheKey)(namespace_collection, {
|
|
22789
|
+
id,
|
|
22790
|
+
tag: "by-id"
|
|
22791
|
+
});
|
|
22792
|
+
try {
|
|
22793
|
+
const cached = await repo.getCache(cacheKey);
|
|
22794
|
+
if (cached)
|
|
22795
|
+
return cached;
|
|
22796
|
+
const result = await repo.collection.findOne({
|
|
22797
|
+
_id: new import_mongodb59.ObjectId(id)
|
|
22798
|
+
});
|
|
22799
|
+
if (!result) {
|
|
22800
|
+
throw new import_utils116.BadRequestError("Asset item not found.");
|
|
22801
|
+
}
|
|
22802
|
+
repo.setCache(cacheKey, result, 300);
|
|
22803
|
+
return result;
|
|
22804
|
+
} catch (error) {
|
|
22805
|
+
if (error instanceof import_utils116.AppError)
|
|
22806
|
+
throw error;
|
|
22807
|
+
throw new import_utils116.InternalServerError("Failed to get asset item.");
|
|
22808
|
+
}
|
|
22809
|
+
}
|
|
22810
|
+
async function updateById(id, value) {
|
|
22811
|
+
try {
|
|
22812
|
+
const result = await repo.collection.findOneAndUpdate(
|
|
22813
|
+
{ _id: new import_mongodb59.ObjectId(id) },
|
|
22814
|
+
{ $set: { ...value, updatedAt: /* @__PURE__ */ new Date() } },
|
|
22815
|
+
{ returnDocument: "after" }
|
|
22816
|
+
);
|
|
22817
|
+
if (!result) {
|
|
22818
|
+
throw new import_utils116.BadRequestError("Asset item not found.");
|
|
22819
|
+
}
|
|
22820
|
+
repo.delCachedData();
|
|
22821
|
+
return result;
|
|
22822
|
+
} catch (error) {
|
|
22823
|
+
if (error instanceof import_utils116.AppError)
|
|
22824
|
+
throw error;
|
|
22825
|
+
throw new import_utils116.InternalServerError("Failed to update asset item.");
|
|
22826
|
+
}
|
|
22827
|
+
}
|
|
22828
|
+
async function incrementQuantity(id, amount, session) {
|
|
22829
|
+
try {
|
|
22830
|
+
const result = await repo.collection.findOneAndUpdate(
|
|
22831
|
+
{ _id: new import_mongodb59.ObjectId(id) },
|
|
22832
|
+
{
|
|
22833
|
+
$inc: { quantityOnHand: amount },
|
|
22834
|
+
$set: { updatedAt: /* @__PURE__ */ new Date() }
|
|
22835
|
+
},
|
|
22836
|
+
{ returnDocument: "after", session }
|
|
22837
|
+
);
|
|
22838
|
+
if (!result) {
|
|
22839
|
+
throw new import_utils116.BadRequestError("Asset item not found.");
|
|
22840
|
+
}
|
|
22841
|
+
repo.delCachedData();
|
|
22842
|
+
return result;
|
|
22843
|
+
} catch (error) {
|
|
22844
|
+
if (error instanceof import_utils116.AppError)
|
|
22845
|
+
throw error;
|
|
22846
|
+
throw new import_utils116.InternalServerError("Failed to update asset item quantity.");
|
|
22847
|
+
}
|
|
22848
|
+
}
|
|
22849
|
+
async function deleteById(id) {
|
|
22850
|
+
try {
|
|
22851
|
+
const result = await repo.collection.findOneAndUpdate(
|
|
22852
|
+
{ _id: new import_mongodb59.ObjectId(id) },
|
|
22853
|
+
{ $set: { deletedAt: /* @__PURE__ */ new Date(), status: "archived" } },
|
|
22854
|
+
{ returnDocument: "after" }
|
|
22855
|
+
);
|
|
22856
|
+
if (!result) {
|
|
22857
|
+
throw new import_utils116.BadRequestError("Asset item not found.");
|
|
22858
|
+
}
|
|
22859
|
+
repo.delCachedData();
|
|
22860
|
+
return result;
|
|
22861
|
+
} catch (error) {
|
|
22862
|
+
if (error instanceof import_utils116.AppError)
|
|
22863
|
+
throw error;
|
|
22864
|
+
throw new import_utils116.InternalServerError("Failed to delete asset item.");
|
|
22865
|
+
}
|
|
22866
|
+
}
|
|
22867
|
+
return {
|
|
22868
|
+
createIndexes,
|
|
22869
|
+
add,
|
|
22870
|
+
getAll,
|
|
22871
|
+
getById,
|
|
22872
|
+
updateById,
|
|
22873
|
+
incrementQuantity,
|
|
22874
|
+
deleteById
|
|
22875
|
+
};
|
|
22876
|
+
}
|
|
22877
|
+
|
|
22878
|
+
// src/resources/asset-item/asset.item.service.ts
|
|
22879
|
+
var import_utils120 = require("@goweekdays/utils");
|
|
22880
|
+
|
|
22881
|
+
// src/resources/tag/tag.repository.ts
|
|
22882
|
+
var import_utils117 = require("@goweekdays/utils");
|
|
22883
|
+
|
|
22884
|
+
// src/resources/tag/tag.model.ts
|
|
22885
|
+
var import_joi97 = __toESM(require("joi"));
|
|
22886
|
+
var import_mongodb60 = require("mongodb");
|
|
22887
|
+
var schemaTagFields = {
|
|
22888
|
+
name: import_joi97.default.string().required(),
|
|
22889
|
+
type: import_joi97.default.string().valid("public", "private").required(),
|
|
22890
|
+
orgId: import_joi97.default.string().hex().optional().allow("", null),
|
|
22891
|
+
categoryPath: import_joi97.default.string().optional().allow("", null),
|
|
22892
|
+
status: import_joi97.default.string().valid("active", "pending").optional()
|
|
22893
|
+
};
|
|
22894
|
+
var schemaTagCreate = import_joi97.default.object(schemaTagFields);
|
|
22895
|
+
var schemaTagStd = import_joi97.default.object({
|
|
22896
|
+
...schemaTagFields,
|
|
22897
|
+
normalizedName: import_joi97.default.string().required(),
|
|
22898
|
+
usageCount: import_joi97.default.number().integer().min(0).optional()
|
|
22899
|
+
});
|
|
22900
|
+
var schemaTagUpdate = import_joi97.default.object({
|
|
22901
|
+
name: import_joi97.default.string().optional(),
|
|
22902
|
+
type: import_joi97.default.string().valid("public", "private").optional(),
|
|
22903
|
+
orgId: import_joi97.default.string().hex().optional().allow("", null),
|
|
22904
|
+
categoryPath: import_joi97.default.string().optional().allow("", null),
|
|
22905
|
+
status: import_joi97.default.string().valid("active", "pending").optional()
|
|
22906
|
+
}).min(1);
|
|
22907
|
+
function modelTag(data) {
|
|
22908
|
+
const { error } = schemaTagStd.validate(data, { allowUnknown: false });
|
|
22909
|
+
if (error) {
|
|
22910
|
+
throw new Error(`Invalid tag data: ${error.message}`);
|
|
22911
|
+
}
|
|
22912
|
+
if (data.orgId) {
|
|
22913
|
+
try {
|
|
22914
|
+
data.orgId = new import_mongodb60.ObjectId(data.orgId);
|
|
22915
|
+
} catch (error2) {
|
|
22916
|
+
throw new Error(`Invalid orgId: ${data.orgId}`);
|
|
22917
|
+
}
|
|
22918
|
+
}
|
|
22919
|
+
return {
|
|
22920
|
+
_id: data._id,
|
|
22921
|
+
name: data.name,
|
|
22922
|
+
normalizedName: data.normalizedName,
|
|
22923
|
+
type: data.type,
|
|
22924
|
+
orgId: data.orgId ?? "",
|
|
22925
|
+
categoryPath: data.categoryPath ?? "",
|
|
22926
|
+
status: data.status ?? "pending",
|
|
22927
|
+
usageCount: data.usageCount ?? 0,
|
|
22928
|
+
createdAt: data.createdAt ?? /* @__PURE__ */ new Date(),
|
|
22929
|
+
updatedAt: data.updatedAt ?? ""
|
|
22930
|
+
};
|
|
22931
|
+
}
|
|
22932
|
+
|
|
22933
|
+
// src/resources/tag/tag.repository.ts
|
|
22934
|
+
var import_mongodb61 = require("mongodb");
|
|
22935
|
+
var import_joi98 = __toESM(require("joi"));
|
|
22936
|
+
function useTagRepo() {
|
|
22937
|
+
const namespace_collection = "tags";
|
|
22938
|
+
const repo = (0, import_utils117.useRepo)(namespace_collection);
|
|
22939
|
+
async function createIndexes() {
|
|
22940
|
+
try {
|
|
22941
|
+
await repo.collection.createIndexes([
|
|
22942
|
+
{ key: { name: 1 } },
|
|
22943
|
+
{ key: { normalizedName: 1 } },
|
|
22944
|
+
{ key: { type: 1 } },
|
|
22945
|
+
{ key: { orgId: 1 } },
|
|
22946
|
+
{ key: { status: 1 } },
|
|
22947
|
+
{ key: { categoryPath: 1 } },
|
|
22948
|
+
{
|
|
22949
|
+
key: { normalizedName: 1, orgId: 1 },
|
|
22950
|
+
unique: true,
|
|
22951
|
+
name: "unique_normalized_name_per_org"
|
|
22952
|
+
}
|
|
22953
|
+
]);
|
|
22954
|
+
} catch (error) {
|
|
22955
|
+
throw new Error("Failed to create index on tags.");
|
|
22956
|
+
}
|
|
22957
|
+
}
|
|
22958
|
+
async function add(value, session) {
|
|
22959
|
+
try {
|
|
22960
|
+
value = modelTag(value);
|
|
22961
|
+
const res = await repo.collection.insertOne(value, { session });
|
|
22962
|
+
repo.delCachedData();
|
|
22963
|
+
return res.insertedId;
|
|
22964
|
+
} catch (error) {
|
|
22965
|
+
import_utils117.logger.log({
|
|
22966
|
+
level: "error",
|
|
22967
|
+
message: error.message
|
|
22968
|
+
});
|
|
22969
|
+
const isDuplicate = /normalizedName|unique/i.test(error.message);
|
|
22970
|
+
if (isDuplicate) {
|
|
22971
|
+
throw new import_utils117.BadRequestError("A tag with this name already exists.");
|
|
22972
|
+
}
|
|
22973
|
+
throw new import_utils117.BadRequestError(`Failed to create tag: ${error.message}`);
|
|
22974
|
+
}
|
|
22975
|
+
}
|
|
22976
|
+
async function getAll(options) {
|
|
22977
|
+
const validation = import_joi98.default.object({
|
|
22978
|
+
page: import_joi98.default.number().min(1).optional().allow("", null),
|
|
22979
|
+
limit: import_joi98.default.number().min(1).optional().allow("", null),
|
|
22980
|
+
search: import_joi98.default.string().optional().allow("", null),
|
|
22981
|
+
type: import_joi98.default.string().valid("public", "private").optional().allow("", null),
|
|
22982
|
+
orgId: import_joi98.default.string().hex().optional().allow("", null),
|
|
22983
|
+
status: import_joi98.default.string().valid("active", "pending").optional().allow("", null),
|
|
22984
|
+
categoryPath: import_joi98.default.string().optional().allow("", null)
|
|
22985
|
+
});
|
|
22986
|
+
const { error } = validation.validate(options);
|
|
22987
|
+
if (error) {
|
|
22988
|
+
throw new import_utils117.BadRequestError(error.message);
|
|
22989
|
+
}
|
|
22990
|
+
options.page = options.page && options.page > 0 ? options.page - 1 : 0;
|
|
22991
|
+
options.limit = options.limit ?? 10;
|
|
22992
|
+
const query = {};
|
|
22993
|
+
const cacheKeyOptions = {
|
|
22994
|
+
limit: options.limit,
|
|
22995
|
+
page: options.page
|
|
22996
|
+
};
|
|
22997
|
+
if (options.search) {
|
|
22998
|
+
query.name = { $regex: options.search, $options: "i" };
|
|
22999
|
+
cacheKeyOptions.search = options.search;
|
|
23000
|
+
}
|
|
23001
|
+
if (options.type) {
|
|
23002
|
+
query.type = options.type;
|
|
23003
|
+
cacheKeyOptions.type = options.type;
|
|
23004
|
+
}
|
|
23005
|
+
if (options.orgId) {
|
|
23006
|
+
query.orgId = new import_mongodb61.ObjectId(options.orgId);
|
|
23007
|
+
cacheKeyOptions.orgId = options.orgId;
|
|
23008
|
+
}
|
|
23009
|
+
if (options.status) {
|
|
23010
|
+
query.status = options.status;
|
|
23011
|
+
cacheKeyOptions.status = options.status;
|
|
23012
|
+
}
|
|
23013
|
+
if (options.categoryPath) {
|
|
23014
|
+
query.categoryPath = options.categoryPath;
|
|
23015
|
+
cacheKeyOptions.categoryPath = options.categoryPath;
|
|
23016
|
+
}
|
|
23017
|
+
const cacheKey = (0, import_utils117.makeCacheKey)(namespace_collection, cacheKeyOptions);
|
|
23018
|
+
try {
|
|
23019
|
+
const cached = await repo.getCache(cacheKey);
|
|
23020
|
+
if (cached) {
|
|
23021
|
+
return cached;
|
|
23022
|
+
}
|
|
23023
|
+
const items = await repo.collection.aggregate([
|
|
23024
|
+
{ $match: query },
|
|
23025
|
+
{ $sort: { name: 1 } },
|
|
23026
|
+
{ $skip: options.page * options.limit },
|
|
23027
|
+
{ $limit: options.limit },
|
|
23028
|
+
{
|
|
23029
|
+
$project: {
|
|
23030
|
+
_id: 1,
|
|
23031
|
+
name: 1,
|
|
23032
|
+
normalizedName: 1,
|
|
23033
|
+
type: 1,
|
|
23034
|
+
orgId: 1,
|
|
23035
|
+
categoryPath: 1,
|
|
23036
|
+
status: 1,
|
|
23037
|
+
usageCount: 1
|
|
23038
|
+
}
|
|
23039
|
+
}
|
|
23040
|
+
]).toArray();
|
|
23041
|
+
const length = await repo.collection.countDocuments(query);
|
|
23042
|
+
const data = (0, import_utils117.paginate)(items, options.page, options.limit, length);
|
|
23043
|
+
repo.setCache(cacheKey, data, 600).then(() => {
|
|
23044
|
+
import_utils117.logger.log({
|
|
23045
|
+
level: "info",
|
|
23046
|
+
message: `Cache set for getAll tags: ${cacheKey}`
|
|
23047
|
+
});
|
|
23048
|
+
}).catch((err) => {
|
|
23049
|
+
import_utils117.logger.log({
|
|
23050
|
+
level: "error",
|
|
23051
|
+
message: `Failed to set cache for getAll tags: ${err.message}`
|
|
23052
|
+
});
|
|
23053
|
+
});
|
|
23054
|
+
return data;
|
|
23055
|
+
} catch (error2) {
|
|
23056
|
+
import_utils117.logger.log({ level: "error", message: `${error2}` });
|
|
23057
|
+
throw error2;
|
|
23058
|
+
}
|
|
23059
|
+
}
|
|
23060
|
+
async function getById(_id) {
|
|
23061
|
+
try {
|
|
23062
|
+
_id = new import_mongodb61.ObjectId(_id);
|
|
23063
|
+
} catch (error) {
|
|
23064
|
+
throw new import_utils117.BadRequestError("Invalid ID.");
|
|
23065
|
+
}
|
|
23066
|
+
const cacheKey = (0, import_utils117.makeCacheKey)(namespace_collection, { _id: String(_id) });
|
|
23067
|
+
try {
|
|
23068
|
+
const cached = await repo.getCache(cacheKey);
|
|
23069
|
+
if (cached) {
|
|
23070
|
+
return cached;
|
|
23071
|
+
}
|
|
23072
|
+
const result = await repo.collection.findOne({ _id });
|
|
23073
|
+
if (!result) {
|
|
23074
|
+
throw new import_utils117.BadRequestError("Tag not found.");
|
|
23075
|
+
}
|
|
23076
|
+
repo.setCache(cacheKey, result, 300).then(() => {
|
|
23077
|
+
import_utils117.logger.log({
|
|
23078
|
+
level: "info",
|
|
23079
|
+
message: `Cache set for tag by id: ${cacheKey}`
|
|
23080
|
+
});
|
|
23081
|
+
}).catch((err) => {
|
|
23082
|
+
import_utils117.logger.log({
|
|
23083
|
+
level: "error",
|
|
23084
|
+
message: `Failed to set cache for tag by id: ${err.message}`
|
|
23085
|
+
});
|
|
23086
|
+
});
|
|
23087
|
+
return result;
|
|
23088
|
+
} catch (error) {
|
|
23089
|
+
if (error instanceof import_utils117.AppError) {
|
|
23090
|
+
throw error;
|
|
23091
|
+
} else {
|
|
23092
|
+
throw new import_utils117.InternalServerError("Failed to get tag.");
|
|
23093
|
+
}
|
|
23094
|
+
}
|
|
23095
|
+
}
|
|
23096
|
+
async function updateById(_id, options, session) {
|
|
23097
|
+
try {
|
|
23098
|
+
_id = new import_mongodb61.ObjectId(_id);
|
|
23099
|
+
} catch (error) {
|
|
23100
|
+
throw new import_utils117.BadRequestError("Invalid Tag ID.");
|
|
23101
|
+
}
|
|
23102
|
+
try {
|
|
23103
|
+
await repo.collection.updateOne(
|
|
23104
|
+
{ _id },
|
|
23105
|
+
{ $set: { ...options, updatedAt: /* @__PURE__ */ new Date() } },
|
|
23106
|
+
{ session }
|
|
23107
|
+
);
|
|
23108
|
+
repo.delCachedData();
|
|
23109
|
+
return "Successfully updated tag.";
|
|
23110
|
+
} catch (error) {
|
|
23111
|
+
const isDuplicate = /normalizedName|unique/i.test(error.message);
|
|
23112
|
+
if (isDuplicate) {
|
|
23113
|
+
throw new import_utils117.BadRequestError("A tag with this name already exists.");
|
|
23114
|
+
}
|
|
23115
|
+
throw new import_utils117.InternalServerError("Failed to update tag.");
|
|
23116
|
+
}
|
|
23117
|
+
}
|
|
23118
|
+
async function deleteById(_id, session) {
|
|
23119
|
+
try {
|
|
23120
|
+
_id = new import_mongodb61.ObjectId(_id);
|
|
23121
|
+
} catch (error) {
|
|
23122
|
+
throw new import_utils117.BadRequestError("Invalid ID.");
|
|
23123
|
+
}
|
|
23124
|
+
try {
|
|
23125
|
+
await repo.collection.deleteOne({ _id }, { session });
|
|
23126
|
+
repo.delCachedData();
|
|
23127
|
+
return "Successfully deleted tag.";
|
|
23128
|
+
} catch (error) {
|
|
23129
|
+
throw new import_utils117.InternalServerError("Failed to delete tag.");
|
|
23130
|
+
}
|
|
23131
|
+
}
|
|
23132
|
+
async function findByNormalizedNameAndOrg(normalizedName, orgId) {
|
|
23133
|
+
try {
|
|
23134
|
+
const query = { normalizedName };
|
|
23135
|
+
query.orgId = new import_mongodb61.ObjectId(orgId);
|
|
23136
|
+
return await repo.collection.findOne(query);
|
|
23137
|
+
} catch (error) {
|
|
23138
|
+
if (error instanceof import_utils117.AppError)
|
|
23139
|
+
throw error;
|
|
23140
|
+
throw new import_utils117.InternalServerError("Failed to find tag.");
|
|
23141
|
+
}
|
|
23142
|
+
}
|
|
23143
|
+
async function incrementUsageCount(_id, amount = 1, session) {
|
|
23144
|
+
try {
|
|
23145
|
+
_id = new import_mongodb61.ObjectId(_id);
|
|
23146
|
+
} catch (error) {
|
|
23147
|
+
throw new import_utils117.BadRequestError("Invalid ID.");
|
|
23148
|
+
}
|
|
23149
|
+
try {
|
|
23150
|
+
await repo.collection.updateOne(
|
|
23151
|
+
{ _id },
|
|
23152
|
+
{ $inc: { usageCount: amount } },
|
|
23153
|
+
{ session }
|
|
23154
|
+
);
|
|
23155
|
+
repo.delCachedData();
|
|
23156
|
+
} catch (error) {
|
|
23157
|
+
throw new import_utils117.InternalServerError("Failed to update tag usage count.");
|
|
23158
|
+
}
|
|
23159
|
+
}
|
|
23160
|
+
return {
|
|
23161
|
+
createIndexes,
|
|
23162
|
+
add,
|
|
23163
|
+
getAll,
|
|
23164
|
+
getById,
|
|
23165
|
+
findByNormalizedNameAndOrg,
|
|
23166
|
+
updateById,
|
|
23167
|
+
deleteById,
|
|
23168
|
+
incrementUsageCount
|
|
23169
|
+
};
|
|
23170
|
+
}
|
|
23171
|
+
|
|
23172
|
+
// src/resources/asset-item-category/category.repository.ts
|
|
23173
|
+
var import_utils119 = require("@goweekdays/utils");
|
|
23174
|
+
|
|
23175
|
+
// src/resources/asset-item-category/category.model.ts
|
|
23176
|
+
var import_joi99 = __toESM(require("joi"));
|
|
23177
|
+
var import_mongodb62 = require("mongodb");
|
|
23178
|
+
var import_utils118 = require("@goweekdays/utils");
|
|
23179
|
+
var categoryLevels = [
|
|
23180
|
+
"department",
|
|
23181
|
+
"category",
|
|
23182
|
+
"subcategory"
|
|
23183
|
+
];
|
|
23184
|
+
var schemaCategoryNodeFields = {
|
|
23185
|
+
orgId: import_joi99.default.string().hex().length(24).optional().allow("", null),
|
|
23186
|
+
level: import_joi99.default.string().valid(...categoryLevels).required(),
|
|
23187
|
+
type: import_joi99.default.string().valid("public", "private").optional().default("public"),
|
|
23188
|
+
name: import_joi99.default.string().required(),
|
|
23189
|
+
displayName: import_joi99.default.string().required(),
|
|
23190
|
+
parentId: import_joi99.default.string().hex().length(24).optional().allow("", null),
|
|
23191
|
+
path: import_joi99.default.string().optional().allow("", null),
|
|
23192
|
+
isActive: import_joi99.default.boolean().optional().default(true)
|
|
23193
|
+
};
|
|
23194
|
+
var schemaCategoryNodeCreate = import_joi99.default.object({
|
|
23195
|
+
displayName: import_joi99.default.string().required(),
|
|
23196
|
+
parentId: import_joi99.default.string().hex().length(24).optional().allow("", null),
|
|
23197
|
+
orgId: import_joi99.default.string().hex().length(24).optional().allow("", null)
|
|
23198
|
+
});
|
|
23199
|
+
var schemaCategoryNodeUpdate = import_joi99.default.object({
|
|
23200
|
+
displayName: import_joi99.default.string().optional()
|
|
23201
|
+
});
|
|
23202
|
+
var schemaCategoryNodeStd = import_joi99.default.object(schemaCategoryNodeFields);
|
|
23203
|
+
var schemaCategoryGetAll = import_joi99.default.object({
|
|
23204
|
+
page: import_joi99.default.number().integer().min(1).default(1),
|
|
23205
|
+
limit: import_joi99.default.number().integer().min(1).max(100).default(20),
|
|
23206
|
+
org: import_joi99.default.string().hex().length(24).optional().allow("", null).default(""),
|
|
23207
|
+
parent: import_joi99.default.string().hex().length(24).optional().allow("", null).default(""),
|
|
23208
|
+
level: import_joi99.default.string().valid(...categoryLevels, "").optional().default(""),
|
|
23209
|
+
type: import_joi99.default.string().valid("public", "private", "").optional().default(""),
|
|
23210
|
+
search: import_joi99.default.string().optional().allow("", null).default(""),
|
|
23211
|
+
status: import_joi99.default.string().optional().allow("", null).default("active")
|
|
23212
|
+
});
|
|
23213
|
+
function normalizeName(displayName) {
|
|
23214
|
+
return displayName.trim().toLowerCase().replace(/\s+/g, "_");
|
|
23215
|
+
}
|
|
23216
|
+
function buildCategoryPath(departmentName, categoryName, subcategoryName) {
|
|
23217
|
+
return [departmentName, categoryName, subcategoryName].filter(Boolean).join(".").replace(/ /g, "_").toLowerCase();
|
|
23218
|
+
}
|
|
23219
|
+
function modelCategoryNode(data) {
|
|
23220
|
+
const { error } = schemaCategoryNodeStd.validate(data);
|
|
23221
|
+
if (error) {
|
|
23222
|
+
throw new import_utils118.BadRequestError(`Invalid category node data: ${error.message}`);
|
|
23223
|
+
}
|
|
23224
|
+
if (data.orgId) {
|
|
23225
|
+
try {
|
|
23226
|
+
data.orgId = new import_mongodb62.ObjectId(data.orgId);
|
|
23227
|
+
} catch {
|
|
23228
|
+
throw new import_utils118.BadRequestError(`Invalid orgId: ${data.orgId}`);
|
|
23229
|
+
}
|
|
23230
|
+
}
|
|
23231
|
+
if (data.parentId) {
|
|
23232
|
+
try {
|
|
23233
|
+
data.parentId = new import_mongodb62.ObjectId(data.parentId);
|
|
23234
|
+
} catch {
|
|
23235
|
+
throw new import_utils118.BadRequestError(`Invalid parentId: ${data.parentId}`);
|
|
23236
|
+
}
|
|
23237
|
+
}
|
|
23238
|
+
return {
|
|
23239
|
+
_id: data._id,
|
|
23240
|
+
orgId: data.orgId ?? "",
|
|
23241
|
+
level: data.level,
|
|
23242
|
+
type: data.type ?? "public",
|
|
23243
|
+
name: data.name,
|
|
23244
|
+
displayName: data.displayName,
|
|
23245
|
+
parentId: data.parentId ?? "",
|
|
23246
|
+
path: data.path ?? "",
|
|
23247
|
+
status: data.status ?? "active",
|
|
23248
|
+
createdAt: data.createdAt ?? /* @__PURE__ */ new Date(),
|
|
23249
|
+
updatedAt: data.updatedAt ?? ""
|
|
23250
|
+
};
|
|
23251
|
+
}
|
|
23252
|
+
|
|
23253
|
+
// src/resources/asset-item-category/category.repository.ts
|
|
23254
|
+
var import_mongodb63 = require("mongodb");
|
|
23255
|
+
function useCategoryRepo() {
|
|
23256
|
+
const namespace_collection = "categories";
|
|
23257
|
+
const repo = (0, import_utils119.useRepo)(namespace_collection);
|
|
23258
|
+
async function createIndexes() {
|
|
23259
|
+
try {
|
|
23260
|
+
await repo.collection.createIndexes([
|
|
23261
|
+
{ key: { level: 1 } },
|
|
23262
|
+
{ key: { parentId: 1 } },
|
|
23263
|
+
{
|
|
23264
|
+
key: { name: "text" },
|
|
23265
|
+
name: "text_name"
|
|
23266
|
+
},
|
|
23267
|
+
{
|
|
23268
|
+
key: { name: 1, parentId: 1 },
|
|
23269
|
+
unique: true,
|
|
23270
|
+
name: "unique_name_per_parent"
|
|
23271
|
+
},
|
|
23272
|
+
{ key: { path: 1 } },
|
|
23273
|
+
{ key: { isActive: 1 } }
|
|
23274
|
+
]);
|
|
23275
|
+
} catch (error) {
|
|
23276
|
+
throw new import_utils119.InternalServerError("Failed to create category indexes.");
|
|
23277
|
+
}
|
|
23278
|
+
}
|
|
23279
|
+
async function add(value, session) {
|
|
23280
|
+
try {
|
|
23281
|
+
value = modelCategoryNode(value);
|
|
23282
|
+
const res = await repo.collection.insertOne(value, { session });
|
|
23283
|
+
repo.delCachedData();
|
|
23284
|
+
return res.insertedId;
|
|
23285
|
+
} catch (error) {
|
|
23286
|
+
import_utils119.logger.log({ level: "error", message: error.message });
|
|
23287
|
+
const isDuplicated = error.message.includes("duplicate");
|
|
23288
|
+
if (isDuplicated) {
|
|
23289
|
+
throw new import_utils119.BadRequestError(
|
|
23290
|
+
"Category name already exists under this parent."
|
|
23291
|
+
);
|
|
23292
|
+
}
|
|
23293
|
+
throw new import_utils119.InternalServerError("Failed to create category node.");
|
|
23294
|
+
}
|
|
23295
|
+
}
|
|
23296
|
+
async function getAll({
|
|
23297
|
+
page = 1,
|
|
23298
|
+
limit = 20,
|
|
23299
|
+
org = "",
|
|
23300
|
+
parent = "",
|
|
23301
|
+
level = "",
|
|
23302
|
+
type = "",
|
|
23303
|
+
search = "",
|
|
23304
|
+
status: status2 = "active"
|
|
23305
|
+
} = {}) {
|
|
23306
|
+
page = page > 0 ? page - 1 : 0;
|
|
23307
|
+
const query = { status: status2 };
|
|
23308
|
+
if (org) {
|
|
23309
|
+
try {
|
|
23310
|
+
query.orgId = new import_mongodb63.ObjectId(org);
|
|
23311
|
+
} catch (error) {
|
|
23312
|
+
throw new import_utils119.BadRequestError("Invalid org ID.");
|
|
23313
|
+
}
|
|
23314
|
+
}
|
|
23315
|
+
if (level) {
|
|
23316
|
+
query.level = level;
|
|
23317
|
+
}
|
|
23318
|
+
if (type) {
|
|
23319
|
+
query.type = type;
|
|
23320
|
+
}
|
|
23321
|
+
if (parent) {
|
|
23322
|
+
try {
|
|
23323
|
+
query.parentId = new import_mongodb63.ObjectId(parent);
|
|
23324
|
+
} catch (error) {
|
|
23325
|
+
throw new import_utils119.BadRequestError("Invalid parentId.");
|
|
23326
|
+
}
|
|
23327
|
+
}
|
|
23328
|
+
const cacheKeyOptions = {
|
|
23329
|
+
page,
|
|
23330
|
+
limit,
|
|
23331
|
+
org,
|
|
23332
|
+
parent,
|
|
23333
|
+
level,
|
|
23334
|
+
type,
|
|
23335
|
+
status: status2,
|
|
23336
|
+
tag: "all"
|
|
23337
|
+
};
|
|
23338
|
+
if (search) {
|
|
23339
|
+
query.$text = { $search: `"${search}"` };
|
|
23340
|
+
cacheKeyOptions.search = search;
|
|
23341
|
+
}
|
|
23342
|
+
const cacheKey = (0, import_utils119.makeCacheKey)(namespace_collection, cacheKeyOptions);
|
|
23343
|
+
import_utils119.logger.log({
|
|
23344
|
+
level: "info",
|
|
23345
|
+
message: `Cache key for getAll categories: ${cacheKey}`
|
|
23346
|
+
});
|
|
23347
|
+
try {
|
|
23348
|
+
const cached = await repo.getCache(cacheKey);
|
|
23349
|
+
if (cached) {
|
|
23350
|
+
import_utils119.logger.log({
|
|
23351
|
+
level: "info",
|
|
23352
|
+
message: `Cache hit for getAll categories: ${cacheKey}`
|
|
23353
|
+
});
|
|
23354
|
+
return cached;
|
|
23355
|
+
}
|
|
23356
|
+
const items = await repo.collection.aggregate([
|
|
23357
|
+
{ $match: query },
|
|
23358
|
+
{ $skip: page * limit },
|
|
23359
|
+
{ $limit: limit }
|
|
23360
|
+
]).toArray();
|
|
23361
|
+
const length = await repo.collection.countDocuments(query);
|
|
23362
|
+
const data = (0, import_utils119.paginate)(items, page, limit, length);
|
|
23363
|
+
repo.setCache(cacheKey, data, 600).then(() => {
|
|
23364
|
+
import_utils119.logger.log({
|
|
23365
|
+
level: "info",
|
|
23366
|
+
message: `Cache set for getAll categories: ${cacheKey}`
|
|
23367
|
+
});
|
|
23368
|
+
}).catch((err) => {
|
|
23369
|
+
import_utils119.logger.log({
|
|
23370
|
+
level: "error",
|
|
23371
|
+
message: `Failed to set cache for getAll categories: ${err.message}`
|
|
23372
|
+
});
|
|
23373
|
+
});
|
|
23374
|
+
return data;
|
|
23375
|
+
} catch (error) {
|
|
23376
|
+
import_utils119.logger.log({ level: "error", message: `${error}` });
|
|
23377
|
+
throw new import_utils119.InternalServerError("Failed to get categories.");
|
|
23378
|
+
}
|
|
23379
|
+
}
|
|
23380
|
+
async function getById(_id) {
|
|
23381
|
+
try {
|
|
23382
|
+
_id = new import_mongodb63.ObjectId(_id);
|
|
23383
|
+
} catch {
|
|
23384
|
+
throw new import_utils119.BadRequestError("Invalid category ID.");
|
|
23385
|
+
}
|
|
23386
|
+
const cacheKey = (0, import_utils119.makeCacheKey)(namespace_collection, {
|
|
23387
|
+
_id: String(_id),
|
|
23388
|
+
tag: "by-id"
|
|
23389
|
+
});
|
|
23390
|
+
try {
|
|
23391
|
+
const cached = await repo.getCache(cacheKey);
|
|
23392
|
+
if (cached)
|
|
23393
|
+
return cached;
|
|
23394
|
+
const result = await repo.collection.findOne({ _id });
|
|
23395
|
+
if (!result) {
|
|
23396
|
+
throw new import_utils119.BadRequestError("Category node not found.");
|
|
23397
|
+
}
|
|
23398
|
+
repo.setCache(cacheKey, result, 300).catch((err) => {
|
|
23399
|
+
import_utils119.logger.log({
|
|
23400
|
+
level: "error",
|
|
23401
|
+
message: `Failed to set cache for category by id: ${err.message}`
|
|
23402
|
+
});
|
|
23403
|
+
});
|
|
23404
|
+
return result;
|
|
23405
|
+
} catch (error) {
|
|
23406
|
+
if (error instanceof import_utils119.AppError)
|
|
23407
|
+
throw error;
|
|
23408
|
+
throw new import_utils119.InternalServerError("Failed to get category node.");
|
|
23409
|
+
}
|
|
23410
|
+
}
|
|
23411
|
+
async function getByNameParent(name, { parentId = "", orgId = "" } = {}) {
|
|
23412
|
+
const query = {
|
|
23413
|
+
$text: { $search: `"${name.trim()}"` }
|
|
23414
|
+
};
|
|
23415
|
+
if (parentId) {
|
|
23416
|
+
try {
|
|
23417
|
+
query.parentId = new import_mongodb63.ObjectId(parentId);
|
|
23418
|
+
} catch (error) {
|
|
23419
|
+
throw new import_utils119.BadRequestError("Invalid parentId.");
|
|
23420
|
+
}
|
|
23421
|
+
}
|
|
23422
|
+
if (orgId) {
|
|
23423
|
+
try {
|
|
23424
|
+
query.orgId = new import_mongodb63.ObjectId(orgId);
|
|
23425
|
+
} catch (error) {
|
|
23426
|
+
throw new import_utils119.BadRequestError("Invalid orgId.");
|
|
23427
|
+
}
|
|
23428
|
+
}
|
|
23429
|
+
try {
|
|
23430
|
+
const cacheKey = (0, import_utils119.makeCacheKey)(namespace_collection, {
|
|
23431
|
+
name,
|
|
23432
|
+
parentId: parentId ?? "null",
|
|
23433
|
+
orgId: orgId ?? "null",
|
|
23434
|
+
tag: "by-name-parent"
|
|
23435
|
+
});
|
|
23436
|
+
import_utils119.logger.log({
|
|
23437
|
+
level: "info",
|
|
23438
|
+
message: `Cache key for getByNameParentId: ${cacheKey}`
|
|
23439
|
+
});
|
|
23440
|
+
const cached = await repo.getCache(cacheKey);
|
|
23441
|
+
if (cached) {
|
|
23442
|
+
import_utils119.logger.log({
|
|
23443
|
+
level: "info",
|
|
23444
|
+
message: `Cache hit for getByNameParentId: ${cacheKey}`
|
|
23445
|
+
});
|
|
23446
|
+
return cached;
|
|
23447
|
+
}
|
|
23448
|
+
const result = await repo.collection.findOne(query);
|
|
23449
|
+
if (result) {
|
|
23450
|
+
repo.setCache(cacheKey, result, 300).catch((err) => {
|
|
23451
|
+
import_utils119.logger.log({
|
|
23452
|
+
level: "error",
|
|
23453
|
+
message: `Failed to set cache for category by name and parentId: ${err.message}`
|
|
23454
|
+
});
|
|
23455
|
+
});
|
|
23456
|
+
}
|
|
23457
|
+
return result;
|
|
23458
|
+
} catch (error) {
|
|
23459
|
+
console.log(error);
|
|
23460
|
+
throw new import_utils119.InternalServerError("Failed to get category node by name.");
|
|
23461
|
+
}
|
|
23462
|
+
}
|
|
23463
|
+
async function updateById(_id, update, session) {
|
|
23464
|
+
try {
|
|
23465
|
+
_id = new import_mongodb63.ObjectId(_id);
|
|
23466
|
+
} catch {
|
|
23467
|
+
throw new import_utils119.BadRequestError("Invalid category ID.");
|
|
23468
|
+
}
|
|
23469
|
+
try {
|
|
23470
|
+
const res = await repo.collection.updateOne(
|
|
23471
|
+
{ _id },
|
|
23472
|
+
{ $set: update },
|
|
23473
|
+
{ session }
|
|
23474
|
+
);
|
|
23475
|
+
if (res.matchedCount === 0) {
|
|
23476
|
+
throw new import_utils119.BadRequestError("Category node not found.");
|
|
23477
|
+
}
|
|
23478
|
+
repo.delCachedData();
|
|
23479
|
+
} catch (error) {
|
|
23480
|
+
if (error instanceof import_utils119.AppError)
|
|
23481
|
+
throw error;
|
|
23482
|
+
import_utils119.logger.log({ level: "error", message: error.message });
|
|
23483
|
+
const isDuplicated = error.message.includes("duplicate");
|
|
23484
|
+
if (isDuplicated) {
|
|
23485
|
+
throw new import_utils119.BadRequestError(
|
|
23486
|
+
"Category name already exists under this parent."
|
|
23487
|
+
);
|
|
23488
|
+
}
|
|
23489
|
+
throw new import_utils119.InternalServerError("Failed to update category node.");
|
|
23490
|
+
}
|
|
23491
|
+
}
|
|
23492
|
+
async function hasChildren(_id) {
|
|
23493
|
+
try {
|
|
23494
|
+
_id = new import_mongodb63.ObjectId(_id);
|
|
23495
|
+
} catch {
|
|
23496
|
+
throw new import_utils119.BadRequestError("Invalid category ID.");
|
|
23497
|
+
}
|
|
23498
|
+
try {
|
|
23499
|
+
const count = await repo.collection.countDocuments({ parentId: _id });
|
|
23500
|
+
return count > 0;
|
|
23501
|
+
} catch (error) {
|
|
23502
|
+
throw new import_utils119.InternalServerError("Failed to check for child categories.");
|
|
23503
|
+
}
|
|
23504
|
+
}
|
|
23505
|
+
async function softDeleteById(_id, session) {
|
|
23506
|
+
try {
|
|
23507
|
+
_id = new import_mongodb63.ObjectId(_id);
|
|
23508
|
+
} catch {
|
|
23509
|
+
throw new import_utils119.BadRequestError("Invalid category ID.");
|
|
23510
|
+
}
|
|
23511
|
+
try {
|
|
23512
|
+
const res = await repo.collection.updateOne(
|
|
23513
|
+
{ _id },
|
|
23514
|
+
{ $set: { status: "deleted", updatedAt: /* @__PURE__ */ new Date() } },
|
|
23515
|
+
{ session }
|
|
23516
|
+
);
|
|
23517
|
+
if (res.matchedCount === 0) {
|
|
23518
|
+
throw new import_utils119.BadRequestError("Category node not found.");
|
|
23519
|
+
}
|
|
23520
|
+
repo.delCachedData();
|
|
23521
|
+
} catch (error) {
|
|
23522
|
+
if (error instanceof import_utils119.AppError)
|
|
23523
|
+
throw error;
|
|
23524
|
+
throw new import_utils119.InternalServerError("Failed to delete category node.");
|
|
23525
|
+
}
|
|
23526
|
+
}
|
|
23527
|
+
return {
|
|
23528
|
+
createIndexes,
|
|
23529
|
+
add,
|
|
23530
|
+
getAll,
|
|
23531
|
+
getById,
|
|
23532
|
+
getByNameParent,
|
|
23533
|
+
updateById,
|
|
23534
|
+
hasChildren,
|
|
23535
|
+
softDeleteById
|
|
23536
|
+
};
|
|
23537
|
+
}
|
|
23538
|
+
|
|
23539
|
+
// src/resources/asset-item/asset.item.service.ts
|
|
23540
|
+
function normalizeTagName(name) {
|
|
23541
|
+
return name.trim().toLowerCase().replace(/\s+/g, "-");
|
|
23542
|
+
}
|
|
23543
|
+
function useAssetItemService() {
|
|
23544
|
+
const assetItemRepo = useAssetItemRepo();
|
|
23545
|
+
const tagRepo = useTagRepo();
|
|
23546
|
+
const categoryRepo = useCategoryRepo();
|
|
23547
|
+
async function resolveTags(tagNames, orgId, categoryPath, session) {
|
|
23548
|
+
const resolvedTags = [];
|
|
23549
|
+
for (const tag of tagNames) {
|
|
23550
|
+
const normalizedName = normalizeTagName(tag.name);
|
|
23551
|
+
const existing = await tagRepo.findByNormalizedNameAndOrg(
|
|
23552
|
+
normalizedName,
|
|
23553
|
+
orgId
|
|
23554
|
+
);
|
|
23555
|
+
if (existing && existing._id) {
|
|
23556
|
+
await tagRepo.incrementUsageCount(existing._id, 1, session);
|
|
23557
|
+
resolvedTags.push({
|
|
23558
|
+
id: String(existing._id),
|
|
23559
|
+
name: existing.name
|
|
23560
|
+
});
|
|
23561
|
+
} else {
|
|
23562
|
+
const tagData = {
|
|
23563
|
+
name: tag.name.trim(),
|
|
23564
|
+
normalizedName,
|
|
23565
|
+
type: "private",
|
|
23566
|
+
orgId,
|
|
23567
|
+
categoryPath,
|
|
23568
|
+
status: "active",
|
|
23569
|
+
usageCount: 1
|
|
23570
|
+
};
|
|
23571
|
+
const tagId = await tagRepo.add(tagData, session);
|
|
23572
|
+
resolvedTags.push({
|
|
23573
|
+
id: String(tagId),
|
|
23574
|
+
name: tag.name.trim()
|
|
23575
|
+
});
|
|
23576
|
+
}
|
|
23577
|
+
}
|
|
23578
|
+
return resolvedTags;
|
|
23579
|
+
}
|
|
23580
|
+
async function add(value) {
|
|
23581
|
+
const session = import_utils120.useAtlas.getClient()?.startSession();
|
|
23582
|
+
if (!session) {
|
|
23583
|
+
throw new import_utils120.InternalServerError(
|
|
23584
|
+
"Unable to start session for asset item service."
|
|
23585
|
+
);
|
|
23586
|
+
}
|
|
23587
|
+
try {
|
|
23588
|
+
await session.startTransaction();
|
|
23589
|
+
const department = await categoryRepo.getById(value.departmentId);
|
|
23590
|
+
value.departmentName = department.displayName;
|
|
23591
|
+
if (value.categoryId) {
|
|
23592
|
+
const category = await categoryRepo.getById(value.categoryId);
|
|
23593
|
+
value.categoryName = category.displayName;
|
|
23594
|
+
}
|
|
23595
|
+
if (value.subcategoryId) {
|
|
23596
|
+
const subcategory = await categoryRepo.getById(value.subcategoryId);
|
|
23597
|
+
value.subcategoryName = subcategory.displayName;
|
|
23598
|
+
}
|
|
23599
|
+
const categoryPath = buildCategoryPath(
|
|
23600
|
+
value.departmentName,
|
|
23601
|
+
value.categoryName ?? "",
|
|
23602
|
+
value.subcategoryName ?? ""
|
|
23603
|
+
);
|
|
23604
|
+
value.categoryPath = categoryPath;
|
|
23605
|
+
const resolvedTags = value.tags.length ? await resolveTags(
|
|
23606
|
+
value.tags,
|
|
23607
|
+
value.orgId,
|
|
23608
|
+
value.categoryPath,
|
|
23609
|
+
session
|
|
23610
|
+
) : [];
|
|
23611
|
+
const assetItem = {
|
|
23612
|
+
...value,
|
|
23613
|
+
tags: resolvedTags
|
|
23614
|
+
};
|
|
23615
|
+
const assetItemId = await assetItemRepo.add(assetItem, session);
|
|
23616
|
+
await session.commitTransaction();
|
|
23617
|
+
return assetItemId;
|
|
23618
|
+
} catch (error) {
|
|
23619
|
+
await session.abortTransaction();
|
|
23620
|
+
if (error instanceof import_utils120.AppError)
|
|
23621
|
+
throw error;
|
|
23622
|
+
throw new import_utils120.InternalServerError("Failed to create asset item.");
|
|
23623
|
+
} finally {
|
|
23624
|
+
session.endSession();
|
|
23625
|
+
}
|
|
23626
|
+
}
|
|
23627
|
+
return {
|
|
23628
|
+
add
|
|
23629
|
+
};
|
|
23630
|
+
}
|
|
23631
|
+
|
|
23632
|
+
// src/resources/asset-item/asset.item.controller.ts
|
|
23633
|
+
var import_joi100 = __toESM(require("joi"));
|
|
23634
|
+
var import_utils121 = require("@goweekdays/utils");
|
|
23635
|
+
var paginationSchema3 = import_joi100.default.object({
|
|
23636
|
+
page: import_joi100.default.number().min(1).optional().allow("", null),
|
|
23637
|
+
limit: import_joi100.default.number().min(1).optional().allow("", null),
|
|
23638
|
+
search: import_joi100.default.string().optional().allow("", null),
|
|
23639
|
+
status: import_joi100.default.string().valid(...assetItemStatuses).optional().allow("", null),
|
|
23640
|
+
assetCategory: import_joi100.default.string().valid(...assetItemCategories).optional().allow("", null),
|
|
23641
|
+
trackingType: import_joi100.default.string().valid(...assetItemTrackingTypes).optional().allow("", null),
|
|
23642
|
+
purpose: import_joi100.default.string().valid(...assetItemPurposes).optional().allow("", null),
|
|
23643
|
+
departmentId: import_joi100.default.string().optional().allow("", null),
|
|
23644
|
+
categoryId: import_joi100.default.string().optional().allow("", null),
|
|
23645
|
+
subcategoryId: import_joi100.default.string().optional().allow("", null),
|
|
23646
|
+
brand: import_joi100.default.string().optional().allow("", null),
|
|
23647
|
+
tags: import_joi100.default.string().optional().allow("", null)
|
|
23648
|
+
});
|
|
23649
|
+
var idParamSchema2 = import_joi100.default.object({
|
|
23650
|
+
id: import_joi100.default.string().hex().length(24).required()
|
|
23651
|
+
});
|
|
23652
|
+
function useAssetItemController() {
|
|
23653
|
+
const repo = useAssetItemRepo();
|
|
23654
|
+
const service = useAssetItemService();
|
|
23655
|
+
async function add(req, res, next) {
|
|
23656
|
+
try {
|
|
23657
|
+
const { error, value } = schemaAssetItemCreate.validate(req.body);
|
|
23658
|
+
if (error) {
|
|
23659
|
+
next(new import_utils121.BadRequestError(error.message));
|
|
23660
|
+
return;
|
|
23661
|
+
}
|
|
23662
|
+
const assetItemId = await service.add(value);
|
|
23663
|
+
res.status(201).json({
|
|
23664
|
+
message: "Asset item created successfully.",
|
|
23665
|
+
assetItemId
|
|
23666
|
+
});
|
|
23667
|
+
} catch (error) {
|
|
23668
|
+
next(error);
|
|
23669
|
+
}
|
|
23670
|
+
}
|
|
23671
|
+
async function getAll(req, res, next) {
|
|
23672
|
+
const { error } = paginationSchema3.validate(req.query);
|
|
23673
|
+
if (error) {
|
|
23674
|
+
next(new import_utils121.BadRequestError(error.message));
|
|
23675
|
+
return;
|
|
23676
|
+
}
|
|
23677
|
+
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
23678
|
+
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
23679
|
+
const search = req.query.search ?? "";
|
|
23680
|
+
const status2 = req.query.status ?? "active";
|
|
23681
|
+
const assetCategory = req.query.assetCategory ?? "";
|
|
23682
|
+
const trackingType = req.query.trackingType ?? "";
|
|
23683
|
+
const purpose = req.query.purpose ?? "";
|
|
23684
|
+
const departmentId = req.query.departmentId ?? "";
|
|
23685
|
+
const categoryId = req.query.categoryId ?? "";
|
|
23686
|
+
const subcategoryId = req.query.subcategoryId ?? "";
|
|
23687
|
+
const brand = req.query.brand ?? "";
|
|
23688
|
+
const tagsParam = req.query.tags ?? "";
|
|
23689
|
+
const tags = tagsParam ? tagsParam.split(",").filter(Boolean) : [];
|
|
23690
|
+
if (!isFinite(page)) {
|
|
23691
|
+
next(new import_utils121.BadRequestError("Invalid page number."));
|
|
23692
|
+
return;
|
|
23693
|
+
}
|
|
23694
|
+
if (!isFinite(limit)) {
|
|
23695
|
+
next(new import_utils121.BadRequestError("Invalid limit number."));
|
|
23696
|
+
return;
|
|
23697
|
+
}
|
|
23698
|
+
try {
|
|
23699
|
+
const results = await repo.getAll({
|
|
23700
|
+
page,
|
|
23701
|
+
limit,
|
|
23702
|
+
search,
|
|
23703
|
+
status: status2,
|
|
23704
|
+
assetCategory,
|
|
23705
|
+
trackingType,
|
|
23706
|
+
purpose,
|
|
23707
|
+
departmentId,
|
|
23708
|
+
categoryId,
|
|
23709
|
+
subcategoryId,
|
|
23710
|
+
brand,
|
|
23711
|
+
tags
|
|
23712
|
+
});
|
|
23713
|
+
res.json(results);
|
|
23714
|
+
} catch (error2) {
|
|
23715
|
+
next(error2);
|
|
23716
|
+
}
|
|
23717
|
+
}
|
|
23718
|
+
async function getById(req, res, next) {
|
|
23719
|
+
const { error } = idParamSchema2.validate(req.params);
|
|
23720
|
+
if (error) {
|
|
23721
|
+
next(new import_utils121.BadRequestError(error.message));
|
|
23722
|
+
return;
|
|
23723
|
+
}
|
|
23724
|
+
try {
|
|
23725
|
+
const assetItem = await repo.getById(req.params.id);
|
|
23726
|
+
res.json(assetItem);
|
|
23727
|
+
} catch (error2) {
|
|
23728
|
+
next(error2);
|
|
23729
|
+
}
|
|
23730
|
+
}
|
|
23731
|
+
async function updateById(req, res, next) {
|
|
23732
|
+
const { error: paramError } = idParamSchema2.validate(req.params);
|
|
23733
|
+
if (paramError) {
|
|
23734
|
+
next(new import_utils121.BadRequestError(paramError.message));
|
|
23735
|
+
return;
|
|
23736
|
+
}
|
|
23737
|
+
const { error: bodyError } = schemaAssetItemUpdate.validate(req.body);
|
|
23738
|
+
if (bodyError) {
|
|
23739
|
+
next(new import_utils121.BadRequestError(bodyError.message));
|
|
23740
|
+
return;
|
|
23741
|
+
}
|
|
23742
|
+
try {
|
|
23743
|
+
const updatedAssetItem = await repo.updateById(req.params.id, req.body);
|
|
23744
|
+
res.json({
|
|
23745
|
+
message: "Asset item updated successfully.",
|
|
23746
|
+
assetItem: updatedAssetItem
|
|
23747
|
+
});
|
|
23748
|
+
} catch (error) {
|
|
23749
|
+
next(error);
|
|
23750
|
+
}
|
|
23751
|
+
}
|
|
23752
|
+
async function deleteById(req, res, next) {
|
|
23753
|
+
const { error } = idParamSchema2.validate(req.params);
|
|
23754
|
+
if (error) {
|
|
23755
|
+
next(new import_utils121.BadRequestError(error.message));
|
|
23756
|
+
return;
|
|
23757
|
+
}
|
|
23758
|
+
try {
|
|
23759
|
+
await repo.deleteById(req.params.id);
|
|
23760
|
+
res.json({
|
|
23761
|
+
message: "Asset item deleted successfully."
|
|
23762
|
+
});
|
|
23763
|
+
} catch (error2) {
|
|
23764
|
+
next(error2);
|
|
23765
|
+
}
|
|
23766
|
+
}
|
|
23767
|
+
return {
|
|
23768
|
+
add,
|
|
23769
|
+
getAll,
|
|
23770
|
+
getById,
|
|
23771
|
+
updateById,
|
|
23772
|
+
deleteById
|
|
23773
|
+
};
|
|
23774
|
+
}
|
|
23775
|
+
|
|
23776
|
+
// src/resources/asset-item-unit/asset.unit.model.ts
|
|
23777
|
+
var import_joi101 = __toESM(require("joi"));
|
|
23778
|
+
var import_mongodb64 = require("mongodb");
|
|
23779
|
+
var assetUnitStatuses = [
|
|
23780
|
+
"available",
|
|
23781
|
+
"assigned",
|
|
23782
|
+
"maintenance",
|
|
23783
|
+
"disposed"
|
|
23784
|
+
];
|
|
23785
|
+
var schemaAssetUnitBase = {
|
|
23786
|
+
itemId: import_joi101.default.string().hex().length(24).required(),
|
|
23787
|
+
serialNumber: import_joi101.default.string().optional().allow("", null),
|
|
23788
|
+
plateNumber: import_joi101.default.string().optional().allow("", null),
|
|
23789
|
+
status: import_joi101.default.string().valid(...assetUnitStatuses).required(),
|
|
23790
|
+
locationId: import_joi101.default.string().optional().allow("", null),
|
|
23791
|
+
assignedTo: import_joi101.default.string().optional().allow("", null)
|
|
23792
|
+
};
|
|
23793
|
+
var schemaAssetUnit = import_joi101.default.object(schemaAssetUnitBase);
|
|
23794
|
+
var schemaAssetUnitUpdate = import_joi101.default.object({
|
|
23795
|
+
serialNumber: import_joi101.default.string().optional().allow("", null),
|
|
23796
|
+
plateNumber: import_joi101.default.string().optional().allow("", null),
|
|
23797
|
+
status: import_joi101.default.string().valid(...assetUnitStatuses).optional(),
|
|
23798
|
+
locationId: import_joi101.default.string().optional().allow("", null),
|
|
23799
|
+
assignedTo: import_joi101.default.string().optional().allow("", null)
|
|
23800
|
+
});
|
|
23801
|
+
function modelAssetUnit(data) {
|
|
23802
|
+
const { error } = schemaAssetUnit.validate(data);
|
|
23803
|
+
if (error) {
|
|
23804
|
+
throw new Error(`Invalid asset unit data: ${error.message}`);
|
|
23805
|
+
}
|
|
23806
|
+
if (data.itemId && typeof data.itemId === "string") {
|
|
23807
|
+
try {
|
|
23808
|
+
data.itemId = new import_mongodb64.ObjectId(data.itemId);
|
|
23809
|
+
} catch {
|
|
23810
|
+
throw new Error(`Invalid itemId format: ${data.itemId}`);
|
|
23811
|
+
}
|
|
23812
|
+
}
|
|
23813
|
+
return {
|
|
23814
|
+
_id: data._id,
|
|
23815
|
+
itemId: data.itemId,
|
|
23816
|
+
serialNumber: data.serialNumber ?? "",
|
|
23817
|
+
plateNumber: data.plateNumber ?? "",
|
|
23818
|
+
status: data.status,
|
|
23819
|
+
locationId: data.locationId ?? "",
|
|
23820
|
+
assignedTo: data.assignedTo ?? "",
|
|
23821
|
+
createdAt: data.createdAt ?? /* @__PURE__ */ new Date(),
|
|
23822
|
+
updatedAt: data.updatedAt ?? ""
|
|
23823
|
+
};
|
|
23824
|
+
}
|
|
23825
|
+
|
|
23826
|
+
// src/resources/asset-item-unit/asset.unit.repository.ts
|
|
23827
|
+
var import_utils122 = require("@goweekdays/utils");
|
|
23828
|
+
var import_mongodb65 = require("mongodb");
|
|
23829
|
+
function useAssetUnitRepo() {
|
|
23830
|
+
const namespace_collection = "asset.units";
|
|
23831
|
+
const repo = (0, import_utils122.useRepo)(namespace_collection);
|
|
23832
|
+
async function createIndexes() {
|
|
23833
|
+
try {
|
|
23834
|
+
await repo.collection.createIndexes([
|
|
23835
|
+
{ key: { itemId: 1 } },
|
|
23836
|
+
{ key: { status: 1 } },
|
|
23837
|
+
{ key: { locationId: 1 } },
|
|
23838
|
+
{ key: { itemId: 1, status: 1 } }
|
|
23839
|
+
]);
|
|
23840
|
+
} catch (error) {
|
|
23841
|
+
throw new import_utils122.BadRequestError("Failed to create asset unit indexes.");
|
|
23842
|
+
}
|
|
23843
|
+
}
|
|
23844
|
+
async function add(value, session) {
|
|
23845
|
+
try {
|
|
23846
|
+
value = modelAssetUnit(value);
|
|
23847
|
+
const res = await repo.collection.insertOne(value, { session });
|
|
23848
|
+
repo.delCachedData();
|
|
23849
|
+
return res.insertedId;
|
|
23850
|
+
} catch (error) {
|
|
23851
|
+
import_utils122.logger.log({ level: "error", message: error.message });
|
|
23852
|
+
throw new import_utils122.InternalServerError("Failed to create asset unit.");
|
|
23853
|
+
}
|
|
23854
|
+
}
|
|
23855
|
+
async function getByItemId({
|
|
23856
|
+
itemId,
|
|
23857
|
+
status: status2 = "",
|
|
23858
|
+
page = 1,
|
|
23859
|
+
limit = 10
|
|
23860
|
+
}) {
|
|
23861
|
+
page = page > 0 ? page - 1 : 0;
|
|
23862
|
+
const query = {
|
|
23863
|
+
itemId: new import_mongodb65.ObjectId(itemId)
|
|
23864
|
+
};
|
|
23865
|
+
if (status2) {
|
|
23866
|
+
query.status = status2;
|
|
23867
|
+
}
|
|
23868
|
+
const cacheKey = (0, import_utils122.makeCacheKey)(namespace_collection, {
|
|
23869
|
+
itemId,
|
|
23870
|
+
status: status2,
|
|
23871
|
+
page,
|
|
23872
|
+
limit,
|
|
23873
|
+
tag: "byItemId"
|
|
23874
|
+
});
|
|
23875
|
+
try {
|
|
23876
|
+
const cached = await repo.getCache(cacheKey);
|
|
23877
|
+
if (cached)
|
|
23878
|
+
return cached;
|
|
23879
|
+
const items = await repo.collection.aggregate([
|
|
23880
|
+
{ $match: query },
|
|
23881
|
+
{ $sort: { _id: -1 } },
|
|
23882
|
+
{ $skip: page * limit },
|
|
23883
|
+
{ $limit: limit }
|
|
23884
|
+
]).toArray();
|
|
23885
|
+
const length = await repo.collection.countDocuments(query);
|
|
23886
|
+
const data = (0, import_utils122.paginate)(items, page, limit, length);
|
|
23887
|
+
repo.setCache(cacheKey, data, 600);
|
|
23888
|
+
return data;
|
|
23889
|
+
} catch (error) {
|
|
23890
|
+
import_utils122.logger.log({ level: "error", message: `${error}` });
|
|
23891
|
+
throw error;
|
|
23892
|
+
}
|
|
23893
|
+
}
|
|
23894
|
+
async function getById(id) {
|
|
23895
|
+
const cacheKey = (0, import_utils122.makeCacheKey)(namespace_collection, {
|
|
23896
|
+
id,
|
|
23897
|
+
tag: "by-id"
|
|
23898
|
+
});
|
|
23899
|
+
try {
|
|
23900
|
+
const cached = await repo.getCache(cacheKey);
|
|
23901
|
+
if (cached)
|
|
23902
|
+
return cached;
|
|
23903
|
+
const result = await repo.collection.findOne({
|
|
23904
|
+
_id: new import_mongodb65.ObjectId(id)
|
|
23905
|
+
});
|
|
23906
|
+
if (!result) {
|
|
23907
|
+
throw new import_utils122.BadRequestError("Asset unit not found.");
|
|
23908
|
+
}
|
|
23909
|
+
repo.setCache(cacheKey, result, 300);
|
|
23910
|
+
return result;
|
|
23911
|
+
} catch (error) {
|
|
23912
|
+
if (error instanceof import_utils122.AppError)
|
|
23913
|
+
throw error;
|
|
23914
|
+
throw new import_utils122.InternalServerError("Failed to get asset unit.");
|
|
23915
|
+
}
|
|
23916
|
+
}
|
|
23917
|
+
async function updateById(id, value, session) {
|
|
23918
|
+
try {
|
|
23919
|
+
const result = await repo.collection.findOneAndUpdate(
|
|
23920
|
+
{ _id: new import_mongodb65.ObjectId(id) },
|
|
23921
|
+
{ $set: { ...value, updatedAt: /* @__PURE__ */ new Date() } },
|
|
23922
|
+
{ returnDocument: "after", session }
|
|
23923
|
+
);
|
|
23924
|
+
if (!result) {
|
|
23925
|
+
throw new import_utils122.BadRequestError("Asset unit not found.");
|
|
23926
|
+
}
|
|
23927
|
+
repo.delCachedData();
|
|
23928
|
+
return result;
|
|
23929
|
+
} catch (error) {
|
|
23930
|
+
if (error instanceof import_utils122.AppError)
|
|
23931
|
+
throw error;
|
|
23932
|
+
throw new import_utils122.InternalServerError("Failed to update asset unit.");
|
|
23933
|
+
}
|
|
23934
|
+
}
|
|
23935
|
+
async function countByItemIdAndStatus(itemId, status2) {
|
|
23936
|
+
try {
|
|
23937
|
+
return await repo.collection.countDocuments({
|
|
23938
|
+
itemId: new import_mongodb65.ObjectId(itemId),
|
|
23939
|
+
status: status2
|
|
23940
|
+
});
|
|
23941
|
+
} catch (error) {
|
|
23942
|
+
throw new import_utils122.InternalServerError("Failed to count asset units.");
|
|
23943
|
+
}
|
|
23944
|
+
}
|
|
23945
|
+
return {
|
|
23946
|
+
createIndexes,
|
|
23947
|
+
add,
|
|
23948
|
+
getByItemId,
|
|
23949
|
+
getById,
|
|
23950
|
+
updateById,
|
|
23951
|
+
countByItemIdAndStatus
|
|
23952
|
+
};
|
|
23953
|
+
}
|
|
23954
|
+
|
|
23955
|
+
// src/resources/asset-item-unit/asset.unit.controller.ts
|
|
23956
|
+
var import_joi102 = __toESM(require("joi"));
|
|
23957
|
+
var import_utils123 = require("@goweekdays/utils");
|
|
23958
|
+
var querySchema = import_joi102.default.object({
|
|
23959
|
+
itemId: import_joi102.default.string().hex().length(24).required(),
|
|
23960
|
+
status: import_joi102.default.string().valid(...assetUnitStatuses).optional().allow("", null),
|
|
23961
|
+
page: import_joi102.default.number().min(1).optional().allow("", null),
|
|
23962
|
+
limit: import_joi102.default.number().min(1).optional().allow("", null)
|
|
23963
|
+
});
|
|
23964
|
+
var idParamSchema3 = import_joi102.default.object({
|
|
23965
|
+
id: import_joi102.default.string().hex().length(24).required()
|
|
23966
|
+
});
|
|
23967
|
+
function useAssetUnitController() {
|
|
23968
|
+
const repo = useAssetUnitRepo();
|
|
23969
|
+
async function add(req, res, next) {
|
|
23970
|
+
try {
|
|
23971
|
+
const { error } = schemaAssetUnit.validate(req.body);
|
|
23972
|
+
if (error) {
|
|
23973
|
+
next(new import_utils123.BadRequestError(error.message));
|
|
23974
|
+
return;
|
|
23975
|
+
}
|
|
23976
|
+
const unitId = await repo.add(req.body);
|
|
23977
|
+
res.status(201).json({
|
|
23978
|
+
message: "Asset unit created successfully.",
|
|
23979
|
+
unitId
|
|
23980
|
+
});
|
|
23981
|
+
} catch (error) {
|
|
23982
|
+
next(error);
|
|
23983
|
+
}
|
|
23984
|
+
}
|
|
23985
|
+
async function getByItemId(req, res, next) {
|
|
23986
|
+
const { error } = querySchema.validate(req.query);
|
|
23987
|
+
if (error) {
|
|
23988
|
+
next(new import_utils123.BadRequestError(error.message));
|
|
23989
|
+
return;
|
|
23990
|
+
}
|
|
23991
|
+
const itemId = req.query.itemId;
|
|
23992
|
+
const status2 = req.query.status ?? "";
|
|
23993
|
+
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
23994
|
+
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
23995
|
+
try {
|
|
23996
|
+
const results = await repo.getByItemId({ itemId, status: status2, page, limit });
|
|
23997
|
+
res.json(results);
|
|
23998
|
+
} catch (error2) {
|
|
23999
|
+
next(error2);
|
|
24000
|
+
}
|
|
24001
|
+
}
|
|
24002
|
+
async function getById(req, res, next) {
|
|
24003
|
+
const { error } = idParamSchema3.validate(req.params);
|
|
24004
|
+
if (error) {
|
|
24005
|
+
next(new import_utils123.BadRequestError(error.message));
|
|
24006
|
+
return;
|
|
24007
|
+
}
|
|
24008
|
+
try {
|
|
24009
|
+
const unit = await repo.getById(req.params.id);
|
|
24010
|
+
res.json(unit);
|
|
24011
|
+
} catch (error2) {
|
|
24012
|
+
next(error2);
|
|
24013
|
+
}
|
|
24014
|
+
}
|
|
24015
|
+
async function updateById(req, res, next) {
|
|
24016
|
+
const { error: paramError } = idParamSchema3.validate(req.params);
|
|
24017
|
+
if (paramError) {
|
|
24018
|
+
next(new import_utils123.BadRequestError(paramError.message));
|
|
24019
|
+
return;
|
|
24020
|
+
}
|
|
24021
|
+
const { error: bodyError } = schemaAssetUnitUpdate.validate(req.body);
|
|
24022
|
+
if (bodyError) {
|
|
24023
|
+
next(new import_utils123.BadRequestError(bodyError.message));
|
|
24024
|
+
return;
|
|
24025
|
+
}
|
|
24026
|
+
try {
|
|
24027
|
+
const updatedUnit = await repo.updateById(req.params.id, req.body);
|
|
24028
|
+
res.json({
|
|
24029
|
+
message: "Asset unit updated successfully.",
|
|
24030
|
+
unit: updatedUnit
|
|
24031
|
+
});
|
|
24032
|
+
} catch (error) {
|
|
24033
|
+
next(error);
|
|
24034
|
+
}
|
|
24035
|
+
}
|
|
24036
|
+
return {
|
|
24037
|
+
add,
|
|
24038
|
+
getByItemId,
|
|
24039
|
+
getById,
|
|
24040
|
+
updateById
|
|
24041
|
+
};
|
|
24042
|
+
}
|
|
24043
|
+
|
|
24044
|
+
// src/resources/asset-item-movement/stock.movement.model.ts
|
|
24045
|
+
var import_joi103 = __toESM(require("joi"));
|
|
24046
|
+
var import_mongodb66 = require("mongodb");
|
|
24047
|
+
var stockMovementTypes = [
|
|
24048
|
+
"in",
|
|
24049
|
+
"out",
|
|
24050
|
+
"transfer",
|
|
24051
|
+
"adjustment",
|
|
24052
|
+
"conversion"
|
|
24053
|
+
];
|
|
24054
|
+
var stockMovementReferenceTypes = [
|
|
24055
|
+
"purchase",
|
|
24056
|
+
"sale",
|
|
24057
|
+
"transfer",
|
|
24058
|
+
"manual",
|
|
24059
|
+
"conversion"
|
|
24060
|
+
];
|
|
24061
|
+
var schemaStockMovement = import_joi103.default.object({
|
|
24062
|
+
itemId: import_joi103.default.string().hex().length(24).required(),
|
|
24063
|
+
type: import_joi103.default.string().valid(...stockMovementTypes).required(),
|
|
24064
|
+
quantity: import_joi103.default.number().positive().required(),
|
|
24065
|
+
unitCost: import_joi103.default.number().min(0).optional().allow(null),
|
|
24066
|
+
totalCost: import_joi103.default.number().min(0).optional().allow(null),
|
|
24067
|
+
reference: import_joi103.default.object({
|
|
24068
|
+
type: import_joi103.default.string().valid(...stockMovementReferenceTypes).required(),
|
|
24069
|
+
id: import_joi103.default.string().optional().allow("", null)
|
|
24070
|
+
}).optional().allow(null),
|
|
24071
|
+
fromLocationId: import_joi103.default.string().optional().allow("", null),
|
|
24072
|
+
toLocationId: import_joi103.default.string().optional().allow("", null),
|
|
24073
|
+
fromItemId: import_joi103.default.string().hex().length(24).optional().allow("", null),
|
|
24074
|
+
toItemId: import_joi103.default.string().hex().length(24).optional().allow("", null),
|
|
24075
|
+
reason: import_joi103.default.string().optional().allow("", null)
|
|
24076
|
+
});
|
|
24077
|
+
function modelStockMovement(data) {
|
|
24078
|
+
const { error } = schemaStockMovement.validate(data);
|
|
24079
|
+
if (error) {
|
|
24080
|
+
throw new Error(`Invalid stock movement data: ${error.message}`);
|
|
24081
|
+
}
|
|
24082
|
+
if (data.itemId && typeof data.itemId === "string") {
|
|
24083
|
+
try {
|
|
24084
|
+
data.itemId = new import_mongodb66.ObjectId(data.itemId);
|
|
24085
|
+
} catch {
|
|
24086
|
+
throw new Error(`Invalid itemId format: ${data.itemId}`);
|
|
24087
|
+
}
|
|
24088
|
+
}
|
|
24089
|
+
return {
|
|
24090
|
+
_id: data._id,
|
|
24091
|
+
itemId: data.itemId,
|
|
24092
|
+
type: data.type,
|
|
24093
|
+
quantity: data.quantity,
|
|
24094
|
+
unitCost: data.unitCost ?? 0,
|
|
24095
|
+
totalCost: data.totalCost ?? 0,
|
|
24096
|
+
reference: data.reference ?? void 0,
|
|
24097
|
+
fromLocationId: data.fromLocationId ?? "",
|
|
24098
|
+
toLocationId: data.toLocationId ?? "",
|
|
24099
|
+
fromItemId: data.fromItemId ?? "",
|
|
24100
|
+
toItemId: data.toItemId ?? "",
|
|
24101
|
+
reason: data.reason ?? "",
|
|
24102
|
+
createdAt: data.createdAt ?? /* @__PURE__ */ new Date()
|
|
24103
|
+
};
|
|
24104
|
+
}
|
|
24105
|
+
|
|
24106
|
+
// src/resources/asset-item-movement/stock.movement.repository.ts
|
|
24107
|
+
var import_utils124 = require("@goweekdays/utils");
|
|
24108
|
+
var import_mongodb67 = require("mongodb");
|
|
24109
|
+
function useStockMovementRepo() {
|
|
24110
|
+
const namespace_collection = "stock.movements";
|
|
24111
|
+
const repo = (0, import_utils124.useRepo)(namespace_collection);
|
|
24112
|
+
async function createIndexes() {
|
|
24113
|
+
try {
|
|
24114
|
+
await repo.collection.createIndexes([
|
|
24115
|
+
{ key: { itemId: 1 } },
|
|
24116
|
+
{ key: { createdAt: -1 } },
|
|
24117
|
+
{ key: { itemId: 1, createdAt: -1 } },
|
|
24118
|
+
{ key: { type: 1 } }
|
|
24119
|
+
]);
|
|
24120
|
+
} catch (error) {
|
|
24121
|
+
throw new import_utils124.BadRequestError("Failed to create stock movement indexes.");
|
|
24122
|
+
}
|
|
24123
|
+
}
|
|
24124
|
+
async function add(value, session) {
|
|
24125
|
+
try {
|
|
24126
|
+
value = modelStockMovement(value);
|
|
24127
|
+
const res = await repo.collection.insertOne(value, { session });
|
|
24128
|
+
repo.delCachedData();
|
|
24129
|
+
return res.insertedId;
|
|
24130
|
+
} catch (error) {
|
|
24131
|
+
import_utils124.logger.log({ level: "error", message: error.message });
|
|
24132
|
+
throw new import_utils124.InternalServerError("Failed to create stock movement.");
|
|
24133
|
+
}
|
|
24134
|
+
}
|
|
24135
|
+
async function getByItemId({
|
|
24136
|
+
itemId,
|
|
24137
|
+
type = "",
|
|
24138
|
+
page = 1,
|
|
24139
|
+
limit = 10
|
|
24140
|
+
}) {
|
|
24141
|
+
page = page > 0 ? page - 1 : 0;
|
|
24142
|
+
const query = {
|
|
24143
|
+
itemId: new import_mongodb67.ObjectId(itemId)
|
|
24144
|
+
};
|
|
24145
|
+
if (type) {
|
|
24146
|
+
query.type = type;
|
|
24147
|
+
}
|
|
24148
|
+
const cacheKey = (0, import_utils124.makeCacheKey)(namespace_collection, {
|
|
24149
|
+
itemId,
|
|
24150
|
+
type,
|
|
24151
|
+
page,
|
|
24152
|
+
limit,
|
|
24153
|
+
tag: "byItemId"
|
|
24154
|
+
});
|
|
24155
|
+
try {
|
|
24156
|
+
const cached = await repo.getCache(cacheKey);
|
|
24157
|
+
if (cached)
|
|
24158
|
+
return cached;
|
|
24159
|
+
const items = await repo.collection.aggregate([
|
|
24160
|
+
{ $match: query },
|
|
24161
|
+
{ $sort: { createdAt: -1 } },
|
|
24162
|
+
{ $skip: page * limit },
|
|
24163
|
+
{ $limit: limit }
|
|
24164
|
+
]).toArray();
|
|
24165
|
+
const length = await repo.collection.countDocuments(query);
|
|
24166
|
+
const data = (0, import_utils124.paginate)(items, page, limit, length);
|
|
24167
|
+
repo.setCache(cacheKey, data, 600);
|
|
24168
|
+
return data;
|
|
24169
|
+
} catch (error) {
|
|
24170
|
+
import_utils124.logger.log({ level: "error", message: `${error}` });
|
|
24171
|
+
throw error;
|
|
24172
|
+
}
|
|
24173
|
+
}
|
|
24174
|
+
async function getById(id) {
|
|
24175
|
+
const cacheKey = (0, import_utils124.makeCacheKey)(namespace_collection, {
|
|
24176
|
+
id,
|
|
24177
|
+
tag: "by-id"
|
|
24178
|
+
});
|
|
24179
|
+
try {
|
|
24180
|
+
const cached = await repo.getCache(cacheKey);
|
|
24181
|
+
if (cached)
|
|
24182
|
+
return cached;
|
|
24183
|
+
const result = await repo.collection.findOne({
|
|
24184
|
+
_id: new import_mongodb67.ObjectId(id)
|
|
24185
|
+
});
|
|
24186
|
+
if (!result) {
|
|
24187
|
+
throw new import_utils124.BadRequestError("Stock movement not found.");
|
|
24188
|
+
}
|
|
24189
|
+
repo.setCache(cacheKey, result, 300);
|
|
24190
|
+
return result;
|
|
24191
|
+
} catch (error) {
|
|
24192
|
+
if (error instanceof import_utils124.AppError)
|
|
24193
|
+
throw error;
|
|
24194
|
+
throw new import_utils124.InternalServerError("Failed to get stock movement.");
|
|
24195
|
+
}
|
|
24196
|
+
}
|
|
24197
|
+
return {
|
|
24198
|
+
createIndexes,
|
|
24199
|
+
add,
|
|
24200
|
+
getByItemId,
|
|
24201
|
+
getById
|
|
24202
|
+
};
|
|
24203
|
+
}
|
|
24204
|
+
|
|
24205
|
+
// src/resources/asset-item-movement/stock.movement.service.ts
|
|
24206
|
+
var import_utils125 = require("@goweekdays/utils");
|
|
24207
|
+
var import_utils126 = require("@goweekdays/utils");
|
|
24208
|
+
function useStockMovementService() {
|
|
24209
|
+
const movementRepo = useStockMovementRepo();
|
|
24210
|
+
const assetItemRepo = useAssetItemRepo();
|
|
24211
|
+
const assetUnitRepo = useAssetUnitRepo();
|
|
24212
|
+
async function stockIn(value, units) {
|
|
24213
|
+
const item = await assetItemRepo.getById(String(value.itemId));
|
|
24214
|
+
const session = import_utils126.useAtlas.getClient()?.startSession();
|
|
24215
|
+
if (!session)
|
|
24216
|
+
throw new import_utils125.BadRequestError("Failed to start session.");
|
|
24217
|
+
try {
|
|
24218
|
+
session.startTransaction();
|
|
24219
|
+
value.type = "in";
|
|
24220
|
+
const movementId = await movementRepo.add(value, session);
|
|
24221
|
+
if (item.trackingType === "individual" && units && units.length > 0) {
|
|
24222
|
+
for (const unit of units) {
|
|
24223
|
+
await assetUnitRepo.add(
|
|
24224
|
+
{
|
|
24225
|
+
itemId: value.itemId,
|
|
24226
|
+
serialNumber: unit.serialNumber,
|
|
24227
|
+
plateNumber: unit.plateNumber,
|
|
24228
|
+
status: "available",
|
|
24229
|
+
locationId: value.toLocationId
|
|
24230
|
+
},
|
|
24231
|
+
session
|
|
24232
|
+
);
|
|
24233
|
+
}
|
|
24234
|
+
}
|
|
24235
|
+
await assetItemRepo.incrementQuantity(
|
|
24236
|
+
String(value.itemId),
|
|
24237
|
+
value.quantity,
|
|
24238
|
+
session
|
|
24239
|
+
);
|
|
24240
|
+
await session.commitTransaction();
|
|
24241
|
+
return movementId;
|
|
24242
|
+
} catch (error) {
|
|
24243
|
+
await session.abortTransaction();
|
|
24244
|
+
throw error;
|
|
24245
|
+
} finally {
|
|
24246
|
+
await session.endSession();
|
|
24247
|
+
}
|
|
24248
|
+
}
|
|
24249
|
+
async function stockOut(value, unitIds) {
|
|
24250
|
+
const item = await assetItemRepo.getById(String(value.itemId));
|
|
24251
|
+
if (item.trackingType === "quantity" && (item.quantityOnHand ?? 0) < value.quantity) {
|
|
24252
|
+
throw new import_utils125.BadRequestError("Insufficient stock.");
|
|
24253
|
+
}
|
|
24254
|
+
const session = import_utils126.useAtlas.getClient()?.startSession();
|
|
24255
|
+
if (!session)
|
|
24256
|
+
throw new import_utils125.BadRequestError("Failed to start session.");
|
|
24257
|
+
try {
|
|
24258
|
+
session.startTransaction();
|
|
24259
|
+
value.type = "out";
|
|
24260
|
+
const movementId = await movementRepo.add(value, session);
|
|
24261
|
+
if (item.trackingType === "individual" && unitIds && unitIds.length > 0) {
|
|
24262
|
+
for (const unitId of unitIds) {
|
|
24263
|
+
await assetUnitRepo.updateById(
|
|
24264
|
+
unitId,
|
|
24265
|
+
{ status: "assigned", assignedTo: value.reference?.id },
|
|
24266
|
+
session
|
|
24267
|
+
);
|
|
24268
|
+
}
|
|
24269
|
+
}
|
|
24270
|
+
await assetItemRepo.incrementQuantity(
|
|
24271
|
+
String(value.itemId),
|
|
24272
|
+
-value.quantity,
|
|
24273
|
+
session
|
|
24274
|
+
);
|
|
24275
|
+
await session.commitTransaction();
|
|
24276
|
+
return movementId;
|
|
24277
|
+
} catch (error) {
|
|
24278
|
+
await session.abortTransaction();
|
|
24279
|
+
throw error;
|
|
24280
|
+
} finally {
|
|
24281
|
+
await session.endSession();
|
|
24282
|
+
}
|
|
24283
|
+
}
|
|
24284
|
+
async function transfer(value, unitIds) {
|
|
24285
|
+
if (!value.fromLocationId || !value.toLocationId) {
|
|
24286
|
+
throw new import_utils125.BadRequestError(
|
|
24287
|
+
"Transfer requires both fromLocationId and toLocationId."
|
|
24288
|
+
);
|
|
24289
|
+
}
|
|
24290
|
+
const item = await assetItemRepo.getById(String(value.itemId));
|
|
24291
|
+
const session = import_utils126.useAtlas.getClient()?.startSession();
|
|
24292
|
+
if (!session)
|
|
24293
|
+
throw new import_utils125.BadRequestError("Failed to start session.");
|
|
24294
|
+
try {
|
|
24295
|
+
session.startTransaction();
|
|
24296
|
+
value.type = "transfer";
|
|
24297
|
+
const movementId = await movementRepo.add(value, session);
|
|
24298
|
+
if (item.trackingType === "individual" && unitIds && unitIds.length > 0) {
|
|
24299
|
+
for (const unitId of unitIds) {
|
|
24300
|
+
await assetUnitRepo.updateById(
|
|
24301
|
+
unitId,
|
|
24302
|
+
{ locationId: value.toLocationId },
|
|
24303
|
+
session
|
|
24304
|
+
);
|
|
24305
|
+
}
|
|
24306
|
+
}
|
|
24307
|
+
await session.commitTransaction();
|
|
24308
|
+
return movementId;
|
|
24309
|
+
} catch (error) {
|
|
24310
|
+
await session.abortTransaction();
|
|
24311
|
+
throw error;
|
|
24312
|
+
} finally {
|
|
24313
|
+
await session.endSession();
|
|
24314
|
+
}
|
|
24315
|
+
}
|
|
24316
|
+
async function adjustment(value) {
|
|
24317
|
+
if (!value.reason) {
|
|
24318
|
+
throw new import_utils125.BadRequestError("Adjustment requires a reason.");
|
|
24319
|
+
}
|
|
24320
|
+
const session = import_utils126.useAtlas.getClient()?.startSession();
|
|
24321
|
+
if (!session)
|
|
24322
|
+
throw new import_utils125.BadRequestError("Failed to start session.");
|
|
24323
|
+
try {
|
|
24324
|
+
session.startTransaction();
|
|
24325
|
+
value.type = "adjustment";
|
|
24326
|
+
const movementId = await movementRepo.add(value, session);
|
|
24327
|
+
await assetItemRepo.incrementQuantity(
|
|
24328
|
+
String(value.itemId),
|
|
24329
|
+
value.quantity,
|
|
24330
|
+
session
|
|
24331
|
+
);
|
|
24332
|
+
await session.commitTransaction();
|
|
24333
|
+
return movementId;
|
|
24334
|
+
} catch (error) {
|
|
24335
|
+
await session.abortTransaction();
|
|
24336
|
+
throw error;
|
|
24337
|
+
} finally {
|
|
24338
|
+
await session.endSession();
|
|
24339
|
+
}
|
|
24340
|
+
}
|
|
24341
|
+
async function conversion(newItem, quantity, unitIds) {
|
|
24342
|
+
const sourceItem = await assetItemRepo.getById(String(newItem.itemRefId));
|
|
24343
|
+
if (!newItem.itemRefId) {
|
|
24344
|
+
throw new import_utils125.BadRequestError("Conversion requires itemRefId.");
|
|
24345
|
+
}
|
|
24346
|
+
if (sourceItem.trackingType === "quantity" && (sourceItem.quantityOnHand ?? 0) < quantity) {
|
|
24347
|
+
throw new import_utils125.BadRequestError("Insufficient stock on source item.");
|
|
24348
|
+
}
|
|
24349
|
+
const session = import_utils126.useAtlas.getClient()?.startSession();
|
|
24350
|
+
if (!session)
|
|
24351
|
+
throw new import_utils125.BadRequestError("Failed to start session.");
|
|
24352
|
+
try {
|
|
24353
|
+
session.startTransaction();
|
|
24354
|
+
const newItemId = await assetItemRepo.add(newItem, session);
|
|
24355
|
+
const movementData = {
|
|
24356
|
+
itemId: newItem.itemRefId,
|
|
24357
|
+
type: "conversion",
|
|
24358
|
+
quantity,
|
|
24359
|
+
fromItemId: String(newItem.itemRefId),
|
|
24360
|
+
toItemId: String(newItemId),
|
|
24361
|
+
reference: { type: "conversion", id: String(newItemId) }
|
|
24362
|
+
};
|
|
24363
|
+
const movementId = await movementRepo.add(movementData, session);
|
|
24364
|
+
await assetItemRepo.incrementQuantity(
|
|
24365
|
+
String(newItem.itemRefId),
|
|
24366
|
+
-quantity,
|
|
24367
|
+
session
|
|
24368
|
+
);
|
|
24369
|
+
await assetItemRepo.incrementQuantity(
|
|
24370
|
+
String(newItemId),
|
|
24371
|
+
quantity,
|
|
24372
|
+
session
|
|
24373
|
+
);
|
|
24374
|
+
if (sourceItem.trackingType === "individual" && unitIds && unitIds.length > 0) {
|
|
24375
|
+
for (const unitId of unitIds) {
|
|
24376
|
+
await assetUnitRepo.updateById(
|
|
24377
|
+
unitId,
|
|
24378
|
+
{ itemId: String(newItemId) },
|
|
24379
|
+
session
|
|
24380
|
+
);
|
|
24381
|
+
}
|
|
24382
|
+
}
|
|
24383
|
+
await session.commitTransaction();
|
|
24384
|
+
return { movementId, newItemId };
|
|
24385
|
+
} catch (error) {
|
|
24386
|
+
await session.abortTransaction();
|
|
24387
|
+
throw error;
|
|
24388
|
+
} finally {
|
|
24389
|
+
await session.endSession();
|
|
24390
|
+
}
|
|
24391
|
+
}
|
|
24392
|
+
return {
|
|
24393
|
+
stockIn,
|
|
24394
|
+
stockOut,
|
|
24395
|
+
transfer,
|
|
24396
|
+
adjustment,
|
|
24397
|
+
conversion
|
|
24398
|
+
};
|
|
24399
|
+
}
|
|
24400
|
+
|
|
24401
|
+
// src/resources/asset-item-movement/stock.movement.controller.ts
|
|
24402
|
+
var import_joi104 = __toESM(require("joi"));
|
|
24403
|
+
var import_utils127 = require("@goweekdays/utils");
|
|
24404
|
+
var querySchema2 = import_joi104.default.object({
|
|
24405
|
+
itemId: import_joi104.default.string().hex().length(24).required(),
|
|
24406
|
+
type: import_joi104.default.string().valid(...stockMovementTypes).optional().allow("", null),
|
|
24407
|
+
page: import_joi104.default.number().min(1).optional().allow("", null),
|
|
24408
|
+
limit: import_joi104.default.number().min(1).optional().allow("", null)
|
|
24409
|
+
});
|
|
24410
|
+
var idParamSchema4 = import_joi104.default.object({
|
|
24411
|
+
id: import_joi104.default.string().hex().length(24).required()
|
|
24412
|
+
});
|
|
24413
|
+
var stockInSchema = import_joi104.default.object({
|
|
24414
|
+
itemId: import_joi104.default.string().hex().length(24).required(),
|
|
24415
|
+
quantity: import_joi104.default.number().positive().required(),
|
|
24416
|
+
unitCost: import_joi104.default.number().min(0).optional().allow(null),
|
|
24417
|
+
totalCost: import_joi104.default.number().min(0).optional().allow(null),
|
|
24418
|
+
reference: import_joi104.default.object({
|
|
24419
|
+
type: import_joi104.default.string().valid("purchase", "sale", "transfer", "manual", "conversion").required(),
|
|
24420
|
+
id: import_joi104.default.string().optional().allow("", null)
|
|
24421
|
+
}).optional().allow(null),
|
|
24422
|
+
toLocationId: import_joi104.default.string().optional().allow("", null),
|
|
24423
|
+
units: import_joi104.default.array().items(
|
|
24424
|
+
import_joi104.default.object({
|
|
24425
|
+
serialNumber: import_joi104.default.string().optional().allow("", null),
|
|
24426
|
+
plateNumber: import_joi104.default.string().optional().allow("", null)
|
|
24427
|
+
})
|
|
24428
|
+
).optional()
|
|
24429
|
+
});
|
|
24430
|
+
var stockOutSchema = import_joi104.default.object({
|
|
24431
|
+
itemId: import_joi104.default.string().hex().length(24).required(),
|
|
24432
|
+
quantity: import_joi104.default.number().positive().required(),
|
|
24433
|
+
unitCost: import_joi104.default.number().min(0).optional().allow(null),
|
|
24434
|
+
totalCost: import_joi104.default.number().min(0).optional().allow(null),
|
|
24435
|
+
reference: import_joi104.default.object({
|
|
24436
|
+
type: import_joi104.default.string().valid("purchase", "sale", "transfer", "manual", "conversion").required(),
|
|
24437
|
+
id: import_joi104.default.string().optional().allow("", null)
|
|
24438
|
+
}).optional().allow(null),
|
|
24439
|
+
fromLocationId: import_joi104.default.string().optional().allow("", null),
|
|
24440
|
+
unitIds: import_joi104.default.array().items(import_joi104.default.string().hex().length(24)).optional()
|
|
24441
|
+
});
|
|
24442
|
+
var transferSchema = import_joi104.default.object({
|
|
24443
|
+
itemId: import_joi104.default.string().hex().length(24).required(),
|
|
24444
|
+
quantity: import_joi104.default.number().positive().required(),
|
|
24445
|
+
fromLocationId: import_joi104.default.string().required(),
|
|
24446
|
+
toLocationId: import_joi104.default.string().required(),
|
|
24447
|
+
reference: import_joi104.default.object({
|
|
24448
|
+
type: import_joi104.default.string().valid("purchase", "sale", "transfer", "manual", "conversion").required(),
|
|
24449
|
+
id: import_joi104.default.string().optional().allow("", null)
|
|
24450
|
+
}).optional().allow(null),
|
|
24451
|
+
unitIds: import_joi104.default.array().items(import_joi104.default.string().hex().length(24)).optional()
|
|
24452
|
+
});
|
|
24453
|
+
var adjustmentSchema = import_joi104.default.object({
|
|
24454
|
+
itemId: import_joi104.default.string().hex().length(24).required(),
|
|
24455
|
+
quantity: import_joi104.default.number().required(),
|
|
24456
|
+
reason: import_joi104.default.string().required(),
|
|
24457
|
+
reference: import_joi104.default.object({
|
|
24458
|
+
type: import_joi104.default.string().valid("purchase", "sale", "transfer", "manual", "conversion").required(),
|
|
24459
|
+
id: import_joi104.default.string().optional().allow("", null)
|
|
24460
|
+
}).optional().allow(null)
|
|
24461
|
+
});
|
|
24462
|
+
var conversionSchema = import_joi104.default.object({
|
|
24463
|
+
quantity: import_joi104.default.number().positive().required(),
|
|
24464
|
+
newItem: import_joi104.default.object({
|
|
24465
|
+
name: import_joi104.default.string().required(),
|
|
24466
|
+
description: import_joi104.default.string().optional().allow("", null),
|
|
24467
|
+
assetCategory: import_joi104.default.string().valid(...assetItemCategories).required(),
|
|
24468
|
+
assetClass: import_joi104.default.string().valid(...assetItemClasses).required(),
|
|
24469
|
+
trackingType: import_joi104.default.string().valid(...assetItemTrackingTypes).required(),
|
|
24470
|
+
purpose: import_joi104.default.string().valid(...assetItemPurposes).required(),
|
|
24471
|
+
itemRefId: import_joi104.default.string().hex().length(24).required(),
|
|
24472
|
+
remarks: import_joi104.default.string().optional().allow("", null),
|
|
24473
|
+
departmentId: import_joi104.default.string().required(),
|
|
24474
|
+
departmentName: import_joi104.default.string().required(),
|
|
24475
|
+
categoryId: import_joi104.default.string().required(),
|
|
24476
|
+
categoryName: import_joi104.default.string().required(),
|
|
24477
|
+
subcategoryId: import_joi104.default.string().required(),
|
|
24478
|
+
subcategoryName: import_joi104.default.string().required(),
|
|
24479
|
+
categoryPath: import_joi104.default.string().required(),
|
|
24480
|
+
brand: import_joi104.default.string().optional().allow("", null),
|
|
24481
|
+
tags: import_joi104.default.array().items(
|
|
24482
|
+
import_joi104.default.object({
|
|
24483
|
+
id: import_joi104.default.string().required(),
|
|
24484
|
+
name: import_joi104.default.string().required()
|
|
24485
|
+
})
|
|
24486
|
+
).optional().default([])
|
|
24487
|
+
}).required(),
|
|
24488
|
+
unitIds: import_joi104.default.array().items(import_joi104.default.string().hex().length(24)).optional()
|
|
24489
|
+
});
|
|
24490
|
+
function useStockMovementController() {
|
|
24491
|
+
const repo = useStockMovementRepo();
|
|
24492
|
+
const service = useStockMovementService();
|
|
24493
|
+
async function stockIn(req, res, next) {
|
|
24494
|
+
try {
|
|
24495
|
+
const { error } = stockInSchema.validate(req.body);
|
|
24496
|
+
if (error) {
|
|
24497
|
+
next(new import_utils127.BadRequestError(error.message));
|
|
24498
|
+
return;
|
|
24499
|
+
}
|
|
24500
|
+
const { units, ...movement } = req.body;
|
|
24501
|
+
const movementId = await service.stockIn(movement, units);
|
|
24502
|
+
res.status(201).json({
|
|
24503
|
+
message: "Stock in recorded successfully.",
|
|
24504
|
+
movementId
|
|
24505
|
+
});
|
|
24506
|
+
} catch (error) {
|
|
24507
|
+
next(error);
|
|
24508
|
+
}
|
|
24509
|
+
}
|
|
24510
|
+
async function stockOut(req, res, next) {
|
|
24511
|
+
try {
|
|
24512
|
+
const { error } = stockOutSchema.validate(req.body);
|
|
24513
|
+
if (error) {
|
|
24514
|
+
next(new import_utils127.BadRequestError(error.message));
|
|
24515
|
+
return;
|
|
24516
|
+
}
|
|
24517
|
+
const { unitIds, ...movement } = req.body;
|
|
24518
|
+
const movementId = await service.stockOut(movement, unitIds);
|
|
24519
|
+
res.status(201).json({
|
|
24520
|
+
message: "Stock out recorded successfully.",
|
|
24521
|
+
movementId
|
|
24522
|
+
});
|
|
24523
|
+
} catch (error) {
|
|
24524
|
+
next(error);
|
|
24525
|
+
}
|
|
24526
|
+
}
|
|
24527
|
+
async function transfer(req, res, next) {
|
|
24528
|
+
try {
|
|
24529
|
+
const { error } = transferSchema.validate(req.body);
|
|
24530
|
+
if (error) {
|
|
24531
|
+
next(new import_utils127.BadRequestError(error.message));
|
|
24532
|
+
return;
|
|
24533
|
+
}
|
|
24534
|
+
const { unitIds, ...movement } = req.body;
|
|
24535
|
+
const movementId = await service.transfer(movement, unitIds);
|
|
24536
|
+
res.status(201).json({
|
|
24537
|
+
message: "Transfer recorded successfully.",
|
|
24538
|
+
movementId
|
|
24539
|
+
});
|
|
24540
|
+
} catch (error) {
|
|
24541
|
+
next(error);
|
|
24542
|
+
}
|
|
24543
|
+
}
|
|
24544
|
+
async function adjustment(req, res, next) {
|
|
24545
|
+
try {
|
|
24546
|
+
const { error } = adjustmentSchema.validate(req.body);
|
|
24547
|
+
if (error) {
|
|
24548
|
+
next(new import_utils127.BadRequestError(error.message));
|
|
24549
|
+
return;
|
|
24550
|
+
}
|
|
24551
|
+
const movementId = await service.adjustment(req.body);
|
|
24552
|
+
res.status(201).json({
|
|
24553
|
+
message: "Adjustment recorded successfully.",
|
|
24554
|
+
movementId
|
|
24555
|
+
});
|
|
24556
|
+
} catch (error) {
|
|
24557
|
+
next(error);
|
|
24558
|
+
}
|
|
24559
|
+
}
|
|
24560
|
+
async function getByItemId(req, res, next) {
|
|
24561
|
+
const { error } = querySchema2.validate(req.query);
|
|
24562
|
+
if (error) {
|
|
24563
|
+
next(new import_utils127.BadRequestError(error.message));
|
|
24564
|
+
return;
|
|
24565
|
+
}
|
|
24566
|
+
const itemId = req.query.itemId;
|
|
24567
|
+
const type = req.query.type ?? "";
|
|
24568
|
+
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
24569
|
+
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
24570
|
+
try {
|
|
24571
|
+
const results = await repo.getByItemId({ itemId, type, page, limit });
|
|
24572
|
+
res.json(results);
|
|
24573
|
+
} catch (error2) {
|
|
24574
|
+
next(error2);
|
|
24575
|
+
}
|
|
24576
|
+
}
|
|
24577
|
+
async function getById(req, res, next) {
|
|
24578
|
+
const { error } = idParamSchema4.validate(req.params);
|
|
24579
|
+
if (error) {
|
|
24580
|
+
next(new import_utils127.BadRequestError(error.message));
|
|
24581
|
+
return;
|
|
24582
|
+
}
|
|
24583
|
+
try {
|
|
24584
|
+
const movement = await repo.getById(req.params.id);
|
|
24585
|
+
res.json(movement);
|
|
24586
|
+
} catch (error2) {
|
|
24587
|
+
next(error2);
|
|
24588
|
+
}
|
|
24589
|
+
}
|
|
24590
|
+
async function conversion(req, res, next) {
|
|
24591
|
+
try {
|
|
24592
|
+
const { error } = conversionSchema.validate(req.body);
|
|
24593
|
+
if (error) {
|
|
24594
|
+
next(new import_utils127.BadRequestError(error.message));
|
|
24595
|
+
return;
|
|
24596
|
+
}
|
|
24597
|
+
const { quantity, newItem, unitIds } = req.body;
|
|
24598
|
+
const result = await service.conversion(newItem, quantity, unitIds);
|
|
24599
|
+
res.status(201).json({
|
|
24600
|
+
message: "Conversion recorded successfully.",
|
|
24601
|
+
movementId: result.movementId,
|
|
24602
|
+
newItemId: result.newItemId
|
|
24603
|
+
});
|
|
24604
|
+
} catch (error) {
|
|
24605
|
+
next(error);
|
|
24606
|
+
}
|
|
24607
|
+
}
|
|
24608
|
+
return {
|
|
24609
|
+
stockIn,
|
|
24610
|
+
stockOut,
|
|
24611
|
+
transfer,
|
|
24612
|
+
adjustment,
|
|
24613
|
+
conversion,
|
|
24614
|
+
getByItemId,
|
|
24615
|
+
getById
|
|
24616
|
+
};
|
|
24617
|
+
}
|
|
24618
|
+
|
|
24619
|
+
// src/resources/asset-item-category/category.service.ts
|
|
24620
|
+
var import_utils128 = require("@goweekdays/utils");
|
|
24621
|
+
function useCategoryService() {
|
|
24622
|
+
const repo = useCategoryRepo();
|
|
24623
|
+
async function add(value) {
|
|
24624
|
+
const { error } = schemaCategoryNodeCreate.validate(value);
|
|
24625
|
+
if (error) {
|
|
24626
|
+
throw new import_utils128.BadRequestError(error.message);
|
|
24627
|
+
}
|
|
24628
|
+
const name = normalizeName(value.displayName);
|
|
24629
|
+
let level;
|
|
24630
|
+
let path = "";
|
|
24631
|
+
if (!value.parentId) {
|
|
24632
|
+
level = "department";
|
|
24633
|
+
const existing = await repo.getByNameParent(name, { orgId: value.orgId });
|
|
24634
|
+
if (existing) {
|
|
24635
|
+
throw new import_utils128.BadRequestError(
|
|
24636
|
+
`Department "${value.displayName}" already exists.`
|
|
24637
|
+
);
|
|
24638
|
+
}
|
|
24639
|
+
} else {
|
|
24640
|
+
const parent = await repo.getById(value.parentId);
|
|
24641
|
+
if (parent && parent.level === "department") {
|
|
24642
|
+
level = "category";
|
|
24643
|
+
} else if (parent && parent.level === "category") {
|
|
24644
|
+
level = "subcategory";
|
|
24645
|
+
} else {
|
|
24646
|
+
throw new import_utils128.BadRequestError(
|
|
24647
|
+
"Cannot create a child under a subcategory. Maximum depth is 3 levels."
|
|
24648
|
+
);
|
|
24649
|
+
}
|
|
24650
|
+
const existing = await repo.getByNameParent(name, {
|
|
24651
|
+
parentId: value.parentId,
|
|
24652
|
+
orgId: value.orgId
|
|
24653
|
+
});
|
|
24654
|
+
if (existing) {
|
|
24655
|
+
throw new import_utils128.BadRequestError(
|
|
24656
|
+
`"${value.displayName}" already exists under this parent.`
|
|
24657
|
+
);
|
|
24658
|
+
}
|
|
24659
|
+
if (level === "subcategory") {
|
|
24660
|
+
const department = await repo.getById(parent.parentId);
|
|
24661
|
+
path = buildCategoryPath(department.name, parent.name, name);
|
|
24662
|
+
}
|
|
24663
|
+
}
|
|
24664
|
+
const data = {
|
|
24665
|
+
orgId: value.orgId ?? "",
|
|
24666
|
+
level,
|
|
24667
|
+
type: value.orgId ? "private" : "public",
|
|
24668
|
+
name,
|
|
24669
|
+
displayName: value.displayName.trim(),
|
|
24670
|
+
parentId: value.parentId ?? "",
|
|
24671
|
+
path
|
|
24672
|
+
};
|
|
24673
|
+
return await repo.add(data);
|
|
24674
|
+
}
|
|
24675
|
+
async function updateById(_id, value) {
|
|
24676
|
+
const { error } = schemaCategoryNodeUpdate.validate(value);
|
|
24677
|
+
if (error) {
|
|
24678
|
+
throw new import_utils128.BadRequestError(error.message);
|
|
24679
|
+
}
|
|
24680
|
+
const update = {
|
|
24681
|
+
displayName: "",
|
|
24682
|
+
name: "",
|
|
24683
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
24684
|
+
};
|
|
24685
|
+
if (value.displayName) {
|
|
24686
|
+
update.displayName = value.displayName.trim();
|
|
24687
|
+
update.name = normalizeName(value.displayName);
|
|
24688
|
+
}
|
|
24689
|
+
await repo.updateById(_id, update);
|
|
24690
|
+
}
|
|
24691
|
+
async function deleteById(_id) {
|
|
24692
|
+
const hasChildren = await repo.hasChildren(_id);
|
|
24693
|
+
if (hasChildren) {
|
|
24694
|
+
throw new import_utils128.BadRequestError(
|
|
24695
|
+
"Cannot delete a category that has child categories. Remove or reassign children first."
|
|
24696
|
+
);
|
|
24697
|
+
}
|
|
24698
|
+
await repo.softDeleteById(_id);
|
|
24699
|
+
}
|
|
24700
|
+
return {
|
|
24701
|
+
add,
|
|
24702
|
+
updateById,
|
|
24703
|
+
deleteById
|
|
24704
|
+
};
|
|
24705
|
+
}
|
|
24706
|
+
|
|
24707
|
+
// src/resources/asset-item-category/category.controller.ts
|
|
24708
|
+
var import_joi105 = __toESM(require("joi"));
|
|
24709
|
+
var import_utils129 = require("@goweekdays/utils");
|
|
24710
|
+
function useCategoryController() {
|
|
24711
|
+
const repo = useCategoryRepo();
|
|
24712
|
+
const service = useCategoryService();
|
|
24713
|
+
async function add(req, res, next) {
|
|
24714
|
+
const orgId = req.params.org ?? "";
|
|
24715
|
+
const { error, value } = schemaCategoryNodeCreate.validate({
|
|
24716
|
+
...req.body,
|
|
24717
|
+
orgId
|
|
24718
|
+
});
|
|
24719
|
+
if (error) {
|
|
24720
|
+
next(new import_utils129.BadRequestError(error.message));
|
|
24721
|
+
return;
|
|
24722
|
+
}
|
|
24723
|
+
try {
|
|
24724
|
+
const id = await service.add(value);
|
|
24725
|
+
res.status(201).json({ message: "Category created successfully.", id });
|
|
24726
|
+
} catch (error2) {
|
|
24727
|
+
next(error2);
|
|
24728
|
+
}
|
|
24729
|
+
}
|
|
24730
|
+
async function getAll(req, res, next) {
|
|
24731
|
+
const org = req.params.org ?? "";
|
|
24732
|
+
const { error, value } = schemaCategoryGetAll.validate({
|
|
24733
|
+
...req.query,
|
|
24734
|
+
org
|
|
24735
|
+
});
|
|
24736
|
+
if (error) {
|
|
24737
|
+
next(new import_utils129.BadRequestError(error.message));
|
|
24738
|
+
return;
|
|
24739
|
+
}
|
|
24740
|
+
try {
|
|
24741
|
+
const data = await repo.getAll(value);
|
|
24742
|
+
res.json(data);
|
|
24743
|
+
} catch (error2) {
|
|
24744
|
+
next(error2);
|
|
24745
|
+
}
|
|
24746
|
+
}
|
|
24747
|
+
async function getById(req, res, next) {
|
|
24748
|
+
const _id = req.params.id;
|
|
24749
|
+
const { error: errorId } = import_joi105.default.string().hex().length(24).required().validate(_id);
|
|
24750
|
+
if (errorId) {
|
|
24751
|
+
next(new import_utils129.BadRequestError("Invalid category ID."));
|
|
24752
|
+
return;
|
|
24753
|
+
}
|
|
24754
|
+
try {
|
|
24755
|
+
const category = await repo.getById(_id);
|
|
24756
|
+
if (!category) {
|
|
24757
|
+
next(new import_utils129.BadRequestError("Category not found."));
|
|
24758
|
+
return;
|
|
24759
|
+
}
|
|
24760
|
+
res.json(category);
|
|
24761
|
+
} catch (error) {
|
|
24762
|
+
next(error);
|
|
24763
|
+
}
|
|
24764
|
+
}
|
|
24765
|
+
async function updateById(req, res, next) {
|
|
24766
|
+
const _id = req.params.id;
|
|
24767
|
+
const { error: errorId } = import_joi105.default.string().hex().length(24).required().validate(_id);
|
|
24768
|
+
if (errorId) {
|
|
24769
|
+
next(new import_utils129.BadRequestError("Invalid category ID."));
|
|
24770
|
+
return;
|
|
24771
|
+
}
|
|
24772
|
+
const { error, value } = schemaCategoryNodeUpdate.validate(req.body);
|
|
24773
|
+
if (error) {
|
|
24774
|
+
next(new import_utils129.BadRequestError(error.message));
|
|
24775
|
+
return;
|
|
24776
|
+
}
|
|
24777
|
+
const { displayName } = value;
|
|
24778
|
+
try {
|
|
24779
|
+
await service.updateById(_id, { displayName });
|
|
24780
|
+
res.json({ message: "Category updated successfully." });
|
|
24781
|
+
} catch (error2) {
|
|
24782
|
+
next(error2);
|
|
24783
|
+
}
|
|
24784
|
+
}
|
|
24785
|
+
async function deleteById(req, res, next) {
|
|
24786
|
+
const _id = req.params.id;
|
|
24787
|
+
const { error: errorId } = import_joi105.default.string().hex().length(24).required().validate(_id);
|
|
24788
|
+
if (errorId) {
|
|
24789
|
+
next(new import_utils129.BadRequestError("Invalid category ID."));
|
|
24790
|
+
return;
|
|
24791
|
+
}
|
|
24792
|
+
try {
|
|
24793
|
+
await service.deleteById(_id);
|
|
24794
|
+
res.json({ message: "Category deleted successfully." });
|
|
24795
|
+
} catch (error) {
|
|
24796
|
+
next(error);
|
|
24797
|
+
}
|
|
24798
|
+
}
|
|
24799
|
+
return {
|
|
24800
|
+
add,
|
|
24801
|
+
getAll,
|
|
24802
|
+
getById,
|
|
24803
|
+
updateById,
|
|
24804
|
+
deleteById
|
|
24805
|
+
};
|
|
24806
|
+
}
|
|
24807
|
+
|
|
24808
|
+
// src/resources/tag/tag.service.ts
|
|
24809
|
+
var import_utils130 = require("@goweekdays/utils");
|
|
24810
|
+
var import_joi106 = __toESM(require("joi"));
|
|
24811
|
+
function normalizeTagName2(name) {
|
|
24812
|
+
return name.trim().toLowerCase().replace(/\s+/g, "-");
|
|
24813
|
+
}
|
|
24814
|
+
function useTagService() {
|
|
24815
|
+
const {
|
|
24816
|
+
add: _add,
|
|
24817
|
+
getById: _getById,
|
|
24818
|
+
updateById: _updateById,
|
|
24819
|
+
deleteById: _deleteById
|
|
24820
|
+
} = useTagRepo();
|
|
24821
|
+
async function add(value) {
|
|
24822
|
+
const { error } = schemaTagCreate.validate(value);
|
|
24823
|
+
if (error) {
|
|
24824
|
+
throw new import_utils130.BadRequestError(error.message);
|
|
24825
|
+
}
|
|
24826
|
+
const normalizedName = normalizeTagName2(value.name);
|
|
24827
|
+
const data = {
|
|
24828
|
+
name: value.name,
|
|
24829
|
+
normalizedName,
|
|
24830
|
+
type: value.type,
|
|
24831
|
+
orgId: value.orgId ?? "",
|
|
24832
|
+
categoryPath: value.categoryPath ?? "",
|
|
24833
|
+
status: "pending",
|
|
24834
|
+
usageCount: 0
|
|
24835
|
+
};
|
|
24836
|
+
try {
|
|
24837
|
+
const id = await _add(data);
|
|
24838
|
+
return id;
|
|
24839
|
+
} catch (error2) {
|
|
24840
|
+
if (error2 instanceof import_utils130.AppError)
|
|
24841
|
+
throw error2;
|
|
24842
|
+
throw new import_utils130.BadRequestError("Failed to add tag.");
|
|
24843
|
+
}
|
|
24844
|
+
}
|
|
24845
|
+
async function updateById(id, value) {
|
|
24846
|
+
const { error: errorId } = import_joi106.default.string().hex().required().validate(id);
|
|
24847
|
+
if (errorId) {
|
|
24848
|
+
throw new import_utils130.BadRequestError("Invalid Tag ID.");
|
|
24849
|
+
}
|
|
24850
|
+
const { error } = schemaTagUpdate.validate(value);
|
|
24851
|
+
if (error) {
|
|
24852
|
+
throw new import_utils130.BadRequestError(`Invalid tag update data: ${error.message}`);
|
|
24853
|
+
}
|
|
24854
|
+
const existingTag = await _getById(id);
|
|
24855
|
+
if (!existingTag) {
|
|
24856
|
+
throw new import_utils130.BadRequestError("Tag not found.");
|
|
24857
|
+
}
|
|
24858
|
+
const updatedData = { ...value };
|
|
24859
|
+
if (value.name) {
|
|
24860
|
+
updatedData.normalizedName = normalizeTagName2(value.name);
|
|
24861
|
+
}
|
|
24862
|
+
const message = await _updateById(id, updatedData);
|
|
24863
|
+
return message;
|
|
24864
|
+
}
|
|
24865
|
+
async function deleteById(id) {
|
|
24866
|
+
const { error } = import_joi106.default.string().hex().required().validate(id);
|
|
24867
|
+
if (error) {
|
|
24868
|
+
throw new import_utils130.BadRequestError("Invalid Tag ID.");
|
|
24869
|
+
}
|
|
24870
|
+
const existingTag = await _getById(id);
|
|
24871
|
+
if (!existingTag) {
|
|
24872
|
+
throw new import_utils130.BadRequestError("Tag not found.");
|
|
24873
|
+
}
|
|
24874
|
+
const message = await _deleteById(id);
|
|
24875
|
+
return message;
|
|
24876
|
+
}
|
|
24877
|
+
return {
|
|
24878
|
+
add,
|
|
24879
|
+
updateById,
|
|
24880
|
+
deleteById
|
|
24881
|
+
};
|
|
24882
|
+
}
|
|
24883
|
+
|
|
24884
|
+
// src/resources/tag/tag.controller.ts
|
|
24885
|
+
var import_joi107 = __toESM(require("joi"));
|
|
24886
|
+
var import_utils131 = require("@goweekdays/utils");
|
|
24887
|
+
function useTagController() {
|
|
24888
|
+
const { getAll: _getAll, getById: _getById } = useTagRepo();
|
|
24889
|
+
const {
|
|
24890
|
+
add: _add,
|
|
24891
|
+
updateById: _updateById,
|
|
24892
|
+
deleteById: _deleteById
|
|
24893
|
+
} = useTagService();
|
|
24894
|
+
async function add(req, res, next) {
|
|
24895
|
+
const value = req.body;
|
|
24896
|
+
const { error } = schemaTagCreate.validate(value);
|
|
24897
|
+
if (error) {
|
|
24898
|
+
next(new import_utils131.BadRequestError(error.message));
|
|
24899
|
+
import_utils131.logger.info(`Controller: ${error.message}`);
|
|
24900
|
+
return;
|
|
24901
|
+
}
|
|
24902
|
+
try {
|
|
24903
|
+
const message = await _add(value);
|
|
24904
|
+
res.json({ message });
|
|
24905
|
+
return;
|
|
24906
|
+
} catch (error2) {
|
|
24907
|
+
next(error2);
|
|
24908
|
+
}
|
|
24909
|
+
}
|
|
24910
|
+
async function getAll(req, res, next) {
|
|
24911
|
+
const query = req.query;
|
|
24912
|
+
const validation = import_joi107.default.object({
|
|
24913
|
+
page: import_joi107.default.number().min(1).optional().allow("", null),
|
|
24914
|
+
limit: import_joi107.default.number().min(1).optional().allow("", null),
|
|
24915
|
+
search: import_joi107.default.string().optional().allow("", null),
|
|
24916
|
+
type: import_joi107.default.string().valid("public", "private").optional().allow("", null),
|
|
24917
|
+
orgId: import_joi107.default.string().hex().optional().allow("", null),
|
|
24918
|
+
status: import_joi107.default.string().valid("active", "pending").optional().allow("", null),
|
|
24919
|
+
categoryPath: import_joi107.default.string().optional().allow("", null)
|
|
24920
|
+
});
|
|
24921
|
+
const { error } = validation.validate(query);
|
|
24922
|
+
if (error) {
|
|
24923
|
+
next(new import_utils131.BadRequestError(error.message));
|
|
24924
|
+
return;
|
|
24925
|
+
}
|
|
24926
|
+
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
24927
|
+
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
24928
|
+
const search = req.query.search ?? "";
|
|
24929
|
+
const type = req.query.type ?? "";
|
|
24930
|
+
const orgId = req.query.orgId ?? "";
|
|
24931
|
+
const status2 = req.query.status ?? "";
|
|
24932
|
+
const categoryPath = req.query.categoryPath ?? "";
|
|
24933
|
+
const isPageNumber = isFinite(page);
|
|
24934
|
+
if (!isPageNumber) {
|
|
24935
|
+
next(new import_utils131.BadRequestError("Invalid page number."));
|
|
24936
|
+
return;
|
|
24937
|
+
}
|
|
24938
|
+
const isLimitNumber = isFinite(limit);
|
|
24939
|
+
if (!isLimitNumber) {
|
|
24940
|
+
next(new import_utils131.BadRequestError("Invalid limit number."));
|
|
24941
|
+
return;
|
|
24942
|
+
}
|
|
24943
|
+
try {
|
|
24944
|
+
const tags = await _getAll({
|
|
24945
|
+
page,
|
|
24946
|
+
limit,
|
|
24947
|
+
search,
|
|
24948
|
+
type,
|
|
24949
|
+
orgId,
|
|
24950
|
+
status: status2,
|
|
24951
|
+
categoryPath
|
|
24952
|
+
});
|
|
24953
|
+
res.json(tags);
|
|
24954
|
+
return;
|
|
24955
|
+
} catch (error2) {
|
|
24956
|
+
next(error2);
|
|
24957
|
+
}
|
|
24958
|
+
}
|
|
24959
|
+
async function getById(req, res, next) {
|
|
24960
|
+
const id = req.params.id;
|
|
24961
|
+
const validation = import_joi107.default.object({
|
|
24962
|
+
id: import_joi107.default.string().hex().required()
|
|
24963
|
+
});
|
|
24964
|
+
const { error } = validation.validate({ id });
|
|
24965
|
+
if (error) {
|
|
24966
|
+
next(new import_utils131.BadRequestError(error.message));
|
|
24967
|
+
return;
|
|
24968
|
+
}
|
|
24969
|
+
try {
|
|
24970
|
+
const tag = await _getById(id);
|
|
24971
|
+
res.json(tag);
|
|
24972
|
+
return;
|
|
24973
|
+
} catch (error2) {
|
|
24974
|
+
next(error2);
|
|
24975
|
+
}
|
|
24976
|
+
}
|
|
24977
|
+
async function updateById(req, res, next) {
|
|
24978
|
+
const _id = req.params.id;
|
|
24979
|
+
const { error: errorId } = import_joi107.default.string().hex().required().validate(_id);
|
|
24980
|
+
if (errorId) {
|
|
24981
|
+
next(new import_utils131.BadRequestError("Invalid Tag ID."));
|
|
24982
|
+
return;
|
|
24983
|
+
}
|
|
24984
|
+
const payload = req.body;
|
|
24985
|
+
const { error } = schemaTagUpdate.validate(payload);
|
|
24986
|
+
if (error) {
|
|
24987
|
+
next(new import_utils131.BadRequestError(error.message));
|
|
24988
|
+
return;
|
|
24989
|
+
}
|
|
24990
|
+
try {
|
|
24991
|
+
const message = await _updateById(_id, payload);
|
|
24992
|
+
res.json({ message });
|
|
24993
|
+
return;
|
|
24994
|
+
} catch (error2) {
|
|
24995
|
+
next(error2);
|
|
24996
|
+
}
|
|
24997
|
+
}
|
|
24998
|
+
async function deleteById(req, res, next) {
|
|
24999
|
+
const id = req.params.id;
|
|
25000
|
+
if (!id) {
|
|
25001
|
+
next(new import_utils131.BadRequestError("Tag ID is required."));
|
|
25002
|
+
return;
|
|
25003
|
+
}
|
|
25004
|
+
try {
|
|
25005
|
+
const message = await _deleteById(id);
|
|
25006
|
+
res.json(message);
|
|
25007
|
+
return;
|
|
25008
|
+
} catch (error) {
|
|
25009
|
+
next(error);
|
|
25010
|
+
}
|
|
25011
|
+
}
|
|
25012
|
+
return {
|
|
25013
|
+
add,
|
|
25014
|
+
getAll,
|
|
25015
|
+
getById,
|
|
25016
|
+
updateById,
|
|
25017
|
+
deleteById
|
|
25018
|
+
};
|
|
25019
|
+
}
|
|
22473
25020
|
// Annotate the CommonJS export names for ESM import in node:
|
|
22474
25021
|
0 && (module.exports = {
|
|
22475
25022
|
ACCESS_TOKEN_EXPIRY,
|
|
@@ -22514,6 +25061,14 @@ function useJournalLineController() {
|
|
|
22514
25061
|
VERIFICATION_USER_INVITE_DURATION,
|
|
22515
25062
|
XENDIT_BASE_URL,
|
|
22516
25063
|
XENDIT_SECRET_KEY,
|
|
25064
|
+
assetItemCategories,
|
|
25065
|
+
assetItemClasses,
|
|
25066
|
+
assetItemPurposes,
|
|
25067
|
+
assetItemStatuses,
|
|
25068
|
+
assetItemTrackingTypes,
|
|
25069
|
+
assetUnitStatuses,
|
|
25070
|
+
buildCategoryPath,
|
|
25071
|
+
categoryLevels,
|
|
22517
25072
|
chartOfAccountControlTypes,
|
|
22518
25073
|
chartOfAccountNormalBalances,
|
|
22519
25074
|
chartOfAccountStatuses,
|
|
@@ -22528,7 +25083,10 @@ function useJournalLineController() {
|
|
|
22528
25083
|
ledgerBillTypes,
|
|
22529
25084
|
modelAccountBalance,
|
|
22530
25085
|
modelApp,
|
|
25086
|
+
modelAssetItem,
|
|
25087
|
+
modelAssetUnit,
|
|
22531
25088
|
modelBusinessProfile,
|
|
25089
|
+
modelCategoryNode,
|
|
22532
25090
|
modelChartOfAccount,
|
|
22533
25091
|
modelCustomer,
|
|
22534
25092
|
modelJobApplication,
|
|
@@ -22554,14 +25112,22 @@ function useJournalLineController() {
|
|
|
22554
25112
|
modelPlan,
|
|
22555
25113
|
modelPromo,
|
|
22556
25114
|
modelRole,
|
|
25115
|
+
modelStockMovement,
|
|
22557
25116
|
modelSubscription,
|
|
22558
25117
|
modelSubscriptionTransaction,
|
|
25118
|
+
modelTag,
|
|
22559
25119
|
modelTax,
|
|
22560
25120
|
modelUser,
|
|
22561
25121
|
modelVerification,
|
|
25122
|
+
normalizeName,
|
|
22562
25123
|
schemaAccountBalance,
|
|
22563
25124
|
schemaApp,
|
|
22564
25125
|
schemaAppUpdate,
|
|
25126
|
+
schemaAssetItem,
|
|
25127
|
+
schemaAssetItemCreate,
|
|
25128
|
+
schemaAssetItemUpdate,
|
|
25129
|
+
schemaAssetUnit,
|
|
25130
|
+
schemaAssetUnitUpdate,
|
|
22565
25131
|
schemaAward,
|
|
22566
25132
|
schemaBuilding,
|
|
22567
25133
|
schemaBuildingUnit,
|
|
@@ -22573,6 +25139,10 @@ function useJournalLineController() {
|
|
|
22573
25139
|
schemaBusinessProfileRegisteredAddress,
|
|
22574
25140
|
schemaBusinessProfileTIN,
|
|
22575
25141
|
schemaBusinessProfileTradeName,
|
|
25142
|
+
schemaCategoryGetAll,
|
|
25143
|
+
schemaCategoryNodeCreate,
|
|
25144
|
+
schemaCategoryNodeStd,
|
|
25145
|
+
schemaCategoryNodeUpdate,
|
|
22576
25146
|
schemaCertification,
|
|
22577
25147
|
schemaChartOfAccountBase,
|
|
22578
25148
|
schemaChartOfAccountStd,
|
|
@@ -22642,6 +25212,7 @@ function useJournalLineController() {
|
|
|
22642
25212
|
schemaRole,
|
|
22643
25213
|
schemaRoleUpdate,
|
|
22644
25214
|
schemaSkill,
|
|
25215
|
+
schemaStockMovement,
|
|
22645
25216
|
schemaSubscribe,
|
|
22646
25217
|
schemaSubscription,
|
|
22647
25218
|
schemaSubscriptionCompute,
|
|
@@ -22649,6 +25220,9 @@ function useJournalLineController() {
|
|
|
22649
25220
|
schemaSubscriptionSeats,
|
|
22650
25221
|
schemaSubscriptionTransaction,
|
|
22651
25222
|
schemaSubscriptionUpdate,
|
|
25223
|
+
schemaTagCreate,
|
|
25224
|
+
schemaTagStd,
|
|
25225
|
+
schemaTagUpdate,
|
|
22652
25226
|
schemaTax,
|
|
22653
25227
|
schemaTaxUpdate,
|
|
22654
25228
|
schemaUpdateOptions,
|
|
@@ -22656,6 +25230,8 @@ function useJournalLineController() {
|
|
|
22656
25230
|
schemaVerification,
|
|
22657
25231
|
schemaVerificationOrgInvite,
|
|
22658
25232
|
schemaWorkExp,
|
|
25233
|
+
stockMovementReferenceTypes,
|
|
25234
|
+
stockMovementTypes,
|
|
22659
25235
|
taxDirections,
|
|
22660
25236
|
taxTypes,
|
|
22661
25237
|
transactionSchema,
|
|
@@ -22663,6 +25239,11 @@ function useJournalLineController() {
|
|
|
22663
25239
|
useAppController,
|
|
22664
25240
|
useAppRepo,
|
|
22665
25241
|
useAppService,
|
|
25242
|
+
useAssetItemController,
|
|
25243
|
+
useAssetItemRepo,
|
|
25244
|
+
useAssetItemService,
|
|
25245
|
+
useAssetUnitController,
|
|
25246
|
+
useAssetUnitRepo,
|
|
22666
25247
|
useAuthController,
|
|
22667
25248
|
useAuthService,
|
|
22668
25249
|
useBuildingController,
|
|
@@ -22673,6 +25254,9 @@ function useJournalLineController() {
|
|
|
22673
25254
|
useBuildingUnitService,
|
|
22674
25255
|
useBusinessProfileCtrl,
|
|
22675
25256
|
useBusinessProfileRepo,
|
|
25257
|
+
useCategoryController,
|
|
25258
|
+
useCategoryRepo,
|
|
25259
|
+
useCategoryService,
|
|
22676
25260
|
useChartOfAccountController,
|
|
22677
25261
|
useChartOfAccountRepo,
|
|
22678
25262
|
useCounterModel,
|
|
@@ -22728,11 +25312,17 @@ function useJournalLineController() {
|
|
|
22728
25312
|
useRoleController,
|
|
22729
25313
|
useRoleRepo,
|
|
22730
25314
|
useRoleService,
|
|
25315
|
+
useStockMovementController,
|
|
25316
|
+
useStockMovementRepo,
|
|
25317
|
+
useStockMovementService,
|
|
22731
25318
|
useSubscriptionController,
|
|
22732
25319
|
useSubscriptionRepo,
|
|
22733
25320
|
useSubscriptionService,
|
|
22734
25321
|
useSubscriptionTransactionController,
|
|
22735
25322
|
useSubscriptionTransactionRepo,
|
|
25323
|
+
useTagController,
|
|
25324
|
+
useTagRepo,
|
|
25325
|
+
useTagService,
|
|
22736
25326
|
useTaxController,
|
|
22737
25327
|
useTaxRepo,
|
|
22738
25328
|
useUserController,
|