@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/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
- quantityOnHand: Joi96.number().min(0).optional()
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: { ...value, updatedAt: /* @__PURE__ */ new Date() } },
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 (error) {
22832
- if (error instanceof AppError51)
22833
- throw error;
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 AppError54,
22890
- InternalServerError as InternalServerError50,
22891
- useAtlas as useAtlas17
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/asset.item.service.ts
23569
- function normalizeTagName(name) {
23570
- return name.trim().toLowerCase().replace(/\s+/g, "-");
23571
- }
23572
- function useAssetItemService() {
23573
- const assetItemRepo = useAssetItemRepo();
23574
- const tagRepo = useTagRepo();
23575
- const categoryRepo = useCategoryRepo();
23576
- async function resolveTags(tagNames, orgId, categoryPath, session) {
23577
- const resolvedTags = [];
23578
- for (const tag of tagNames) {
23579
- const normalizedName = normalizeTagName(tag.name);
23580
- const existing = await tagRepo.findByNormalizedNameAndOrg(
23581
- normalizedName,
23582
- orgId
23583
- );
23584
- if (existing && existing._id) {
23585
- await tagRepo.incrementUsageCount(existing._id, 1, session);
23586
- resolvedTags.push({
23587
- id: String(existing._id),
23588
- name: existing.name
23589
- });
23590
- } else {
23591
- const tagData = {
23592
- name: tag.name.trim(),
23593
- normalizedName,
23594
- type: "private",
23595
- orgId,
23596
- categoryPath,
23597
- status: "active",
23598
- usageCount: 1
23599
- };
23600
- const tagId = await tagRepo.add(tagData, session);
23601
- resolvedTags.push({
23602
- id: String(tagId),
23603
- name: tag.name.trim()
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
- async function add(value) {
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
- await session.startTransaction();
23618
- const department = await categoryRepo.getById(value.departmentId);
23619
- value.departmentName = department.displayName;
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
- add
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/asset.item.controller.ts
23662
- import Joi100 from "joi";
23663
- import { BadRequestError as BadRequestError114 } from "@goweekdays/utils";
23664
- var paginationSchema3 = Joi100.object({
23665
- page: Joi100.number().min(1).optional().allow("", null),
23666
- limit: Joi100.number().min(1).optional().allow("", null),
23667
- search: Joi100.string().optional().allow("", null),
23668
- status: Joi100.string().valid(...assetItemStatuses).optional().allow("", null),
23669
- assetCategory: Joi100.string().valid(...assetItemCategories).optional().allow("", null),
23670
- trackingType: Joi100.string().valid(...assetItemTrackingTypes).optional().allow("", null),
23671
- purpose: Joi100.string().valid(...assetItemPurposes).optional().allow("", null),
23672
- departmentId: Joi100.string().optional().allow("", null),
23673
- categoryId: Joi100.string().optional().allow("", null),
23674
- subcategoryId: Joi100.string().optional().allow("", null),
23675
- brand: Joi100.string().optional().allow("", null),
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
- const { error, value } = schemaAssetItemCreate.validate(req.body);
23687
- if (error) {
23688
- next(new BadRequestError114(error.message));
23689
- return;
23690
- }
23691
- const assetItemId = await service.add(value);
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
- next(error);
23694
+ throw new BadRequestError113("Failed to create stock movement indexes.");
23698
23695
  }
23699
23696
  }
23700
- async function getAll(req, res, next) {
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
- const results = await repo.getAll({
23729
- page,
23730
- limit,
23731
- search,
23732
- status: status2,
23733
- assetCategory,
23734
- trackingType,
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 getById(req, res, next) {
23748
- const { error } = idParamSchema2.validate(req.params);
23749
- if (error) {
23750
- next(new BadRequestError114(error.message));
23751
- return;
23752
- }
23753
- try {
23754
- const assetItem = await repo.getById(req.params.id);
23755
- res.json(assetItem);
23756
- } catch (error2) {
23757
- next(error2);
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 { error: bodyError } = schemaAssetItemUpdate.validate(req.body);
23767
- if (bodyError) {
23768
- next(new BadRequestError114(bodyError.message));
23769
- return;
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 updatedAssetItem = await repo.updateById(req.params.id, req.body);
23773
- res.json({
23774
- message: "Asset item updated successfully.",
23775
- assetItem: updatedAssetItem
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
- next(error);
23765
+ if (error instanceof AppError54)
23766
+ throw error;
23767
+ throw new InternalServerError50("Failed to get stock movement.");
23779
23768
  }
23780
23769
  }
23781
- async function deleteById(req, res, next) {
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.deleteById(req.params.id);
23789
- res.json({
23790
- message: "Asset item deleted successfully."
23772
+ return await repo.collection.countDocuments({
23773
+ itemId: new ObjectId65(itemId)
23791
23774
  });
23792
- } catch (error2) {
23793
- next(error2);
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
- getAll,
23782
+ getByItemId,
23799
23783
  getById,
23800
- updateById,
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 ObjectId64 } from "mongodb";
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 ObjectId64(data.itemId);
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 = useRepo35(namespace_collection);
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 BadRequestError115("Failed to create asset unit indexes.");
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
- logger57.log({ level: "error", message: error.message });
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 ObjectId65(itemId)
23889
+ itemId: new ObjectId67(itemId)
23901
23890
  };
23902
23891
  if (status2) {
23903
23892
  query.status = status2;
23904
23893
  }
23905
- const cacheKey = makeCacheKey36(namespace_collection, {
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 = paginate32(items, page, limit, length);
23912
+ const data = paginate33(items, page, limit, length);
23924
23913
  repo.setCache(cacheKey, data, 600);
23925
23914
  return data;
23926
23915
  } catch (error) {
23927
- logger57.log({ level: "error", message: `${error}` });
23916
+ logger58.log({ level: "error", message: `${error}` });
23928
23917
  throw error;
23929
23918
  }
23930
23919
  }
23931
23920
  async function getById(id) {
23932
- const cacheKey = makeCacheKey36(namespace_collection, {
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 ObjectId65(id)
23930
+ _id: new ObjectId67(id)
23942
23931
  });
23943
23932
  if (!result) {
23944
- throw new BadRequestError115("Asset unit not found.");
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 ObjectId65(id) },
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 BadRequestError115("Asset unit not found.");
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 ObjectId65(itemId),
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-unit/asset.unit.controller.ts
23993
- import Joi102 from "joi";
23994
- import { BadRequestError as BadRequestError116 } from "@goweekdays/utils";
23995
- var querySchema = Joi102.object({
23996
- itemId: Joi102.string().hex().length(24).required(),
23997
- status: Joi102.string().valid(...assetUnitStatuses).optional().allow("", null),
23998
- page: Joi102.number().min(1).optional().allow("", null),
23999
- limit: Joi102.number().min(1).optional().allow("", null)
24000
- });
24001
- var idParamSchema3 = Joi102.object({
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
- const { error } = schemaAssetUnit.validate(req.body);
24009
- if (error) {
24010
- next(new BadRequestError116(error.message));
24011
- return;
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
- const unitId = await repo.add(req.body);
24014
- res.status(201).json({
24015
- message: "Asset unit created successfully.",
24016
- unitId
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
- next(error);
24027
+ await session.abortTransaction();
24028
+ throw error;
24029
+ } finally {
24030
+ await session.endSession();
24020
24031
  }
24021
24032
  }
24022
- async function getByItemId(req, res, next) {
24023
- const { error } = querySchema.validate(req.query);
24024
- if (error) {
24025
- next(new BadRequestError116(error.message));
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 itemId = req.query.itemId;
24029
- const status2 = req.query.status ?? "";
24030
- const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
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
- const results = await repo.getByItemId({ itemId, status: status2, page, limit });
24034
- res.json(results);
24035
- } catch (error2) {
24036
- next(error2);
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 getById(req, res, next) {
24040
- const { error } = idParamSchema3.validate(req.params);
24041
- if (error) {
24042
- next(new BadRequestError116(error.message));
24043
- 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
+ );
24044
24073
  }
24045
- try {
24046
- const unit = await repo.getById(req.params.id);
24047
- res.json(unit);
24048
- } catch (error2) {
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 = "in";
24080
+ value.type = "transfer";
24265
24081
  const movementId = await movementRepo.add(value, session);
24266
- if (item.trackingType === "individual" && units && units.length > 0) {
24267
- for (const unit of units) {
24268
- await assetUnitRepo.add(
24269
- {
24270
- itemId: value.itemId,
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 stockOut(value, unitIds) {
24295
- const item = await assetItemRepo.getById(String(value.itemId));
24296
- if (item.trackingType === "quantity" && (item.quantityOnHand ?? 0) < value.quantity) {
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 = useAtlas18.getClient()?.startSession();
24104
+ const session = useAtlas17.getClient()?.startSession();
24300
24105
  if (!session)
24301
- throw new BadRequestError118("Failed to start session.");
24106
+ throw new BadRequestError115("Failed to start session.");
24302
24107
  try {
24303
24108
  session.startTransaction();
24304
- value.type = "out";
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
- -value.quantity,
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 transfer(value, unitIds) {
24330
- if (!value.fromLocationId || !value.toLocationId) {
24331
- throw new BadRequestError118(
24332
- "Transfer requires both fromLocationId and toLocationId."
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
- const item = await assetItemRepo.getById(String(value.itemId));
24336
- const session = useAtlas18.getClient()?.startSession();
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 BadRequestError118("Failed to start session.");
24135
+ throw new BadRequestError115("Failed to start session.");
24339
24136
  try {
24340
24137
  session.startTransaction();
24341
- value.type = "transfer";
24342
- const movementId = await movementRepo.add(value, session);
24343
- if (item.trackingType === "individual" && unitIds && unitIds.length > 0) {
24344
- for (const unitId of unitIds) {
24345
- await assetUnitRepo.updateById(
24346
- unitId,
24347
- { locationId: value.toLocationId },
24348
- session
24349
- );
24350
- }
24351
- }
24352
- await session.commitTransaction();
24353
- return movementId;
24354
- } catch (error) {
24355
- await session.abortTransaction();
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
- async function adjustment(value) {
24362
- if (!value.reason) {
24363
- throw new BadRequestError118("Adjustment requires a reason.");
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 BadRequestError118("Failed to start session.");
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
- value.type = "adjustment";
24371
- const movementId = await movementRepo.add(value, session);
24372
- await assetItemRepo.incrementQuantity(
24373
- String(value.itemId),
24374
- value.quantity,
24375
- session
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 movementId;
24572
+ return assetItemId;
24379
24573
  } catch (error) {
24380
24574
  await session.abortTransaction();
24381
- throw error;
24575
+ if (error instanceof AppError56)
24576
+ throw error;
24577
+ throw new InternalServerError52("Failed to create asset item.");
24382
24578
  } finally {
24383
- await session.endSession();
24579
+ session.endSession();
24384
24580
  }
24385
24581
  }
24386
- async function conversion(newItem, quantity, unitIds) {
24387
- const sourceItem = await assetItemRepo.getById(String(newItem.itemRefId));
24388
- if (!newItem.itemRefId) {
24389
- throw new BadRequestError118("Conversion requires itemRefId.");
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
- if (sourceItem.trackingType === "quantity" && (sourceItem.quantityOnHand ?? 0) < quantity) {
24392
- throw new BadRequestError118("Insufficient stock on source item.");
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
- session.startTransaction();
24399
- const newItemId = await assetItemRepo.add(newItem, session);
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
- await session.abortTransaction();
24432
- throw error;
24433
- } finally {
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
- stockIn,
24439
- stockOut,
24440
- transfer,
24441
- adjustment,
24442
- conversion
24615
+ add,
24616
+ makePublic,
24617
+ deleteById
24443
24618
  };
24444
24619
  }
24445
24620
 
24446
- // src/resources/asset-item-movement/stock.movement.controller.ts
24621
+ // src/resources/asset-item/asset.item.controller.ts
24447
24622
  import Joi104 from "joi";
24448
- import { BadRequestError as BadRequestError119 } from "@goweekdays/utils";
24449
- var querySchema2 = Joi104.object({
24450
- itemId: Joi104.string().hex().length(24).required(),
24451
- type: Joi104.string().valid(...stockMovementTypes).optional().allow("", null),
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
- var stockInSchema = Joi104.object({
24459
- itemId: Joi104.string().hex().length(24).required(),
24460
- quantity: Joi104.number().positive().required(),
24461
- unitCost: Joi104.number().min(0).optional().allow(null),
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 } = stockInSchema.validate(req.body);
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 { units, ...movement } = req.body;
24546
- const movementId = await service.stockIn(movement, units);
24656
+ const assetItemId = await service.add(value);
24547
24657
  res.status(201).json({
24548
- message: "Stock in recorded successfully.",
24549
- movementId
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 stockOut(req, res, next) {
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 { error } = stockOutSchema.validate(req.body);
24558
- if (error) {
24559
- next(new BadRequestError119(error.message));
24560
- return;
24561
- }
24562
- const { unitIds, ...movement } = req.body;
24563
- const movementId = await service.stockOut(movement, unitIds);
24564
- res.status(201).json({
24565
- message: "Stock out recorded successfully.",
24566
- movementId
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
- } catch (error) {
24569
- next(error);
24709
+ res.json(results);
24710
+ } catch (error2) {
24711
+ next(error2);
24570
24712
  }
24571
24713
  }
24572
- async function transfer(req, res, next) {
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 { error } = transferSchema.validate(req.body);
24575
- if (error) {
24576
- next(new BadRequestError119(error.message));
24577
- return;
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 adjustment(req, res, next) {
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 { error } = adjustmentSchema.validate(req.body);
24592
- if (error) {
24593
- next(new BadRequestError119(error.message));
24594
- return;
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 getByItemId(req, res, next) {
24606
- const { error } = querySchema2.validate(req.query);
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 results = await repo.getByItemId({ itemId, type, page, limit });
24617
- res.json(results);
24762
+ const message = await service.makePublic(value.id, value.isPublic);
24763
+ res.json({
24764
+ message
24765
+ });
24766
+ return;
24618
24767
  } catch (error2) {
24619
- next(error2);
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 getById(req, res, next) {
24623
- const { error } = idParamSchema4.validate(req.params);
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
- const movement = await repo.getById(req.params.id);
24630
- res.json(movement);
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
- stockIn,
24655
- stockOut,
24656
- transfer,
24657
- adjustment,
24658
- conversion,
24659
- getByItemId,
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 AppError57, BadRequestError as BadRequestError122 } from "@goweekdays/utils";
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 AppError57)
25024
+ if (error2 instanceof AppError58)
24886
25025
  throw error2;
24887
25026
  throw new BadRequestError122("Failed to add tag.");
24888
25027
  }