@energio/holded-mcp 1.0.0 → 1.1.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/index.js +1 -1
- package/dist/schemas/accounting/account-balances.d.ts +7 -4
- package/dist/schemas/accounting/account-balances.d.ts.map +1 -1
- package/dist/schemas/accounting/account-balances.js +5 -7
- package/dist/schemas/accounting/account-balances.js.map +1 -1
- package/dist/schemas/accounting/daily-ledger.d.ts +7 -3
- package/dist/schemas/accounting/daily-ledger.d.ts.map +1 -1
- package/dist/schemas/accounting/daily-ledger.js +5 -6
- package/dist/schemas/accounting/daily-ledger.js.map +1 -1
- package/dist/schemas/accounting/date-range.d.ts +45 -0
- package/dist/schemas/accounting/date-range.d.ts.map +1 -0
- package/dist/schemas/accounting/date-range.js +74 -0
- package/dist/schemas/accounting/date-range.js.map +1 -0
- package/dist/tools/accounting/account-balances.d.ts +5 -12
- package/dist/tools/accounting/account-balances.d.ts.map +1 -1
- package/dist/tools/accounting/account-balances.js +38 -96
- package/dist/tools/accounting/account-balances.js.map +1 -1
- package/dist/tools/accounting/daily-ledger.d.ts.map +1 -1
- package/dist/tools/accounting/daily-ledger.js +21 -14
- package/dist/tools/accounting/daily-ledger.js.map +1 -1
- package/dist/utils/timezone.d.ts +43 -0
- package/dist/utils/timezone.d.ts.map +1 -0
- package/dist/utils/timezone.js +106 -0
- package/dist/utils/timezone.js.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5,15 +5,18 @@ import { z } from "zod";
|
|
|
5
5
|
/**
|
|
6
6
|
* Account Balances input schema
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* Combines shared date range fields with account-specific options.
|
|
9
|
+
* Accepts either date mode (YYYY-MM-DD) or raw timestamp mode.
|
|
10
10
|
*/
|
|
11
11
|
export declare const AccountBalancesInputSchema: z.ZodObject<{
|
|
12
|
-
starttmp: z.ZodNumber;
|
|
13
|
-
endtmp: z.ZodNumber;
|
|
14
12
|
account_filter: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
15
13
|
include_opening: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
16
14
|
response_format: z.ZodDefault<z.ZodEnum<typeof import("../../constants.js").ResponseFormat>>;
|
|
15
|
+
start_date: z.ZodOptional<z.ZodString>;
|
|
16
|
+
end_date: z.ZodOptional<z.ZodString>;
|
|
17
|
+
starttmp: z.ZodOptional<z.ZodNumber>;
|
|
18
|
+
endtmp: z.ZodOptional<z.ZodNumber>;
|
|
19
|
+
raw_timestamps: z.ZodDefault<z.ZodBoolean>;
|
|
17
20
|
}, z.core.$strict>;
|
|
18
21
|
export type AccountBalancesInput = z.infer<typeof AccountBalancesInputSchema>;
|
|
19
22
|
//# sourceMappingURL=account-balances.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-balances.d.ts","sourceRoot":"","sources":["../../../src/schemas/accounting/account-balances.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"account-balances.d.ts","sourceRoot":"","sources":["../../../src/schemas/accounting/account-balances.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;kBAY2C,CAAC;AAEnF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC"}
|
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { ResponseFormatSchema } from "../common.js";
|
|
6
|
+
import { accountingDateRangeFields, accountingDateRangeRefinement, ACCOUNTING_DATE_RANGE_ERROR, } from "./date-range.js";
|
|
6
7
|
/**
|
|
7
8
|
* Account Balances input schema
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
10
|
+
* Combines shared date range fields with account-specific options.
|
|
11
|
+
* Accepts either date mode (YYYY-MM-DD) or raw timestamp mode.
|
|
11
12
|
*/
|
|
12
13
|
export const AccountBalancesInputSchema = z.strictObject({
|
|
13
|
-
|
|
14
|
-
endtmp: z.number().int().positive().describe("Period end as Unix timestamp (required)"),
|
|
14
|
+
...accountingDateRangeFields,
|
|
15
15
|
account_filter: z
|
|
16
16
|
.array(z.number().int().positive())
|
|
17
17
|
.optional()
|
|
@@ -22,7 +22,5 @@ export const AccountBalancesInputSchema = z.strictObject({
|
|
|
22
22
|
.default(false)
|
|
23
23
|
.describe("Include opening balance entry in totals (default: false, set true for balance sheet)"),
|
|
24
24
|
response_format: ResponseFormatSchema,
|
|
25
|
-
}).refine(
|
|
26
|
-
message: "starttmp must be less than or equal to endtmp (start date cannot be after end date)",
|
|
27
|
-
});
|
|
25
|
+
}).refine(accountingDateRangeRefinement, { message: ACCOUNTING_DATE_RANGE_ERROR });
|
|
28
26
|
//# sourceMappingURL=account-balances.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-balances.js","sourceRoot":"","sources":["../../../src/schemas/accounting/account-balances.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"account-balances.js","sourceRoot":"","sources":["../../../src/schemas/accounting/account-balances.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC7B,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC;AAEzB;;;;;GAKG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,YAAY,CAAC;IACvD,GAAG,yBAAyB;IAC5B,cAAc,EAAE,CAAC;SACd,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;SAClC,QAAQ,EAAE;SACV,QAAQ,CAAC,sEAAsE,CAAC;IACnF,eAAe,EAAE,CAAC;SACf,OAAO,EAAE;SACT,QAAQ,EAAE;SACV,OAAO,CAAC,KAAK,CAAC;SACd,QAAQ,CAAC,sFAAsF,CAAC;IACnG,eAAe,EAAE,oBAAoB;CACtC,CAAC,CAAC,MAAM,CAAC,6BAA6B,EAAE,EAAE,OAAO,EAAE,2BAA2B,EAAE,CAAC,CAAC"}
|
|
@@ -5,13 +5,17 @@ import { z } from "zod";
|
|
|
5
5
|
/**
|
|
6
6
|
* List daily ledger entries input schema
|
|
7
7
|
*
|
|
8
|
-
*
|
|
8
|
+
* Combines shared date range fields with pagination.
|
|
9
|
+
* The API requires starttmp and endtmp (resolved from dates internally).
|
|
9
10
|
*/
|
|
10
11
|
export declare const ListDailyLedgerInputSchema: z.ZodObject<{
|
|
11
|
-
starttmp: z.ZodNumber;
|
|
12
|
-
endtmp: z.ZodNumber;
|
|
13
12
|
page: z.ZodDefault<z.ZodNumber>;
|
|
14
13
|
response_format: z.ZodDefault<z.ZodEnum<typeof import("../../constants.js").ResponseFormat>>;
|
|
14
|
+
start_date: z.ZodOptional<z.ZodString>;
|
|
15
|
+
end_date: z.ZodOptional<z.ZodString>;
|
|
16
|
+
starttmp: z.ZodOptional<z.ZodNumber>;
|
|
17
|
+
endtmp: z.ZodOptional<z.ZodNumber>;
|
|
18
|
+
raw_timestamps: z.ZodDefault<z.ZodBoolean>;
|
|
15
19
|
}, z.core.$strict>;
|
|
16
20
|
export type ListDailyLedgerInput = z.infer<typeof ListDailyLedgerInputSchema>;
|
|
17
21
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daily-ledger.d.ts","sourceRoot":"","sources":["../../../src/schemas/accounting/daily-ledger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"daily-ledger.d.ts","sourceRoot":"","sources":["../../../src/schemas/accounting/daily-ledger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAWxB;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;kBAI2C,CAAC;AAEnF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E;;;;GAIG;AACH,eAAO,MAAM,eAAe;;;;;;kBAM1B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;kBAyBlC,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AACxD,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC"}
|
|
@@ -3,19 +3,18 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { PaginationSchema, ResponseFormatSchema, } from "../common.js";
|
|
6
|
+
import { accountingDateRangeFields, accountingDateRangeRefinement, ACCOUNTING_DATE_RANGE_ERROR, } from "./date-range.js";
|
|
6
7
|
/**
|
|
7
8
|
* List daily ledger entries input schema
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
+
* Combines shared date range fields with pagination.
|
|
11
|
+
* The API requires starttmp and endtmp (resolved from dates internally).
|
|
10
12
|
*/
|
|
11
13
|
export const ListDailyLedgerInputSchema = z.strictObject({
|
|
12
|
-
|
|
13
|
-
endtmp: z.number().int().positive().describe("Ending timestamp as Unix timestamp (required, filters entries until this date)"),
|
|
14
|
+
...accountingDateRangeFields,
|
|
14
15
|
page: PaginationSchema.shape.page,
|
|
15
16
|
response_format: ResponseFormatSchema,
|
|
16
|
-
}).refine(
|
|
17
|
-
message: "starttmp must be less than or equal to endtmp (start date cannot be after end date)",
|
|
18
|
-
});
|
|
17
|
+
}).refine(accountingDateRangeRefinement, { message: ACCOUNTING_DATE_RANGE_ERROR });
|
|
19
18
|
/**
|
|
20
19
|
* Entry line schema for create entry
|
|
21
20
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daily-ledger.js","sourceRoot":"","sources":["../../../src/schemas/accounting/daily-ledger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"daily-ledger.js","sourceRoot":"","sources":["../../../src/schemas/accounting/daily-ledger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC7B,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC;AAEzB;;;;;GAKG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,YAAY,CAAC;IACvD,GAAG,yBAAyB;IAC5B,IAAI,EAAE,gBAAgB,CAAC,KAAK,CAAC,IAAI;IACjC,eAAe,EAAE,oBAAoB;CACtC,CAAC,CAAC,MAAM,CAAC,6BAA6B,EAAE,EAAE,OAAO,EAAE,2BAA2B,EAAE,CAAC,CAAC;AAInF;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,YAAY,CAAC;IAC5C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,oCAAoC,EAAE,CAAC,CAAC,QAAQ,CAAC,+CAA+C,CAAC;IAC/I,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kDAAkD,CAAC;IAChG,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mDAAmD,CAAC;IAClG,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kBAAkB,CAAC;IAC/D,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;CACnF,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,YAAY,CAAC;IACnD,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;IACrF,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,qCAAqC,EAAE,CAAC,CAAC,QAAQ,CAAC,kDAAkD,CAAC;IACvJ,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC;CACpD,CAAC,CAAC,MAAM,CACP,CAAC,IAAI,EAAE,EAAE;IACP,mEAAmE;IACnE,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;QAC5D,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QAC/D,IAAI,QAAQ,IAAI,SAAS,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC,CAAC,mBAAmB;QACnC,CAAC;QACD,IAAI,CAAC,QAAQ,IAAI,CAAC,SAAS,EAAE,CAAC;YAC5B,OAAO,KAAK,CAAC,CAAC,yBAAyB;QACzC,CAAC;IACH,CAAC;IACD,iDAAiD;IACjD,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACjF,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACnF,OAAO,IAAI,CAAC,GAAG,CAAC,WAAW,GAAG,YAAY,CAAC,GAAG,IAAI,CAAC,CAAC,yCAAyC;AAC/F,CAAC,EACD;IACE,OAAO,EAAE,kGAAkG;CAC5G,CACF,CAAA"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Zod fields and validation for accounting date range input.
|
|
3
|
+
*
|
|
4
|
+
* Supports two mutually exclusive modes:
|
|
5
|
+
* - Date mode (default): start_date + end_date as YYYY-MM-DD strings
|
|
6
|
+
* - Raw timestamp mode: starttmp + endtmp as Unix timestamps (opt-in via raw_timestamps: true)
|
|
7
|
+
*
|
|
8
|
+
* Exports reusable fields + refinement so tool schemas can extend without .innerType() hacks.
|
|
9
|
+
*/
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
/**
|
|
12
|
+
* Reusable field definitions for accounting date range schemas.
|
|
13
|
+
* Spread into z.strictObject() alongside tool-specific fields.
|
|
14
|
+
*/
|
|
15
|
+
export declare const accountingDateRangeFields: {
|
|
16
|
+
start_date: z.ZodOptional<z.ZodString>;
|
|
17
|
+
end_date: z.ZodOptional<z.ZodString>;
|
|
18
|
+
starttmp: z.ZodOptional<z.ZodNumber>;
|
|
19
|
+
endtmp: z.ZodOptional<z.ZodNumber>;
|
|
20
|
+
raw_timestamps: z.ZodDefault<z.ZodBoolean>;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Refinement function for date range mutual exclusivity.
|
|
24
|
+
* Works on any object that includes the date range fields.
|
|
25
|
+
*/
|
|
26
|
+
export declare function accountingDateRangeRefinement(data: {
|
|
27
|
+
raw_timestamps: boolean;
|
|
28
|
+
start_date?: string;
|
|
29
|
+
end_date?: string;
|
|
30
|
+
starttmp?: number;
|
|
31
|
+
endtmp?: number;
|
|
32
|
+
}): boolean;
|
|
33
|
+
export declare const ACCOUNTING_DATE_RANGE_ERROR: string;
|
|
34
|
+
/**
|
|
35
|
+
* Standalone schema for direct use (e.g., testing).
|
|
36
|
+
*/
|
|
37
|
+
export declare const AccountingDateRangeSchema: z.ZodObject<{
|
|
38
|
+
start_date: z.ZodOptional<z.ZodString>;
|
|
39
|
+
end_date: z.ZodOptional<z.ZodString>;
|
|
40
|
+
starttmp: z.ZodOptional<z.ZodNumber>;
|
|
41
|
+
endtmp: z.ZodOptional<z.ZodNumber>;
|
|
42
|
+
raw_timestamps: z.ZodDefault<z.ZodBoolean>;
|
|
43
|
+
}, z.core.$strict>;
|
|
44
|
+
export type AccountingDateRange = z.infer<typeof AccountingDateRangeSchema>;
|
|
45
|
+
//# sourceMappingURL=date-range.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-range.d.ts","sourceRoot":"","sources":["../../../src/schemas/accounting/date-range.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;;GAGG;AACH,eAAO,MAAM,yBAAyB;;;;;;CA2BrC,CAAC;AAEF;;;GAGG;AACH,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE;IAAE,cAAc,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5G,OAAO,CAUT;AAED,eAAO,MAAM,2BAA2B,QAII,CAAC;AAE7C;;GAEG;AACH,eAAO,MAAM,yBAAyB;;;;;;kBAE4C,CAAC;AAEnF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Zod fields and validation for accounting date range input.
|
|
3
|
+
*
|
|
4
|
+
* Supports two mutually exclusive modes:
|
|
5
|
+
* - Date mode (default): start_date + end_date as YYYY-MM-DD strings
|
|
6
|
+
* - Raw timestamp mode: starttmp + endtmp as Unix timestamps (opt-in via raw_timestamps: true)
|
|
7
|
+
*
|
|
8
|
+
* Exports reusable fields + refinement so tool schemas can extend without .innerType() hacks.
|
|
9
|
+
*/
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
const DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
|
12
|
+
/**
|
|
13
|
+
* Reusable field definitions for accounting date range schemas.
|
|
14
|
+
* Spread into z.strictObject() alongside tool-specific fields.
|
|
15
|
+
*/
|
|
16
|
+
export const accountingDateRangeFields = {
|
|
17
|
+
start_date: z
|
|
18
|
+
.string()
|
|
19
|
+
.regex(DATE_REGEX, "start_date must be in YYYY-MM-DD format")
|
|
20
|
+
.optional()
|
|
21
|
+
.describe("Period start date in YYYY-MM-DD format (default mode)"),
|
|
22
|
+
end_date: z
|
|
23
|
+
.string()
|
|
24
|
+
.regex(DATE_REGEX, "end_date must be in YYYY-MM-DD format")
|
|
25
|
+
.optional()
|
|
26
|
+
.describe("Period end date in YYYY-MM-DD format (default mode, inclusive)"),
|
|
27
|
+
starttmp: z
|
|
28
|
+
.number()
|
|
29
|
+
.int()
|
|
30
|
+
.positive()
|
|
31
|
+
.optional()
|
|
32
|
+
.describe("Period start as Unix timestamp (raw_timestamps mode only)"),
|
|
33
|
+
endtmp: z
|
|
34
|
+
.number()
|
|
35
|
+
.int()
|
|
36
|
+
.positive()
|
|
37
|
+
.optional()
|
|
38
|
+
.describe("Period end as Unix timestamp (raw_timestamps mode only)"),
|
|
39
|
+
raw_timestamps: z
|
|
40
|
+
.boolean()
|
|
41
|
+
.default(false)
|
|
42
|
+
.describe("Set to true to use starttmp/endtmp instead of start_date/end_date"),
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Refinement function for date range mutual exclusivity.
|
|
46
|
+
* Works on any object that includes the date range fields.
|
|
47
|
+
*/
|
|
48
|
+
export function accountingDateRangeRefinement(data) {
|
|
49
|
+
if (data.raw_timestamps) {
|
|
50
|
+
if (data.start_date !== undefined || data.end_date !== undefined)
|
|
51
|
+
return false;
|
|
52
|
+
if (data.starttmp === undefined || data.endtmp === undefined)
|
|
53
|
+
return false;
|
|
54
|
+
return data.starttmp <= data.endtmp;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
if (data.starttmp !== undefined || data.endtmp !== undefined)
|
|
58
|
+
return false;
|
|
59
|
+
if (data.start_date === undefined || data.end_date === undefined)
|
|
60
|
+
return false;
|
|
61
|
+
return data.end_date >= data.start_date;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export const ACCOUNTING_DATE_RANGE_ERROR = "Invalid date range input. " +
|
|
65
|
+
"Date mode (default): provide start_date and end_date (YYYY-MM-DD), do not include starttmp/endtmp. " +
|
|
66
|
+
"Raw timestamp mode: set raw_timestamps=true with starttmp and endtmp, do not include start_date/end_date. " +
|
|
67
|
+
"In raw mode, starttmp must be <= endtmp.";
|
|
68
|
+
/**
|
|
69
|
+
* Standalone schema for direct use (e.g., testing).
|
|
70
|
+
*/
|
|
71
|
+
export const AccountingDateRangeSchema = z
|
|
72
|
+
.strictObject(accountingDateRangeFields)
|
|
73
|
+
.refine(accountingDateRangeRefinement, { message: ACCOUNTING_DATE_RANGE_ERROR });
|
|
74
|
+
//# sourceMappingURL=date-range.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-range.js","sourceRoot":"","sources":["../../../src/schemas/accounting/date-range.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,UAAU,GAAG,qBAAqB,CAAC;AAEzC;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,UAAU,EAAE,CAAC;SACV,MAAM,EAAE;SACR,KAAK,CAAC,UAAU,EAAE,yCAAyC,CAAC;SAC5D,QAAQ,EAAE;SACV,QAAQ,CAAC,uDAAuD,CAAC;IACpE,QAAQ,EAAE,CAAC;SACR,MAAM,EAAE;SACR,KAAK,CAAC,UAAU,EAAE,uCAAuC,CAAC;SAC1D,QAAQ,EAAE;SACV,QAAQ,CAAC,gEAAgE,CAAC;IAC7E,QAAQ,EAAE,CAAC;SACR,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,QAAQ,EAAE;SACV,QAAQ,CAAC,2DAA2D,CAAC;IACxE,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,QAAQ,EAAE;SACV,QAAQ,CAAC,yDAAyD,CAAC;IACtE,cAAc,EAAE,CAAC;SACd,OAAO,EAAE;SACT,OAAO,CAAC,KAAK,CAAC;SACd,QAAQ,CAAC,mEAAmE,CAAC;CACjF,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,6BAA6B,CAC3C,IAA6G;IAE7G,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAC/E,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAC3E,OAAO,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC;IACtC,CAAC;SAAM,CAAC;QACN,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAC3E,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAC/E,OAAO,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,UAAU,CAAC;IAC1C,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,2BAA2B,GACtC,4BAA4B;IAC5B,qGAAqG;IACrG,4GAA4G;IAC5G,0CAA0C,CAAC;AAE7C;;GAEG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC;KACvC,YAAY,CAAC,yBAAyB,CAAC;KACvC,MAAM,CAAC,6BAA6B,EAAE,EAAE,OAAO,EAAE,2BAA2B,EAAE,CAAC,CAAC"}
|
|
@@ -2,26 +2,19 @@
|
|
|
2
2
|
* Account Balances tool for Holded API
|
|
3
3
|
*
|
|
4
4
|
* Computes accurate, date-scoped per-account debit/credit/balance totals
|
|
5
|
-
* by aggregating from individual daily ledger entries
|
|
6
|
-
*
|
|
5
|
+
* by aggregating from individual daily ledger entries. Uses CET-aligned
|
|
6
|
+
* timestamps to prevent cross-fiscal-year leakage.
|
|
7
7
|
*/
|
|
8
8
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9
9
|
import type { LedgerEntryLine, AccountBalance } from "../../types.js";
|
|
10
10
|
/**
|
|
11
|
-
* Filter out
|
|
12
|
-
*
|
|
13
|
-
* Detection uses two signals:
|
|
14
|
-
* 1. Entries sharing the same timestamp as the opening balance entry are candidates.
|
|
15
|
-
* 2. Candidates are confirmed as leaked if their entryNumber also appears at a
|
|
16
|
-
* different timestamp (duplicate = different fiscal year).
|
|
17
|
-
* 3. Fallback: candidates at the opening timestamp whose type is not "opening" or
|
|
18
|
-
* "vat_regularization" are excluded even without a duplicate entryNumber.
|
|
11
|
+
* Filter out opening balance entries if not requested.
|
|
12
|
+
* Simple type-based filter — CET-aligned boundaries prevent cross-year leakage.
|
|
19
13
|
*
|
|
20
14
|
* Exported for unit testing.
|
|
21
15
|
*/
|
|
22
|
-
export declare function
|
|
16
|
+
export declare function filterOpeningEntries(entries: LedgerEntryLine[], includeOpening: boolean): {
|
|
23
17
|
filtered: LedgerEntryLine[];
|
|
24
|
-
leakedCount: number;
|
|
25
18
|
openingExcluded: boolean;
|
|
26
19
|
};
|
|
27
20
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-balances.d.ts","sourceRoot":"","sources":["../../../src/tools/accounting/account-balances.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGpE,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"account-balances.d.ts","sourceRoot":"","sources":["../../../src/tools/accounting/account-balances.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGpE,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAwDtE;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,eAAe,EAAE,EAC1B,cAAc,EAAE,OAAO,GACtB;IAAE,QAAQ,EAAE,eAAe,EAAE,CAAC;IAAC,eAAe,EAAE,OAAO,CAAA;CAAE,CAiB3D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,eAAe,EAAE,GACzB,GAAG,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAchD;AAED;;GAEG;AACH,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,MAAM,CAqBhF;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA4GpE"}
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
* Account Balances tool for Holded API
|
|
3
3
|
*
|
|
4
4
|
* Computes accurate, date-scoped per-account debit/credit/balance totals
|
|
5
|
-
* by aggregating from individual daily ledger entries
|
|
6
|
-
*
|
|
5
|
+
* by aggregating from individual daily ledger entries. Uses CET-aligned
|
|
6
|
+
* timestamps to prevent cross-fiscal-year leakage.
|
|
7
7
|
*/
|
|
8
8
|
import { makeApiRequest } from "../../services/api.js";
|
|
9
9
|
import { ResponseFormat } from "../../constants.js";
|
|
10
10
|
import { withErrorHandling } from "../utilities.js";
|
|
11
11
|
import { AccountBalancesInputSchema, } from "../../schemas/accounting/account-balances.js";
|
|
12
|
+
import { resolveTimestamps } from "../../utils/timezone.js";
|
|
12
13
|
/** Maximum entry lines per page returned by the daily ledger API (docs say 500, actual is 250) */
|
|
13
14
|
const LEDGER_PAGE_SIZE = 250;
|
|
14
15
|
/**
|
|
@@ -32,88 +33,26 @@ async function fetchAllLedgerEntries(starttmp, endtmp) {
|
|
|
32
33
|
return { entries: allEntries, pagesFetched: page };
|
|
33
34
|
}
|
|
34
35
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*/
|
|
38
|
-
const BOUNDARY_SAFE_TYPES = new Set(["opening", "vat_regularization"]);
|
|
39
|
-
/**
|
|
40
|
-
* Filter out cross-fiscal-year leaked entries from daily ledger results.
|
|
41
|
-
*
|
|
42
|
-
* Detection uses two signals:
|
|
43
|
-
* 1. Entries sharing the same timestamp as the opening balance entry are candidates.
|
|
44
|
-
* 2. Candidates are confirmed as leaked if their entryNumber also appears at a
|
|
45
|
-
* different timestamp (duplicate = different fiscal year).
|
|
46
|
-
* 3. Fallback: candidates at the opening timestamp whose type is not "opening" or
|
|
47
|
-
* "vat_regularization" are excluded even without a duplicate entryNumber.
|
|
36
|
+
* Filter out opening balance entries if not requested.
|
|
37
|
+
* Simple type-based filter — CET-aligned boundaries prevent cross-year leakage.
|
|
48
38
|
*
|
|
49
39
|
* Exported for unit testing.
|
|
50
40
|
*/
|
|
51
|
-
export function
|
|
52
|
-
if (
|
|
53
|
-
return { filtered:
|
|
54
|
-
}
|
|
55
|
-
// Step 1: Find opening entry timestamps
|
|
56
|
-
const openingTimestamps = new Set();
|
|
57
|
-
for (const e of entries) {
|
|
58
|
-
if (e.type === "opening") {
|
|
59
|
-
openingTimestamps.add(e.timestamp);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
// No opening entry → no fiscal year boundary in range → no leakage possible
|
|
63
|
-
if (openingTimestamps.size === 0) {
|
|
64
|
-
return { filtered: [...entries], leakedCount: 0, openingExcluded: false };
|
|
41
|
+
export function filterOpeningEntries(entries, includeOpening) {
|
|
42
|
+
if (includeOpening) {
|
|
43
|
+
return { filtered: entries, openingExcluded: false };
|
|
65
44
|
}
|
|
66
|
-
// Step 2: Build a map of entryNumber → set of distinct timestamps
|
|
67
|
-
const entryNumberTimestamps = new Map();
|
|
68
|
-
for (const e of entries) {
|
|
69
|
-
let ts = entryNumberTimestamps.get(e.entryNumber);
|
|
70
|
-
if (!ts) {
|
|
71
|
-
ts = new Set();
|
|
72
|
-
entryNumberTimestamps.set(e.entryNumber, ts);
|
|
73
|
-
}
|
|
74
|
-
ts.add(e.timestamp);
|
|
75
|
-
}
|
|
76
|
-
// Step 3: Filter
|
|
77
|
-
let leakedCount = 0;
|
|
78
45
|
let openingExcluded = false;
|
|
79
46
|
const filtered = [];
|
|
80
47
|
for (const e of entries) {
|
|
81
|
-
const atBoundary = openingTimestamps.has(e.timestamp);
|
|
82
|
-
if (!atBoundary) {
|
|
83
|
-
// Not at boundary → always keep
|
|
84
|
-
filtered.push(e);
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
// At boundary: handle by type
|
|
88
48
|
if (e.type === "opening") {
|
|
89
|
-
|
|
90
|
-
filtered.push(e);
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
openingExcluded = true;
|
|
94
|
-
}
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
if (BOUNDARY_SAFE_TYPES.has(e.type)) {
|
|
98
|
-
// vat_regularization at boundary is legitimate
|
|
99
|
-
filtered.push(e);
|
|
100
|
-
continue;
|
|
101
|
-
}
|
|
102
|
-
// Non-safe type at boundary → check for duplicate entryNumber (primary signal)
|
|
103
|
-
const timestamps = entryNumberTimestamps.get(e.entryNumber);
|
|
104
|
-
const hasDuplicate = timestamps.size > 1;
|
|
105
|
-
if (hasDuplicate) {
|
|
106
|
-
// Confirmed leak: same entryNumber exists at a different timestamp
|
|
107
|
-
leakedCount++;
|
|
49
|
+
openingExcluded = true;
|
|
108
50
|
}
|
|
109
51
|
else {
|
|
110
|
-
|
|
111
|
-
// Still exclude — legitimate entries at the exact boundary are limited to safe types.
|
|
112
|
-
leakedCount++;
|
|
52
|
+
filtered.push(e);
|
|
113
53
|
}
|
|
114
|
-
// Either way, this entry is excluded (not pushed to filtered)
|
|
115
54
|
}
|
|
116
|
-
return { filtered,
|
|
55
|
+
return { filtered, openingExcluded };
|
|
117
56
|
}
|
|
118
57
|
/**
|
|
119
58
|
* Aggregate daily ledger entries into per-account debit/credit totals.
|
|
@@ -161,19 +100,26 @@ export function registerAccountBalancesTools(server) {
|
|
|
161
100
|
title: "Holded Accounting Account Balances",
|
|
162
101
|
description: `Compute accurate, date-scoped per-account debit/credit/balance totals.
|
|
163
102
|
|
|
164
|
-
Aggregates from individual daily ledger entries
|
|
103
|
+
Aggregates from individual daily ledger entries to produce correct period-scoped balances. Dates are interpreted in Spanish local time (CET/CEST).
|
|
165
104
|
|
|
166
105
|
Use this tool when you need account totals for a specific period (P&L, trial balance, balance sheet).
|
|
167
106
|
|
|
168
|
-
Args:
|
|
169
|
-
-
|
|
170
|
-
-
|
|
107
|
+
Args (default mode — recommended):
|
|
108
|
+
- start_date (string): Period start date in YYYY-MM-DD format
|
|
109
|
+
- end_date (string): Period end date in YYYY-MM-DD format (inclusive)
|
|
110
|
+
|
|
111
|
+
Args (raw timestamp mode — set raw_timestamps: true):
|
|
112
|
+
- starttmp (number): Period start as Unix timestamp (CET-aligned recommended)
|
|
113
|
+
- endtmp (number): Period end as Unix timestamp (CET-aligned recommended)
|
|
114
|
+
- raw_timestamps (boolean): Must be true to use timestamp mode
|
|
115
|
+
|
|
116
|
+
Additional args:
|
|
171
117
|
- account_filter (number[]): Filter to specific account numbers (optional, returns all if omitted)
|
|
172
118
|
- include_opening (boolean): Include opening balance entry in totals (default: false — set true for balance sheet, false for P&L)
|
|
173
119
|
- response_format ('json' | 'markdown'): Output format (default: 'json')
|
|
174
120
|
|
|
175
121
|
Returns:
|
|
176
|
-
Per-account debit/credit/balance totals
|
|
122
|
+
Per-account debit/credit/balance totals with period metadata.`,
|
|
177
123
|
inputSchema: AccountBalancesInputSchema,
|
|
178
124
|
annotations: {
|
|
179
125
|
readOnlyHint: true,
|
|
@@ -182,20 +128,22 @@ Returns:
|
|
|
182
128
|
openWorldHint: true,
|
|
183
129
|
},
|
|
184
130
|
}, withErrorHandling(async (params) => {
|
|
185
|
-
const
|
|
186
|
-
// 1.
|
|
131
|
+
const typedParams = params;
|
|
132
|
+
// 1. Resolve dates to timestamps
|
|
133
|
+
const { starttmp, endtmp } = resolveTimestamps(typedParams);
|
|
134
|
+
// 2. Fetch all ledger entries (auto-paginate)
|
|
187
135
|
const { entries, pagesFetched } = await fetchAllLedgerEntries(starttmp, endtmp);
|
|
188
|
-
//
|
|
189
|
-
const { filtered,
|
|
190
|
-
//
|
|
136
|
+
// 3. Filter opening entries if not requested
|
|
137
|
+
const { filtered, openingExcluded } = filterOpeningEntries(entries, typedParams.include_opening);
|
|
138
|
+
// 4. Aggregate by account
|
|
191
139
|
const totals = aggregateByAccount(filtered);
|
|
192
|
-
//
|
|
140
|
+
// 5. Enrich with account metadata from list_accounts
|
|
193
141
|
const accountMeta = await makeApiRequest("accounting", "chartofaccounts", "GET", undefined, { starttmp, endtmp, includeEmpty: 0 });
|
|
194
142
|
const metaByNum = new Map();
|
|
195
143
|
for (const a of accountMeta) {
|
|
196
144
|
metaByNum.set(a.num, a);
|
|
197
145
|
}
|
|
198
|
-
//
|
|
146
|
+
// 6. Build result
|
|
199
147
|
let accounts = [];
|
|
200
148
|
for (const [num, { debit, credit }] of totals) {
|
|
201
149
|
const meta = metaByNum.get(num);
|
|
@@ -208,9 +156,9 @@ Returns:
|
|
|
208
156
|
balance: Math.round((debit - credit) * 100) / 100,
|
|
209
157
|
});
|
|
210
158
|
}
|
|
211
|
-
//
|
|
212
|
-
if (account_filter && account_filter.length > 0) {
|
|
213
|
-
const filterSet = new Set(account_filter);
|
|
159
|
+
// 7. Apply account filter
|
|
160
|
+
if (typedParams.account_filter && typedParams.account_filter.length > 0) {
|
|
161
|
+
const filterSet = new Set(typedParams.account_filter);
|
|
214
162
|
accounts = accounts.filter((a) => filterSet.has(a.num));
|
|
215
163
|
}
|
|
216
164
|
// Sort by account number
|
|
@@ -218,17 +166,11 @@ Returns:
|
|
|
218
166
|
const structured = {
|
|
219
167
|
accounts,
|
|
220
168
|
count: accounts.length,
|
|
221
|
-
period: {
|
|
222
|
-
|
|
223
|
-
endtmp,
|
|
224
|
-
},
|
|
225
|
-
filtered_entries: {
|
|
226
|
-
leaked_cross_year: leakedCount,
|
|
227
|
-
opening_balance_excluded: openingExcluded,
|
|
228
|
-
},
|
|
169
|
+
period: { starttmp, endtmp },
|
|
170
|
+
opening_balance_excluded: openingExcluded,
|
|
229
171
|
pages_fetched: pagesFetched,
|
|
230
172
|
};
|
|
231
|
-
const textContent = response_format === ResponseFormat.MARKDOWN
|
|
173
|
+
const textContent = typedParams.response_format === ResponseFormat.MARKDOWN
|
|
232
174
|
? formatAccountBalancesMarkdown(accounts)
|
|
233
175
|
: JSON.stringify(structured, null, 2);
|
|
234
176
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-balances.js","sourceRoot":"","sources":["../../../src/tools/accounting/account-balances.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EACL,0BAA0B,GAE3B,MAAM,8CAA8C,CAAC;
|
|
1
|
+
{"version":3,"file":"account-balances.js","sourceRoot":"","sources":["../../../src/tools/accounting/account-balances.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EACL,0BAA0B,GAE3B,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D,kGAAkG;AAClG,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAY7B;;GAEG;AACH,KAAK,UAAU,qBAAqB,CAClC,QAAgB,EAChB,MAAc;IAEd,MAAM,UAAU,GAAsB,EAAE,CAAC;IACzC,IAAI,IAAI,GAAG,CAAC,CAAC;IAEb,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,WAAW,GAA4B,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;QAClE,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;YACb,WAAW,CAAC,IAAI,GAAG,IAAI,CAAC;QAC1B,CAAC;QAED,MAAM,WAAW,GAAG,MAAM,cAAc,CACtC,YAAY,EACZ,aAAa,EACb,KAAK,EACL,SAAS,EACT,WAAW,CACZ,CAAC;QAEF,UAAU,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC;QAEhC,IAAI,WAAW,CAAC,MAAM,GAAG,gBAAgB,EAAE,CAAC;YAC1C,MAAM;QACR,CAAC;QACD,IAAI,EAAE,CAAC;IACT,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;AACrD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAA0B,EAC1B,cAAuB;IAEvB,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC;IACvD,CAAC;IAED,IAAI,eAAe,GAAG,KAAK,CAAC;IAC5B,MAAM,QAAQ,GAAsB,EAAE,CAAC;IAEvC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACzB,eAAe,GAAG,IAAI,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC;AACvC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAA0B;IAE1B,MAAM,GAAG,GAAG,IAAI,GAAG,EAA6C,CAAC;IAEjE,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAC7B,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,GAAG,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;YAC9B,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC1B,CAAC;QACD,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC;QACrB,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC;IACzB,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,6BAA6B,CAAC,QAA0B;IACtE,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QACrB,OAAO,qDAAqD,CAAC;IAC/D,CAAC;IAED,MAAM,KAAK,GAAG;QACZ,oBAAoB;QACpB,EAAE;QACF,SAAS,QAAQ,CAAC,MAAM,YAAY;QACpC,EAAE;QACF,uDAAuD;QACvD,uDAAuD;KACxD,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CACR,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CACnH,CAAC;IACJ,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,4BAA4B,CAAC,MAAiB;IAC5D,MAAM,CAAC,YAAY,CACjB,yCAAyC,EACzC;QACE,KAAK,EAAE,oCAAoC;QAC3C,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;gEAqB6C;QAC1D,WAAW,EAAE,0BAA0B;QACvC,WAAW,EAAE;YACX,YAAY,EAAE,IAAI;YAClB,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,IAAI;YACpB,aAAa,EAAE,IAAI;SACpB;KACF,EACD,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;QACjC,MAAM,WAAW,GAAG,MAAyC,CAAC;QAE9D,iCAAiC;QACjC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;QAE5D,8CAA8C;QAC9C,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,qBAAqB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAEhF,6CAA6C;QAC7C,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG,oBAAoB,CACxD,OAAO,EACP,WAAW,CAAC,eAAe,CAC5B,CAAC;QAEF,0BAA0B;QAC1B,MAAM,MAAM,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QAE5C,qDAAqD;QACrD,MAAM,WAAW,GAAG,MAAM,cAAc,CACtC,YAAY,EACZ,iBAAiB,EACjB,KAAK,EACL,SAAS,EACT,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,EAAE,CACtC,CAAC;QACF,MAAM,SAAS,GAAG,IAAI,GAAG,EAA2B,CAAC;QACrD,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;YAC5B,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAC1B,CAAC;QAED,kBAAkB;QAClB,IAAI,QAAQ,GAAqB,EAAE,CAAC;QACpC,KAAK,MAAM,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,IAAI,MAAM,EAAE,CAAC;YAC9C,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAChC,QAAQ,CAAC,IAAI,CAAC;gBACZ,GAAG;gBACH,IAAI,EAAE,IAAI,EAAE,IAAI,IAAI,MAAM,CAAC,GAAG,CAAC;gBAC/B,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE;gBACxB,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG;gBACpC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,GAAG,GAAG;gBACtC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG;aAClD,CAAC,CAAC;QACL,CAAC;QAED,0BAA0B;QAC1B,IAAI,WAAW,CAAC,cAAc,IAAI,WAAW,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxE,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;YACtD,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1D,CAAC;QAED,yBAAyB;QACzB,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QAEvC,MAAM,UAAU,GAAG;YACjB,QAAQ;YACR,KAAK,EAAE,QAAQ,CAAC,MAAM;YACtB,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE;YAC5B,wBAAwB,EAAE,eAAe;YACzC,aAAa,EAAE,YAAY;SAC5B,CAAC;QAEF,MAAM,WAAW,GACf,WAAW,CAAC,eAAe,KAAK,cAAc,CAAC,QAAQ;YACrD,CAAC,CAAC,6BAA6B,CAAC,QAAQ,CAAC;YACzC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAE1C,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;YAC9C,iBAAiB,EAAE,UAAU;SAC9B,CAAC;IACJ,CAAC,CAAC,CACH,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daily-ledger.d.ts","sourceRoot":"","sources":["../../../src/tools/accounting/daily-ledger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"daily-ledger.d.ts","sourceRoot":"","sources":["../../../src/tools/accounting/daily-ledger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAapE;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAsIhE"}
|
|
@@ -5,6 +5,7 @@ import { makeApiRequest, toStructuredContent } from "../../services/api.js";
|
|
|
5
5
|
import { ResponseFormat } from "../../constants.js";
|
|
6
6
|
import { withErrorHandling } from "../utilities.js";
|
|
7
7
|
import { ListDailyLedgerInputSchema, CreateEntryInputSchema, } from "../../schemas/accounting/daily-ledger.js";
|
|
8
|
+
import { resolveTimestamps } from "../../utils/timezone.js";
|
|
8
9
|
/**
|
|
9
10
|
* Register all daily ledger-related tools
|
|
10
11
|
*/
|
|
@@ -14,13 +15,20 @@ export function registerDailyLedgerTools(server) {
|
|
|
14
15
|
title: "List Holded Daily Ledger Entries",
|
|
15
16
|
description: `List daily ledger entries from Holded Accounting.
|
|
16
17
|
|
|
17
|
-
Args:
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
|
|
18
|
+
Args (default mode — recommended):
|
|
19
|
+
- start_date (string): Period start date in YYYY-MM-DD format
|
|
20
|
+
- end_date (string): Period end date in YYYY-MM-DD format (inclusive)
|
|
21
|
+
|
|
22
|
+
Args (raw timestamp mode — set raw_timestamps: true):
|
|
23
|
+
- starttmp (number): Starting Unix timestamp (CET-aligned recommended)
|
|
24
|
+
- endtmp (number): Ending Unix timestamp (CET-aligned recommended)
|
|
25
|
+
- raw_timestamps (boolean): Must be true to use timestamp mode
|
|
26
|
+
|
|
27
|
+
Additional args:
|
|
28
|
+
- page (number): Page number for pagination (default: 1, max 250 items per page)
|
|
21
29
|
- response_format ('json' | 'markdown'): Output format (default: 'json')
|
|
22
30
|
|
|
23
|
-
Note:
|
|
31
|
+
Note: Dates are interpreted in Spanish local time (CET/CEST). The API uses a half-open interval [starttmp, endtmp). Pagination order is non-deterministic.
|
|
24
32
|
|
|
25
33
|
Returns:
|
|
26
34
|
Array of daily ledger entries with date, account, amount, and description.`,
|
|
@@ -32,17 +40,16 @@ Returns:
|
|
|
32
40
|
openWorldHint: true,
|
|
33
41
|
},
|
|
34
42
|
}, withErrorHandling(async (params) => {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
queryParams.page = page;
|
|
43
|
+
const typedParams = params;
|
|
44
|
+
// Resolve dates to timestamps
|
|
45
|
+
const { starttmp, endtmp } = resolveTimestamps(typedParams);
|
|
46
|
+
const queryParams = { starttmp, endtmp };
|
|
47
|
+
if (typedParams.page > 1) {
|
|
48
|
+
queryParams.page = typedParams.page;
|
|
42
49
|
}
|
|
43
50
|
const entries = await makeApiRequest("accounting", "dailyledger", "GET", undefined, queryParams);
|
|
44
51
|
let textContent;
|
|
45
|
-
if (response_format === ResponseFormat.MARKDOWN) {
|
|
52
|
+
if (typedParams.response_format === ResponseFormat.MARKDOWN) {
|
|
46
53
|
if (!entries.length) {
|
|
47
54
|
textContent = "No daily ledger entries found.";
|
|
48
55
|
}
|
|
@@ -66,7 +73,7 @@ Returns:
|
|
|
66
73
|
}
|
|
67
74
|
return {
|
|
68
75
|
content: [{ type: "text", text: textContent }],
|
|
69
|
-
structuredContent: { entries, count: entries.length, page },
|
|
76
|
+
structuredContent: { entries, count: entries.length, page: typedParams.page },
|
|
70
77
|
};
|
|
71
78
|
}));
|
|
72
79
|
// Create Daily Ledger Entry
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daily-ledger.js","sourceRoot":"","sources":["../../../src/tools/accounting/daily-ledger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EACL,0BAA0B,EAC1B,sBAAsB,GAGvB,MAAM,0CAA0C,CAAC;
|
|
1
|
+
{"version":3,"file":"daily-ledger.js","sourceRoot":"","sources":["../../../src/tools/accounting/daily-ledger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CET/CEST timezone conversion utility for Holded API.
|
|
3
|
+
*
|
|
4
|
+
* The Holded API stores accounting entry timestamps at midnight Spanish local
|
|
5
|
+
* time (Europe/Madrid: CET in winter UTC+1, CEST in summer UTC+2). The daily
|
|
6
|
+
* ledger filter uses a half-open interval [starttmp, endtmp).
|
|
7
|
+
*
|
|
8
|
+
* This utility converts YYYY-MM-DD date strings to the correct CET-aligned
|
|
9
|
+
* Unix timestamps for querying the API.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Convert a YYYY-MM-DD date string to the Unix timestamp of midnight
|
|
13
|
+
* in the Europe/Madrid timezone (CET or CEST depending on date).
|
|
14
|
+
*
|
|
15
|
+
* Uses the Intl API to correctly handle DST transitions.
|
|
16
|
+
*/
|
|
17
|
+
export declare function dateToMidnightCET(dateStr: string): number;
|
|
18
|
+
/**
|
|
19
|
+
* Resolve date range input to { starttmp, endtmp } timestamps.
|
|
20
|
+
* Supports both date mode (YYYY-MM-DD) and raw timestamp mode.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveTimestamps(params: {
|
|
23
|
+
raw_timestamps: boolean;
|
|
24
|
+
starttmp?: number;
|
|
25
|
+
endtmp?: number;
|
|
26
|
+
start_date?: string;
|
|
27
|
+
end_date?: string;
|
|
28
|
+
}): {
|
|
29
|
+
starttmp: number;
|
|
30
|
+
endtmp: number;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Convert an inclusive date range [startDate, endDate] to the half-open
|
|
34
|
+
* timestamp interval [starttmp, endtmp) that the Holded API expects.
|
|
35
|
+
*
|
|
36
|
+
* - starttmp = midnight CET of startDate (inclusive, matches API's >= filter)
|
|
37
|
+
* - endtmp = midnight CET of (endDate + 1 day) (exclusive, matches API's < filter)
|
|
38
|
+
*/
|
|
39
|
+
export declare function datesToApiRange(startDate: string, endDate: string): {
|
|
40
|
+
starttmp: number;
|
|
41
|
+
endtmp: number;
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=timezone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timezone.d.ts","sourceRoot":"","sources":["../../src/utils/timezone.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA+BzD;AAqCD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAKvC;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CA+BtC"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CET/CEST timezone conversion utility for Holded API.
|
|
3
|
+
*
|
|
4
|
+
* The Holded API stores accounting entry timestamps at midnight Spanish local
|
|
5
|
+
* time (Europe/Madrid: CET in winter UTC+1, CEST in summer UTC+2). The daily
|
|
6
|
+
* ledger filter uses a half-open interval [starttmp, endtmp).
|
|
7
|
+
*
|
|
8
|
+
* This utility converts YYYY-MM-DD date strings to the correct CET-aligned
|
|
9
|
+
* Unix timestamps for querying the API.
|
|
10
|
+
*/
|
|
11
|
+
const TIMEZONE = 'Europe/Madrid';
|
|
12
|
+
const DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
|
13
|
+
const MAX_RANGE_SECONDS = 365 * 86400; // API limit: 1 year
|
|
14
|
+
/**
|
|
15
|
+
* Convert a YYYY-MM-DD date string to the Unix timestamp of midnight
|
|
16
|
+
* in the Europe/Madrid timezone (CET or CEST depending on date).
|
|
17
|
+
*
|
|
18
|
+
* Uses the Intl API to correctly handle DST transitions.
|
|
19
|
+
*/
|
|
20
|
+
export function dateToMidnightCET(dateStr) {
|
|
21
|
+
if (!DATE_REGEX.test(dateStr)) {
|
|
22
|
+
throw new Error(`Invalid date format: "${dateStr}". Expected YYYY-MM-DD.`);
|
|
23
|
+
}
|
|
24
|
+
const [year, month, day] = dateStr.split('-').map(Number);
|
|
25
|
+
// Validate the date is real by constructing it and checking components
|
|
26
|
+
const probe = new Date(Date.UTC(year, month - 1, day, 12, 0, 0));
|
|
27
|
+
if (probe.getUTCFullYear() !== year ||
|
|
28
|
+
probe.getUTCMonth() !== month - 1 ||
|
|
29
|
+
probe.getUTCDate() !== day) {
|
|
30
|
+
throw new Error(`Invalid date: "${dateStr}" does not exist.`);
|
|
31
|
+
}
|
|
32
|
+
// Start with an initial guess: UTC midnight of the target date
|
|
33
|
+
const utcMidnight = Date.UTC(year, month - 1, day, 0, 0, 0);
|
|
34
|
+
// Get the offset at this approximate time
|
|
35
|
+
const offset = getUtcOffsetMs(utcMidnight);
|
|
36
|
+
// Midnight Madrid = UTC midnight minus offset
|
|
37
|
+
const candidate = utcMidnight - offset;
|
|
38
|
+
// Verify: the offset might differ at the candidate time (DST edge case).
|
|
39
|
+
const offsetAtCandidate = getUtcOffsetMs(candidate);
|
|
40
|
+
const result = utcMidnight - offsetAtCandidate;
|
|
41
|
+
return result / 1000;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get the UTC offset in milliseconds for Europe/Madrid at a given UTC instant.
|
|
45
|
+
* Positive means ahead of UTC (e.g., +3600000 for CET).
|
|
46
|
+
*/
|
|
47
|
+
function getUtcOffsetMs(utcMs) {
|
|
48
|
+
const date = new Date(utcMs);
|
|
49
|
+
const parts = new Intl.DateTimeFormat('en-US', {
|
|
50
|
+
timeZone: TIMEZONE,
|
|
51
|
+
year: 'numeric',
|
|
52
|
+
month: '2-digit',
|
|
53
|
+
day: '2-digit',
|
|
54
|
+
hour: '2-digit',
|
|
55
|
+
minute: '2-digit',
|
|
56
|
+
second: '2-digit',
|
|
57
|
+
hour12: false,
|
|
58
|
+
}).formatToParts(date);
|
|
59
|
+
const get = (type) => {
|
|
60
|
+
const part = parts.find((p) => p.type === type);
|
|
61
|
+
return part ? parseInt(part.value, 10) : 0;
|
|
62
|
+
};
|
|
63
|
+
const localMs = Date.UTC(get('year'), get('month') - 1, get('day'), get('hour'), get('minute'), get('second'));
|
|
64
|
+
return localMs - utcMs;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Resolve date range input to { starttmp, endtmp } timestamps.
|
|
68
|
+
* Supports both date mode (YYYY-MM-DD) and raw timestamp mode.
|
|
69
|
+
*/
|
|
70
|
+
export function resolveTimestamps(params) {
|
|
71
|
+
if (params.raw_timestamps) {
|
|
72
|
+
return { starttmp: params.starttmp, endtmp: params.endtmp };
|
|
73
|
+
}
|
|
74
|
+
return datesToApiRange(params.start_date, params.end_date);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Convert an inclusive date range [startDate, endDate] to the half-open
|
|
78
|
+
* timestamp interval [starttmp, endtmp) that the Holded API expects.
|
|
79
|
+
*
|
|
80
|
+
* - starttmp = midnight CET of startDate (inclusive, matches API's >= filter)
|
|
81
|
+
* - endtmp = midnight CET of (endDate + 1 day) (exclusive, matches API's < filter)
|
|
82
|
+
*/
|
|
83
|
+
export function datesToApiRange(startDate, endDate) {
|
|
84
|
+
const starttmp = dateToMidnightCET(startDate);
|
|
85
|
+
// Validate endDate before computing next day (catches format/existence errors early)
|
|
86
|
+
dateToMidnightCET(endDate);
|
|
87
|
+
// Compute endDate + 1 day
|
|
88
|
+
const [ey, em, ed] = endDate.split('-').map(Number);
|
|
89
|
+
const nextDay = new Date(Date.UTC(ey, em - 1, ed + 1, 12, 0, 0));
|
|
90
|
+
const nextDayStr = [
|
|
91
|
+
String(nextDay.getUTCFullYear()).padStart(4, '0'),
|
|
92
|
+
String(nextDay.getUTCMonth() + 1).padStart(2, '0'),
|
|
93
|
+
String(nextDay.getUTCDate()).padStart(2, '0'),
|
|
94
|
+
].join('-');
|
|
95
|
+
const endtmp = dateToMidnightCET(nextDayStr);
|
|
96
|
+
if (endtmp <= starttmp) {
|
|
97
|
+
throw new Error(`end_date must be on or after start_date (got start_date="${startDate}", end_date="${endDate}").`);
|
|
98
|
+
}
|
|
99
|
+
const rangeSeconds = endtmp - starttmp;
|
|
100
|
+
if (rangeSeconds > MAX_RANGE_SECONDS) {
|
|
101
|
+
throw new Error(`Date range exceeds the Holded API limit of 365 days (got ${Math.ceil(rangeSeconds / 86400)} days). ` +
|
|
102
|
+
`For leap years or ranges over 365 days, split into two queries.`);
|
|
103
|
+
}
|
|
104
|
+
return { starttmp, endtmp };
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=timezone.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timezone.js","sourceRoot":"","sources":["../../src/utils/timezone.ts"],"names":[],"mappings":"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"}
|