@goweekdays/core 2.15.9 → 2.15.10
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 +6 -0
- package/dist/index.d.ts +23 -4
- package/dist/index.js +888 -765
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +797 -669
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -22572,19 +22572,36 @@ var schemaAssetItemCreate = Joi96.object({
|
|
|
22572
22572
|
}, "subcategory-requires-category");
|
|
22573
22573
|
var schemaAssetItemUpdate = Joi96.object({
|
|
22574
22574
|
name: Joi96.string().optional(),
|
|
22575
|
+
sku: Joi96.string().optional().allow("", null),
|
|
22576
|
+
price: Joi96.number().min(0).optional().allow("", null),
|
|
22577
|
+
unit: Joi96.string().optional(),
|
|
22575
22578
|
description: Joi96.string().optional().allow("", null),
|
|
22576
22579
|
assetCategory: Joi96.string().valid(...assetItemCategories).optional(),
|
|
22577
|
-
assetClass: Joi96.string().valid(...assetItemClasses).optional(),
|
|
22580
|
+
assetClass: Joi96.string().valid(...assetItemClasses).optional().allow("", null),
|
|
22581
|
+
trackingType: Joi96.string().valid(...assetItemTrackingTypes).optional(),
|
|
22582
|
+
departmentId: Joi96.string().hex().length(24).optional(),
|
|
22583
|
+
departmentName: Joi96.string().optional(),
|
|
22584
|
+
categoryId: Joi96.string().hex().length(24).optional().allow("", null),
|
|
22585
|
+
categoryName: Joi96.string().optional().allow("", null),
|
|
22586
|
+
subcategoryId: Joi96.string().hex().length(24).optional().allow("", null),
|
|
22587
|
+
subcategoryName: Joi96.string().optional().allow("", null),
|
|
22588
|
+
categoryPath: Joi96.string().optional().allow("", null),
|
|
22578
22589
|
brand: Joi96.string().optional().allow("", null),
|
|
22579
|
-
sku: Joi96.string().optional().allow("", null),
|
|
22580
|
-
price: Joi96.number().min(0).optional().allow("", null),
|
|
22581
22590
|
tags: Joi96.array().items(
|
|
22582
22591
|
Joi96.object({
|
|
22583
22592
|
id: Joi96.string().required(),
|
|
22584
22593
|
name: Joi96.string().required()
|
|
22585
22594
|
})
|
|
22586
22595
|
).optional(),
|
|
22587
|
-
|
|
22596
|
+
isPublic: Joi96.boolean().optional(),
|
|
22597
|
+
updatedAt: Joi96.date().optional()
|
|
22598
|
+
}).custom((value, helpers) => {
|
|
22599
|
+
if (value.subcategoryId && !value.categoryId) {
|
|
22600
|
+
return helpers.error("any.custom", {
|
|
22601
|
+
message: "categoryId is required when subcategoryId is provided."
|
|
22602
|
+
});
|
|
22603
|
+
}
|
|
22604
|
+
return value;
|
|
22588
22605
|
});
|
|
22589
22606
|
function modelAssetItem(data) {
|
|
22590
22607
|
const { error } = schemaAssetItem.validate(data);
|
|
@@ -22695,6 +22712,7 @@ function useAssetItemRepo() {
|
|
|
22695
22712
|
{ key: { trackingType: 1 } },
|
|
22696
22713
|
{ key: { status: 1 } },
|
|
22697
22714
|
{ key: { name: "text" } },
|
|
22715
|
+
{ key: { public: 1 } },
|
|
22698
22716
|
{
|
|
22699
22717
|
key: {
|
|
22700
22718
|
orgId: 1,
|
|
@@ -22739,10 +22757,14 @@ function useAssetItemRepo() {
|
|
|
22739
22757
|
categoryId = "",
|
|
22740
22758
|
subcategoryId = "",
|
|
22741
22759
|
brand = "",
|
|
22742
|
-
tags = []
|
|
22760
|
+
tags = [],
|
|
22761
|
+
isPublic = false
|
|
22743
22762
|
} = {}) {
|
|
22744
22763
|
page = page > 0 ? page - 1 : 0;
|
|
22745
22764
|
const query = { status: status2 };
|
|
22765
|
+
if (isPublic) {
|
|
22766
|
+
query.isPublic = true;
|
|
22767
|
+
}
|
|
22746
22768
|
if (search) {
|
|
22747
22769
|
query.$text = { $search: search };
|
|
22748
22770
|
}
|
|
@@ -22783,7 +22805,8 @@ function useAssetItemRepo() {
|
|
|
22783
22805
|
subcategoryId,
|
|
22784
22806
|
brand,
|
|
22785
22807
|
tags: tags.join(","),
|
|
22786
|
-
tag: "getAll"
|
|
22808
|
+
tag: "getAll",
|
|
22809
|
+
public: isPublic ? "true" : "false"
|
|
22787
22810
|
});
|
|
22788
22811
|
try {
|
|
22789
22812
|
const cached = await repo.getCache(cacheKey);
|
|
@@ -22828,10 +22851,16 @@ function useAssetItemRepo() {
|
|
|
22828
22851
|
}
|
|
22829
22852
|
}
|
|
22830
22853
|
async function updateById(id, value) {
|
|
22854
|
+
const { error, value: validatedValue } = schemaAssetItemUpdate.validate(value);
|
|
22855
|
+
if (error) {
|
|
22856
|
+
throw new BadRequestError109(
|
|
22857
|
+
`Invalid asset item update data: ${error.message}`
|
|
22858
|
+
);
|
|
22859
|
+
}
|
|
22831
22860
|
try {
|
|
22832
22861
|
const result = await repo.collection.findOneAndUpdate(
|
|
22833
22862
|
{ _id: new ObjectId59(id) },
|
|
22834
|
-
{ $set: { ...
|
|
22863
|
+
{ $set: { ...validatedValue, updatedAt: /* @__PURE__ */ new Date() } },
|
|
22835
22864
|
{ returnDocument: "after" }
|
|
22836
22865
|
);
|
|
22837
22866
|
if (!result) {
|
|
@@ -22839,9 +22868,9 @@ function useAssetItemRepo() {
|
|
|
22839
22868
|
}
|
|
22840
22869
|
repo.delCachedData();
|
|
22841
22870
|
return result;
|
|
22842
|
-
} catch (
|
|
22843
|
-
if (
|
|
22844
|
-
throw
|
|
22871
|
+
} catch (error2) {
|
|
22872
|
+
if (error2 instanceof AppError51)
|
|
22873
|
+
throw error2;
|
|
22845
22874
|
throw new InternalServerError47("Failed to update asset item.");
|
|
22846
22875
|
}
|
|
22847
22876
|
}
|
|
@@ -22897,9 +22926,10 @@ function useAssetItemRepo() {
|
|
|
22897
22926
|
|
|
22898
22927
|
// src/resources/asset-item/asset.item.service.ts
|
|
22899
22928
|
import {
|
|
22900
|
-
AppError as
|
|
22901
|
-
|
|
22902
|
-
|
|
22929
|
+
AppError as AppError56,
|
|
22930
|
+
BadRequestError as BadRequestError118,
|
|
22931
|
+
InternalServerError as InternalServerError52,
|
|
22932
|
+
useAtlas as useAtlas18
|
|
22903
22933
|
} from "@goweekdays/utils";
|
|
22904
22934
|
|
|
22905
22935
|
// src/resources/tag/tag.repository.ts
|
|
@@ -23576,246 +23606,203 @@ function useCategoryRepo() {
|
|
|
23576
23606
|
};
|
|
23577
23607
|
}
|
|
23578
23608
|
|
|
23579
|
-
// src/resources/asset-item/
|
|
23580
|
-
|
|
23581
|
-
|
|
23582
|
-
|
|
23583
|
-
|
|
23584
|
-
|
|
23585
|
-
|
|
23586
|
-
|
|
23587
|
-
|
|
23588
|
-
|
|
23589
|
-
|
|
23590
|
-
|
|
23591
|
-
|
|
23592
|
-
|
|
23593
|
-
|
|
23594
|
-
|
|
23595
|
-
|
|
23596
|
-
|
|
23597
|
-
|
|
23598
|
-
|
|
23599
|
-
|
|
23600
|
-
|
|
23601
|
-
|
|
23602
|
-
|
|
23603
|
-
|
|
23604
|
-
|
|
23605
|
-
|
|
23606
|
-
|
|
23607
|
-
|
|
23608
|
-
|
|
23609
|
-
|
|
23610
|
-
|
|
23611
|
-
|
|
23612
|
-
|
|
23613
|
-
|
|
23614
|
-
|
|
23615
|
-
|
|
23616
|
-
}
|
|
23617
|
-
}
|
|
23618
|
-
return resolvedTags;
|
|
23609
|
+
// src/resources/asset-item-movement/stock.movement.model.ts
|
|
23610
|
+
import Joi100 from "joi";
|
|
23611
|
+
import { ObjectId as ObjectId64 } from "mongodb";
|
|
23612
|
+
var stockMovementTypes = [
|
|
23613
|
+
"in",
|
|
23614
|
+
"out",
|
|
23615
|
+
"transfer",
|
|
23616
|
+
"adjustment",
|
|
23617
|
+
"conversion"
|
|
23618
|
+
];
|
|
23619
|
+
var stockMovementReferenceTypes = [
|
|
23620
|
+
"purchase",
|
|
23621
|
+
"sale",
|
|
23622
|
+
"transfer",
|
|
23623
|
+
"manual",
|
|
23624
|
+
"conversion"
|
|
23625
|
+
];
|
|
23626
|
+
var schemaStockMovement = Joi100.object({
|
|
23627
|
+
itemId: Joi100.string().hex().length(24).required(),
|
|
23628
|
+
type: Joi100.string().valid(...stockMovementTypes).required(),
|
|
23629
|
+
quantity: Joi100.number().positive().required(),
|
|
23630
|
+
unitCost: Joi100.number().min(0).optional().allow(null),
|
|
23631
|
+
totalCost: Joi100.number().min(0).optional().allow(null),
|
|
23632
|
+
reference: Joi100.object({
|
|
23633
|
+
type: Joi100.string().valid(...stockMovementReferenceTypes).required(),
|
|
23634
|
+
id: Joi100.string().optional().allow("", null)
|
|
23635
|
+
}).optional().allow(null),
|
|
23636
|
+
fromLocationId: Joi100.string().optional().allow("", null),
|
|
23637
|
+
toLocationId: Joi100.string().optional().allow("", null),
|
|
23638
|
+
fromItemId: Joi100.string().hex().length(24).optional().allow("", null),
|
|
23639
|
+
toItemId: Joi100.string().hex().length(24).optional().allow("", null),
|
|
23640
|
+
reason: Joi100.string().optional().allow("", null)
|
|
23641
|
+
});
|
|
23642
|
+
function modelStockMovement(data) {
|
|
23643
|
+
const { error } = schemaStockMovement.validate(data);
|
|
23644
|
+
if (error) {
|
|
23645
|
+
throw new Error(`Invalid stock movement data: ${error.message}`);
|
|
23619
23646
|
}
|
|
23620
|
-
|
|
23621
|
-
const session = useAtlas17.getClient()?.startSession();
|
|
23622
|
-
if (!session) {
|
|
23623
|
-
throw new InternalServerError50(
|
|
23624
|
-
"Unable to start session for asset item service."
|
|
23625
|
-
);
|
|
23626
|
-
}
|
|
23647
|
+
if (data.itemId && typeof data.itemId === "string") {
|
|
23627
23648
|
try {
|
|
23628
|
-
|
|
23629
|
-
|
|
23630
|
-
|
|
23631
|
-
if (value.categoryId) {
|
|
23632
|
-
const category = await categoryRepo.getById(value.categoryId);
|
|
23633
|
-
value.categoryName = category.displayName;
|
|
23634
|
-
}
|
|
23635
|
-
if (value.subcategoryId) {
|
|
23636
|
-
const subcategory = await categoryRepo.getById(value.subcategoryId);
|
|
23637
|
-
value.subcategoryName = subcategory.displayName;
|
|
23638
|
-
}
|
|
23639
|
-
const categoryPath = buildCategoryPath(
|
|
23640
|
-
value.departmentName,
|
|
23641
|
-
value.categoryName ?? "",
|
|
23642
|
-
value.subcategoryName ?? ""
|
|
23643
|
-
);
|
|
23644
|
-
value.categoryPath = categoryPath;
|
|
23645
|
-
const resolvedTags = value.tags.length ? await resolveTags(
|
|
23646
|
-
value.tags,
|
|
23647
|
-
value.orgId,
|
|
23648
|
-
value.categoryPath,
|
|
23649
|
-
session
|
|
23650
|
-
) : [];
|
|
23651
|
-
const assetItem = {
|
|
23652
|
-
...value,
|
|
23653
|
-
tags: resolvedTags
|
|
23654
|
-
};
|
|
23655
|
-
const assetItemId = await assetItemRepo.add(assetItem, session);
|
|
23656
|
-
await session.commitTransaction();
|
|
23657
|
-
return assetItemId;
|
|
23658
|
-
} catch (error) {
|
|
23659
|
-
await session.abortTransaction();
|
|
23660
|
-
if (error instanceof AppError54)
|
|
23661
|
-
throw error;
|
|
23662
|
-
throw new InternalServerError50("Failed to create asset item.");
|
|
23663
|
-
} finally {
|
|
23664
|
-
session.endSession();
|
|
23649
|
+
data.itemId = new ObjectId64(data.itemId);
|
|
23650
|
+
} catch {
|
|
23651
|
+
throw new Error(`Invalid itemId format: ${data.itemId}`);
|
|
23665
23652
|
}
|
|
23666
23653
|
}
|
|
23667
23654
|
return {
|
|
23668
|
-
|
|
23655
|
+
_id: data._id,
|
|
23656
|
+
itemId: data.itemId,
|
|
23657
|
+
type: data.type,
|
|
23658
|
+
quantity: data.quantity,
|
|
23659
|
+
unitCost: data.unitCost ?? 0,
|
|
23660
|
+
totalCost: data.totalCost ?? 0,
|
|
23661
|
+
reference: data.reference ?? void 0,
|
|
23662
|
+
fromLocationId: data.fromLocationId ?? "",
|
|
23663
|
+
toLocationId: data.toLocationId ?? "",
|
|
23664
|
+
fromItemId: data.fromItemId ?? "",
|
|
23665
|
+
toItemId: data.toItemId ?? "",
|
|
23666
|
+
reason: data.reason ?? "",
|
|
23667
|
+
createdAt: data.createdAt ?? /* @__PURE__ */ new Date()
|
|
23669
23668
|
};
|
|
23670
23669
|
}
|
|
23671
23670
|
|
|
23672
|
-
// src/resources/asset-item/
|
|
23673
|
-
import
|
|
23674
|
-
|
|
23675
|
-
|
|
23676
|
-
|
|
23677
|
-
|
|
23678
|
-
|
|
23679
|
-
|
|
23680
|
-
|
|
23681
|
-
|
|
23682
|
-
|
|
23683
|
-
|
|
23684
|
-
|
|
23685
|
-
|
|
23686
|
-
|
|
23687
|
-
tags: Joi100.string().optional().allow("", null)
|
|
23688
|
-
});
|
|
23689
|
-
var idParamSchema2 = Joi100.object({
|
|
23690
|
-
id: Joi100.string().hex().length(24).required()
|
|
23691
|
-
});
|
|
23692
|
-
function useAssetItemController() {
|
|
23693
|
-
const repo = useAssetItemRepo();
|
|
23694
|
-
const service = useAssetItemService();
|
|
23695
|
-
async function add(req, res, next) {
|
|
23671
|
+
// src/resources/asset-item-movement/stock.movement.repository.ts
|
|
23672
|
+
import {
|
|
23673
|
+
AppError as AppError54,
|
|
23674
|
+
BadRequestError as BadRequestError113,
|
|
23675
|
+
InternalServerError as InternalServerError50,
|
|
23676
|
+
logger as logger57,
|
|
23677
|
+
makeCacheKey as makeCacheKey36,
|
|
23678
|
+
paginate as paginate32,
|
|
23679
|
+
useRepo as useRepo35
|
|
23680
|
+
} from "@goweekdays/utils";
|
|
23681
|
+
import { ObjectId as ObjectId65 } from "mongodb";
|
|
23682
|
+
function useStockMovementRepo() {
|
|
23683
|
+
const namespace_collection = "stock.movements";
|
|
23684
|
+
const repo = useRepo35(namespace_collection);
|
|
23685
|
+
async function createIndexes() {
|
|
23696
23686
|
try {
|
|
23697
|
-
|
|
23698
|
-
|
|
23699
|
-
|
|
23700
|
-
|
|
23701
|
-
|
|
23702
|
-
|
|
23703
|
-
res.status(201).json({
|
|
23704
|
-
message: "Asset item created successfully.",
|
|
23705
|
-
assetItemId
|
|
23706
|
-
});
|
|
23687
|
+
await repo.collection.createIndexes([
|
|
23688
|
+
{ key: { itemId: 1 } },
|
|
23689
|
+
{ key: { createdAt: -1 } },
|
|
23690
|
+
{ key: { itemId: 1, createdAt: -1 } },
|
|
23691
|
+
{ key: { type: 1 } }
|
|
23692
|
+
]);
|
|
23707
23693
|
} catch (error) {
|
|
23708
|
-
|
|
23694
|
+
throw new BadRequestError113("Failed to create stock movement indexes.");
|
|
23709
23695
|
}
|
|
23710
23696
|
}
|
|
23711
|
-
async function
|
|
23712
|
-
const { error } = paginationSchema3.validate(req.query);
|
|
23713
|
-
if (error) {
|
|
23714
|
-
next(new BadRequestError114(error.message));
|
|
23715
|
-
return;
|
|
23716
|
-
}
|
|
23717
|
-
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
23718
|
-
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
23719
|
-
const search = req.query.search ?? "";
|
|
23720
|
-
const status2 = req.query.status ?? "active";
|
|
23721
|
-
const assetCategory = req.query.assetCategory ?? "";
|
|
23722
|
-
const trackingType = req.query.trackingType ?? "";
|
|
23723
|
-
const purpose = req.query.purpose ?? "";
|
|
23724
|
-
const departmentId = req.query.departmentId ?? "";
|
|
23725
|
-
const categoryId = req.query.categoryId ?? "";
|
|
23726
|
-
const subcategoryId = req.query.subcategoryId ?? "";
|
|
23727
|
-
const brand = req.query.brand ?? "";
|
|
23728
|
-
const tagsParam = req.query.tags ?? "";
|
|
23729
|
-
const tags = tagsParam ? tagsParam.split(",").filter(Boolean) : [];
|
|
23730
|
-
if (!isFinite(page)) {
|
|
23731
|
-
next(new BadRequestError114("Invalid page number."));
|
|
23732
|
-
return;
|
|
23733
|
-
}
|
|
23734
|
-
if (!isFinite(limit)) {
|
|
23735
|
-
next(new BadRequestError114("Invalid limit number."));
|
|
23736
|
-
return;
|
|
23737
|
-
}
|
|
23697
|
+
async function add(value, session) {
|
|
23738
23698
|
try {
|
|
23739
|
-
|
|
23740
|
-
|
|
23741
|
-
|
|
23742
|
-
|
|
23743
|
-
|
|
23744
|
-
|
|
23745
|
-
|
|
23746
|
-
purpose,
|
|
23747
|
-
departmentId,
|
|
23748
|
-
categoryId,
|
|
23749
|
-
subcategoryId,
|
|
23750
|
-
brand,
|
|
23751
|
-
tags
|
|
23752
|
-
});
|
|
23753
|
-
res.json(results);
|
|
23754
|
-
} catch (error2) {
|
|
23755
|
-
next(error2);
|
|
23699
|
+
value = modelStockMovement(value);
|
|
23700
|
+
const res = await repo.collection.insertOne(value, { session });
|
|
23701
|
+
repo.delCachedData();
|
|
23702
|
+
return res.insertedId;
|
|
23703
|
+
} catch (error) {
|
|
23704
|
+
logger57.log({ level: "error", message: error.message });
|
|
23705
|
+
throw new InternalServerError50("Failed to create stock movement.");
|
|
23756
23706
|
}
|
|
23757
23707
|
}
|
|
23758
|
-
async function
|
|
23759
|
-
|
|
23760
|
-
|
|
23761
|
-
|
|
23762
|
-
|
|
23708
|
+
async function getByItemId({
|
|
23709
|
+
itemId,
|
|
23710
|
+
type = "",
|
|
23711
|
+
page = 1,
|
|
23712
|
+
limit = 10
|
|
23713
|
+
}) {
|
|
23714
|
+
page = page > 0 ? page - 1 : 0;
|
|
23715
|
+
const query = {
|
|
23716
|
+
itemId: new ObjectId65(itemId)
|
|
23717
|
+
};
|
|
23718
|
+
if (type) {
|
|
23719
|
+
query.type = type;
|
|
23763
23720
|
}
|
|
23721
|
+
const cacheKey = makeCacheKey36(namespace_collection, {
|
|
23722
|
+
itemId,
|
|
23723
|
+
type,
|
|
23724
|
+
page,
|
|
23725
|
+
limit,
|
|
23726
|
+
tag: "byItemId"
|
|
23727
|
+
});
|
|
23764
23728
|
try {
|
|
23765
|
-
const
|
|
23766
|
-
|
|
23767
|
-
|
|
23768
|
-
|
|
23729
|
+
const cached = await repo.getCache(cacheKey);
|
|
23730
|
+
if (cached)
|
|
23731
|
+
return cached;
|
|
23732
|
+
const items = await repo.collection.aggregate([
|
|
23733
|
+
{ $match: query },
|
|
23734
|
+
{ $sort: { createdAt: -1 } },
|
|
23735
|
+
{ $skip: page * limit },
|
|
23736
|
+
{ $limit: limit }
|
|
23737
|
+
]).toArray();
|
|
23738
|
+
const length = await repo.collection.countDocuments(query);
|
|
23739
|
+
const data = paginate32(items, page, limit, length);
|
|
23740
|
+
repo.setCache(cacheKey, data, 600);
|
|
23741
|
+
return data;
|
|
23742
|
+
} catch (error) {
|
|
23743
|
+
logger57.log({ level: "error", message: `${error}` });
|
|
23744
|
+
throw error;
|
|
23769
23745
|
}
|
|
23770
23746
|
}
|
|
23771
|
-
async function
|
|
23772
|
-
const
|
|
23773
|
-
|
|
23774
|
-
|
|
23775
|
-
|
|
23776
|
-
}
|
|
23777
|
-
const { error: bodyError } = schemaAssetItemUpdate.validate(req.body);
|
|
23778
|
-
if (bodyError) {
|
|
23779
|
-
next(new BadRequestError114(bodyError.message));
|
|
23780
|
-
return;
|
|
23781
|
-
}
|
|
23747
|
+
async function getById(id) {
|
|
23748
|
+
const cacheKey = makeCacheKey36(namespace_collection, {
|
|
23749
|
+
id,
|
|
23750
|
+
tag: "by-id"
|
|
23751
|
+
});
|
|
23782
23752
|
try {
|
|
23783
|
-
const
|
|
23784
|
-
|
|
23785
|
-
|
|
23786
|
-
|
|
23753
|
+
const cached = await repo.getCache(cacheKey);
|
|
23754
|
+
if (cached)
|
|
23755
|
+
return cached;
|
|
23756
|
+
const result = await repo.collection.findOne({
|
|
23757
|
+
_id: new ObjectId65(id)
|
|
23787
23758
|
});
|
|
23759
|
+
if (!result) {
|
|
23760
|
+
throw new BadRequestError113("Stock movement not found.");
|
|
23761
|
+
}
|
|
23762
|
+
repo.setCache(cacheKey, result, 300);
|
|
23763
|
+
return result;
|
|
23788
23764
|
} catch (error) {
|
|
23789
|
-
|
|
23765
|
+
if (error instanceof AppError54)
|
|
23766
|
+
throw error;
|
|
23767
|
+
throw new InternalServerError50("Failed to get stock movement.");
|
|
23790
23768
|
}
|
|
23791
23769
|
}
|
|
23792
|
-
async function
|
|
23793
|
-
const { error } = idParamSchema2.validate(req.params);
|
|
23794
|
-
if (error) {
|
|
23795
|
-
next(new BadRequestError114(error.message));
|
|
23796
|
-
return;
|
|
23797
|
-
}
|
|
23770
|
+
async function countByItemId(itemId) {
|
|
23798
23771
|
try {
|
|
23799
|
-
await repo.
|
|
23800
|
-
|
|
23801
|
-
message: "Asset item deleted successfully."
|
|
23772
|
+
return await repo.collection.countDocuments({
|
|
23773
|
+
itemId: new ObjectId65(itemId)
|
|
23802
23774
|
});
|
|
23803
|
-
} catch (
|
|
23804
|
-
|
|
23775
|
+
} catch (error) {
|
|
23776
|
+
throw new InternalServerError50("Failed to count stock movements.");
|
|
23805
23777
|
}
|
|
23806
23778
|
}
|
|
23807
23779
|
return {
|
|
23780
|
+
createIndexes,
|
|
23808
23781
|
add,
|
|
23809
|
-
|
|
23782
|
+
getByItemId,
|
|
23810
23783
|
getById,
|
|
23811
|
-
|
|
23812
|
-
deleteById
|
|
23784
|
+
countByItemId
|
|
23813
23785
|
};
|
|
23814
23786
|
}
|
|
23815
23787
|
|
|
23788
|
+
// src/resources/asset-item-movement/stock.movement.service.ts
|
|
23789
|
+
import { BadRequestError as BadRequestError115 } from "@goweekdays/utils";
|
|
23790
|
+
import { useAtlas as useAtlas17 } from "@goweekdays/utils";
|
|
23791
|
+
|
|
23792
|
+
// src/resources/asset-item-unit/asset.unit.repository.ts
|
|
23793
|
+
import {
|
|
23794
|
+
AppError as AppError55,
|
|
23795
|
+
BadRequestError as BadRequestError114,
|
|
23796
|
+
InternalServerError as InternalServerError51,
|
|
23797
|
+
logger as logger58,
|
|
23798
|
+
makeCacheKey as makeCacheKey37,
|
|
23799
|
+
paginate as paginate33,
|
|
23800
|
+
useRepo as useRepo36
|
|
23801
|
+
} from "@goweekdays/utils";
|
|
23802
|
+
|
|
23816
23803
|
// src/resources/asset-item-unit/asset.unit.model.ts
|
|
23817
23804
|
import Joi101 from "joi";
|
|
23818
|
-
import { ObjectId as
|
|
23805
|
+
import { ObjectId as ObjectId66 } from "mongodb";
|
|
23819
23806
|
var assetUnitStatuses = [
|
|
23820
23807
|
"available",
|
|
23821
23808
|
"assigned",
|
|
@@ -23845,7 +23832,7 @@ function modelAssetUnit(data) {
|
|
|
23845
23832
|
}
|
|
23846
23833
|
if (data.itemId && typeof data.itemId === "string") {
|
|
23847
23834
|
try {
|
|
23848
|
-
data.itemId = new
|
|
23835
|
+
data.itemId = new ObjectId66(data.itemId);
|
|
23849
23836
|
} catch {
|
|
23850
23837
|
throw new Error(`Invalid itemId format: ${data.itemId}`);
|
|
23851
23838
|
}
|
|
@@ -23864,19 +23851,10 @@ function modelAssetUnit(data) {
|
|
|
23864
23851
|
}
|
|
23865
23852
|
|
|
23866
23853
|
// src/resources/asset-item-unit/asset.unit.repository.ts
|
|
23867
|
-
import {
|
|
23868
|
-
AppError as AppError55,
|
|
23869
|
-
BadRequestError as BadRequestError115,
|
|
23870
|
-
InternalServerError as InternalServerError51,
|
|
23871
|
-
logger as logger57,
|
|
23872
|
-
makeCacheKey as makeCacheKey36,
|
|
23873
|
-
paginate as paginate32,
|
|
23874
|
-
useRepo as useRepo35
|
|
23875
|
-
} from "@goweekdays/utils";
|
|
23876
|
-
import { ObjectId as ObjectId65 } from "mongodb";
|
|
23854
|
+
import { ObjectId as ObjectId67 } from "mongodb";
|
|
23877
23855
|
function useAssetUnitRepo() {
|
|
23878
23856
|
const namespace_collection = "asset.units";
|
|
23879
|
-
const repo =
|
|
23857
|
+
const repo = useRepo36(namespace_collection);
|
|
23880
23858
|
async function createIndexes() {
|
|
23881
23859
|
try {
|
|
23882
23860
|
await repo.collection.createIndexes([
|
|
@@ -23886,7 +23864,7 @@ function useAssetUnitRepo() {
|
|
|
23886
23864
|
{ key: { itemId: 1, status: 1 } }
|
|
23887
23865
|
]);
|
|
23888
23866
|
} catch (error) {
|
|
23889
|
-
throw new
|
|
23867
|
+
throw new BadRequestError114("Failed to create asset unit indexes.");
|
|
23890
23868
|
}
|
|
23891
23869
|
}
|
|
23892
23870
|
async function add(value, session) {
|
|
@@ -23896,7 +23874,7 @@ function useAssetUnitRepo() {
|
|
|
23896
23874
|
repo.delCachedData();
|
|
23897
23875
|
return res.insertedId;
|
|
23898
23876
|
} catch (error) {
|
|
23899
|
-
|
|
23877
|
+
logger58.log({ level: "error", message: error.message });
|
|
23900
23878
|
throw new InternalServerError51("Failed to create asset unit.");
|
|
23901
23879
|
}
|
|
23902
23880
|
}
|
|
@@ -23908,12 +23886,12 @@ function useAssetUnitRepo() {
|
|
|
23908
23886
|
}) {
|
|
23909
23887
|
page = page > 0 ? page - 1 : 0;
|
|
23910
23888
|
const query = {
|
|
23911
|
-
itemId: new
|
|
23889
|
+
itemId: new ObjectId67(itemId)
|
|
23912
23890
|
};
|
|
23913
23891
|
if (status2) {
|
|
23914
23892
|
query.status = status2;
|
|
23915
23893
|
}
|
|
23916
|
-
const cacheKey =
|
|
23894
|
+
const cacheKey = makeCacheKey37(namespace_collection, {
|
|
23917
23895
|
itemId,
|
|
23918
23896
|
status: status2,
|
|
23919
23897
|
page,
|
|
@@ -23931,16 +23909,16 @@ function useAssetUnitRepo() {
|
|
|
23931
23909
|
{ $limit: limit }
|
|
23932
23910
|
]).toArray();
|
|
23933
23911
|
const length = await repo.collection.countDocuments(query);
|
|
23934
|
-
const data =
|
|
23912
|
+
const data = paginate33(items, page, limit, length);
|
|
23935
23913
|
repo.setCache(cacheKey, data, 600);
|
|
23936
23914
|
return data;
|
|
23937
23915
|
} catch (error) {
|
|
23938
|
-
|
|
23916
|
+
logger58.log({ level: "error", message: `${error}` });
|
|
23939
23917
|
throw error;
|
|
23940
23918
|
}
|
|
23941
23919
|
}
|
|
23942
23920
|
async function getById(id) {
|
|
23943
|
-
const cacheKey =
|
|
23921
|
+
const cacheKey = makeCacheKey37(namespace_collection, {
|
|
23944
23922
|
id,
|
|
23945
23923
|
tag: "by-id"
|
|
23946
23924
|
});
|
|
@@ -23949,10 +23927,10 @@ function useAssetUnitRepo() {
|
|
|
23949
23927
|
if (cached)
|
|
23950
23928
|
return cached;
|
|
23951
23929
|
const result = await repo.collection.findOne({
|
|
23952
|
-
_id: new
|
|
23930
|
+
_id: new ObjectId67(id)
|
|
23953
23931
|
});
|
|
23954
23932
|
if (!result) {
|
|
23955
|
-
throw new
|
|
23933
|
+
throw new BadRequestError114("Asset unit not found.");
|
|
23956
23934
|
}
|
|
23957
23935
|
repo.setCache(cacheKey, result, 300);
|
|
23958
23936
|
return result;
|
|
@@ -23965,12 +23943,12 @@ function useAssetUnitRepo() {
|
|
|
23965
23943
|
async function updateById(id, value, session) {
|
|
23966
23944
|
try {
|
|
23967
23945
|
const result = await repo.collection.findOneAndUpdate(
|
|
23968
|
-
{ _id: new
|
|
23946
|
+
{ _id: new ObjectId67(id) },
|
|
23969
23947
|
{ $set: { ...value, updatedAt: /* @__PURE__ */ new Date() } },
|
|
23970
23948
|
{ returnDocument: "after", session }
|
|
23971
23949
|
);
|
|
23972
23950
|
if (!result) {
|
|
23973
|
-
throw new
|
|
23951
|
+
throw new BadRequestError114("Asset unit not found.");
|
|
23974
23952
|
}
|
|
23975
23953
|
repo.delCachedData();
|
|
23976
23954
|
return result;
|
|
@@ -23980,10 +23958,19 @@ function useAssetUnitRepo() {
|
|
|
23980
23958
|
throw new InternalServerError51("Failed to update asset unit.");
|
|
23981
23959
|
}
|
|
23982
23960
|
}
|
|
23961
|
+
async function countByItemId(itemId) {
|
|
23962
|
+
try {
|
|
23963
|
+
return await repo.collection.countDocuments({
|
|
23964
|
+
itemId: new ObjectId67(itemId)
|
|
23965
|
+
});
|
|
23966
|
+
} catch (error) {
|
|
23967
|
+
throw new InternalServerError51("Failed to count asset units.");
|
|
23968
|
+
}
|
|
23969
|
+
}
|
|
23983
23970
|
async function countByItemIdAndStatus(itemId, status2) {
|
|
23984
23971
|
try {
|
|
23985
23972
|
return await repo.collection.countDocuments({
|
|
23986
|
-
itemId: new
|
|
23973
|
+
itemId: new ObjectId67(itemId),
|
|
23987
23974
|
status: status2
|
|
23988
23975
|
});
|
|
23989
23976
|
} catch (error) {
|
|
@@ -23996,357 +23983,98 @@ function useAssetUnitRepo() {
|
|
|
23996
23983
|
getByItemId,
|
|
23997
23984
|
getById,
|
|
23998
23985
|
updateById,
|
|
23986
|
+
countByItemId,
|
|
23999
23987
|
countByItemIdAndStatus
|
|
24000
23988
|
};
|
|
24001
23989
|
}
|
|
24002
23990
|
|
|
24003
|
-
// src/resources/asset-item-
|
|
24004
|
-
|
|
24005
|
-
|
|
24006
|
-
|
|
24007
|
-
|
|
24008
|
-
|
|
24009
|
-
|
|
24010
|
-
|
|
24011
|
-
|
|
24012
|
-
|
|
24013
|
-
id: Joi102.string().hex().length(24).required()
|
|
24014
|
-
});
|
|
24015
|
-
function useAssetUnitController() {
|
|
24016
|
-
const repo = useAssetUnitRepo();
|
|
24017
|
-
async function add(req, res, next) {
|
|
23991
|
+
// src/resources/asset-item-movement/stock.movement.service.ts
|
|
23992
|
+
function useStockMovementService() {
|
|
23993
|
+
const movementRepo = useStockMovementRepo();
|
|
23994
|
+
const assetItemRepo = useAssetItemRepo();
|
|
23995
|
+
const assetUnitRepo = useAssetUnitRepo();
|
|
23996
|
+
async function stockIn(value, units) {
|
|
23997
|
+
const item = await assetItemRepo.getById(String(value.itemId));
|
|
23998
|
+
const session = useAtlas17.getClient()?.startSession();
|
|
23999
|
+
if (!session)
|
|
24000
|
+
throw new BadRequestError115("Failed to start session.");
|
|
24018
24001
|
try {
|
|
24019
|
-
|
|
24020
|
-
|
|
24021
|
-
|
|
24022
|
-
|
|
24002
|
+
session.startTransaction();
|
|
24003
|
+
value.type = "in";
|
|
24004
|
+
const movementId = await movementRepo.add(value, session);
|
|
24005
|
+
if (item.trackingType === "individual" && units && units.length > 0) {
|
|
24006
|
+
for (const unit of units) {
|
|
24007
|
+
await assetUnitRepo.add(
|
|
24008
|
+
{
|
|
24009
|
+
itemId: value.itemId,
|
|
24010
|
+
serialNumber: unit.serialNumber,
|
|
24011
|
+
plateNumber: unit.plateNumber,
|
|
24012
|
+
status: "available",
|
|
24013
|
+
locationId: value.toLocationId
|
|
24014
|
+
},
|
|
24015
|
+
session
|
|
24016
|
+
);
|
|
24017
|
+
}
|
|
24023
24018
|
}
|
|
24024
|
-
|
|
24025
|
-
|
|
24026
|
-
|
|
24027
|
-
|
|
24028
|
-
|
|
24019
|
+
await assetItemRepo.incrementQuantity(
|
|
24020
|
+
String(value.itemId),
|
|
24021
|
+
value.quantity,
|
|
24022
|
+
session
|
|
24023
|
+
);
|
|
24024
|
+
await session.commitTransaction();
|
|
24025
|
+
return movementId;
|
|
24029
24026
|
} catch (error) {
|
|
24030
|
-
|
|
24031
|
-
|
|
24032
|
-
|
|
24033
|
-
|
|
24034
|
-
const { error } = querySchema.validate(req.query);
|
|
24035
|
-
if (error) {
|
|
24036
|
-
next(new BadRequestError116(error.message));
|
|
24037
|
-
return;
|
|
24038
|
-
}
|
|
24039
|
-
const itemId = req.query.itemId;
|
|
24040
|
-
const status2 = req.query.status ?? "";
|
|
24041
|
-
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
24042
|
-
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
24043
|
-
try {
|
|
24044
|
-
const results = await repo.getByItemId({ itemId, status: status2, page, limit });
|
|
24045
|
-
res.json(results);
|
|
24046
|
-
} catch (error2) {
|
|
24047
|
-
next(error2);
|
|
24027
|
+
await session.abortTransaction();
|
|
24028
|
+
throw error;
|
|
24029
|
+
} finally {
|
|
24030
|
+
await session.endSession();
|
|
24048
24031
|
}
|
|
24049
24032
|
}
|
|
24050
|
-
async function
|
|
24051
|
-
const
|
|
24052
|
-
if (
|
|
24053
|
-
|
|
24054
|
-
return;
|
|
24033
|
+
async function stockOut(value, unitIds) {
|
|
24034
|
+
const item = await assetItemRepo.getById(String(value.itemId));
|
|
24035
|
+
if (item.trackingType === "quantity" && (item.quantityOnHand ?? 0) < value.quantity) {
|
|
24036
|
+
throw new BadRequestError115("Insufficient stock.");
|
|
24055
24037
|
}
|
|
24038
|
+
const session = useAtlas17.getClient()?.startSession();
|
|
24039
|
+
if (!session)
|
|
24040
|
+
throw new BadRequestError115("Failed to start session.");
|
|
24056
24041
|
try {
|
|
24057
|
-
|
|
24058
|
-
|
|
24059
|
-
|
|
24060
|
-
|
|
24042
|
+
session.startTransaction();
|
|
24043
|
+
value.type = "out";
|
|
24044
|
+
const movementId = await movementRepo.add(value, session);
|
|
24045
|
+
if (item.trackingType === "individual" && unitIds && unitIds.length > 0) {
|
|
24046
|
+
for (const unitId of unitIds) {
|
|
24047
|
+
await assetUnitRepo.updateById(
|
|
24048
|
+
unitId,
|
|
24049
|
+
{ status: "assigned", assignedTo: value.reference?.id },
|
|
24050
|
+
session
|
|
24051
|
+
);
|
|
24052
|
+
}
|
|
24053
|
+
}
|
|
24054
|
+
await assetItemRepo.incrementQuantity(
|
|
24055
|
+
String(value.itemId),
|
|
24056
|
+
-value.quantity,
|
|
24057
|
+
session
|
|
24058
|
+
);
|
|
24059
|
+
await session.commitTransaction();
|
|
24060
|
+
return movementId;
|
|
24061
|
+
} catch (error) {
|
|
24062
|
+
await session.abortTransaction();
|
|
24063
|
+
throw error;
|
|
24064
|
+
} finally {
|
|
24065
|
+
await session.endSession();
|
|
24061
24066
|
}
|
|
24062
24067
|
}
|
|
24063
|
-
async function
|
|
24064
|
-
|
|
24065
|
-
|
|
24066
|
-
|
|
24067
|
-
|
|
24068
|
-
}
|
|
24069
|
-
const { error: bodyError } = schemaAssetUnitUpdate.validate(req.body);
|
|
24070
|
-
if (bodyError) {
|
|
24071
|
-
next(new BadRequestError116(bodyError.message));
|
|
24072
|
-
return;
|
|
24068
|
+
async function transfer(value, unitIds) {
|
|
24069
|
+
if (!value.fromLocationId || !value.toLocationId) {
|
|
24070
|
+
throw new BadRequestError115(
|
|
24071
|
+
"Transfer requires both fromLocationId and toLocationId."
|
|
24072
|
+
);
|
|
24073
24073
|
}
|
|
24074
|
-
|
|
24075
|
-
|
|
24076
|
-
|
|
24077
|
-
|
|
24078
|
-
unit: updatedUnit
|
|
24079
|
-
});
|
|
24080
|
-
} catch (error) {
|
|
24081
|
-
next(error);
|
|
24082
|
-
}
|
|
24083
|
-
}
|
|
24084
|
-
return {
|
|
24085
|
-
add,
|
|
24086
|
-
getByItemId,
|
|
24087
|
-
getById,
|
|
24088
|
-
updateById
|
|
24089
|
-
};
|
|
24090
|
-
}
|
|
24091
|
-
|
|
24092
|
-
// src/resources/asset-item-movement/stock.movement.model.ts
|
|
24093
|
-
import Joi103 from "joi";
|
|
24094
|
-
import { ObjectId as ObjectId66 } from "mongodb";
|
|
24095
|
-
var stockMovementTypes = [
|
|
24096
|
-
"in",
|
|
24097
|
-
"out",
|
|
24098
|
-
"transfer",
|
|
24099
|
-
"adjustment",
|
|
24100
|
-
"conversion"
|
|
24101
|
-
];
|
|
24102
|
-
var stockMovementReferenceTypes = [
|
|
24103
|
-
"purchase",
|
|
24104
|
-
"sale",
|
|
24105
|
-
"transfer",
|
|
24106
|
-
"manual",
|
|
24107
|
-
"conversion"
|
|
24108
|
-
];
|
|
24109
|
-
var schemaStockMovement = Joi103.object({
|
|
24110
|
-
itemId: Joi103.string().hex().length(24).required(),
|
|
24111
|
-
type: Joi103.string().valid(...stockMovementTypes).required(),
|
|
24112
|
-
quantity: Joi103.number().positive().required(),
|
|
24113
|
-
unitCost: Joi103.number().min(0).optional().allow(null),
|
|
24114
|
-
totalCost: Joi103.number().min(0).optional().allow(null),
|
|
24115
|
-
reference: Joi103.object({
|
|
24116
|
-
type: Joi103.string().valid(...stockMovementReferenceTypes).required(),
|
|
24117
|
-
id: Joi103.string().optional().allow("", null)
|
|
24118
|
-
}).optional().allow(null),
|
|
24119
|
-
fromLocationId: Joi103.string().optional().allow("", null),
|
|
24120
|
-
toLocationId: Joi103.string().optional().allow("", null),
|
|
24121
|
-
fromItemId: Joi103.string().hex().length(24).optional().allow("", null),
|
|
24122
|
-
toItemId: Joi103.string().hex().length(24).optional().allow("", null),
|
|
24123
|
-
reason: Joi103.string().optional().allow("", null)
|
|
24124
|
-
});
|
|
24125
|
-
function modelStockMovement(data) {
|
|
24126
|
-
const { error } = schemaStockMovement.validate(data);
|
|
24127
|
-
if (error) {
|
|
24128
|
-
throw new Error(`Invalid stock movement data: ${error.message}`);
|
|
24129
|
-
}
|
|
24130
|
-
if (data.itemId && typeof data.itemId === "string") {
|
|
24131
|
-
try {
|
|
24132
|
-
data.itemId = new ObjectId66(data.itemId);
|
|
24133
|
-
} catch {
|
|
24134
|
-
throw new Error(`Invalid itemId format: ${data.itemId}`);
|
|
24135
|
-
}
|
|
24136
|
-
}
|
|
24137
|
-
return {
|
|
24138
|
-
_id: data._id,
|
|
24139
|
-
itemId: data.itemId,
|
|
24140
|
-
type: data.type,
|
|
24141
|
-
quantity: data.quantity,
|
|
24142
|
-
unitCost: data.unitCost ?? 0,
|
|
24143
|
-
totalCost: data.totalCost ?? 0,
|
|
24144
|
-
reference: data.reference ?? void 0,
|
|
24145
|
-
fromLocationId: data.fromLocationId ?? "",
|
|
24146
|
-
toLocationId: data.toLocationId ?? "",
|
|
24147
|
-
fromItemId: data.fromItemId ?? "",
|
|
24148
|
-
toItemId: data.toItemId ?? "",
|
|
24149
|
-
reason: data.reason ?? "",
|
|
24150
|
-
createdAt: data.createdAt ?? /* @__PURE__ */ new Date()
|
|
24151
|
-
};
|
|
24152
|
-
}
|
|
24153
|
-
|
|
24154
|
-
// src/resources/asset-item-movement/stock.movement.repository.ts
|
|
24155
|
-
import {
|
|
24156
|
-
AppError as AppError56,
|
|
24157
|
-
BadRequestError as BadRequestError117,
|
|
24158
|
-
InternalServerError as InternalServerError52,
|
|
24159
|
-
logger as logger58,
|
|
24160
|
-
makeCacheKey as makeCacheKey37,
|
|
24161
|
-
paginate as paginate33,
|
|
24162
|
-
useRepo as useRepo36
|
|
24163
|
-
} from "@goweekdays/utils";
|
|
24164
|
-
import { ObjectId as ObjectId67 } from "mongodb";
|
|
24165
|
-
function useStockMovementRepo() {
|
|
24166
|
-
const namespace_collection = "stock.movements";
|
|
24167
|
-
const repo = useRepo36(namespace_collection);
|
|
24168
|
-
async function createIndexes() {
|
|
24169
|
-
try {
|
|
24170
|
-
await repo.collection.createIndexes([
|
|
24171
|
-
{ key: { itemId: 1 } },
|
|
24172
|
-
{ key: { createdAt: -1 } },
|
|
24173
|
-
{ key: { itemId: 1, createdAt: -1 } },
|
|
24174
|
-
{ key: { type: 1 } }
|
|
24175
|
-
]);
|
|
24176
|
-
} catch (error) {
|
|
24177
|
-
throw new BadRequestError117("Failed to create stock movement indexes.");
|
|
24178
|
-
}
|
|
24179
|
-
}
|
|
24180
|
-
async function add(value, session) {
|
|
24181
|
-
try {
|
|
24182
|
-
value = modelStockMovement(value);
|
|
24183
|
-
const res = await repo.collection.insertOne(value, { session });
|
|
24184
|
-
repo.delCachedData();
|
|
24185
|
-
return res.insertedId;
|
|
24186
|
-
} catch (error) {
|
|
24187
|
-
logger58.log({ level: "error", message: error.message });
|
|
24188
|
-
throw new InternalServerError52("Failed to create stock movement.");
|
|
24189
|
-
}
|
|
24190
|
-
}
|
|
24191
|
-
async function getByItemId({
|
|
24192
|
-
itemId,
|
|
24193
|
-
type = "",
|
|
24194
|
-
page = 1,
|
|
24195
|
-
limit = 10
|
|
24196
|
-
}) {
|
|
24197
|
-
page = page > 0 ? page - 1 : 0;
|
|
24198
|
-
const query = {
|
|
24199
|
-
itemId: new ObjectId67(itemId)
|
|
24200
|
-
};
|
|
24201
|
-
if (type) {
|
|
24202
|
-
query.type = type;
|
|
24203
|
-
}
|
|
24204
|
-
const cacheKey = makeCacheKey37(namespace_collection, {
|
|
24205
|
-
itemId,
|
|
24206
|
-
type,
|
|
24207
|
-
page,
|
|
24208
|
-
limit,
|
|
24209
|
-
tag: "byItemId"
|
|
24210
|
-
});
|
|
24211
|
-
try {
|
|
24212
|
-
const cached = await repo.getCache(cacheKey);
|
|
24213
|
-
if (cached)
|
|
24214
|
-
return cached;
|
|
24215
|
-
const items = await repo.collection.aggregate([
|
|
24216
|
-
{ $match: query },
|
|
24217
|
-
{ $sort: { createdAt: -1 } },
|
|
24218
|
-
{ $skip: page * limit },
|
|
24219
|
-
{ $limit: limit }
|
|
24220
|
-
]).toArray();
|
|
24221
|
-
const length = await repo.collection.countDocuments(query);
|
|
24222
|
-
const data = paginate33(items, page, limit, length);
|
|
24223
|
-
repo.setCache(cacheKey, data, 600);
|
|
24224
|
-
return data;
|
|
24225
|
-
} catch (error) {
|
|
24226
|
-
logger58.log({ level: "error", message: `${error}` });
|
|
24227
|
-
throw error;
|
|
24228
|
-
}
|
|
24229
|
-
}
|
|
24230
|
-
async function getById(id) {
|
|
24231
|
-
const cacheKey = makeCacheKey37(namespace_collection, {
|
|
24232
|
-
id,
|
|
24233
|
-
tag: "by-id"
|
|
24234
|
-
});
|
|
24235
|
-
try {
|
|
24236
|
-
const cached = await repo.getCache(cacheKey);
|
|
24237
|
-
if (cached)
|
|
24238
|
-
return cached;
|
|
24239
|
-
const result = await repo.collection.findOne({
|
|
24240
|
-
_id: new ObjectId67(id)
|
|
24241
|
-
});
|
|
24242
|
-
if (!result) {
|
|
24243
|
-
throw new BadRequestError117("Stock movement not found.");
|
|
24244
|
-
}
|
|
24245
|
-
repo.setCache(cacheKey, result, 300);
|
|
24246
|
-
return result;
|
|
24247
|
-
} catch (error) {
|
|
24248
|
-
if (error instanceof AppError56)
|
|
24249
|
-
throw error;
|
|
24250
|
-
throw new InternalServerError52("Failed to get stock movement.");
|
|
24251
|
-
}
|
|
24252
|
-
}
|
|
24253
|
-
return {
|
|
24254
|
-
createIndexes,
|
|
24255
|
-
add,
|
|
24256
|
-
getByItemId,
|
|
24257
|
-
getById
|
|
24258
|
-
};
|
|
24259
|
-
}
|
|
24260
|
-
|
|
24261
|
-
// src/resources/asset-item-movement/stock.movement.service.ts
|
|
24262
|
-
import { BadRequestError as BadRequestError118 } from "@goweekdays/utils";
|
|
24263
|
-
import { useAtlas as useAtlas18 } from "@goweekdays/utils";
|
|
24264
|
-
function useStockMovementService() {
|
|
24265
|
-
const movementRepo = useStockMovementRepo();
|
|
24266
|
-
const assetItemRepo = useAssetItemRepo();
|
|
24267
|
-
const assetUnitRepo = useAssetUnitRepo();
|
|
24268
|
-
async function stockIn(value, units) {
|
|
24269
|
-
const item = await assetItemRepo.getById(String(value.itemId));
|
|
24270
|
-
const session = useAtlas18.getClient()?.startSession();
|
|
24271
|
-
if (!session)
|
|
24272
|
-
throw new BadRequestError118("Failed to start session.");
|
|
24273
|
-
try {
|
|
24274
|
-
session.startTransaction();
|
|
24275
|
-
value.type = "in";
|
|
24276
|
-
const movementId = await movementRepo.add(value, session);
|
|
24277
|
-
if (item.trackingType === "individual" && units && units.length > 0) {
|
|
24278
|
-
for (const unit of units) {
|
|
24279
|
-
await assetUnitRepo.add(
|
|
24280
|
-
{
|
|
24281
|
-
itemId: value.itemId,
|
|
24282
|
-
serialNumber: unit.serialNumber,
|
|
24283
|
-
plateNumber: unit.plateNumber,
|
|
24284
|
-
status: "available",
|
|
24285
|
-
locationId: value.toLocationId
|
|
24286
|
-
},
|
|
24287
|
-
session
|
|
24288
|
-
);
|
|
24289
|
-
}
|
|
24290
|
-
}
|
|
24291
|
-
await assetItemRepo.incrementQuantity(
|
|
24292
|
-
String(value.itemId),
|
|
24293
|
-
value.quantity,
|
|
24294
|
-
session
|
|
24295
|
-
);
|
|
24296
|
-
await session.commitTransaction();
|
|
24297
|
-
return movementId;
|
|
24298
|
-
} catch (error) {
|
|
24299
|
-
await session.abortTransaction();
|
|
24300
|
-
throw error;
|
|
24301
|
-
} finally {
|
|
24302
|
-
await session.endSession();
|
|
24303
|
-
}
|
|
24304
|
-
}
|
|
24305
|
-
async function stockOut(value, unitIds) {
|
|
24306
|
-
const item = await assetItemRepo.getById(String(value.itemId));
|
|
24307
|
-
if (item.trackingType === "quantity" && (item.quantityOnHand ?? 0) < value.quantity) {
|
|
24308
|
-
throw new BadRequestError118("Insufficient stock.");
|
|
24309
|
-
}
|
|
24310
|
-
const session = useAtlas18.getClient()?.startSession();
|
|
24311
|
-
if (!session)
|
|
24312
|
-
throw new BadRequestError118("Failed to start session.");
|
|
24313
|
-
try {
|
|
24314
|
-
session.startTransaction();
|
|
24315
|
-
value.type = "out";
|
|
24316
|
-
const movementId = await movementRepo.add(value, session);
|
|
24317
|
-
if (item.trackingType === "individual" && unitIds && unitIds.length > 0) {
|
|
24318
|
-
for (const unitId of unitIds) {
|
|
24319
|
-
await assetUnitRepo.updateById(
|
|
24320
|
-
unitId,
|
|
24321
|
-
{ status: "assigned", assignedTo: value.reference?.id },
|
|
24322
|
-
session
|
|
24323
|
-
);
|
|
24324
|
-
}
|
|
24325
|
-
}
|
|
24326
|
-
await assetItemRepo.incrementQuantity(
|
|
24327
|
-
String(value.itemId),
|
|
24328
|
-
-value.quantity,
|
|
24329
|
-
session
|
|
24330
|
-
);
|
|
24331
|
-
await session.commitTransaction();
|
|
24332
|
-
return movementId;
|
|
24333
|
-
} catch (error) {
|
|
24334
|
-
await session.abortTransaction();
|
|
24335
|
-
throw error;
|
|
24336
|
-
} finally {
|
|
24337
|
-
await session.endSession();
|
|
24338
|
-
}
|
|
24339
|
-
}
|
|
24340
|
-
async function transfer(value, unitIds) {
|
|
24341
|
-
if (!value.fromLocationId || !value.toLocationId) {
|
|
24342
|
-
throw new BadRequestError118(
|
|
24343
|
-
"Transfer requires both fromLocationId and toLocationId."
|
|
24344
|
-
);
|
|
24345
|
-
}
|
|
24346
|
-
const item = await assetItemRepo.getById(String(value.itemId));
|
|
24347
|
-
const session = useAtlas18.getClient()?.startSession();
|
|
24348
|
-
if (!session)
|
|
24349
|
-
throw new BadRequestError118("Failed to start session.");
|
|
24074
|
+
const item = await assetItemRepo.getById(String(value.itemId));
|
|
24075
|
+
const session = useAtlas17.getClient()?.startSession();
|
|
24076
|
+
if (!session)
|
|
24077
|
+
throw new BadRequestError115("Failed to start session.");
|
|
24350
24078
|
try {
|
|
24351
24079
|
session.startTransaction();
|
|
24352
24080
|
value.type = "transfer";
|
|
@@ -24371,11 +24099,11 @@ function useStockMovementService() {
|
|
|
24371
24099
|
}
|
|
24372
24100
|
async function adjustment(value) {
|
|
24373
24101
|
if (!value.reason) {
|
|
24374
|
-
throw new
|
|
24102
|
+
throw new BadRequestError115("Adjustment requires a reason.");
|
|
24375
24103
|
}
|
|
24376
|
-
const session =
|
|
24104
|
+
const session = useAtlas17.getClient()?.startSession();
|
|
24377
24105
|
if (!session)
|
|
24378
|
-
throw new
|
|
24106
|
+
throw new BadRequestError115("Failed to start session.");
|
|
24379
24107
|
try {
|
|
24380
24108
|
session.startTransaction();
|
|
24381
24109
|
value.type = "adjustment";
|
|
@@ -24397,14 +24125,14 @@ function useStockMovementService() {
|
|
|
24397
24125
|
async function conversion(newItem, quantity, unitIds) {
|
|
24398
24126
|
const sourceItem = await assetItemRepo.getById(String(newItem.itemRefId));
|
|
24399
24127
|
if (!newItem.itemRefId) {
|
|
24400
|
-
throw new
|
|
24128
|
+
throw new BadRequestError115("Conversion requires itemRefId.");
|
|
24401
24129
|
}
|
|
24402
24130
|
if (sourceItem.trackingType === "quantity" && (sourceItem.quantityOnHand ?? 0) < quantity) {
|
|
24403
|
-
throw new
|
|
24131
|
+
throw new BadRequestError115("Insufficient stock on source item.");
|
|
24404
24132
|
}
|
|
24405
|
-
const session =
|
|
24133
|
+
const session = useAtlas17.getClient()?.startSession();
|
|
24406
24134
|
if (!session)
|
|
24407
|
-
throw new
|
|
24135
|
+
throw new BadRequestError115("Failed to start session.");
|
|
24408
24136
|
try {
|
|
24409
24137
|
session.startTransaction();
|
|
24410
24138
|
const newItemId = await assetItemRepo.add(newItem, session);
|
|
@@ -24455,93 +24183,93 @@ function useStockMovementService() {
|
|
|
24455
24183
|
}
|
|
24456
24184
|
|
|
24457
24185
|
// src/resources/asset-item-movement/stock.movement.controller.ts
|
|
24458
|
-
import
|
|
24459
|
-
import { BadRequestError as
|
|
24460
|
-
var
|
|
24461
|
-
itemId:
|
|
24462
|
-
type:
|
|
24463
|
-
page:
|
|
24464
|
-
limit:
|
|
24465
|
-
});
|
|
24466
|
-
var idParamSchema4 = Joi104.object({
|
|
24467
|
-
id: Joi104.string().hex().length(24).required()
|
|
24186
|
+
import Joi102 from "joi";
|
|
24187
|
+
import { BadRequestError as BadRequestError116 } from "@goweekdays/utils";
|
|
24188
|
+
var querySchema = Joi102.object({
|
|
24189
|
+
itemId: Joi102.string().hex().length(24).required(),
|
|
24190
|
+
type: Joi102.string().valid(...stockMovementTypes).optional().allow("", null),
|
|
24191
|
+
page: Joi102.number().min(1).optional().allow("", null),
|
|
24192
|
+
limit: Joi102.number().min(1).optional().allow("", null)
|
|
24468
24193
|
});
|
|
24469
|
-
var
|
|
24470
|
-
|
|
24471
|
-
|
|
24472
|
-
|
|
24473
|
-
|
|
24474
|
-
|
|
24475
|
-
|
|
24476
|
-
|
|
24194
|
+
var idParamSchema2 = Joi102.object({
|
|
24195
|
+
id: Joi102.string().hex().length(24).required()
|
|
24196
|
+
});
|
|
24197
|
+
var stockInSchema = Joi102.object({
|
|
24198
|
+
itemId: Joi102.string().hex().length(24).required(),
|
|
24199
|
+
quantity: Joi102.number().positive().required(),
|
|
24200
|
+
unitCost: Joi102.number().min(0).optional().allow(null),
|
|
24201
|
+
totalCost: Joi102.number().min(0).optional().allow(null),
|
|
24202
|
+
reference: Joi102.object({
|
|
24203
|
+
type: Joi102.string().valid("purchase", "sale", "transfer", "manual", "conversion").required(),
|
|
24204
|
+
id: Joi102.string().optional().allow("", null)
|
|
24477
24205
|
}).optional().allow(null),
|
|
24478
|
-
toLocationId:
|
|
24479
|
-
units:
|
|
24480
|
-
|
|
24481
|
-
serialNumber:
|
|
24482
|
-
plateNumber:
|
|
24206
|
+
toLocationId: Joi102.string().optional().allow("", null),
|
|
24207
|
+
units: Joi102.array().items(
|
|
24208
|
+
Joi102.object({
|
|
24209
|
+
serialNumber: Joi102.string().optional().allow("", null),
|
|
24210
|
+
plateNumber: Joi102.string().optional().allow("", null)
|
|
24483
24211
|
})
|
|
24484
24212
|
).optional()
|
|
24485
24213
|
});
|
|
24486
|
-
var stockOutSchema =
|
|
24487
|
-
itemId:
|
|
24488
|
-
quantity:
|
|
24489
|
-
unitCost:
|
|
24490
|
-
totalCost:
|
|
24491
|
-
reference:
|
|
24492
|
-
type:
|
|
24493
|
-
id:
|
|
24214
|
+
var stockOutSchema = Joi102.object({
|
|
24215
|
+
itemId: Joi102.string().hex().length(24).required(),
|
|
24216
|
+
quantity: Joi102.number().positive().required(),
|
|
24217
|
+
unitCost: Joi102.number().min(0).optional().allow(null),
|
|
24218
|
+
totalCost: Joi102.number().min(0).optional().allow(null),
|
|
24219
|
+
reference: Joi102.object({
|
|
24220
|
+
type: Joi102.string().valid("purchase", "sale", "transfer", "manual", "conversion").required(),
|
|
24221
|
+
id: Joi102.string().optional().allow("", null)
|
|
24494
24222
|
}).optional().allow(null),
|
|
24495
|
-
fromLocationId:
|
|
24496
|
-
unitIds:
|
|
24223
|
+
fromLocationId: Joi102.string().optional().allow("", null),
|
|
24224
|
+
unitIds: Joi102.array().items(Joi102.string().hex().length(24)).optional()
|
|
24497
24225
|
});
|
|
24498
|
-
var transferSchema =
|
|
24499
|
-
itemId:
|
|
24500
|
-
quantity:
|
|
24501
|
-
fromLocationId:
|
|
24502
|
-
toLocationId:
|
|
24503
|
-
reference:
|
|
24504
|
-
type:
|
|
24505
|
-
id:
|
|
24226
|
+
var transferSchema = Joi102.object({
|
|
24227
|
+
itemId: Joi102.string().hex().length(24).required(),
|
|
24228
|
+
quantity: Joi102.number().positive().required(),
|
|
24229
|
+
fromLocationId: Joi102.string().required(),
|
|
24230
|
+
toLocationId: Joi102.string().required(),
|
|
24231
|
+
reference: Joi102.object({
|
|
24232
|
+
type: Joi102.string().valid("purchase", "sale", "transfer", "manual", "conversion").required(),
|
|
24233
|
+
id: Joi102.string().optional().allow("", null)
|
|
24506
24234
|
}).optional().allow(null),
|
|
24507
|
-
unitIds:
|
|
24235
|
+
unitIds: Joi102.array().items(Joi102.string().hex().length(24)).optional()
|
|
24508
24236
|
});
|
|
24509
|
-
var adjustmentSchema =
|
|
24510
|
-
itemId:
|
|
24511
|
-
quantity:
|
|
24512
|
-
reason:
|
|
24513
|
-
reference:
|
|
24514
|
-
type:
|
|
24515
|
-
id:
|
|
24237
|
+
var adjustmentSchema = Joi102.object({
|
|
24238
|
+
itemId: Joi102.string().hex().length(24).required(),
|
|
24239
|
+
quantity: Joi102.number().required(),
|
|
24240
|
+
reason: Joi102.string().required(),
|
|
24241
|
+
reference: Joi102.object({
|
|
24242
|
+
type: Joi102.string().valid("purchase", "sale", "transfer", "manual", "conversion").required(),
|
|
24243
|
+
id: Joi102.string().optional().allow("", null)
|
|
24516
24244
|
}).optional().allow(null)
|
|
24517
24245
|
});
|
|
24518
|
-
var conversionSchema =
|
|
24519
|
-
quantity:
|
|
24520
|
-
newItem:
|
|
24521
|
-
name:
|
|
24522
|
-
description:
|
|
24523
|
-
assetCategory:
|
|
24524
|
-
assetClass:
|
|
24525
|
-
trackingType:
|
|
24526
|
-
purpose:
|
|
24527
|
-
itemRefId:
|
|
24528
|
-
remarks:
|
|
24529
|
-
departmentId:
|
|
24530
|
-
departmentName:
|
|
24531
|
-
categoryId:
|
|
24532
|
-
categoryName:
|
|
24533
|
-
subcategoryId:
|
|
24534
|
-
subcategoryName:
|
|
24535
|
-
categoryPath:
|
|
24536
|
-
brand:
|
|
24537
|
-
tags:
|
|
24538
|
-
|
|
24539
|
-
id:
|
|
24540
|
-
name:
|
|
24246
|
+
var conversionSchema = Joi102.object({
|
|
24247
|
+
quantity: Joi102.number().positive().required(),
|
|
24248
|
+
newItem: Joi102.object({
|
|
24249
|
+
name: Joi102.string().required(),
|
|
24250
|
+
description: Joi102.string().optional().allow("", null),
|
|
24251
|
+
assetCategory: Joi102.string().valid(...assetItemCategories).required(),
|
|
24252
|
+
assetClass: Joi102.string().valid(...assetItemClasses).required(),
|
|
24253
|
+
trackingType: Joi102.string().valid(...assetItemTrackingTypes).required(),
|
|
24254
|
+
purpose: Joi102.string().valid(...assetItemPurposes).required(),
|
|
24255
|
+
itemRefId: Joi102.string().hex().length(24).required(),
|
|
24256
|
+
remarks: Joi102.string().optional().allow("", null),
|
|
24257
|
+
departmentId: Joi102.string().required(),
|
|
24258
|
+
departmentName: Joi102.string().required(),
|
|
24259
|
+
categoryId: Joi102.string().required(),
|
|
24260
|
+
categoryName: Joi102.string().required(),
|
|
24261
|
+
subcategoryId: Joi102.string().required(),
|
|
24262
|
+
subcategoryName: Joi102.string().required(),
|
|
24263
|
+
categoryPath: Joi102.string().required(),
|
|
24264
|
+
brand: Joi102.string().optional().allow("", null),
|
|
24265
|
+
tags: Joi102.array().items(
|
|
24266
|
+
Joi102.object({
|
|
24267
|
+
id: Joi102.string().required(),
|
|
24268
|
+
name: Joi102.string().required()
|
|
24541
24269
|
})
|
|
24542
24270
|
).optional().default([])
|
|
24543
24271
|
}).required(),
|
|
24544
|
-
unitIds:
|
|
24272
|
+
unitIds: Joi102.array().items(Joi102.string().hex().length(24)).optional()
|
|
24545
24273
|
});
|
|
24546
24274
|
function useStockMovementController() {
|
|
24547
24275
|
const repo = useStockMovementRepo();
|
|
@@ -24550,7 +24278,7 @@ function useStockMovementController() {
|
|
|
24550
24278
|
try {
|
|
24551
24279
|
const { error } = stockInSchema.validate(req.body);
|
|
24552
24280
|
if (error) {
|
|
24553
|
-
next(new
|
|
24281
|
+
next(new BadRequestError116(error.message));
|
|
24554
24282
|
return;
|
|
24555
24283
|
}
|
|
24556
24284
|
const { units, ...movement } = req.body;
|
|
@@ -24567,7 +24295,7 @@ function useStockMovementController() {
|
|
|
24567
24295
|
try {
|
|
24568
24296
|
const { error } = stockOutSchema.validate(req.body);
|
|
24569
24297
|
if (error) {
|
|
24570
|
-
next(new
|
|
24298
|
+
next(new BadRequestError116(error.message));
|
|
24571
24299
|
return;
|
|
24572
24300
|
}
|
|
24573
24301
|
const { unitIds, ...movement } = req.body;
|
|
@@ -24584,7 +24312,7 @@ function useStockMovementController() {
|
|
|
24584
24312
|
try {
|
|
24585
24313
|
const { error } = transferSchema.validate(req.body);
|
|
24586
24314
|
if (error) {
|
|
24587
|
-
next(new
|
|
24315
|
+
next(new BadRequestError116(error.message));
|
|
24588
24316
|
return;
|
|
24589
24317
|
}
|
|
24590
24318
|
const { unitIds, ...movement } = req.body;
|
|
@@ -24601,7 +24329,7 @@ function useStockMovementController() {
|
|
|
24601
24329
|
try {
|
|
24602
24330
|
const { error } = adjustmentSchema.validate(req.body);
|
|
24603
24331
|
if (error) {
|
|
24604
|
-
next(new
|
|
24332
|
+
next(new BadRequestError116(error.message));
|
|
24605
24333
|
return;
|
|
24606
24334
|
}
|
|
24607
24335
|
const movementId = await service.adjustment(req.body);
|
|
@@ -24614,9 +24342,9 @@ function useStockMovementController() {
|
|
|
24614
24342
|
}
|
|
24615
24343
|
}
|
|
24616
24344
|
async function getByItemId(req, res, next) {
|
|
24617
|
-
const { error } =
|
|
24345
|
+
const { error } = querySchema.validate(req.query);
|
|
24618
24346
|
if (error) {
|
|
24619
|
-
next(new
|
|
24347
|
+
next(new BadRequestError116(error.message));
|
|
24620
24348
|
return;
|
|
24621
24349
|
}
|
|
24622
24350
|
const itemId = req.query.itemId;
|
|
@@ -24631,9 +24359,9 @@ function useStockMovementController() {
|
|
|
24631
24359
|
}
|
|
24632
24360
|
}
|
|
24633
24361
|
async function getById(req, res, next) {
|
|
24634
|
-
const { error } =
|
|
24362
|
+
const { error } = idParamSchema2.validate(req.params);
|
|
24635
24363
|
if (error) {
|
|
24636
|
-
next(new
|
|
24364
|
+
next(new BadRequestError116(error.message));
|
|
24637
24365
|
return;
|
|
24638
24366
|
}
|
|
24639
24367
|
try {
|
|
@@ -24647,7 +24375,7 @@ function useStockMovementController() {
|
|
|
24647
24375
|
try {
|
|
24648
24376
|
const { error } = conversionSchema.validate(req.body);
|
|
24649
24377
|
if (error) {
|
|
24650
|
-
next(new
|
|
24378
|
+
next(new BadRequestError116(error.message));
|
|
24651
24379
|
return;
|
|
24652
24380
|
}
|
|
24653
24381
|
const { quantity, newItem, unitIds } = req.body;
|
|
@@ -24672,6 +24400,406 @@ function useStockMovementController() {
|
|
|
24672
24400
|
};
|
|
24673
24401
|
}
|
|
24674
24402
|
|
|
24403
|
+
// src/resources/asset-item-unit/asset.unit.controller.ts
|
|
24404
|
+
import Joi103 from "joi";
|
|
24405
|
+
import { BadRequestError as BadRequestError117 } from "@goweekdays/utils";
|
|
24406
|
+
var querySchema2 = Joi103.object({
|
|
24407
|
+
itemId: Joi103.string().hex().length(24).required(),
|
|
24408
|
+
status: Joi103.string().valid(...assetUnitStatuses).optional().allow("", null),
|
|
24409
|
+
page: Joi103.number().min(1).optional().allow("", null),
|
|
24410
|
+
limit: Joi103.number().min(1).optional().allow("", null)
|
|
24411
|
+
});
|
|
24412
|
+
var idParamSchema3 = Joi103.object({
|
|
24413
|
+
id: Joi103.string().hex().length(24).required()
|
|
24414
|
+
});
|
|
24415
|
+
function useAssetUnitController() {
|
|
24416
|
+
const repo = useAssetUnitRepo();
|
|
24417
|
+
async function add(req, res, next) {
|
|
24418
|
+
try {
|
|
24419
|
+
const { error } = schemaAssetUnit.validate(req.body);
|
|
24420
|
+
if (error) {
|
|
24421
|
+
next(new BadRequestError117(error.message));
|
|
24422
|
+
return;
|
|
24423
|
+
}
|
|
24424
|
+
const unitId = await repo.add(req.body);
|
|
24425
|
+
res.status(201).json({
|
|
24426
|
+
message: "Asset unit created successfully.",
|
|
24427
|
+
unitId
|
|
24428
|
+
});
|
|
24429
|
+
} catch (error) {
|
|
24430
|
+
next(error);
|
|
24431
|
+
}
|
|
24432
|
+
}
|
|
24433
|
+
async function getByItemId(req, res, next) {
|
|
24434
|
+
const { error } = querySchema2.validate(req.query);
|
|
24435
|
+
if (error) {
|
|
24436
|
+
next(new BadRequestError117(error.message));
|
|
24437
|
+
return;
|
|
24438
|
+
}
|
|
24439
|
+
const itemId = req.query.itemId;
|
|
24440
|
+
const status2 = req.query.status ?? "";
|
|
24441
|
+
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
24442
|
+
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
24443
|
+
try {
|
|
24444
|
+
const results = await repo.getByItemId({ itemId, status: status2, page, limit });
|
|
24445
|
+
res.json(results);
|
|
24446
|
+
} catch (error2) {
|
|
24447
|
+
next(error2);
|
|
24448
|
+
}
|
|
24449
|
+
}
|
|
24450
|
+
async function getById(req, res, next) {
|
|
24451
|
+
const { error } = idParamSchema3.validate(req.params);
|
|
24452
|
+
if (error) {
|
|
24453
|
+
next(new BadRequestError117(error.message));
|
|
24454
|
+
return;
|
|
24455
|
+
}
|
|
24456
|
+
try {
|
|
24457
|
+
const unit = await repo.getById(req.params.id);
|
|
24458
|
+
res.json(unit);
|
|
24459
|
+
} catch (error2) {
|
|
24460
|
+
next(error2);
|
|
24461
|
+
}
|
|
24462
|
+
}
|
|
24463
|
+
async function updateById(req, res, next) {
|
|
24464
|
+
const { error: paramError } = idParamSchema3.validate(req.params);
|
|
24465
|
+
if (paramError) {
|
|
24466
|
+
next(new BadRequestError117(paramError.message));
|
|
24467
|
+
return;
|
|
24468
|
+
}
|
|
24469
|
+
const { error: bodyError } = schemaAssetUnitUpdate.validate(req.body);
|
|
24470
|
+
if (bodyError) {
|
|
24471
|
+
next(new BadRequestError117(bodyError.message));
|
|
24472
|
+
return;
|
|
24473
|
+
}
|
|
24474
|
+
try {
|
|
24475
|
+
const updatedUnit = await repo.updateById(req.params.id, req.body);
|
|
24476
|
+
res.json({
|
|
24477
|
+
message: "Asset unit updated successfully.",
|
|
24478
|
+
unit: updatedUnit
|
|
24479
|
+
});
|
|
24480
|
+
} catch (error) {
|
|
24481
|
+
next(error);
|
|
24482
|
+
}
|
|
24483
|
+
}
|
|
24484
|
+
return {
|
|
24485
|
+
add,
|
|
24486
|
+
getByItemId,
|
|
24487
|
+
getById,
|
|
24488
|
+
updateById
|
|
24489
|
+
};
|
|
24490
|
+
}
|
|
24491
|
+
|
|
24492
|
+
// src/resources/asset-item/asset.item.service.ts
|
|
24493
|
+
function normalizeTagName(name) {
|
|
24494
|
+
return name.trim().toLowerCase().replace(/\s+/g, "-");
|
|
24495
|
+
}
|
|
24496
|
+
function useAssetItemService() {
|
|
24497
|
+
const assetItemRepo = useAssetItemRepo();
|
|
24498
|
+
const tagRepo = useTagRepo();
|
|
24499
|
+
const categoryRepo = useCategoryRepo();
|
|
24500
|
+
const stockMovementRepo = useStockMovementRepo();
|
|
24501
|
+
const assetUnitRepo = useAssetUnitRepo();
|
|
24502
|
+
async function resolveTags(tagNames, orgId, categoryPath, session) {
|
|
24503
|
+
const resolvedTags = [];
|
|
24504
|
+
for (const tag of tagNames) {
|
|
24505
|
+
const normalizedName = normalizeTagName(tag.name);
|
|
24506
|
+
const existing = await tagRepo.findByNormalizedNameAndOrg(
|
|
24507
|
+
normalizedName,
|
|
24508
|
+
orgId
|
|
24509
|
+
);
|
|
24510
|
+
if (existing && existing._id) {
|
|
24511
|
+
await tagRepo.incrementUsageCount(existing._id, 1, session);
|
|
24512
|
+
resolvedTags.push({
|
|
24513
|
+
id: String(existing._id),
|
|
24514
|
+
name: existing.name
|
|
24515
|
+
});
|
|
24516
|
+
} else {
|
|
24517
|
+
const tagData = {
|
|
24518
|
+
name: tag.name.trim(),
|
|
24519
|
+
normalizedName,
|
|
24520
|
+
type: "private",
|
|
24521
|
+
orgId,
|
|
24522
|
+
categoryPath,
|
|
24523
|
+
status: "active",
|
|
24524
|
+
usageCount: 1
|
|
24525
|
+
};
|
|
24526
|
+
const tagId = await tagRepo.add(tagData, session);
|
|
24527
|
+
resolvedTags.push({
|
|
24528
|
+
id: String(tagId),
|
|
24529
|
+
name: tag.name.trim()
|
|
24530
|
+
});
|
|
24531
|
+
}
|
|
24532
|
+
}
|
|
24533
|
+
return resolvedTags;
|
|
24534
|
+
}
|
|
24535
|
+
async function add(value) {
|
|
24536
|
+
const session = useAtlas18.getClient()?.startSession();
|
|
24537
|
+
if (!session) {
|
|
24538
|
+
throw new InternalServerError52(
|
|
24539
|
+
"Unable to start session for asset item service."
|
|
24540
|
+
);
|
|
24541
|
+
}
|
|
24542
|
+
try {
|
|
24543
|
+
await session.startTransaction();
|
|
24544
|
+
const department = await categoryRepo.getById(value.departmentId);
|
|
24545
|
+
value.departmentName = department.displayName;
|
|
24546
|
+
if (value.categoryId) {
|
|
24547
|
+
const category = await categoryRepo.getById(value.categoryId);
|
|
24548
|
+
value.categoryName = category.displayName;
|
|
24549
|
+
}
|
|
24550
|
+
if (value.subcategoryId) {
|
|
24551
|
+
const subcategory = await categoryRepo.getById(value.subcategoryId);
|
|
24552
|
+
value.subcategoryName = subcategory.displayName;
|
|
24553
|
+
}
|
|
24554
|
+
const categoryPath = buildCategoryPath(
|
|
24555
|
+
value.departmentName,
|
|
24556
|
+
value.categoryName ?? "",
|
|
24557
|
+
value.subcategoryName ?? ""
|
|
24558
|
+
);
|
|
24559
|
+
value.categoryPath = categoryPath;
|
|
24560
|
+
const resolvedTags = value.tags.length ? await resolveTags(
|
|
24561
|
+
value.tags,
|
|
24562
|
+
value.orgId,
|
|
24563
|
+
value.categoryPath,
|
|
24564
|
+
session
|
|
24565
|
+
) : [];
|
|
24566
|
+
const assetItem = {
|
|
24567
|
+
...value,
|
|
24568
|
+
tags: resolvedTags
|
|
24569
|
+
};
|
|
24570
|
+
const assetItemId = await assetItemRepo.add(assetItem, session);
|
|
24571
|
+
await session.commitTransaction();
|
|
24572
|
+
return assetItemId;
|
|
24573
|
+
} catch (error) {
|
|
24574
|
+
await session.abortTransaction();
|
|
24575
|
+
if (error instanceof AppError56)
|
|
24576
|
+
throw error;
|
|
24577
|
+
throw new InternalServerError52("Failed to create asset item.");
|
|
24578
|
+
} finally {
|
|
24579
|
+
session.endSession();
|
|
24580
|
+
}
|
|
24581
|
+
}
|
|
24582
|
+
async function makePublic(id, value) {
|
|
24583
|
+
try {
|
|
24584
|
+
await assetItemRepo.updateById(id, {
|
|
24585
|
+
isPublic: value,
|
|
24586
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
24587
|
+
});
|
|
24588
|
+
return "Asset item visibility updated successfully.";
|
|
24589
|
+
} catch (error) {
|
|
24590
|
+
if (error instanceof AppError56)
|
|
24591
|
+
throw error;
|
|
24592
|
+
throw new InternalServerError52("Failed to update asset item visibility.");
|
|
24593
|
+
}
|
|
24594
|
+
}
|
|
24595
|
+
async function deleteById(id) {
|
|
24596
|
+
const [movementCount, unitCount] = await Promise.all([
|
|
24597
|
+
stockMovementRepo.countByItemId(id),
|
|
24598
|
+
assetUnitRepo.countByItemId(id)
|
|
24599
|
+
]);
|
|
24600
|
+
if (movementCount > 0 || unitCount > 0) {
|
|
24601
|
+
throw new BadRequestError118(
|
|
24602
|
+
"Cannot delete asset item because it has associated movement or unit records."
|
|
24603
|
+
);
|
|
24604
|
+
}
|
|
24605
|
+
try {
|
|
24606
|
+
await assetItemRepo.deleteById(id);
|
|
24607
|
+
return "Asset item deleted successfully.";
|
|
24608
|
+
} catch (error) {
|
|
24609
|
+
if (error instanceof AppError56)
|
|
24610
|
+
throw error;
|
|
24611
|
+
throw new InternalServerError52("Failed to delete asset item.");
|
|
24612
|
+
}
|
|
24613
|
+
}
|
|
24614
|
+
return {
|
|
24615
|
+
add,
|
|
24616
|
+
makePublic,
|
|
24617
|
+
deleteById
|
|
24618
|
+
};
|
|
24619
|
+
}
|
|
24620
|
+
|
|
24621
|
+
// src/resources/asset-item/asset.item.controller.ts
|
|
24622
|
+
import Joi104 from "joi";
|
|
24623
|
+
import {
|
|
24624
|
+
AppError as AppError57,
|
|
24625
|
+
BadRequestError as BadRequestError119,
|
|
24626
|
+
InternalServerError as InternalServerError53
|
|
24627
|
+
} from "@goweekdays/utils";
|
|
24628
|
+
var paginationSchema3 = Joi104.object({
|
|
24629
|
+
page: Joi104.number().min(1).optional().allow("", null),
|
|
24630
|
+
limit: Joi104.number().min(1).optional().allow("", null),
|
|
24631
|
+
search: Joi104.string().optional().allow("", null),
|
|
24632
|
+
status: Joi104.string().valid(...assetItemStatuses).optional().allow("", null),
|
|
24633
|
+
assetCategory: Joi104.string().valid(...assetItemCategories).optional().allow("", null),
|
|
24634
|
+
trackingType: Joi104.string().valid(...assetItemTrackingTypes).optional().allow("", null),
|
|
24635
|
+
purpose: Joi104.string().valid(...assetItemPurposes).optional().allow("", null),
|
|
24636
|
+
departmentId: Joi104.string().optional().allow("", null),
|
|
24637
|
+
categoryId: Joi104.string().optional().allow("", null),
|
|
24638
|
+
subcategoryId: Joi104.string().optional().allow("", null),
|
|
24639
|
+
brand: Joi104.string().optional().allow("", null),
|
|
24640
|
+
tags: Joi104.string().optional().allow("", null),
|
|
24641
|
+
public: Joi104.boolean().optional().allow("", null)
|
|
24642
|
+
});
|
|
24643
|
+
var idParamSchema4 = Joi104.object({
|
|
24644
|
+
id: Joi104.string().hex().length(24).required()
|
|
24645
|
+
});
|
|
24646
|
+
function useAssetItemController() {
|
|
24647
|
+
const repo = useAssetItemRepo();
|
|
24648
|
+
const service = useAssetItemService();
|
|
24649
|
+
async function add(req, res, next) {
|
|
24650
|
+
try {
|
|
24651
|
+
const { error, value } = schemaAssetItemCreate.validate(req.body);
|
|
24652
|
+
if (error) {
|
|
24653
|
+
next(new BadRequestError119(error.message));
|
|
24654
|
+
return;
|
|
24655
|
+
}
|
|
24656
|
+
const assetItemId = await service.add(value);
|
|
24657
|
+
res.status(201).json({
|
|
24658
|
+
message: "Asset item created successfully.",
|
|
24659
|
+
assetItemId
|
|
24660
|
+
});
|
|
24661
|
+
} catch (error) {
|
|
24662
|
+
next(error);
|
|
24663
|
+
}
|
|
24664
|
+
}
|
|
24665
|
+
async function getAll(req, res, next) {
|
|
24666
|
+
const { error } = paginationSchema3.validate(req.query);
|
|
24667
|
+
if (error) {
|
|
24668
|
+
next(new BadRequestError119(error.message));
|
|
24669
|
+
return;
|
|
24670
|
+
}
|
|
24671
|
+
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
24672
|
+
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
24673
|
+
const search = req.query.search ?? "";
|
|
24674
|
+
const status2 = req.query.status ?? "active";
|
|
24675
|
+
const assetCategory = req.query.assetCategory ?? "";
|
|
24676
|
+
const trackingType = req.query.trackingType ?? "";
|
|
24677
|
+
const purpose = req.query.purpose ?? "";
|
|
24678
|
+
const departmentId = req.query.departmentId ?? "";
|
|
24679
|
+
const categoryId = req.query.categoryId ?? "";
|
|
24680
|
+
const subcategoryId = req.query.subcategoryId ?? "";
|
|
24681
|
+
const brand = req.query.brand ?? "";
|
|
24682
|
+
const tagsParam = req.query.tags ?? "";
|
|
24683
|
+
const tags = tagsParam ? tagsParam.split(",").filter(Boolean) : [];
|
|
24684
|
+
const isPublic = req.query.public === "true";
|
|
24685
|
+
if (!isFinite(page)) {
|
|
24686
|
+
next(new BadRequestError119("Invalid page number."));
|
|
24687
|
+
return;
|
|
24688
|
+
}
|
|
24689
|
+
if (!isFinite(limit)) {
|
|
24690
|
+
next(new BadRequestError119("Invalid limit number."));
|
|
24691
|
+
return;
|
|
24692
|
+
}
|
|
24693
|
+
try {
|
|
24694
|
+
const results = await repo.getAll({
|
|
24695
|
+
page,
|
|
24696
|
+
limit,
|
|
24697
|
+
search,
|
|
24698
|
+
status: status2,
|
|
24699
|
+
assetCategory,
|
|
24700
|
+
trackingType,
|
|
24701
|
+
purpose,
|
|
24702
|
+
departmentId,
|
|
24703
|
+
categoryId,
|
|
24704
|
+
subcategoryId,
|
|
24705
|
+
brand,
|
|
24706
|
+
tags,
|
|
24707
|
+
isPublic
|
|
24708
|
+
});
|
|
24709
|
+
res.json(results);
|
|
24710
|
+
} catch (error2) {
|
|
24711
|
+
next(error2);
|
|
24712
|
+
}
|
|
24713
|
+
}
|
|
24714
|
+
async function getById(req, res, next) {
|
|
24715
|
+
const { error } = idParamSchema4.validate(req.params);
|
|
24716
|
+
if (error) {
|
|
24717
|
+
next(new BadRequestError119(error.message));
|
|
24718
|
+
return;
|
|
24719
|
+
}
|
|
24720
|
+
try {
|
|
24721
|
+
const assetItem = await repo.getById(req.params.id);
|
|
24722
|
+
res.json(assetItem);
|
|
24723
|
+
} catch (error2) {
|
|
24724
|
+
next(error2);
|
|
24725
|
+
}
|
|
24726
|
+
}
|
|
24727
|
+
async function updateById(req, res, next) {
|
|
24728
|
+
const { error: paramError } = Joi104.object({
|
|
24729
|
+
id: Joi104.string().hex().length(24).required(),
|
|
24730
|
+
orgId: Joi104.string().hex().required()
|
|
24731
|
+
}).validate(req.params);
|
|
24732
|
+
if (paramError) {
|
|
24733
|
+
next(new BadRequestError119(paramError.message));
|
|
24734
|
+
return;
|
|
24735
|
+
}
|
|
24736
|
+
const { error: bodyError } = schemaAssetItemUpdate.validate(req.body);
|
|
24737
|
+
if (bodyError) {
|
|
24738
|
+
next(new BadRequestError119(bodyError.message));
|
|
24739
|
+
return;
|
|
24740
|
+
}
|
|
24741
|
+
try {
|
|
24742
|
+
const updatedAssetItem = await repo.updateById(req.params.id, req.body);
|
|
24743
|
+
res.json({
|
|
24744
|
+
message: "Asset item updated successfully.",
|
|
24745
|
+
assetItem: updatedAssetItem
|
|
24746
|
+
});
|
|
24747
|
+
} catch (error) {
|
|
24748
|
+
next(error);
|
|
24749
|
+
}
|
|
24750
|
+
}
|
|
24751
|
+
async function makePublic(req, res, next) {
|
|
24752
|
+
const { error, value } = Joi104.object({
|
|
24753
|
+
orgId: Joi104.string().hex().required(),
|
|
24754
|
+
id: Joi104.string().hex().length(24).required(),
|
|
24755
|
+
isPublic: Joi104.boolean().required()
|
|
24756
|
+
}).validate({ ...req.params, ...req.body });
|
|
24757
|
+
if (error) {
|
|
24758
|
+
next(new BadRequestError119(error.message));
|
|
24759
|
+
return;
|
|
24760
|
+
}
|
|
24761
|
+
try {
|
|
24762
|
+
const message = await service.makePublic(value.id, value.isPublic);
|
|
24763
|
+
res.json({
|
|
24764
|
+
message
|
|
24765
|
+
});
|
|
24766
|
+
return;
|
|
24767
|
+
} catch (error2) {
|
|
24768
|
+
if (error2 instanceof AppError57) {
|
|
24769
|
+
next(error2);
|
|
24770
|
+
return;
|
|
24771
|
+
}
|
|
24772
|
+
next(new InternalServerError53("Failed to update asset item visibility."));
|
|
24773
|
+
}
|
|
24774
|
+
}
|
|
24775
|
+
async function deleteById(req, res, next) {
|
|
24776
|
+
const { error } = Joi104.object({
|
|
24777
|
+
id: Joi104.string().hex().length(24).required(),
|
|
24778
|
+
orgId: Joi104.string().hex().required()
|
|
24779
|
+
}).validate(req.params);
|
|
24780
|
+
if (error) {
|
|
24781
|
+
next(new BadRequestError119(error.message));
|
|
24782
|
+
return;
|
|
24783
|
+
}
|
|
24784
|
+
try {
|
|
24785
|
+
await service.deleteById(req.params.id);
|
|
24786
|
+
res.json({
|
|
24787
|
+
message: "Asset item deleted successfully."
|
|
24788
|
+
});
|
|
24789
|
+
} catch (error2) {
|
|
24790
|
+
next(error2);
|
|
24791
|
+
}
|
|
24792
|
+
}
|
|
24793
|
+
return {
|
|
24794
|
+
add,
|
|
24795
|
+
getAll,
|
|
24796
|
+
getById,
|
|
24797
|
+
updateById,
|
|
24798
|
+
makePublic,
|
|
24799
|
+
deleteById
|
|
24800
|
+
};
|
|
24801
|
+
}
|
|
24802
|
+
|
|
24675
24803
|
// src/resources/asset-item-category/category.service.ts
|
|
24676
24804
|
import { BadRequestError as BadRequestError120 } from "@goweekdays/utils";
|
|
24677
24805
|
function useCategoryService() {
|
|
@@ -24862,7 +24990,7 @@ function useCategoryController() {
|
|
|
24862
24990
|
}
|
|
24863
24991
|
|
|
24864
24992
|
// src/resources/tag/tag.service.ts
|
|
24865
|
-
import { AppError as
|
|
24993
|
+
import { AppError as AppError58, BadRequestError as BadRequestError122 } from "@goweekdays/utils";
|
|
24866
24994
|
import Joi106 from "joi";
|
|
24867
24995
|
function normalizeTagName2(name) {
|
|
24868
24996
|
return name.trim().toLowerCase().replace(/\s+/g, "-");
|
|
@@ -24893,7 +25021,7 @@ function useTagService() {
|
|
|
24893
25021
|
const id = await _add(data);
|
|
24894
25022
|
return id;
|
|
24895
25023
|
} catch (error2) {
|
|
24896
|
-
if (error2 instanceof
|
|
25024
|
+
if (error2 instanceof AppError58)
|
|
24897
25025
|
throw error2;
|
|
24898
25026
|
throw new BadRequestError122("Failed to add tag.");
|
|
24899
25027
|
}
|