@zauru-sdk/utils 6.27.0 → 6.29.0
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/esm/zauru/cloudwatch-logger.server.js +59 -3
- package/dist/esm/zauru/cloudwatch.server.js +3 -1
- package/dist/esm/zauru/purchase-orders.utils.js +71 -23
- package/dist/esm/zauru/remix-entry.server.js +2 -0
- package/dist/esm/zauru/remix-handle-request.server.js +17 -0
- package/dist/zauru/cloudwatch-logger.server.d.ts +5 -0
- package/dist/zauru/cloudwatch.server.d.ts +1 -7
- package/dist/zauru/purchase-orders.utils.d.ts +12 -9
- package/dist/zauru/remix-entry.server.d.ts +0 -7
- package/package.json +5 -5
|
@@ -51,12 +51,65 @@ let clientKey = null;
|
|
|
51
51
|
const sequenceTokens = {};
|
|
52
52
|
/** Serializes PutLogEvents so sequence tokens are not overwritten. */
|
|
53
53
|
let putChain = Promise.resolve();
|
|
54
|
+
let processHandlersInstalled = false;
|
|
55
|
+
let loggedConfigOnce = false;
|
|
54
56
|
function envTrim(name) {
|
|
55
57
|
const v = process.env[name];
|
|
56
58
|
if (!v || v.trim() === "")
|
|
57
59
|
return undefined;
|
|
58
60
|
return v.trim();
|
|
59
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Netlify streaming functions freeze the isolate when the response ends.
|
|
64
|
+
* `waitUntil` keeps the Lambda awake until PutLogEvents finishes.
|
|
65
|
+
*/
|
|
66
|
+
function keepIsolateAlive(work) {
|
|
67
|
+
const g = globalThis;
|
|
68
|
+
try {
|
|
69
|
+
g.waitUntil?.(work);
|
|
70
|
+
g.Netlify?.waitUntil?.(work);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Hosts without waitUntil still await in the request path.
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function logConfigOnce() {
|
|
77
|
+
if (loggedConfigOnce)
|
|
78
|
+
return;
|
|
79
|
+
loggedConfigOnce = true;
|
|
80
|
+
const cfg = resolveCloudWatchConfig();
|
|
81
|
+
if (cfg.enabled) {
|
|
82
|
+
console.log(`[observability] CloudWatch enabled group=${cfg.logGroup} env=${cfg.appEnv} creds=${cfg.prefixLabel} region=${cfg.region}`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
console.log(`[observability] CloudWatch disabled: ${cfg.skipReason}`);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Catch throws that happen after Remix already returned 200 (fire-and-forget
|
|
89
|
+
* async work). Remix `handleError` does not see those.
|
|
90
|
+
*/
|
|
91
|
+
export function installProcessErrorHandlers() {
|
|
92
|
+
if (processHandlersInstalled)
|
|
93
|
+
return;
|
|
94
|
+
if (typeof process === "undefined" || typeof process.on !== "function") {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
processHandlersInstalled = true;
|
|
98
|
+
const report = (source, reason) => {
|
|
99
|
+
const err = reason instanceof Error ? reason : new Error(String(reason));
|
|
100
|
+
void logError("server", {
|
|
101
|
+
message: err.message,
|
|
102
|
+
stack: err.stack,
|
|
103
|
+
source,
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
process.on("unhandledRejection", (reason) => {
|
|
107
|
+
report("unhandledRejection", reason);
|
|
108
|
+
});
|
|
109
|
+
process.on("uncaughtException", (err) => {
|
|
110
|
+
report("uncaughtException", err);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
60
113
|
function resolveAwsCreds() {
|
|
61
114
|
for (const c of AWS_CANDIDATES) {
|
|
62
115
|
const region = envTrim(c.region);
|
|
@@ -201,9 +254,9 @@ async function fetchSequenceToken(cw, logGroup, stream) {
|
|
|
201
254
|
const res = await cw.send(new DescribeLogStreamsCommand({
|
|
202
255
|
logGroupName: logGroup,
|
|
203
256
|
logStreamNamePrefix: stream,
|
|
204
|
-
limit: 1,
|
|
205
257
|
}));
|
|
206
|
-
|
|
258
|
+
const match = res.logStreams?.find((s) => s.logStreamName === stream);
|
|
259
|
+
return match?.uploadSequenceToken;
|
|
207
260
|
}
|
|
208
261
|
catch {
|
|
209
262
|
return undefined;
|
|
@@ -350,6 +403,7 @@ async function emitToCloudWatch(origin, lines) {
|
|
|
350
403
|
* For client/server also prints with `console.error` (Netlify fallback).
|
|
351
404
|
*/
|
|
352
405
|
export async function logEvent(origin, payload) {
|
|
406
|
+
logConfigOnce();
|
|
353
407
|
const line = buildLogLine(origin, {
|
|
354
408
|
...payload,
|
|
355
409
|
appVersion: payload.appVersion ?? getDefaultAppVersion(),
|
|
@@ -360,7 +414,9 @@ export async function logEvent(origin, payload) {
|
|
|
360
414
|
else {
|
|
361
415
|
console.log(`[observability] ${line}`);
|
|
362
416
|
}
|
|
363
|
-
|
|
417
|
+
const pending = emitToCloudWatch(origin, [line]);
|
|
418
|
+
keepIsolateAlive(pending);
|
|
419
|
+
return pending;
|
|
364
420
|
}
|
|
365
421
|
/**
|
|
366
422
|
* Sends an error event to CloudWatch (and console.error as fallback).
|
|
@@ -4,5 +4,7 @@
|
|
|
4
4
|
* Import from `@zauru-sdk/utils/cloudwatch.server` (not the main barrel) so
|
|
5
5
|
* the AWS SDK stays out of the client bundle.
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
import { installProcessErrorHandlers } from "./cloudwatch-logger.server.js";
|
|
8
|
+
installProcessErrorHandlers();
|
|
9
|
+
export { installProcessErrorHandlers, isCloudWatchEnabled, logError, logEvent, resolveCloudWatchConfig, withBackgroundLogs, } from "./cloudwatch-logger.server.js";
|
|
8
10
|
export { createClientErrorsAction, handleError, } from "./cloudwatch-remix.server.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CURRENCY_PREFIX, calculatePurchaseOrderFinancials, getNewlyAppliedRejectionRuleSteps, getNewDateByFormat, getStringFullDate, getZauruDateByText, handlePossibleAxiosErrors, mergeJsonMemo, parseJsonMemo, } from "@zauru-sdk/common";
|
|
1
|
+
import { CURRENCY_PREFIX, buildPurchaseOrderDetailsCostAttributes, calculatePurchaseOrderFinancials, getNewlyAppliedRejectionRuleSteps, getNewDateByFormat, getStringFullDate, getZauruDateByText, handlePossibleAxiosErrors, mergeJsonMemo, parseJsonMemo, } from "@zauru-sdk/common";
|
|
2
2
|
import { commitSession, createNewPurchaseOrder, deleteDelivery, deletePurchaseOrder, deleteReception, getLotesWithPurchaseFormated, getPurchase, getPurchaseOrder, getPurchasesListDataTables, getVariablesByName, inactivarLote, updateReceivedPurchaseOrder, } from "@zauru-sdk/services";
|
|
3
3
|
import { getActiveRejectionPercentageAdjustmentRules } from "./rejectionPercentageAdjustmentRules.utils.js";
|
|
4
4
|
/**
|
|
@@ -152,19 +152,70 @@ export const getPurchaseOrderDataTables = async (headers, search, tag_id, agency
|
|
|
152
152
|
const purchaseOrderDataTable = getPurchaseOrderFormated(ordersDataTables?.data[0]);
|
|
153
153
|
return purchaseOrderDataTable;
|
|
154
154
|
};
|
|
155
|
+
const unitCostOverridesFromAttributes = (attributes) => {
|
|
156
|
+
const unitCostByDetailId = new Map();
|
|
157
|
+
for (const detail of Object.values(attributes ?? {})) {
|
|
158
|
+
const id = Number(detail.id);
|
|
159
|
+
const unitCost = Number(detail.unit_cost);
|
|
160
|
+
if (Number.isFinite(id) && Number.isFinite(unitCost)) {
|
|
161
|
+
unitCostByDetailId.set(id, unitCost);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return unitCostByDetailId;
|
|
165
|
+
};
|
|
155
166
|
/**
|
|
156
167
|
* updatePurchaseItemPrice
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
* @param purchase_id
|
|
168
|
+
* Writes unit_cost and the matching line `cost` (qty × unit_cost) so Baculo
|
|
169
|
+
* refreshes subtotal. Recalculates monetary discount when it is not passed.
|
|
160
170
|
*/
|
|
161
|
-
export const updatePurchaseItemPrice = async (headers, data, purchase_id) => {
|
|
171
|
+
export const updatePurchaseItemPrice = async (headers, data, purchase_id, session) => {
|
|
162
172
|
return handlePossibleAxiosErrors(async () => {
|
|
173
|
+
const restPurchaseResponse = await getPurchase(headers, purchase_id);
|
|
174
|
+
if (restPurchaseResponse.error || !restPurchaseResponse.data) {
|
|
175
|
+
throw new Error(restPurchaseResponse.userMsg ||
|
|
176
|
+
"No se pudo obtener la orden de compra para actualizar el precio");
|
|
177
|
+
}
|
|
178
|
+
const loadedPurchase = restPurchaseResponse.data;
|
|
179
|
+
const details = extractPurchaseOrderFinancialDetails(loadedPurchase);
|
|
180
|
+
if (details.length === 0) {
|
|
181
|
+
throw new Error("No se pudieron obtener los detalles de la orden de compra para actualizar el costo");
|
|
182
|
+
}
|
|
183
|
+
const unitCostByDetailId = unitCostOverridesFromAttributes(data.purchase_order_details_attributes);
|
|
184
|
+
const mergedDetails = details.map((detail) => {
|
|
185
|
+
const id = Number(detail.id);
|
|
186
|
+
return {
|
|
187
|
+
...detail,
|
|
188
|
+
unit_cost: unitCostByDetailId.get(id) ?? detail.unit_cost,
|
|
189
|
+
};
|
|
190
|
+
});
|
|
191
|
+
const detailsAttributes = buildPurchaseOrderDetailsCostAttributes(mergedDetails, unitCostByDetailId);
|
|
192
|
+
if (Object.keys(detailsAttributes).length === 0) {
|
|
193
|
+
throw new Error("No se pudieron armar los costos de línea de la orden de compra");
|
|
194
|
+
}
|
|
195
|
+
let discount = data.discount;
|
|
196
|
+
if (discount === undefined) {
|
|
197
|
+
const rules = await getActiveRejectionPercentageAdjustmentRules(headers, session);
|
|
198
|
+
const purchaseRecord = loadedPurchase;
|
|
199
|
+
discount = calculatePurchaseOrderFinancials({
|
|
200
|
+
details: mergedDetails,
|
|
201
|
+
memo: data.memo ?? loadedPurchase.memo,
|
|
202
|
+
rules,
|
|
203
|
+
ctx: {
|
|
204
|
+
itemIds: extractPurchaseOrderItemIds(loadedPurchase),
|
|
205
|
+
tipo: purchaseRecord.reference,
|
|
206
|
+
providerCategoryId: purchaseRecord.payee?.payee_category_id ??
|
|
207
|
+
purchaseRecord.payee_category_id,
|
|
208
|
+
},
|
|
209
|
+
}).discount;
|
|
210
|
+
}
|
|
163
211
|
const body = {
|
|
164
212
|
purchase_order: {
|
|
165
|
-
purchase_order_details_attributes:
|
|
213
|
+
purchase_order_details_attributes: detailsAttributes,
|
|
166
214
|
...(data.memo !== undefined ? { memo: data.memo } : {}),
|
|
167
|
-
|
|
215
|
+
discount,
|
|
216
|
+
...(data.other_charges !== undefined && data.other_charges !== null
|
|
217
|
+
? { other_charges: Number(data.other_charges) }
|
|
218
|
+
: {}),
|
|
168
219
|
},
|
|
169
220
|
};
|
|
170
221
|
const responseUpdate = await updateReceivedPurchaseOrder(headers, body, purchase_id);
|
|
@@ -289,22 +340,19 @@ export const updateOchAndDis = async (headers, data, purchase_id, session) => {
|
|
|
289
340
|
},
|
|
290
341
|
});
|
|
291
342
|
const newlyAppliedSteps = getNewlyAppliedRejectionRuleSteps(previousCalculations, financials.rejectionCalculations);
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
const responseUpdate = await updateReceivedPurchaseOrder(headers, body, purchase_id);
|
|
306
|
-
if (responseUpdate.error || responseUpdate.data === null) {
|
|
307
|
-
throw new Error(responseUpdate.userMsg);
|
|
343
|
+
const persistResponse = await updatePurchaseItemPrice(headers, {
|
|
344
|
+
memo: mergeJsonMemo(memoForOrigin, {
|
|
345
|
+
rejectionPercentage: financials.rejectionPercentage,
|
|
346
|
+
rejectionCalculations: financials.rejectionCalculations,
|
|
347
|
+
rejectionLayers: financials.rejectionLayers,
|
|
348
|
+
}),
|
|
349
|
+
discount: financials.discount,
|
|
350
|
+
...(data.other_charges !== undefined && data.other_charges !== null
|
|
351
|
+
? { other_charges: Number(data.other_charges) }
|
|
352
|
+
: {}),
|
|
353
|
+
}, purchase_id, session);
|
|
354
|
+
if (persistResponse.error) {
|
|
355
|
+
throw new Error(persistResponse.userMsg);
|
|
308
356
|
}
|
|
309
357
|
return {
|
|
310
358
|
rejectionPercentage: financials.rejectionPercentage,
|
|
@@ -5,4 +5,6 @@
|
|
|
5
5
|
* export { handleRequest as default, handleError } from "@zauru-sdk/utils/remix-entry.server";
|
|
6
6
|
* ```
|
|
7
7
|
*/
|
|
8
|
+
import { installProcessErrorHandlers } from "./cloudwatch-logger.server.js";
|
|
9
|
+
installProcessErrorHandlers();
|
|
8
10
|
export { handleRequest, handleError } from "./remix-handle-request.server.js";
|
|
@@ -3,6 +3,7 @@ import { PassThrough } from "node:stream";
|
|
|
3
3
|
import { createReadableStreamFromReadable } from "@remix-run/node";
|
|
4
4
|
import { RemixServer } from "@remix-run/react";
|
|
5
5
|
import { renderToPipeableStream } from "react-dom/server";
|
|
6
|
+
import { logError } from "./cloudwatch-logger.server.js";
|
|
6
7
|
import { handleError } from "./cloudwatch-remix.server.js";
|
|
7
8
|
const ABORT_DELAY = 5_000;
|
|
8
9
|
const RemixServerComponent = RemixServer;
|
|
@@ -53,6 +54,14 @@ function handleBotRequest(request, responseStatusCode, responseHeaders, remixCon
|
|
|
53
54
|
responseStatusCode = 500;
|
|
54
55
|
if (shellRendered) {
|
|
55
56
|
console.error(error);
|
|
57
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
58
|
+
void logError("server", {
|
|
59
|
+
message: err.message,
|
|
60
|
+
stack: err.stack,
|
|
61
|
+
url: request.url,
|
|
62
|
+
method: request.method,
|
|
63
|
+
source: "ssr",
|
|
64
|
+
});
|
|
56
65
|
}
|
|
57
66
|
},
|
|
58
67
|
});
|
|
@@ -85,6 +94,14 @@ function handleBrowserRequest(request, responseStatusCode, responseHeaders, remi
|
|
|
85
94
|
responseStatusCode = 500;
|
|
86
95
|
if (shellRendered) {
|
|
87
96
|
console.error(error);
|
|
97
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
98
|
+
void logError("server", {
|
|
99
|
+
message: err.message,
|
|
100
|
+
stack: err.stack,
|
|
101
|
+
url: request.url,
|
|
102
|
+
method: request.method,
|
|
103
|
+
source: "ssr",
|
|
104
|
+
});
|
|
88
105
|
}
|
|
89
106
|
},
|
|
90
107
|
});
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
export type LogOrigin = "client" | "server" | "background";
|
|
2
2
|
export type LogLevel = "error" | "info" | "warn";
|
|
3
|
+
/**
|
|
4
|
+
* Catch throws that happen after Remix already returned 200 (fire-and-forget
|
|
5
|
+
* async work). Remix `handleError` does not see those.
|
|
6
|
+
*/
|
|
7
|
+
export declare function installProcessErrorHandlers(): void;
|
|
3
8
|
export type CloudWatchConfigInfo = {
|
|
4
9
|
enabled: boolean;
|
|
5
10
|
logGroup?: string;
|
|
@@ -1,8 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
* Server-only CloudWatch observability for Remix webapps.
|
|
3
|
-
*
|
|
4
|
-
* Import from `@zauru-sdk/utils/cloudwatch.server` (not the main barrel) so
|
|
5
|
-
* the AWS SDK stays out of the client bundle.
|
|
6
|
-
*/
|
|
7
|
-
export { isCloudWatchEnabled, logError, logEvent, resolveCloudWatchConfig, withBackgroundLogs, type CloudWatchConfigInfo, type LogErrorResult, type LogLevel, type LogOrigin, type LogPayload, } from "./cloudwatch-logger.server.js";
|
|
1
|
+
export { installProcessErrorHandlers, isCloudWatchEnabled, logError, logEvent, resolveCloudWatchConfig, withBackgroundLogs, type CloudWatchConfigInfo, type LogErrorResult, type LogLevel, type LogOrigin, type LogPayload, } from "./cloudwatch-logger.server.js";
|
|
8
2
|
export { createClientErrorsAction, handleError, } from "./cloudwatch-remix.server.js";
|
|
@@ -21,21 +21,23 @@ export declare const getPurchasesDataTableListFormated: (headers: any, session:
|
|
|
21
21
|
* @returns
|
|
22
22
|
*/
|
|
23
23
|
export declare const getPurchaseOrderDataTables: (headers: any, search: string, tag_id?: string, agency_id?: string) => Promise<PurchasesDataTableListFormatedSchema>;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
* @param headers
|
|
27
|
-
* @param data
|
|
28
|
-
* @param purchase_id
|
|
29
|
-
*/
|
|
30
|
-
export declare const updatePurchaseItemPrice: (headers: any, data: {
|
|
31
|
-
purchase_order_details_attributes: Record<string, {
|
|
24
|
+
type PurchaseOrderPriceUpdateInput = {
|
|
25
|
+
purchase_order_details_attributes?: Record<string, {
|
|
32
26
|
unit_cost: number;
|
|
33
27
|
item_id: number;
|
|
34
28
|
id: number;
|
|
29
|
+
cost?: number;
|
|
35
30
|
}>;
|
|
36
31
|
memo?: string;
|
|
37
32
|
discount?: number;
|
|
38
|
-
|
|
33
|
+
other_charges?: number | string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* updatePurchaseItemPrice
|
|
37
|
+
* Writes unit_cost and the matching line `cost` (qty × unit_cost) so Baculo
|
|
38
|
+
* refreshes subtotal. Recalculates monetary discount when it is not passed.
|
|
39
|
+
*/
|
|
40
|
+
export declare const updatePurchaseItemPrice: (headers: any, data: PurchaseOrderPriceUpdateInput, purchase_id: number, session?: Session) => Promise<AxiosUtilsResponse<boolean>>;
|
|
39
41
|
/**
|
|
40
42
|
* updateOchAndDis
|
|
41
43
|
* Updates rejectionPercentage in purchase_order.memo and the monetary
|
|
@@ -119,3 +121,4 @@ export declare const deleteTask: (session: Session, timeStamp: number) => Promis
|
|
|
119
121
|
export declare const createNewLabItemRequest: (headers: any, session: Session, body: Partial<PurchaseOrderGraphQL> & {
|
|
120
122
|
[key: string]: any;
|
|
121
123
|
}) => Promise<AxiosUtilsResponse<PurchaseOrderGraphQL>>;
|
|
124
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zauru-sdk/utils",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.29.0",
|
|
4
4
|
"description": "Utilidades, parseo de request y demás para desarrollo de webapps de Zauru",
|
|
5
5
|
"main": "./dist/esm/index.js",
|
|
6
6
|
"module": "./dist/esm/index.js",
|
|
@@ -71,12 +71,12 @@
|
|
|
71
71
|
"dependencies": {
|
|
72
72
|
"@aws-sdk/client-cloudwatch-logs": "^3.1123.0",
|
|
73
73
|
"@remix-run/node": "^2.8.1",
|
|
74
|
-
"@zauru-sdk/common": "^6.
|
|
74
|
+
"@zauru-sdk/common": "^6.13.0",
|
|
75
75
|
"@zauru-sdk/config": "^6.0.0",
|
|
76
76
|
"@zauru-sdk/graphql": "^6.9.1",
|
|
77
|
-
"@zauru-sdk/services": "^6.
|
|
78
|
-
"@zauru-sdk/types": "^6.
|
|
77
|
+
"@zauru-sdk/services": "^6.16.0",
|
|
78
|
+
"@zauru-sdk/types": "^6.29.0",
|
|
79
79
|
"moment": "^2.30.1"
|
|
80
80
|
},
|
|
81
|
-
"gitHead": "
|
|
81
|
+
"gitHead": "4355159e008ace641976ba4c5598fc3c7a8e8263"
|
|
82
82
|
}
|