@goweekdays/core 2.15.8 → 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 +12 -0
- package/dist/index.d.ts +24 -4
- package/dist/index.js +900 -766
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +933 -794
- 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);
|
|
@@ -22632,10 +22649,12 @@ function modelAssetItem(data) {
|
|
|
22632
22649
|
"Marketplace price is required for items marked as public."
|
|
22633
22650
|
);
|
|
22634
22651
|
}
|
|
22652
|
+
data.normalizedName = data.name.trim().toLowerCase();
|
|
22635
22653
|
return {
|
|
22636
22654
|
_id: data._id,
|
|
22637
22655
|
orgId: data.orgId,
|
|
22638
22656
|
name: data.name,
|
|
22657
|
+
normalizedName: data.normalizedName,
|
|
22639
22658
|
description: data.description ?? "",
|
|
22640
22659
|
assetCategory: data.assetCategory,
|
|
22641
22660
|
assetClass: data.assetClass,
|
|
@@ -22692,7 +22711,17 @@ function useAssetItemRepo() {
|
|
|
22692
22711
|
{ key: { purpose: 1 } },
|
|
22693
22712
|
{ key: { trackingType: 1 } },
|
|
22694
22713
|
{ key: { status: 1 } },
|
|
22695
|
-
{ key: { name: "text" } }
|
|
22714
|
+
{ key: { name: "text" } },
|
|
22715
|
+
{ key: { public: 1 } },
|
|
22716
|
+
{
|
|
22717
|
+
key: {
|
|
22718
|
+
orgId: 1,
|
|
22719
|
+
normalizedName: 1,
|
|
22720
|
+
purpose: 1,
|
|
22721
|
+
itemRefId: 1
|
|
22722
|
+
},
|
|
22723
|
+
unique: true
|
|
22724
|
+
}
|
|
22696
22725
|
]);
|
|
22697
22726
|
} catch (error) {
|
|
22698
22727
|
throw new BadRequestError109("Failed to create asset item indexes.");
|
|
@@ -22728,10 +22757,14 @@ function useAssetItemRepo() {
|
|
|
22728
22757
|
categoryId = "",
|
|
22729
22758
|
subcategoryId = "",
|
|
22730
22759
|
brand = "",
|
|
22731
|
-
tags = []
|
|
22760
|
+
tags = [],
|
|
22761
|
+
isPublic = false
|
|
22732
22762
|
} = {}) {
|
|
22733
22763
|
page = page > 0 ? page - 1 : 0;
|
|
22734
22764
|
const query = { status: status2 };
|
|
22765
|
+
if (isPublic) {
|
|
22766
|
+
query.isPublic = true;
|
|
22767
|
+
}
|
|
22735
22768
|
if (search) {
|
|
22736
22769
|
query.$text = { $search: search };
|
|
22737
22770
|
}
|
|
@@ -22772,7 +22805,8 @@ function useAssetItemRepo() {
|
|
|
22772
22805
|
subcategoryId,
|
|
22773
22806
|
brand,
|
|
22774
22807
|
tags: tags.join(","),
|
|
22775
|
-
tag: "getAll"
|
|
22808
|
+
tag: "getAll",
|
|
22809
|
+
public: isPublic ? "true" : "false"
|
|
22776
22810
|
});
|
|
22777
22811
|
try {
|
|
22778
22812
|
const cached = await repo.getCache(cacheKey);
|
|
@@ -22817,10 +22851,16 @@ function useAssetItemRepo() {
|
|
|
22817
22851
|
}
|
|
22818
22852
|
}
|
|
22819
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
|
+
}
|
|
22820
22860
|
try {
|
|
22821
22861
|
const result = await repo.collection.findOneAndUpdate(
|
|
22822
22862
|
{ _id: new ObjectId59(id) },
|
|
22823
|
-
{ $set: { ...
|
|
22863
|
+
{ $set: { ...validatedValue, updatedAt: /* @__PURE__ */ new Date() } },
|
|
22824
22864
|
{ returnDocument: "after" }
|
|
22825
22865
|
);
|
|
22826
22866
|
if (!result) {
|
|
@@ -22828,9 +22868,9 @@ function useAssetItemRepo() {
|
|
|
22828
22868
|
}
|
|
22829
22869
|
repo.delCachedData();
|
|
22830
22870
|
return result;
|
|
22831
|
-
} catch (
|
|
22832
|
-
if (
|
|
22833
|
-
throw
|
|
22871
|
+
} catch (error2) {
|
|
22872
|
+
if (error2 instanceof AppError51)
|
|
22873
|
+
throw error2;
|
|
22834
22874
|
throw new InternalServerError47("Failed to update asset item.");
|
|
22835
22875
|
}
|
|
22836
22876
|
}
|
|
@@ -22886,9 +22926,10 @@ function useAssetItemRepo() {
|
|
|
22886
22926
|
|
|
22887
22927
|
// src/resources/asset-item/asset.item.service.ts
|
|
22888
22928
|
import {
|
|
22889
|
-
AppError as
|
|
22890
|
-
|
|
22891
|
-
|
|
22929
|
+
AppError as AppError56,
|
|
22930
|
+
BadRequestError as BadRequestError118,
|
|
22931
|
+
InternalServerError as InternalServerError52,
|
|
22932
|
+
useAtlas as useAtlas18
|
|
22892
22933
|
} from "@goweekdays/utils";
|
|
22893
22934
|
|
|
22894
22935
|
// src/resources/tag/tag.repository.ts
|
|
@@ -23565,246 +23606,203 @@ function useCategoryRepo() {
|
|
|
23565
23606
|
};
|
|
23566
23607
|
}
|
|
23567
23608
|
|
|
23568
|
-
// src/resources/asset-item/
|
|
23569
|
-
|
|
23570
|
-
|
|
23571
|
-
|
|
23572
|
-
|
|
23573
|
-
|
|
23574
|
-
|
|
23575
|
-
|
|
23576
|
-
|
|
23577
|
-
|
|
23578
|
-
|
|
23579
|
-
|
|
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
|
-
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}`);
|
|
23608
23646
|
}
|
|
23609
|
-
|
|
23610
|
-
const session = useAtlas17.getClient()?.startSession();
|
|
23611
|
-
if (!session) {
|
|
23612
|
-
throw new InternalServerError50(
|
|
23613
|
-
"Unable to start session for asset item service."
|
|
23614
|
-
);
|
|
23615
|
-
}
|
|
23647
|
+
if (data.itemId && typeof data.itemId === "string") {
|
|
23616
23648
|
try {
|
|
23617
|
-
|
|
23618
|
-
|
|
23619
|
-
|
|
23620
|
-
if (value.categoryId) {
|
|
23621
|
-
const category = await categoryRepo.getById(value.categoryId);
|
|
23622
|
-
value.categoryName = category.displayName;
|
|
23623
|
-
}
|
|
23624
|
-
if (value.subcategoryId) {
|
|
23625
|
-
const subcategory = await categoryRepo.getById(value.subcategoryId);
|
|
23626
|
-
value.subcategoryName = subcategory.displayName;
|
|
23627
|
-
}
|
|
23628
|
-
const categoryPath = buildCategoryPath(
|
|
23629
|
-
value.departmentName,
|
|
23630
|
-
value.categoryName ?? "",
|
|
23631
|
-
value.subcategoryName ?? ""
|
|
23632
|
-
);
|
|
23633
|
-
value.categoryPath = categoryPath;
|
|
23634
|
-
const resolvedTags = value.tags.length ? await resolveTags(
|
|
23635
|
-
value.tags,
|
|
23636
|
-
value.orgId,
|
|
23637
|
-
value.categoryPath,
|
|
23638
|
-
session
|
|
23639
|
-
) : [];
|
|
23640
|
-
const assetItem = {
|
|
23641
|
-
...value,
|
|
23642
|
-
tags: resolvedTags
|
|
23643
|
-
};
|
|
23644
|
-
const assetItemId = await assetItemRepo.add(assetItem, session);
|
|
23645
|
-
await session.commitTransaction();
|
|
23646
|
-
return assetItemId;
|
|
23647
|
-
} catch (error) {
|
|
23648
|
-
await session.abortTransaction();
|
|
23649
|
-
if (error instanceof AppError54)
|
|
23650
|
-
throw error;
|
|
23651
|
-
throw new InternalServerError50("Failed to create asset item.");
|
|
23652
|
-
} finally {
|
|
23653
|
-
session.endSession();
|
|
23649
|
+
data.itemId = new ObjectId64(data.itemId);
|
|
23650
|
+
} catch {
|
|
23651
|
+
throw new Error(`Invalid itemId format: ${data.itemId}`);
|
|
23654
23652
|
}
|
|
23655
23653
|
}
|
|
23656
23654
|
return {
|
|
23657
|
-
|
|
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()
|
|
23658
23668
|
};
|
|
23659
23669
|
}
|
|
23660
23670
|
|
|
23661
|
-
// src/resources/asset-item/
|
|
23662
|
-
import
|
|
23663
|
-
|
|
23664
|
-
|
|
23665
|
-
|
|
23666
|
-
|
|
23667
|
-
|
|
23668
|
-
|
|
23669
|
-
|
|
23670
|
-
|
|
23671
|
-
|
|
23672
|
-
|
|
23673
|
-
|
|
23674
|
-
|
|
23675
|
-
|
|
23676
|
-
tags: Joi100.string().optional().allow("", null)
|
|
23677
|
-
});
|
|
23678
|
-
var idParamSchema2 = Joi100.object({
|
|
23679
|
-
id: Joi100.string().hex().length(24).required()
|
|
23680
|
-
});
|
|
23681
|
-
function useAssetItemController() {
|
|
23682
|
-
const repo = useAssetItemRepo();
|
|
23683
|
-
const service = useAssetItemService();
|
|
23684
|
-
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() {
|
|
23685
23686
|
try {
|
|
23686
|
-
|
|
23687
|
-
|
|
23688
|
-
|
|
23689
|
-
|
|
23690
|
-
|
|
23691
|
-
|
|
23692
|
-
res.status(201).json({
|
|
23693
|
-
message: "Asset item created successfully.",
|
|
23694
|
-
assetItemId
|
|
23695
|
-
});
|
|
23687
|
+
await repo.collection.createIndexes([
|
|
23688
|
+
{ key: { itemId: 1 } },
|
|
23689
|
+
{ key: { createdAt: -1 } },
|
|
23690
|
+
{ key: { itemId: 1, createdAt: -1 } },
|
|
23691
|
+
{ key: { type: 1 } }
|
|
23692
|
+
]);
|
|
23696
23693
|
} catch (error) {
|
|
23697
|
-
|
|
23694
|
+
throw new BadRequestError113("Failed to create stock movement indexes.");
|
|
23698
23695
|
}
|
|
23699
23696
|
}
|
|
23700
|
-
async function
|
|
23701
|
-
const { error } = paginationSchema3.validate(req.query);
|
|
23702
|
-
if (error) {
|
|
23703
|
-
next(new BadRequestError114(error.message));
|
|
23704
|
-
return;
|
|
23705
|
-
}
|
|
23706
|
-
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
23707
|
-
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
23708
|
-
const search = req.query.search ?? "";
|
|
23709
|
-
const status2 = req.query.status ?? "active";
|
|
23710
|
-
const assetCategory = req.query.assetCategory ?? "";
|
|
23711
|
-
const trackingType = req.query.trackingType ?? "";
|
|
23712
|
-
const purpose = req.query.purpose ?? "";
|
|
23713
|
-
const departmentId = req.query.departmentId ?? "";
|
|
23714
|
-
const categoryId = req.query.categoryId ?? "";
|
|
23715
|
-
const subcategoryId = req.query.subcategoryId ?? "";
|
|
23716
|
-
const brand = req.query.brand ?? "";
|
|
23717
|
-
const tagsParam = req.query.tags ?? "";
|
|
23718
|
-
const tags = tagsParam ? tagsParam.split(",").filter(Boolean) : [];
|
|
23719
|
-
if (!isFinite(page)) {
|
|
23720
|
-
next(new BadRequestError114("Invalid page number."));
|
|
23721
|
-
return;
|
|
23722
|
-
}
|
|
23723
|
-
if (!isFinite(limit)) {
|
|
23724
|
-
next(new BadRequestError114("Invalid limit number."));
|
|
23725
|
-
return;
|
|
23726
|
-
}
|
|
23697
|
+
async function add(value, session) {
|
|
23727
23698
|
try {
|
|
23728
|
-
|
|
23729
|
-
|
|
23730
|
-
|
|
23731
|
-
|
|
23732
|
-
|
|
23733
|
-
|
|
23734
|
-
|
|
23735
|
-
purpose,
|
|
23736
|
-
departmentId,
|
|
23737
|
-
categoryId,
|
|
23738
|
-
subcategoryId,
|
|
23739
|
-
brand,
|
|
23740
|
-
tags
|
|
23741
|
-
});
|
|
23742
|
-
res.json(results);
|
|
23743
|
-
} catch (error2) {
|
|
23744
|
-
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.");
|
|
23745
23706
|
}
|
|
23746
23707
|
}
|
|
23747
|
-
async function
|
|
23748
|
-
|
|
23749
|
-
|
|
23750
|
-
|
|
23751
|
-
|
|
23752
|
-
|
|
23753
|
-
|
|
23754
|
-
|
|
23755
|
-
|
|
23756
|
-
}
|
|
23757
|
-
|
|
23758
|
-
|
|
23759
|
-
}
|
|
23760
|
-
async function updateById(req, res, next) {
|
|
23761
|
-
const { error: paramError } = idParamSchema2.validate(req.params);
|
|
23762
|
-
if (paramError) {
|
|
23763
|
-
next(new BadRequestError114(paramError.message));
|
|
23764
|
-
return;
|
|
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;
|
|
23765
23720
|
}
|
|
23766
|
-
const
|
|
23767
|
-
|
|
23768
|
-
|
|
23769
|
-
|
|
23721
|
+
const cacheKey = makeCacheKey36(namespace_collection, {
|
|
23722
|
+
itemId,
|
|
23723
|
+
type,
|
|
23724
|
+
page,
|
|
23725
|
+
limit,
|
|
23726
|
+
tag: "byItemId"
|
|
23727
|
+
});
|
|
23728
|
+
try {
|
|
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;
|
|
23770
23745
|
}
|
|
23746
|
+
}
|
|
23747
|
+
async function getById(id) {
|
|
23748
|
+
const cacheKey = makeCacheKey36(namespace_collection, {
|
|
23749
|
+
id,
|
|
23750
|
+
tag: "by-id"
|
|
23751
|
+
});
|
|
23771
23752
|
try {
|
|
23772
|
-
const
|
|
23773
|
-
|
|
23774
|
-
|
|
23775
|
-
|
|
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)
|
|
23776
23758
|
});
|
|
23759
|
+
if (!result) {
|
|
23760
|
+
throw new BadRequestError113("Stock movement not found.");
|
|
23761
|
+
}
|
|
23762
|
+
repo.setCache(cacheKey, result, 300);
|
|
23763
|
+
return result;
|
|
23777
23764
|
} catch (error) {
|
|
23778
|
-
|
|
23765
|
+
if (error instanceof AppError54)
|
|
23766
|
+
throw error;
|
|
23767
|
+
throw new InternalServerError50("Failed to get stock movement.");
|
|
23779
23768
|
}
|
|
23780
23769
|
}
|
|
23781
|
-
async function
|
|
23782
|
-
const { error } = idParamSchema2.validate(req.params);
|
|
23783
|
-
if (error) {
|
|
23784
|
-
next(new BadRequestError114(error.message));
|
|
23785
|
-
return;
|
|
23786
|
-
}
|
|
23770
|
+
async function countByItemId(itemId) {
|
|
23787
23771
|
try {
|
|
23788
|
-
await repo.
|
|
23789
|
-
|
|
23790
|
-
message: "Asset item deleted successfully."
|
|
23772
|
+
return await repo.collection.countDocuments({
|
|
23773
|
+
itemId: new ObjectId65(itemId)
|
|
23791
23774
|
});
|
|
23792
|
-
} catch (
|
|
23793
|
-
|
|
23775
|
+
} catch (error) {
|
|
23776
|
+
throw new InternalServerError50("Failed to count stock movements.");
|
|
23794
23777
|
}
|
|
23795
23778
|
}
|
|
23796
23779
|
return {
|
|
23780
|
+
createIndexes,
|
|
23797
23781
|
add,
|
|
23798
|
-
|
|
23782
|
+
getByItemId,
|
|
23799
23783
|
getById,
|
|
23800
|
-
|
|
23801
|
-
deleteById
|
|
23784
|
+
countByItemId
|
|
23802
23785
|
};
|
|
23803
23786
|
}
|
|
23804
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
|
+
|
|
23805
23803
|
// src/resources/asset-item-unit/asset.unit.model.ts
|
|
23806
23804
|
import Joi101 from "joi";
|
|
23807
|
-
import { ObjectId as
|
|
23805
|
+
import { ObjectId as ObjectId66 } from "mongodb";
|
|
23808
23806
|
var assetUnitStatuses = [
|
|
23809
23807
|
"available",
|
|
23810
23808
|
"assigned",
|
|
@@ -23834,7 +23832,7 @@ function modelAssetUnit(data) {
|
|
|
23834
23832
|
}
|
|
23835
23833
|
if (data.itemId && typeof data.itemId === "string") {
|
|
23836
23834
|
try {
|
|
23837
|
-
data.itemId = new
|
|
23835
|
+
data.itemId = new ObjectId66(data.itemId);
|
|
23838
23836
|
} catch {
|
|
23839
23837
|
throw new Error(`Invalid itemId format: ${data.itemId}`);
|
|
23840
23838
|
}
|
|
@@ -23853,19 +23851,10 @@ function modelAssetUnit(data) {
|
|
|
23853
23851
|
}
|
|
23854
23852
|
|
|
23855
23853
|
// src/resources/asset-item-unit/asset.unit.repository.ts
|
|
23856
|
-
import {
|
|
23857
|
-
AppError as AppError55,
|
|
23858
|
-
BadRequestError as BadRequestError115,
|
|
23859
|
-
InternalServerError as InternalServerError51,
|
|
23860
|
-
logger as logger57,
|
|
23861
|
-
makeCacheKey as makeCacheKey36,
|
|
23862
|
-
paginate as paginate32,
|
|
23863
|
-
useRepo as useRepo35
|
|
23864
|
-
} from "@goweekdays/utils";
|
|
23865
|
-
import { ObjectId as ObjectId65 } from "mongodb";
|
|
23854
|
+
import { ObjectId as ObjectId67 } from "mongodb";
|
|
23866
23855
|
function useAssetUnitRepo() {
|
|
23867
23856
|
const namespace_collection = "asset.units";
|
|
23868
|
-
const repo =
|
|
23857
|
+
const repo = useRepo36(namespace_collection);
|
|
23869
23858
|
async function createIndexes() {
|
|
23870
23859
|
try {
|
|
23871
23860
|
await repo.collection.createIndexes([
|
|
@@ -23875,7 +23864,7 @@ function useAssetUnitRepo() {
|
|
|
23875
23864
|
{ key: { itemId: 1, status: 1 } }
|
|
23876
23865
|
]);
|
|
23877
23866
|
} catch (error) {
|
|
23878
|
-
throw new
|
|
23867
|
+
throw new BadRequestError114("Failed to create asset unit indexes.");
|
|
23879
23868
|
}
|
|
23880
23869
|
}
|
|
23881
23870
|
async function add(value, session) {
|
|
@@ -23885,7 +23874,7 @@ function useAssetUnitRepo() {
|
|
|
23885
23874
|
repo.delCachedData();
|
|
23886
23875
|
return res.insertedId;
|
|
23887
23876
|
} catch (error) {
|
|
23888
|
-
|
|
23877
|
+
logger58.log({ level: "error", message: error.message });
|
|
23889
23878
|
throw new InternalServerError51("Failed to create asset unit.");
|
|
23890
23879
|
}
|
|
23891
23880
|
}
|
|
@@ -23897,12 +23886,12 @@ function useAssetUnitRepo() {
|
|
|
23897
23886
|
}) {
|
|
23898
23887
|
page = page > 0 ? page - 1 : 0;
|
|
23899
23888
|
const query = {
|
|
23900
|
-
itemId: new
|
|
23889
|
+
itemId: new ObjectId67(itemId)
|
|
23901
23890
|
};
|
|
23902
23891
|
if (status2) {
|
|
23903
23892
|
query.status = status2;
|
|
23904
23893
|
}
|
|
23905
|
-
const cacheKey =
|
|
23894
|
+
const cacheKey = makeCacheKey37(namespace_collection, {
|
|
23906
23895
|
itemId,
|
|
23907
23896
|
status: status2,
|
|
23908
23897
|
page,
|
|
@@ -23920,16 +23909,16 @@ function useAssetUnitRepo() {
|
|
|
23920
23909
|
{ $limit: limit }
|
|
23921
23910
|
]).toArray();
|
|
23922
23911
|
const length = await repo.collection.countDocuments(query);
|
|
23923
|
-
const data =
|
|
23912
|
+
const data = paginate33(items, page, limit, length);
|
|
23924
23913
|
repo.setCache(cacheKey, data, 600);
|
|
23925
23914
|
return data;
|
|
23926
23915
|
} catch (error) {
|
|
23927
|
-
|
|
23916
|
+
logger58.log({ level: "error", message: `${error}` });
|
|
23928
23917
|
throw error;
|
|
23929
23918
|
}
|
|
23930
23919
|
}
|
|
23931
23920
|
async function getById(id) {
|
|
23932
|
-
const cacheKey =
|
|
23921
|
+
const cacheKey = makeCacheKey37(namespace_collection, {
|
|
23933
23922
|
id,
|
|
23934
23923
|
tag: "by-id"
|
|
23935
23924
|
});
|
|
@@ -23938,10 +23927,10 @@ function useAssetUnitRepo() {
|
|
|
23938
23927
|
if (cached)
|
|
23939
23928
|
return cached;
|
|
23940
23929
|
const result = await repo.collection.findOne({
|
|
23941
|
-
_id: new
|
|
23930
|
+
_id: new ObjectId67(id)
|
|
23942
23931
|
});
|
|
23943
23932
|
if (!result) {
|
|
23944
|
-
throw new
|
|
23933
|
+
throw new BadRequestError114("Asset unit not found.");
|
|
23945
23934
|
}
|
|
23946
23935
|
repo.setCache(cacheKey, result, 300);
|
|
23947
23936
|
return result;
|
|
@@ -23954,12 +23943,12 @@ function useAssetUnitRepo() {
|
|
|
23954
23943
|
async function updateById(id, value, session) {
|
|
23955
23944
|
try {
|
|
23956
23945
|
const result = await repo.collection.findOneAndUpdate(
|
|
23957
|
-
{ _id: new
|
|
23946
|
+
{ _id: new ObjectId67(id) },
|
|
23958
23947
|
{ $set: { ...value, updatedAt: /* @__PURE__ */ new Date() } },
|
|
23959
23948
|
{ returnDocument: "after", session }
|
|
23960
23949
|
);
|
|
23961
23950
|
if (!result) {
|
|
23962
|
-
throw new
|
|
23951
|
+
throw new BadRequestError114("Asset unit not found.");
|
|
23963
23952
|
}
|
|
23964
23953
|
repo.delCachedData();
|
|
23965
23954
|
return result;
|
|
@@ -23969,10 +23958,19 @@ function useAssetUnitRepo() {
|
|
|
23969
23958
|
throw new InternalServerError51("Failed to update asset unit.");
|
|
23970
23959
|
}
|
|
23971
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
|
+
}
|
|
23972
23970
|
async function countByItemIdAndStatus(itemId, status2) {
|
|
23973
23971
|
try {
|
|
23974
23972
|
return await repo.collection.countDocuments({
|
|
23975
|
-
itemId: new
|
|
23973
|
+
itemId: new ObjectId67(itemId),
|
|
23976
23974
|
status: status2
|
|
23977
23975
|
});
|
|
23978
23976
|
} catch (error) {
|
|
@@ -23985,303 +23983,111 @@ function useAssetUnitRepo() {
|
|
|
23985
23983
|
getByItemId,
|
|
23986
23984
|
getById,
|
|
23987
23985
|
updateById,
|
|
23986
|
+
countByItemId,
|
|
23988
23987
|
countByItemIdAndStatus
|
|
23989
23988
|
};
|
|
23990
23989
|
}
|
|
23991
23990
|
|
|
23992
|
-
// src/resources/asset-item-
|
|
23993
|
-
|
|
23994
|
-
|
|
23995
|
-
|
|
23996
|
-
|
|
23997
|
-
|
|
23998
|
-
|
|
23999
|
-
|
|
24000
|
-
|
|
24001
|
-
|
|
24002
|
-
id: Joi102.string().hex().length(24).required()
|
|
24003
|
-
});
|
|
24004
|
-
function useAssetUnitController() {
|
|
24005
|
-
const repo = useAssetUnitRepo();
|
|
24006
|
-
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.");
|
|
24007
24001
|
try {
|
|
24008
|
-
|
|
24009
|
-
|
|
24010
|
-
|
|
24011
|
-
|
|
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
|
+
}
|
|
24012
24018
|
}
|
|
24013
|
-
|
|
24014
|
-
|
|
24015
|
-
|
|
24016
|
-
|
|
24017
|
-
|
|
24019
|
+
await assetItemRepo.incrementQuantity(
|
|
24020
|
+
String(value.itemId),
|
|
24021
|
+
value.quantity,
|
|
24022
|
+
session
|
|
24023
|
+
);
|
|
24024
|
+
await session.commitTransaction();
|
|
24025
|
+
return movementId;
|
|
24018
24026
|
} catch (error) {
|
|
24019
|
-
|
|
24027
|
+
await session.abortTransaction();
|
|
24028
|
+
throw error;
|
|
24029
|
+
} finally {
|
|
24030
|
+
await session.endSession();
|
|
24020
24031
|
}
|
|
24021
24032
|
}
|
|
24022
|
-
async function
|
|
24023
|
-
const
|
|
24024
|
-
if (
|
|
24025
|
-
|
|
24026
|
-
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.");
|
|
24027
24037
|
}
|
|
24028
|
-
const
|
|
24029
|
-
|
|
24030
|
-
|
|
24031
|
-
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
24038
|
+
const session = useAtlas17.getClient()?.startSession();
|
|
24039
|
+
if (!session)
|
|
24040
|
+
throw new BadRequestError115("Failed to start session.");
|
|
24032
24041
|
try {
|
|
24033
|
-
|
|
24034
|
-
|
|
24035
|
-
|
|
24036
|
-
|
|
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();
|
|
24037
24066
|
}
|
|
24038
24067
|
}
|
|
24039
|
-
async function
|
|
24040
|
-
|
|
24041
|
-
|
|
24042
|
-
|
|
24043
|
-
|
|
24068
|
+
async function transfer(value, unitIds) {
|
|
24069
|
+
if (!value.fromLocationId || !value.toLocationId) {
|
|
24070
|
+
throw new BadRequestError115(
|
|
24071
|
+
"Transfer requires both fromLocationId and toLocationId."
|
|
24072
|
+
);
|
|
24044
24073
|
}
|
|
24045
|
-
|
|
24046
|
-
|
|
24047
|
-
|
|
24048
|
-
|
|
24049
|
-
next(error2);
|
|
24050
|
-
}
|
|
24051
|
-
}
|
|
24052
|
-
async function updateById(req, res, next) {
|
|
24053
|
-
const { error: paramError } = idParamSchema3.validate(req.params);
|
|
24054
|
-
if (paramError) {
|
|
24055
|
-
next(new BadRequestError116(paramError.message));
|
|
24056
|
-
return;
|
|
24057
|
-
}
|
|
24058
|
-
const { error: bodyError } = schemaAssetUnitUpdate.validate(req.body);
|
|
24059
|
-
if (bodyError) {
|
|
24060
|
-
next(new BadRequestError116(bodyError.message));
|
|
24061
|
-
return;
|
|
24062
|
-
}
|
|
24063
|
-
try {
|
|
24064
|
-
const updatedUnit = await repo.updateById(req.params.id, req.body);
|
|
24065
|
-
res.json({
|
|
24066
|
-
message: "Asset unit updated successfully.",
|
|
24067
|
-
unit: updatedUnit
|
|
24068
|
-
});
|
|
24069
|
-
} catch (error) {
|
|
24070
|
-
next(error);
|
|
24071
|
-
}
|
|
24072
|
-
}
|
|
24073
|
-
return {
|
|
24074
|
-
add,
|
|
24075
|
-
getByItemId,
|
|
24076
|
-
getById,
|
|
24077
|
-
updateById
|
|
24078
|
-
};
|
|
24079
|
-
}
|
|
24080
|
-
|
|
24081
|
-
// src/resources/asset-item-movement/stock.movement.model.ts
|
|
24082
|
-
import Joi103 from "joi";
|
|
24083
|
-
import { ObjectId as ObjectId66 } from "mongodb";
|
|
24084
|
-
var stockMovementTypes = [
|
|
24085
|
-
"in",
|
|
24086
|
-
"out",
|
|
24087
|
-
"transfer",
|
|
24088
|
-
"adjustment",
|
|
24089
|
-
"conversion"
|
|
24090
|
-
];
|
|
24091
|
-
var stockMovementReferenceTypes = [
|
|
24092
|
-
"purchase",
|
|
24093
|
-
"sale",
|
|
24094
|
-
"transfer",
|
|
24095
|
-
"manual",
|
|
24096
|
-
"conversion"
|
|
24097
|
-
];
|
|
24098
|
-
var schemaStockMovement = Joi103.object({
|
|
24099
|
-
itemId: Joi103.string().hex().length(24).required(),
|
|
24100
|
-
type: Joi103.string().valid(...stockMovementTypes).required(),
|
|
24101
|
-
quantity: Joi103.number().positive().required(),
|
|
24102
|
-
unitCost: Joi103.number().min(0).optional().allow(null),
|
|
24103
|
-
totalCost: Joi103.number().min(0).optional().allow(null),
|
|
24104
|
-
reference: Joi103.object({
|
|
24105
|
-
type: Joi103.string().valid(...stockMovementReferenceTypes).required(),
|
|
24106
|
-
id: Joi103.string().optional().allow("", null)
|
|
24107
|
-
}).optional().allow(null),
|
|
24108
|
-
fromLocationId: Joi103.string().optional().allow("", null),
|
|
24109
|
-
toLocationId: Joi103.string().optional().allow("", null),
|
|
24110
|
-
fromItemId: Joi103.string().hex().length(24).optional().allow("", null),
|
|
24111
|
-
toItemId: Joi103.string().hex().length(24).optional().allow("", null),
|
|
24112
|
-
reason: Joi103.string().optional().allow("", null)
|
|
24113
|
-
});
|
|
24114
|
-
function modelStockMovement(data) {
|
|
24115
|
-
const { error } = schemaStockMovement.validate(data);
|
|
24116
|
-
if (error) {
|
|
24117
|
-
throw new Error(`Invalid stock movement data: ${error.message}`);
|
|
24118
|
-
}
|
|
24119
|
-
if (data.itemId && typeof data.itemId === "string") {
|
|
24120
|
-
try {
|
|
24121
|
-
data.itemId = new ObjectId66(data.itemId);
|
|
24122
|
-
} catch {
|
|
24123
|
-
throw new Error(`Invalid itemId format: ${data.itemId}`);
|
|
24124
|
-
}
|
|
24125
|
-
}
|
|
24126
|
-
return {
|
|
24127
|
-
_id: data._id,
|
|
24128
|
-
itemId: data.itemId,
|
|
24129
|
-
type: data.type,
|
|
24130
|
-
quantity: data.quantity,
|
|
24131
|
-
unitCost: data.unitCost ?? 0,
|
|
24132
|
-
totalCost: data.totalCost ?? 0,
|
|
24133
|
-
reference: data.reference ?? void 0,
|
|
24134
|
-
fromLocationId: data.fromLocationId ?? "",
|
|
24135
|
-
toLocationId: data.toLocationId ?? "",
|
|
24136
|
-
fromItemId: data.fromItemId ?? "",
|
|
24137
|
-
toItemId: data.toItemId ?? "",
|
|
24138
|
-
reason: data.reason ?? "",
|
|
24139
|
-
createdAt: data.createdAt ?? /* @__PURE__ */ new Date()
|
|
24140
|
-
};
|
|
24141
|
-
}
|
|
24142
|
-
|
|
24143
|
-
// src/resources/asset-item-movement/stock.movement.repository.ts
|
|
24144
|
-
import {
|
|
24145
|
-
AppError as AppError56,
|
|
24146
|
-
BadRequestError as BadRequestError117,
|
|
24147
|
-
InternalServerError as InternalServerError52,
|
|
24148
|
-
logger as logger58,
|
|
24149
|
-
makeCacheKey as makeCacheKey37,
|
|
24150
|
-
paginate as paginate33,
|
|
24151
|
-
useRepo as useRepo36
|
|
24152
|
-
} from "@goweekdays/utils";
|
|
24153
|
-
import { ObjectId as ObjectId67 } from "mongodb";
|
|
24154
|
-
function useStockMovementRepo() {
|
|
24155
|
-
const namespace_collection = "stock.movements";
|
|
24156
|
-
const repo = useRepo36(namespace_collection);
|
|
24157
|
-
async function createIndexes() {
|
|
24158
|
-
try {
|
|
24159
|
-
await repo.collection.createIndexes([
|
|
24160
|
-
{ key: { itemId: 1 } },
|
|
24161
|
-
{ key: { createdAt: -1 } },
|
|
24162
|
-
{ key: { itemId: 1, createdAt: -1 } },
|
|
24163
|
-
{ key: { type: 1 } }
|
|
24164
|
-
]);
|
|
24165
|
-
} catch (error) {
|
|
24166
|
-
throw new BadRequestError117("Failed to create stock movement indexes.");
|
|
24167
|
-
}
|
|
24168
|
-
}
|
|
24169
|
-
async function add(value, session) {
|
|
24170
|
-
try {
|
|
24171
|
-
value = modelStockMovement(value);
|
|
24172
|
-
const res = await repo.collection.insertOne(value, { session });
|
|
24173
|
-
repo.delCachedData();
|
|
24174
|
-
return res.insertedId;
|
|
24175
|
-
} catch (error) {
|
|
24176
|
-
logger58.log({ level: "error", message: error.message });
|
|
24177
|
-
throw new InternalServerError52("Failed to create stock movement.");
|
|
24178
|
-
}
|
|
24179
|
-
}
|
|
24180
|
-
async function getByItemId({
|
|
24181
|
-
itemId,
|
|
24182
|
-
type = "",
|
|
24183
|
-
page = 1,
|
|
24184
|
-
limit = 10
|
|
24185
|
-
}) {
|
|
24186
|
-
page = page > 0 ? page - 1 : 0;
|
|
24187
|
-
const query = {
|
|
24188
|
-
itemId: new ObjectId67(itemId)
|
|
24189
|
-
};
|
|
24190
|
-
if (type) {
|
|
24191
|
-
query.type = type;
|
|
24192
|
-
}
|
|
24193
|
-
const cacheKey = makeCacheKey37(namespace_collection, {
|
|
24194
|
-
itemId,
|
|
24195
|
-
type,
|
|
24196
|
-
page,
|
|
24197
|
-
limit,
|
|
24198
|
-
tag: "byItemId"
|
|
24199
|
-
});
|
|
24200
|
-
try {
|
|
24201
|
-
const cached = await repo.getCache(cacheKey);
|
|
24202
|
-
if (cached)
|
|
24203
|
-
return cached;
|
|
24204
|
-
const items = await repo.collection.aggregate([
|
|
24205
|
-
{ $match: query },
|
|
24206
|
-
{ $sort: { createdAt: -1 } },
|
|
24207
|
-
{ $skip: page * limit },
|
|
24208
|
-
{ $limit: limit }
|
|
24209
|
-
]).toArray();
|
|
24210
|
-
const length = await repo.collection.countDocuments(query);
|
|
24211
|
-
const data = paginate33(items, page, limit, length);
|
|
24212
|
-
repo.setCache(cacheKey, data, 600);
|
|
24213
|
-
return data;
|
|
24214
|
-
} catch (error) {
|
|
24215
|
-
logger58.log({ level: "error", message: `${error}` });
|
|
24216
|
-
throw error;
|
|
24217
|
-
}
|
|
24218
|
-
}
|
|
24219
|
-
async function getById(id) {
|
|
24220
|
-
const cacheKey = makeCacheKey37(namespace_collection, {
|
|
24221
|
-
id,
|
|
24222
|
-
tag: "by-id"
|
|
24223
|
-
});
|
|
24224
|
-
try {
|
|
24225
|
-
const cached = await repo.getCache(cacheKey);
|
|
24226
|
-
if (cached)
|
|
24227
|
-
return cached;
|
|
24228
|
-
const result = await repo.collection.findOne({
|
|
24229
|
-
_id: new ObjectId67(id)
|
|
24230
|
-
});
|
|
24231
|
-
if (!result) {
|
|
24232
|
-
throw new BadRequestError117("Stock movement not found.");
|
|
24233
|
-
}
|
|
24234
|
-
repo.setCache(cacheKey, result, 300);
|
|
24235
|
-
return result;
|
|
24236
|
-
} catch (error) {
|
|
24237
|
-
if (error instanceof AppError56)
|
|
24238
|
-
throw error;
|
|
24239
|
-
throw new InternalServerError52("Failed to get stock movement.");
|
|
24240
|
-
}
|
|
24241
|
-
}
|
|
24242
|
-
return {
|
|
24243
|
-
createIndexes,
|
|
24244
|
-
add,
|
|
24245
|
-
getByItemId,
|
|
24246
|
-
getById
|
|
24247
|
-
};
|
|
24248
|
-
}
|
|
24249
|
-
|
|
24250
|
-
// src/resources/asset-item-movement/stock.movement.service.ts
|
|
24251
|
-
import { BadRequestError as BadRequestError118 } from "@goweekdays/utils";
|
|
24252
|
-
import { useAtlas as useAtlas18 } from "@goweekdays/utils";
|
|
24253
|
-
function useStockMovementService() {
|
|
24254
|
-
const movementRepo = useStockMovementRepo();
|
|
24255
|
-
const assetItemRepo = useAssetItemRepo();
|
|
24256
|
-
const assetUnitRepo = useAssetUnitRepo();
|
|
24257
|
-
async function stockIn(value, units) {
|
|
24258
|
-
const item = await assetItemRepo.getById(String(value.itemId));
|
|
24259
|
-
const session = useAtlas18.getClient()?.startSession();
|
|
24260
|
-
if (!session)
|
|
24261
|
-
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.");
|
|
24262
24078
|
try {
|
|
24263
24079
|
session.startTransaction();
|
|
24264
|
-
value.type = "
|
|
24080
|
+
value.type = "transfer";
|
|
24265
24081
|
const movementId = await movementRepo.add(value, session);
|
|
24266
|
-
if (item.trackingType === "individual" &&
|
|
24267
|
-
for (const
|
|
24268
|
-
await assetUnitRepo.
|
|
24269
|
-
|
|
24270
|
-
|
|
24271
|
-
serialNumber: unit.serialNumber,
|
|
24272
|
-
plateNumber: unit.plateNumber,
|
|
24273
|
-
status: "available",
|
|
24274
|
-
locationId: value.toLocationId
|
|
24275
|
-
},
|
|
24082
|
+
if (item.trackingType === "individual" && unitIds && unitIds.length > 0) {
|
|
24083
|
+
for (const unitId of unitIds) {
|
|
24084
|
+
await assetUnitRepo.updateById(
|
|
24085
|
+
unitId,
|
|
24086
|
+
{ locationId: value.toLocationId },
|
|
24276
24087
|
session
|
|
24277
24088
|
);
|
|
24278
24089
|
}
|
|
24279
24090
|
}
|
|
24280
|
-
await assetItemRepo.incrementQuantity(
|
|
24281
|
-
String(value.itemId),
|
|
24282
|
-
value.quantity,
|
|
24283
|
-
session
|
|
24284
|
-
);
|
|
24285
24091
|
await session.commitTransaction();
|
|
24286
24092
|
return movementId;
|
|
24287
24093
|
} catch (error) {
|
|
@@ -24291,30 +24097,20 @@ function useStockMovementService() {
|
|
|
24291
24097
|
await session.endSession();
|
|
24292
24098
|
}
|
|
24293
24099
|
}
|
|
24294
|
-
async function
|
|
24295
|
-
|
|
24296
|
-
|
|
24297
|
-
throw new BadRequestError118("Insufficient stock.");
|
|
24100
|
+
async function adjustment(value) {
|
|
24101
|
+
if (!value.reason) {
|
|
24102
|
+
throw new BadRequestError115("Adjustment requires a reason.");
|
|
24298
24103
|
}
|
|
24299
|
-
const session =
|
|
24104
|
+
const session = useAtlas17.getClient()?.startSession();
|
|
24300
24105
|
if (!session)
|
|
24301
|
-
throw new
|
|
24106
|
+
throw new BadRequestError115("Failed to start session.");
|
|
24302
24107
|
try {
|
|
24303
24108
|
session.startTransaction();
|
|
24304
|
-
value.type = "
|
|
24109
|
+
value.type = "adjustment";
|
|
24305
24110
|
const movementId = await movementRepo.add(value, session);
|
|
24306
|
-
if (item.trackingType === "individual" && unitIds && unitIds.length > 0) {
|
|
24307
|
-
for (const unitId of unitIds) {
|
|
24308
|
-
await assetUnitRepo.updateById(
|
|
24309
|
-
unitId,
|
|
24310
|
-
{ status: "assigned", assignedTo: value.reference?.id },
|
|
24311
|
-
session
|
|
24312
|
-
);
|
|
24313
|
-
}
|
|
24314
|
-
}
|
|
24315
24111
|
await assetItemRepo.incrementQuantity(
|
|
24316
24112
|
String(value.itemId),
|
|
24317
|
-
|
|
24113
|
+
value.quantity,
|
|
24318
24114
|
session
|
|
24319
24115
|
);
|
|
24320
24116
|
await session.commitTransaction();
|
|
@@ -24326,338 +24122,681 @@ function useStockMovementService() {
|
|
|
24326
24122
|
await session.endSession();
|
|
24327
24123
|
}
|
|
24328
24124
|
}
|
|
24329
|
-
async function
|
|
24330
|
-
|
|
24331
|
-
|
|
24332
|
-
|
|
24333
|
-
);
|
|
24125
|
+
async function conversion(newItem, quantity, unitIds) {
|
|
24126
|
+
const sourceItem = await assetItemRepo.getById(String(newItem.itemRefId));
|
|
24127
|
+
if (!newItem.itemRefId) {
|
|
24128
|
+
throw new BadRequestError115("Conversion requires itemRefId.");
|
|
24334
24129
|
}
|
|
24335
|
-
|
|
24336
|
-
|
|
24130
|
+
if (sourceItem.trackingType === "quantity" && (sourceItem.quantityOnHand ?? 0) < quantity) {
|
|
24131
|
+
throw new BadRequestError115("Insufficient stock on source item.");
|
|
24132
|
+
}
|
|
24133
|
+
const session = useAtlas17.getClient()?.startSession();
|
|
24337
24134
|
if (!session)
|
|
24338
|
-
throw new
|
|
24135
|
+
throw new BadRequestError115("Failed to start session.");
|
|
24339
24136
|
try {
|
|
24340
24137
|
session.startTransaction();
|
|
24341
|
-
|
|
24342
|
-
const
|
|
24343
|
-
|
|
24344
|
-
|
|
24345
|
-
|
|
24346
|
-
|
|
24347
|
-
|
|
24348
|
-
|
|
24349
|
-
|
|
24350
|
-
|
|
24351
|
-
|
|
24352
|
-
|
|
24353
|
-
|
|
24354
|
-
|
|
24355
|
-
|
|
24138
|
+
const newItemId = await assetItemRepo.add(newItem, session);
|
|
24139
|
+
const movementData = {
|
|
24140
|
+
itemId: newItem.itemRefId,
|
|
24141
|
+
type: "conversion",
|
|
24142
|
+
quantity,
|
|
24143
|
+
fromItemId: String(newItem.itemRefId),
|
|
24144
|
+
toItemId: String(newItemId),
|
|
24145
|
+
reference: { type: "conversion", id: String(newItemId) }
|
|
24146
|
+
};
|
|
24147
|
+
const movementId = await movementRepo.add(movementData, session);
|
|
24148
|
+
await assetItemRepo.incrementQuantity(
|
|
24149
|
+
String(newItem.itemRefId),
|
|
24150
|
+
-quantity,
|
|
24151
|
+
session
|
|
24152
|
+
);
|
|
24153
|
+
await assetItemRepo.incrementQuantity(
|
|
24154
|
+
String(newItemId),
|
|
24155
|
+
quantity,
|
|
24156
|
+
session
|
|
24157
|
+
);
|
|
24158
|
+
if (sourceItem.trackingType === "individual" && unitIds && unitIds.length > 0) {
|
|
24159
|
+
for (const unitId of unitIds) {
|
|
24160
|
+
await assetUnitRepo.updateById(
|
|
24161
|
+
unitId,
|
|
24162
|
+
{ itemId: String(newItemId) },
|
|
24163
|
+
session
|
|
24164
|
+
);
|
|
24165
|
+
}
|
|
24166
|
+
}
|
|
24167
|
+
await session.commitTransaction();
|
|
24168
|
+
return { movementId, newItemId };
|
|
24169
|
+
} catch (error) {
|
|
24170
|
+
await session.abortTransaction();
|
|
24356
24171
|
throw error;
|
|
24357
24172
|
} finally {
|
|
24358
24173
|
await session.endSession();
|
|
24359
24174
|
}
|
|
24360
24175
|
}
|
|
24361
|
-
|
|
24362
|
-
|
|
24363
|
-
|
|
24176
|
+
return {
|
|
24177
|
+
stockIn,
|
|
24178
|
+
stockOut,
|
|
24179
|
+
transfer,
|
|
24180
|
+
adjustment,
|
|
24181
|
+
conversion
|
|
24182
|
+
};
|
|
24183
|
+
}
|
|
24184
|
+
|
|
24185
|
+
// src/resources/asset-item-movement/stock.movement.controller.ts
|
|
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)
|
|
24193
|
+
});
|
|
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)
|
|
24205
|
+
}).optional().allow(null),
|
|
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)
|
|
24211
|
+
})
|
|
24212
|
+
).optional()
|
|
24213
|
+
});
|
|
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)
|
|
24222
|
+
}).optional().allow(null),
|
|
24223
|
+
fromLocationId: Joi102.string().optional().allow("", null),
|
|
24224
|
+
unitIds: Joi102.array().items(Joi102.string().hex().length(24)).optional()
|
|
24225
|
+
});
|
|
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)
|
|
24234
|
+
}).optional().allow(null),
|
|
24235
|
+
unitIds: Joi102.array().items(Joi102.string().hex().length(24)).optional()
|
|
24236
|
+
});
|
|
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)
|
|
24244
|
+
}).optional().allow(null)
|
|
24245
|
+
});
|
|
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()
|
|
24269
|
+
})
|
|
24270
|
+
).optional().default([])
|
|
24271
|
+
}).required(),
|
|
24272
|
+
unitIds: Joi102.array().items(Joi102.string().hex().length(24)).optional()
|
|
24273
|
+
});
|
|
24274
|
+
function useStockMovementController() {
|
|
24275
|
+
const repo = useStockMovementRepo();
|
|
24276
|
+
const service = useStockMovementService();
|
|
24277
|
+
async function stockIn(req, res, next) {
|
|
24278
|
+
try {
|
|
24279
|
+
const { error } = stockInSchema.validate(req.body);
|
|
24280
|
+
if (error) {
|
|
24281
|
+
next(new BadRequestError116(error.message));
|
|
24282
|
+
return;
|
|
24283
|
+
}
|
|
24284
|
+
const { units, ...movement } = req.body;
|
|
24285
|
+
const movementId = await service.stockIn(movement, units);
|
|
24286
|
+
res.status(201).json({
|
|
24287
|
+
message: "Stock in recorded successfully.",
|
|
24288
|
+
movementId
|
|
24289
|
+
});
|
|
24290
|
+
} catch (error) {
|
|
24291
|
+
next(error);
|
|
24292
|
+
}
|
|
24293
|
+
}
|
|
24294
|
+
async function stockOut(req, res, next) {
|
|
24295
|
+
try {
|
|
24296
|
+
const { error } = stockOutSchema.validate(req.body);
|
|
24297
|
+
if (error) {
|
|
24298
|
+
next(new BadRequestError116(error.message));
|
|
24299
|
+
return;
|
|
24300
|
+
}
|
|
24301
|
+
const { unitIds, ...movement } = req.body;
|
|
24302
|
+
const movementId = await service.stockOut(movement, unitIds);
|
|
24303
|
+
res.status(201).json({
|
|
24304
|
+
message: "Stock out recorded successfully.",
|
|
24305
|
+
movementId
|
|
24306
|
+
});
|
|
24307
|
+
} catch (error) {
|
|
24308
|
+
next(error);
|
|
24309
|
+
}
|
|
24310
|
+
}
|
|
24311
|
+
async function transfer(req, res, next) {
|
|
24312
|
+
try {
|
|
24313
|
+
const { error } = transferSchema.validate(req.body);
|
|
24314
|
+
if (error) {
|
|
24315
|
+
next(new BadRequestError116(error.message));
|
|
24316
|
+
return;
|
|
24317
|
+
}
|
|
24318
|
+
const { unitIds, ...movement } = req.body;
|
|
24319
|
+
const movementId = await service.transfer(movement, unitIds);
|
|
24320
|
+
res.status(201).json({
|
|
24321
|
+
message: "Transfer recorded successfully.",
|
|
24322
|
+
movementId
|
|
24323
|
+
});
|
|
24324
|
+
} catch (error) {
|
|
24325
|
+
next(error);
|
|
24326
|
+
}
|
|
24327
|
+
}
|
|
24328
|
+
async function adjustment(req, res, next) {
|
|
24329
|
+
try {
|
|
24330
|
+
const { error } = adjustmentSchema.validate(req.body);
|
|
24331
|
+
if (error) {
|
|
24332
|
+
next(new BadRequestError116(error.message));
|
|
24333
|
+
return;
|
|
24334
|
+
}
|
|
24335
|
+
const movementId = await service.adjustment(req.body);
|
|
24336
|
+
res.status(201).json({
|
|
24337
|
+
message: "Adjustment recorded successfully.",
|
|
24338
|
+
movementId
|
|
24339
|
+
});
|
|
24340
|
+
} catch (error) {
|
|
24341
|
+
next(error);
|
|
24342
|
+
}
|
|
24343
|
+
}
|
|
24344
|
+
async function getByItemId(req, res, next) {
|
|
24345
|
+
const { error } = querySchema.validate(req.query);
|
|
24346
|
+
if (error) {
|
|
24347
|
+
next(new BadRequestError116(error.message));
|
|
24348
|
+
return;
|
|
24349
|
+
}
|
|
24350
|
+
const itemId = req.query.itemId;
|
|
24351
|
+
const type = req.query.type ?? "";
|
|
24352
|
+
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
24353
|
+
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
24354
|
+
try {
|
|
24355
|
+
const results = await repo.getByItemId({ itemId, type, page, limit });
|
|
24356
|
+
res.json(results);
|
|
24357
|
+
} catch (error2) {
|
|
24358
|
+
next(error2);
|
|
24359
|
+
}
|
|
24360
|
+
}
|
|
24361
|
+
async function getById(req, res, next) {
|
|
24362
|
+
const { error } = idParamSchema2.validate(req.params);
|
|
24363
|
+
if (error) {
|
|
24364
|
+
next(new BadRequestError116(error.message));
|
|
24365
|
+
return;
|
|
24366
|
+
}
|
|
24367
|
+
try {
|
|
24368
|
+
const movement = await repo.getById(req.params.id);
|
|
24369
|
+
res.json(movement);
|
|
24370
|
+
} catch (error2) {
|
|
24371
|
+
next(error2);
|
|
24372
|
+
}
|
|
24373
|
+
}
|
|
24374
|
+
async function conversion(req, res, next) {
|
|
24375
|
+
try {
|
|
24376
|
+
const { error } = conversionSchema.validate(req.body);
|
|
24377
|
+
if (error) {
|
|
24378
|
+
next(new BadRequestError116(error.message));
|
|
24379
|
+
return;
|
|
24380
|
+
}
|
|
24381
|
+
const { quantity, newItem, unitIds } = req.body;
|
|
24382
|
+
const result = await service.conversion(newItem, quantity, unitIds);
|
|
24383
|
+
res.status(201).json({
|
|
24384
|
+
message: "Conversion recorded successfully.",
|
|
24385
|
+
movementId: result.movementId,
|
|
24386
|
+
newItemId: result.newItemId
|
|
24387
|
+
});
|
|
24388
|
+
} catch (error) {
|
|
24389
|
+
next(error);
|
|
24390
|
+
}
|
|
24391
|
+
}
|
|
24392
|
+
return {
|
|
24393
|
+
stockIn,
|
|
24394
|
+
stockOut,
|
|
24395
|
+
transfer,
|
|
24396
|
+
adjustment,
|
|
24397
|
+
conversion,
|
|
24398
|
+
getByItemId,
|
|
24399
|
+
getById
|
|
24400
|
+
};
|
|
24401
|
+
}
|
|
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
|
+
}
|
|
24364
24532
|
}
|
|
24533
|
+
return resolvedTags;
|
|
24534
|
+
}
|
|
24535
|
+
async function add(value) {
|
|
24365
24536
|
const session = useAtlas18.getClient()?.startSession();
|
|
24366
|
-
if (!session)
|
|
24367
|
-
throw new
|
|
24537
|
+
if (!session) {
|
|
24538
|
+
throw new InternalServerError52(
|
|
24539
|
+
"Unable to start session for asset item service."
|
|
24540
|
+
);
|
|
24541
|
+
}
|
|
24368
24542
|
try {
|
|
24369
|
-
session.startTransaction();
|
|
24370
|
-
|
|
24371
|
-
|
|
24372
|
-
|
|
24373
|
-
|
|
24374
|
-
value.
|
|
24375
|
-
|
|
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 ?? ""
|
|
24376
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);
|
|
24377
24571
|
await session.commitTransaction();
|
|
24378
|
-
return
|
|
24572
|
+
return assetItemId;
|
|
24379
24573
|
} catch (error) {
|
|
24380
24574
|
await session.abortTransaction();
|
|
24381
|
-
|
|
24575
|
+
if (error instanceof AppError56)
|
|
24576
|
+
throw error;
|
|
24577
|
+
throw new InternalServerError52("Failed to create asset item.");
|
|
24382
24578
|
} finally {
|
|
24383
|
-
|
|
24579
|
+
session.endSession();
|
|
24384
24580
|
}
|
|
24385
24581
|
}
|
|
24386
|
-
async function
|
|
24387
|
-
|
|
24388
|
-
|
|
24389
|
-
|
|
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.");
|
|
24390
24593
|
}
|
|
24391
|
-
|
|
24392
|
-
|
|
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
|
+
);
|
|
24393
24604
|
}
|
|
24394
|
-
const session = useAtlas18.getClient()?.startSession();
|
|
24395
|
-
if (!session)
|
|
24396
|
-
throw new BadRequestError118("Failed to start session.");
|
|
24397
24605
|
try {
|
|
24398
|
-
|
|
24399
|
-
|
|
24400
|
-
const movementData = {
|
|
24401
|
-
itemId: newItem.itemRefId,
|
|
24402
|
-
type: "conversion",
|
|
24403
|
-
quantity,
|
|
24404
|
-
fromItemId: String(newItem.itemRefId),
|
|
24405
|
-
toItemId: String(newItemId),
|
|
24406
|
-
reference: { type: "conversion", id: String(newItemId) }
|
|
24407
|
-
};
|
|
24408
|
-
const movementId = await movementRepo.add(movementData, session);
|
|
24409
|
-
await assetItemRepo.incrementQuantity(
|
|
24410
|
-
String(newItem.itemRefId),
|
|
24411
|
-
-quantity,
|
|
24412
|
-
session
|
|
24413
|
-
);
|
|
24414
|
-
await assetItemRepo.incrementQuantity(
|
|
24415
|
-
String(newItemId),
|
|
24416
|
-
quantity,
|
|
24417
|
-
session
|
|
24418
|
-
);
|
|
24419
|
-
if (sourceItem.trackingType === "individual" && unitIds && unitIds.length > 0) {
|
|
24420
|
-
for (const unitId of unitIds) {
|
|
24421
|
-
await assetUnitRepo.updateById(
|
|
24422
|
-
unitId,
|
|
24423
|
-
{ itemId: String(newItemId) },
|
|
24424
|
-
session
|
|
24425
|
-
);
|
|
24426
|
-
}
|
|
24427
|
-
}
|
|
24428
|
-
await session.commitTransaction();
|
|
24429
|
-
return { movementId, newItemId };
|
|
24606
|
+
await assetItemRepo.deleteById(id);
|
|
24607
|
+
return "Asset item deleted successfully.";
|
|
24430
24608
|
} catch (error) {
|
|
24431
|
-
|
|
24432
|
-
|
|
24433
|
-
|
|
24434
|
-
await session.endSession();
|
|
24609
|
+
if (error instanceof AppError56)
|
|
24610
|
+
throw error;
|
|
24611
|
+
throw new InternalServerError52("Failed to delete asset item.");
|
|
24435
24612
|
}
|
|
24436
24613
|
}
|
|
24437
24614
|
return {
|
|
24438
|
-
|
|
24439
|
-
|
|
24440
|
-
|
|
24441
|
-
adjustment,
|
|
24442
|
-
conversion
|
|
24615
|
+
add,
|
|
24616
|
+
makePublic,
|
|
24617
|
+
deleteById
|
|
24443
24618
|
};
|
|
24444
24619
|
}
|
|
24445
24620
|
|
|
24446
|
-
// src/resources/asset-item
|
|
24621
|
+
// src/resources/asset-item/asset.item.controller.ts
|
|
24447
24622
|
import Joi104 from "joi";
|
|
24448
|
-
import {
|
|
24449
|
-
|
|
24450
|
-
|
|
24451
|
-
|
|
24623
|
+
import {
|
|
24624
|
+
AppError as AppError57,
|
|
24625
|
+
BadRequestError as BadRequestError119,
|
|
24626
|
+
InternalServerError as InternalServerError53
|
|
24627
|
+
} from "@goweekdays/utils";
|
|
24628
|
+
var paginationSchema3 = Joi104.object({
|
|
24452
24629
|
page: Joi104.number().min(1).optional().allow("", null),
|
|
24453
|
-
limit: 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)
|
|
24454
24642
|
});
|
|
24455
24643
|
var idParamSchema4 = Joi104.object({
|
|
24456
24644
|
id: Joi104.string().hex().length(24).required()
|
|
24457
24645
|
});
|
|
24458
|
-
|
|
24459
|
-
|
|
24460
|
-
|
|
24461
|
-
|
|
24462
|
-
totalCost: Joi104.number().min(0).optional().allow(null),
|
|
24463
|
-
reference: Joi104.object({
|
|
24464
|
-
type: Joi104.string().valid("purchase", "sale", "transfer", "manual", "conversion").required(),
|
|
24465
|
-
id: Joi104.string().optional().allow("", null)
|
|
24466
|
-
}).optional().allow(null),
|
|
24467
|
-
toLocationId: Joi104.string().optional().allow("", null),
|
|
24468
|
-
units: Joi104.array().items(
|
|
24469
|
-
Joi104.object({
|
|
24470
|
-
serialNumber: Joi104.string().optional().allow("", null),
|
|
24471
|
-
plateNumber: Joi104.string().optional().allow("", null)
|
|
24472
|
-
})
|
|
24473
|
-
).optional()
|
|
24474
|
-
});
|
|
24475
|
-
var stockOutSchema = Joi104.object({
|
|
24476
|
-
itemId: Joi104.string().hex().length(24).required(),
|
|
24477
|
-
quantity: Joi104.number().positive().required(),
|
|
24478
|
-
unitCost: Joi104.number().min(0).optional().allow(null),
|
|
24479
|
-
totalCost: Joi104.number().min(0).optional().allow(null),
|
|
24480
|
-
reference: Joi104.object({
|
|
24481
|
-
type: Joi104.string().valid("purchase", "sale", "transfer", "manual", "conversion").required(),
|
|
24482
|
-
id: Joi104.string().optional().allow("", null)
|
|
24483
|
-
}).optional().allow(null),
|
|
24484
|
-
fromLocationId: Joi104.string().optional().allow("", null),
|
|
24485
|
-
unitIds: Joi104.array().items(Joi104.string().hex().length(24)).optional()
|
|
24486
|
-
});
|
|
24487
|
-
var transferSchema = Joi104.object({
|
|
24488
|
-
itemId: Joi104.string().hex().length(24).required(),
|
|
24489
|
-
quantity: Joi104.number().positive().required(),
|
|
24490
|
-
fromLocationId: Joi104.string().required(),
|
|
24491
|
-
toLocationId: Joi104.string().required(),
|
|
24492
|
-
reference: Joi104.object({
|
|
24493
|
-
type: Joi104.string().valid("purchase", "sale", "transfer", "manual", "conversion").required(),
|
|
24494
|
-
id: Joi104.string().optional().allow("", null)
|
|
24495
|
-
}).optional().allow(null),
|
|
24496
|
-
unitIds: Joi104.array().items(Joi104.string().hex().length(24)).optional()
|
|
24497
|
-
});
|
|
24498
|
-
var adjustmentSchema = Joi104.object({
|
|
24499
|
-
itemId: Joi104.string().hex().length(24).required(),
|
|
24500
|
-
quantity: Joi104.number().required(),
|
|
24501
|
-
reason: Joi104.string().required(),
|
|
24502
|
-
reference: Joi104.object({
|
|
24503
|
-
type: Joi104.string().valid("purchase", "sale", "transfer", "manual", "conversion").required(),
|
|
24504
|
-
id: Joi104.string().optional().allow("", null)
|
|
24505
|
-
}).optional().allow(null)
|
|
24506
|
-
});
|
|
24507
|
-
var conversionSchema = Joi104.object({
|
|
24508
|
-
quantity: Joi104.number().positive().required(),
|
|
24509
|
-
newItem: Joi104.object({
|
|
24510
|
-
name: Joi104.string().required(),
|
|
24511
|
-
description: Joi104.string().optional().allow("", null),
|
|
24512
|
-
assetCategory: Joi104.string().valid(...assetItemCategories).required(),
|
|
24513
|
-
assetClass: Joi104.string().valid(...assetItemClasses).required(),
|
|
24514
|
-
trackingType: Joi104.string().valid(...assetItemTrackingTypes).required(),
|
|
24515
|
-
purpose: Joi104.string().valid(...assetItemPurposes).required(),
|
|
24516
|
-
itemRefId: Joi104.string().hex().length(24).required(),
|
|
24517
|
-
remarks: Joi104.string().optional().allow("", null),
|
|
24518
|
-
departmentId: Joi104.string().required(),
|
|
24519
|
-
departmentName: Joi104.string().required(),
|
|
24520
|
-
categoryId: Joi104.string().required(),
|
|
24521
|
-
categoryName: Joi104.string().required(),
|
|
24522
|
-
subcategoryId: Joi104.string().required(),
|
|
24523
|
-
subcategoryName: Joi104.string().required(),
|
|
24524
|
-
categoryPath: Joi104.string().required(),
|
|
24525
|
-
brand: Joi104.string().optional().allow("", null),
|
|
24526
|
-
tags: Joi104.array().items(
|
|
24527
|
-
Joi104.object({
|
|
24528
|
-
id: Joi104.string().required(),
|
|
24529
|
-
name: Joi104.string().required()
|
|
24530
|
-
})
|
|
24531
|
-
).optional().default([])
|
|
24532
|
-
}).required(),
|
|
24533
|
-
unitIds: Joi104.array().items(Joi104.string().hex().length(24)).optional()
|
|
24534
|
-
});
|
|
24535
|
-
function useStockMovementController() {
|
|
24536
|
-
const repo = useStockMovementRepo();
|
|
24537
|
-
const service = useStockMovementService();
|
|
24538
|
-
async function stockIn(req, res, next) {
|
|
24646
|
+
function useAssetItemController() {
|
|
24647
|
+
const repo = useAssetItemRepo();
|
|
24648
|
+
const service = useAssetItemService();
|
|
24649
|
+
async function add(req, res, next) {
|
|
24539
24650
|
try {
|
|
24540
|
-
const { error } =
|
|
24651
|
+
const { error, value } = schemaAssetItemCreate.validate(req.body);
|
|
24541
24652
|
if (error) {
|
|
24542
24653
|
next(new BadRequestError119(error.message));
|
|
24543
24654
|
return;
|
|
24544
24655
|
}
|
|
24545
|
-
const
|
|
24546
|
-
const movementId = await service.stockIn(movement, units);
|
|
24656
|
+
const assetItemId = await service.add(value);
|
|
24547
24657
|
res.status(201).json({
|
|
24548
|
-
message: "
|
|
24549
|
-
|
|
24658
|
+
message: "Asset item created successfully.",
|
|
24659
|
+
assetItemId
|
|
24550
24660
|
});
|
|
24551
24661
|
} catch (error) {
|
|
24552
24662
|
next(error);
|
|
24553
24663
|
}
|
|
24554
24664
|
}
|
|
24555
|
-
async function
|
|
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
|
+
}
|
|
24556
24693
|
try {
|
|
24557
|
-
const
|
|
24558
|
-
|
|
24559
|
-
|
|
24560
|
-
|
|
24561
|
-
|
|
24562
|
-
|
|
24563
|
-
|
|
24564
|
-
|
|
24565
|
-
|
|
24566
|
-
|
|
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
|
|
24567
24708
|
});
|
|
24568
|
-
|
|
24569
|
-
|
|
24709
|
+
res.json(results);
|
|
24710
|
+
} catch (error2) {
|
|
24711
|
+
next(error2);
|
|
24570
24712
|
}
|
|
24571
24713
|
}
|
|
24572
|
-
async function
|
|
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
|
+
}
|
|
24573
24720
|
try {
|
|
24574
|
-
const
|
|
24575
|
-
|
|
24576
|
-
|
|
24577
|
-
|
|
24578
|
-
}
|
|
24579
|
-
const { unitIds, ...movement } = req.body;
|
|
24580
|
-
const movementId = await service.transfer(movement, unitIds);
|
|
24581
|
-
res.status(201).json({
|
|
24582
|
-
message: "Transfer recorded successfully.",
|
|
24583
|
-
movementId
|
|
24584
|
-
});
|
|
24585
|
-
} catch (error) {
|
|
24586
|
-
next(error);
|
|
24721
|
+
const assetItem = await repo.getById(req.params.id);
|
|
24722
|
+
res.json(assetItem);
|
|
24723
|
+
} catch (error2) {
|
|
24724
|
+
next(error2);
|
|
24587
24725
|
}
|
|
24588
24726
|
}
|
|
24589
|
-
async function
|
|
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
|
+
}
|
|
24590
24741
|
try {
|
|
24591
|
-
const
|
|
24592
|
-
|
|
24593
|
-
|
|
24594
|
-
|
|
24595
|
-
}
|
|
24596
|
-
const movementId = await service.adjustment(req.body);
|
|
24597
|
-
res.status(201).json({
|
|
24598
|
-
message: "Adjustment recorded successfully.",
|
|
24599
|
-
movementId
|
|
24742
|
+
const updatedAssetItem = await repo.updateById(req.params.id, req.body);
|
|
24743
|
+
res.json({
|
|
24744
|
+
message: "Asset item updated successfully.",
|
|
24745
|
+
assetItem: updatedAssetItem
|
|
24600
24746
|
});
|
|
24601
24747
|
} catch (error) {
|
|
24602
24748
|
next(error);
|
|
24603
24749
|
}
|
|
24604
24750
|
}
|
|
24605
|
-
async function
|
|
24606
|
-
const { error } =
|
|
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 });
|
|
24607
24757
|
if (error) {
|
|
24608
24758
|
next(new BadRequestError119(error.message));
|
|
24609
24759
|
return;
|
|
24610
24760
|
}
|
|
24611
|
-
const itemId = req.query.itemId;
|
|
24612
|
-
const type = req.query.type ?? "";
|
|
24613
|
-
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
24614
|
-
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
24615
24761
|
try {
|
|
24616
|
-
const
|
|
24617
|
-
res.json(
|
|
24762
|
+
const message = await service.makePublic(value.id, value.isPublic);
|
|
24763
|
+
res.json({
|
|
24764
|
+
message
|
|
24765
|
+
});
|
|
24766
|
+
return;
|
|
24618
24767
|
} catch (error2) {
|
|
24619
|
-
|
|
24768
|
+
if (error2 instanceof AppError57) {
|
|
24769
|
+
next(error2);
|
|
24770
|
+
return;
|
|
24771
|
+
}
|
|
24772
|
+
next(new InternalServerError53("Failed to update asset item visibility."));
|
|
24620
24773
|
}
|
|
24621
24774
|
}
|
|
24622
|
-
async function
|
|
24623
|
-
const { error } =
|
|
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);
|
|
24624
24780
|
if (error) {
|
|
24625
24781
|
next(new BadRequestError119(error.message));
|
|
24626
24782
|
return;
|
|
24627
24783
|
}
|
|
24628
24784
|
try {
|
|
24629
|
-
|
|
24630
|
-
res.json(
|
|
24785
|
+
await service.deleteById(req.params.id);
|
|
24786
|
+
res.json({
|
|
24787
|
+
message: "Asset item deleted successfully."
|
|
24788
|
+
});
|
|
24631
24789
|
} catch (error2) {
|
|
24632
24790
|
next(error2);
|
|
24633
24791
|
}
|
|
24634
24792
|
}
|
|
24635
|
-
async function conversion(req, res, next) {
|
|
24636
|
-
try {
|
|
24637
|
-
const { error } = conversionSchema.validate(req.body);
|
|
24638
|
-
if (error) {
|
|
24639
|
-
next(new BadRequestError119(error.message));
|
|
24640
|
-
return;
|
|
24641
|
-
}
|
|
24642
|
-
const { quantity, newItem, unitIds } = req.body;
|
|
24643
|
-
const result = await service.conversion(newItem, quantity, unitIds);
|
|
24644
|
-
res.status(201).json({
|
|
24645
|
-
message: "Conversion recorded successfully.",
|
|
24646
|
-
movementId: result.movementId,
|
|
24647
|
-
newItemId: result.newItemId
|
|
24648
|
-
});
|
|
24649
|
-
} catch (error) {
|
|
24650
|
-
next(error);
|
|
24651
|
-
}
|
|
24652
|
-
}
|
|
24653
24793
|
return {
|
|
24654
|
-
|
|
24655
|
-
|
|
24656
|
-
|
|
24657
|
-
|
|
24658
|
-
|
|
24659
|
-
|
|
24660
|
-
getById
|
|
24794
|
+
add,
|
|
24795
|
+
getAll,
|
|
24796
|
+
getById,
|
|
24797
|
+
updateById,
|
|
24798
|
+
makePublic,
|
|
24799
|
+
deleteById
|
|
24661
24800
|
};
|
|
24662
24801
|
}
|
|
24663
24802
|
|
|
@@ -24851,7 +24990,7 @@ function useCategoryController() {
|
|
|
24851
24990
|
}
|
|
24852
24991
|
|
|
24853
24992
|
// src/resources/tag/tag.service.ts
|
|
24854
|
-
import { AppError as
|
|
24993
|
+
import { AppError as AppError58, BadRequestError as BadRequestError122 } from "@goweekdays/utils";
|
|
24855
24994
|
import Joi106 from "joi";
|
|
24856
24995
|
function normalizeTagName2(name) {
|
|
24857
24996
|
return name.trim().toLowerCase().replace(/\s+/g, "-");
|
|
@@ -24882,7 +25021,7 @@ function useTagService() {
|
|
|
24882
25021
|
const id = await _add(data);
|
|
24883
25022
|
return id;
|
|
24884
25023
|
} catch (error2) {
|
|
24885
|
-
if (error2 instanceof
|
|
25024
|
+
if (error2 instanceof AppError58)
|
|
24886
25025
|
throw error2;
|
|
24887
25026
|
throw new BadRequestError122("Failed to add tag.");
|
|
24888
25027
|
}
|