rez_core 6.5.83 → 6.5.84

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rez_core",
3
- "version": "6.5.83",
3
+ "version": "6.5.84",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "private": false,
@@ -1245,18 +1245,19 @@ export class FilterService {
1245
1245
  }
1246
1246
 
1247
1247
  if (op === 'equal') {
1248
- return {
1249
- query: `e.${attr}::text IN (:...${key})`,
1250
- params: { [key]: arr },
1251
- };
1252
- }
1248
+ return {
1249
+ query: `e.${attr}::text = ANY(:${key})`,
1250
+ params: { [key]: arr },
1251
+ };
1252
+ }
1253
+
1254
+ if (op === 'not_equal') {
1255
+ return {
1256
+ query: `NOT (e.${attr}::text = ANY(:${key}))`,
1257
+ params: { [key]: arr },
1258
+ };
1259
+ }
1253
1260
 
1254
- if (op === 'not_equal') {
1255
- return {
1256
- query: `e.${attr}::text NOT IN (:...${key})`,
1257
- params: { [key]: arr },
1258
- };
1259
- }
1260
1261
 
1261
1262
  if (op === 'contains') {
1262
1263
  return {
@@ -60,84 +60,177 @@ export class ListMasterItemService extends EntityServiceImpl {
60
60
  loggedInUser,
61
61
  ): Promise<any> {
62
62
  const entId = loggedInUser.enterprise_id;
63
-
64
63
  const errors: any[] = [];
65
64
 
66
- for (let i = 0; i < items.length; i++) {
67
- const item = items[i];
68
- const name = item.name?.trim();
69
- const code = item.code?.trim();
70
- const itemId = item.id;
65
+ // Step 1: Validate and prepare items
66
+ const processedItems = items.map((item, index) => ({
67
+ ...item,
68
+ index,
69
+ name: item.name?.trim(),
70
+ code: item.code?.trim(),
71
+ itemId: item.id,
72
+ }));
71
73
 
72
- try {
73
- if (!name) {
74
- throw new BadRequestException(`name is missing at index ${i}`);
75
- }
74
+ // Step 2: Fetch all existing items for this list type in one query
75
+ const existingItems = await this.listItemsRepo.findAllItemsByListType(
76
+ listType,
77
+ 'asc',
78
+ entId,
79
+ );
76
80
 
77
- // Check if this is an update (item has an id)
78
- let existingItem;
79
- if (itemId) {
80
- existingItem = await this.listItemsRepo.findOneByCondition({
81
- id: itemId,
82
- listtype: listType,
83
- enterprise_id: entId,
84
- });
81
+ // Create lookup maps for efficient validation
82
+ const existingItemsById = new Map(existingItems.map(item => [item.id, item]));
83
+ const existingItemsByName = new Map(existingItems.map(item => [item.name?.toLowerCase(), item]));
84
+ const existingItemsByCode = new Map(
85
+ existingItems.filter(item => item.code).map(item => [item.code?.toLowerCase(), item])
86
+ );
85
87
 
86
- if (!existingItem) {
87
- throw new BadRequestException(
88
- `list item with id ${itemId} not found`,
89
- );
90
- }
91
- }
88
+ // Step 3: Validate all items and collect errors
89
+ const validItems: any[] = [];
92
90
 
93
- // Check for duplicate name (excluding current item if updating)
94
- const nameExists = await this.listItemsRepo.findOneByCondition({
95
- name,
96
- listtype: listType,
97
- enterprise_id: entId,
91
+ for (const processedItem of processedItems) {
92
+ const { index, name, code, itemId } = processedItem;
93
+ const itemErrors: any[] = [];
94
+
95
+ // Validate required fields - both name and code are required
96
+ if (!name) {
97
+ itemErrors.push({
98
+ field: 'name',
99
+ message: 'Name is required',
98
100
  });
101
+ }
99
102
 
100
- if (nameExists && (!itemId || nameExists.id !== itemId)) {
101
- throw new BadRequestException(
102
- `list item with name ${name} already exists`,
103
- );
103
+ if (!code) {
104
+ itemErrors.push({
105
+ field: 'code',
106
+ message: 'Code is required',
107
+ });
108
+ }
109
+
110
+ // Determine if this is an update by checking for existing item
111
+ let existingItem: any = null;
112
+ let effectiveItemId = itemId; // Track the ID we'll use for this item
113
+
114
+ // First, check if ID is provided and exists
115
+ if (itemId) {
116
+ existingItem = existingItemsById.get(itemId) || null;
117
+ if (!existingItem) {
118
+ itemErrors.push({
119
+ field: 'id',
120
+ message: `List item with id ${itemId} not found`,
121
+ });
122
+ } else if (existingItem.listtype !== listType) {
123
+ itemErrors.push({
124
+ field: 'id',
125
+ message: `List item with id ${itemId} does not belong to list type ${listType}`,
126
+ });
127
+ }
128
+ } else {
129
+ // If no ID provided, check if item exists by name or code (for upsert)
130
+ const existingByName = name ? existingItemsByName.get(name.toLowerCase()) : null;
131
+ const existingByCode = code ? existingItemsByCode.get(code.toLowerCase()) : null;
132
+
133
+ // If both name and code match the same item, it's an update - use that item's ID
134
+ if (existingByName && existingByCode && existingByName.id === existingByCode.id) {
135
+ existingItem = existingByName;
136
+ effectiveItemId = existingItem.id; // Automatically use the existing item's ID
104
137
  }
138
+ // If name matches one item and code matches a different item, it's a conflict
139
+ else if (existingByName && existingByCode && existingByName.id !== existingByCode.id) {
140
+ itemErrors.push({
141
+ field: 'name',
142
+ message: `Name "${name}" belongs to item with code "${existingByName.code}"`,
143
+ });
144
+ itemErrors.push({
145
+ field: 'code',
146
+ message: `Code "${code}" belongs to item with name "${existingByCode.name}"`,
147
+ });
148
+ }
149
+ // If only name matches, it's a conflict (trying to create with existing name but different code)
150
+ else if (existingByName) {
151
+ itemErrors.push({
152
+ field: 'name',
153
+ message: `List item with name "${name}" already exists with code "${existingByName.code}"`,
154
+ });
155
+ }
156
+ // If only code matches, it's a conflict (trying to create with existing code but different name)
157
+ else if (existingByCode) {
158
+ itemErrors.push({
159
+ field: 'code',
160
+ message: `List item with code "${code}" already exists with name "${existingByCode.name}"`,
161
+ });
162
+ }
163
+ }
105
164
 
106
- // Check for duplicate code (excluding current item if updating)
107
- if (code) {
108
- const codeExists = await this.listItemsRepo.findOneByCondition({
109
- code,
110
- listtype: listType,
111
- enterprise_id: entId,
165
+ // Check for duplicates within the current batch
166
+ if (name) {
167
+ const duplicateInBatch = processedItems.find(
168
+ (other, otherIndex) =>
169
+ otherIndex < index &&
170
+ other.name?.toLowerCase() === name.toLowerCase() &&
171
+ (!effectiveItemId || other.itemId !== effectiveItemId)
172
+ );
173
+ if (duplicateInBatch) {
174
+ itemErrors.push({
175
+ field: 'name',
176
+ message: `Duplicate name "${name}" found in batch at row ${duplicateInBatch.index}`,
112
177
  });
178
+ }
179
+ }
113
180
 
114
- if (codeExists && (!itemId || codeExists.id !== itemId)) {
115
- throw new BadRequestException(
116
- `list item with code ${code} already exists`,
117
- );
118
- }
181
+ if (code) {
182
+ const duplicateCodeInBatch = processedItems.find(
183
+ (other, otherIndex) =>
184
+ otherIndex < index &&
185
+ other.code?.toLowerCase() === code.toLowerCase() &&
186
+ (!effectiveItemId || other.itemId !== effectiveItemId)
187
+ );
188
+ if (duplicateCodeInBatch) {
189
+ itemErrors.push({
190
+ field: 'code',
191
+ message: `Duplicate code "${code}" found in batch at row ${duplicateCodeInBatch.index}`,
192
+ });
119
193
  }
194
+ }
195
+
196
+ // If there are validation errors, add to errors array
197
+ if (itemErrors.length > 0) {
198
+ errors.push({
199
+ row: index,
200
+ errors: itemErrors,
201
+ });
202
+ } else {
203
+ validItems.push({
204
+ ...processedItem,
205
+ existingItem,
206
+ });
207
+ }
208
+ }
120
209
 
121
- // Update or Create
210
+ // Step 4: Process valid items (create or update)
211
+ for (const validItem of validItems) {
212
+ const { index, name, code, itemId, existingItem, ...restOfItem } = validItem;
213
+
214
+ try {
122
215
  if (existingItem) {
123
- // Update path
216
+ // Update existing item
124
217
  await this.updateEntity(
125
218
  {
126
- ...item,
127
- id: itemId,
219
+ ...restOfItem,
220
+ id: existingItem.id, // Use the existing item's ID
128
221
  name,
129
- code: code || existingItem.code,
222
+ code,
130
223
  listtype: listType,
131
224
  },
132
225
  loggedInUser,
133
226
  );
134
227
  } else {
135
- // Creation path
228
+ // Create new item
136
229
  await this.createEntity(
137
230
  {
138
- ...item,
231
+ ...restOfItem,
139
232
  name,
140
- code: code,
233
+ code,
141
234
  listtype: listType,
142
235
  value: '',
143
236
  },
@@ -145,19 +238,20 @@ export class ListMasterItemService extends EntityServiceImpl {
145
238
  );
146
239
  }
147
240
  } catch (error) {
241
+ // Catch any runtime errors during create/update
148
242
  errors.push({
149
- row: i,
243
+ row: index,
150
244
  errors: [
151
245
  {
152
- field: 'name',
153
- message:
154
- error.message || 'An error occurred while processing the item',
246
+ field: 'general',
247
+ message: error.message || 'An error occurred while processing the item',
155
248
  },
156
249
  ],
157
250
  });
158
251
  }
159
252
  }
160
253
 
254
+ // Step 5: Fetch updated items and return response
161
255
  const updatedItems = await this.listItemsRepo.findAllItemsByListType(
162
256
  listType,
163
257
  'asc',