erpnext-queue-client 2.7.7 → 2.8.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/erpnext/doctypeResourceRequest.d.ts +6 -4
- package/dist/erpnext/doctypeResourceRequest.js +2 -1
- package/dist/erpnext/model/Customer.d.ts +3 -1
- package/dist/erpnext/model/Customer.js +2 -1
- package/dist/erpnext/model/DocTypeHelpers.d.ts +17 -1
- package/dist/erpnext/resourceRequest.d.ts +5 -2
- package/dist/erpnext/resourceRequest.js +44 -3
- package/dist/erpnext/resourceRequest.test.js +90 -3
- package/package.json +1 -1
|
@@ -10,7 +10,7 @@ export declare class ERPNextDoctypeResourceRequest<TModel extends AnyZodObject>
|
|
|
10
10
|
protected resourceModel: TModel;
|
|
11
11
|
protected baseRequest: ERPNextResourceRequest;
|
|
12
12
|
constructor(temporalClient: TemporalClient, resourceName: string, resourceModel: TModel);
|
|
13
|
-
getList<TFieldOptions extends KeysOfModel<TModel>, TSelectedFields extends readonly TFieldOptions[] | readonly ["*"] | undefined = undefined, TAsDict extends boolean | undefined = undefined>(args: {
|
|
13
|
+
getList<TFieldOptions extends KeysOfModel<TModel>, TSelectedFields extends readonly TFieldOptions[] | readonly ["*"] | undefined = undefined, TAsDict extends boolean | undefined = undefined, TReturnPaginationMeta extends boolean | undefined = undefined>(args: {
|
|
14
14
|
fields?: TSelectedFields;
|
|
15
15
|
filters?: (string | string[])[][];
|
|
16
16
|
skip?: number;
|
|
@@ -18,9 +18,10 @@ export declare class ERPNextDoctypeResourceRequest<TModel extends AnyZodObject>
|
|
|
18
18
|
priority?: number;
|
|
19
19
|
asDict?: TAsDict;
|
|
20
20
|
params?: Record<string, string>;
|
|
21
|
+
returnPaginationMeta?: TReturnPaginationMeta;
|
|
21
22
|
resourceModel?: undefined;
|
|
22
|
-
}): Promise<GetListReturnValue<TModel, TFieldOptions, TSelectedFields, TAsDict>>;
|
|
23
|
-
getList<TOverrideModel extends AnyZodObject, TFieldOptions extends KeysOfModel<TOverrideModel>, TSelectedFields extends readonly TFieldOptions[] | readonly ["*"] | undefined = undefined, TAsDict extends boolean | undefined = undefined>(args: {
|
|
23
|
+
}): Promise<GetListReturnValue<TModel, TFieldOptions, TSelectedFields, TAsDict, TReturnPaginationMeta>>;
|
|
24
|
+
getList<TOverrideModel extends AnyZodObject, TFieldOptions extends KeysOfModel<TOverrideModel>, TSelectedFields extends readonly TFieldOptions[] | readonly ["*"] | undefined = undefined, TAsDict extends boolean | undefined = undefined, TReturnPaginationMeta extends boolean | undefined = undefined>(args: {
|
|
24
25
|
fields?: TSelectedFields;
|
|
25
26
|
filters?: (string | string[])[][];
|
|
26
27
|
skip?: number;
|
|
@@ -29,7 +30,8 @@ export declare class ERPNextDoctypeResourceRequest<TModel extends AnyZodObject>
|
|
|
29
30
|
priority?: number;
|
|
30
31
|
asDict?: TAsDict;
|
|
31
32
|
params?: Record<string, string>;
|
|
32
|
-
|
|
33
|
+
returnPaginationMeta?: TReturnPaginationMeta;
|
|
34
|
+
}): Promise<GetListReturnValue<TOverrideModel, TFieldOptions, TSelectedFields, TAsDict, TReturnPaginationMeta>>;
|
|
33
35
|
getById({ resourceId, priority, }: {
|
|
34
36
|
resourceId: string;
|
|
35
37
|
priority?: number;
|
|
@@ -13,7 +13,7 @@ class ERPNextDoctypeResourceRequest {
|
|
|
13
13
|
this.resourceModel = resourceModel.describe(resourceName);
|
|
14
14
|
this.baseRequest = new resourceRequest_1.ERPNextResourceRequest(this.temporalClient);
|
|
15
15
|
}
|
|
16
|
-
async getList({ fields, filters, skip, limit, resourceModel, priority = 5, asDict, params, } = {}) {
|
|
16
|
+
async getList({ fields, filters, skip, limit, resourceModel, priority = 5, asDict, params, returnPaginationMeta, } = {}) {
|
|
17
17
|
return await this.baseRequest.getList({
|
|
18
18
|
resourceName: this.resourceName,
|
|
19
19
|
resourceModel: resourceModel ?? this.resourceModel,
|
|
@@ -24,6 +24,7 @@ class ERPNextDoctypeResourceRequest {
|
|
|
24
24
|
...(skip !== undefined ? { skip } : {}),
|
|
25
25
|
...(params !== undefined ? { params } : {}),
|
|
26
26
|
...(asDict !== undefined ? { asDict } : {}),
|
|
27
|
+
...(returnPaginationMeta !== undefined ? { returnPaginationMeta } : {}),
|
|
27
28
|
});
|
|
28
29
|
}
|
|
29
30
|
async getById({ resourceId, priority = 5, }) {
|
|
@@ -42,7 +42,9 @@ export declare const Customer: z.ZodObject<{
|
|
|
42
42
|
account?: string | null | undefined;
|
|
43
43
|
debtor_creditor_number?: string | null | undefined;
|
|
44
44
|
advance_account?: string | null | undefined;
|
|
45
|
-
}>, "many"
|
|
45
|
+
}>, "many"> & {
|
|
46
|
+
__optionalForInput: true;
|
|
47
|
+
};
|
|
46
48
|
credit_limits: z.ZodNullable<z.ZodOptional<z.ZodArray<z.ZodOptional<z.ZodString>, "many">>>;
|
|
47
49
|
sales_team: z.ZodNullable<z.ZodOptional<z.ZodArray<z.ZodOptional<z.ZodString>, "many">>>;
|
|
48
50
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Customer = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const Boolean_1 = require("./Boolean");
|
|
6
|
+
const zodContextOptionals_1 = require("../../utils/zodContextOptionals");
|
|
6
7
|
const AccountListEntry = zod_1.z.object({
|
|
7
8
|
company: zod_1.z.string(),
|
|
8
9
|
account: zod_1.z.string().optional().nullable(),
|
|
@@ -28,7 +29,7 @@ exports.Customer = zod_1.z
|
|
|
28
29
|
default_commission_rate: zod_1.z.number().optional().nullable(),
|
|
29
30
|
default_currency: zod_1.z.string().optional().nullable(),
|
|
30
31
|
companies: zod_1.z.array(zod_1.z.string().optional()).optional().nullable(),
|
|
31
|
-
accounts: zod_1.z.array(AccountListEntry),
|
|
32
|
+
accounts: (0, zodContextOptionals_1.optionalForInput)(zod_1.z.array(AccountListEntry)),
|
|
32
33
|
credit_limits: zod_1.z.array(zod_1.z.string().optional()).optional().nullable(),
|
|
33
34
|
sales_team: zod_1.z.array(zod_1.z.string().optional()).optional().nullable(),
|
|
34
35
|
})
|
|
@@ -2,6 +2,12 @@ import { AnyZodObject, z, ZodArray, ZodNullable, ZodObject, ZodOptional, ZodRawS
|
|
|
2
2
|
import { Prettify } from "../../utils/utils";
|
|
3
3
|
import { KeysOf } from "../../utils/zodUtils";
|
|
4
4
|
import { DocTypeChildTableEntryMeta, DocTypeMeta, SubmittableMeta } from "./ERPNextDocTypeMeta";
|
|
5
|
+
type ListFilterValue = string | string[];
|
|
6
|
+
export type ListFilter<TValue extends ListFilterValue = ListFilterValue> = [
|
|
7
|
+
field: string,
|
|
8
|
+
operator: string,
|
|
9
|
+
value: TValue
|
|
10
|
+
];
|
|
5
11
|
export type KeysOfModel<T> = Extract<KeysOf<T extends AnyZodObject ? DocModelListEntryType<T> : never>, string>;
|
|
6
12
|
/**
|
|
7
13
|
* Extracts the alias from a field name if it contains " as ".
|
|
@@ -16,9 +22,19 @@ type ExtractAlias<T extends string> = T extends `${infer _Before} as ${infer Ali
|
|
|
16
22
|
type PickWithAliases<TModel extends AnyZodObject, TFields extends readonly string[]> = {
|
|
17
23
|
[K in TFields[number] as ExtractAlias<K>]: ExtractAlias<K> extends keyof z.infer<DocModelListEntryType<TModel>> ? z.infer<DocModelListEntryType<TModel>>[ExtractAlias<K>] : never;
|
|
18
24
|
};
|
|
19
|
-
export type
|
|
25
|
+
export type ListPaginationMeta = {
|
|
26
|
+
totalCount: number;
|
|
27
|
+
page?: number;
|
|
28
|
+
pageSize?: number;
|
|
29
|
+
};
|
|
30
|
+
export type GetListPaginatedReturnValue<TListData> = Prettify<{
|
|
31
|
+
page: TListData;
|
|
32
|
+
pagination: ListPaginationMeta;
|
|
33
|
+
}>;
|
|
34
|
+
type GetListData<TModel, TFieldOptions extends string, TSelectedFields, TAsDict> = Prettify<TAsDict extends false ? Array<Array<string>> : TModel extends AnyZodObject ? TSelectedFields extends undefined ? Array<{
|
|
20
35
|
name: string;
|
|
21
36
|
}> : TSelectedFields extends readonly ["*"] ? Array<z.infer<DocModelListEntryType<TModel>>> : TSelectedFields extends readonly TFieldOptions[] ? Array<PickWithAliases<TModel, TSelectedFields>> : any : any>;
|
|
37
|
+
export type GetListReturnValue<TModel, TFieldOptions extends string, TSelectedFields, TAsDict, TReturnPaginationMeta extends boolean | undefined = undefined> = TReturnPaginationMeta extends true ? GetListPaginatedReturnValue<GetListData<TModel, TFieldOptions, TSelectedFields, TAsDict>> : GetListData<TModel, TFieldOptions, TSelectedFields, TAsDict>;
|
|
22
38
|
export declare const LoadDocumentWrapper: <T extends AnyZodObject>(BaseModel: T) => z.ZodObject<{
|
|
23
39
|
[x: string]: any;
|
|
24
40
|
} & {
|
|
@@ -10,7 +10,9 @@ export declare class ERPNextResourceRequest {
|
|
|
10
10
|
protected temporalClient: TemporalClient;
|
|
11
11
|
constructor(temporalClient: TemporalClient);
|
|
12
12
|
private getParams;
|
|
13
|
-
|
|
13
|
+
private buildListPaginationMeta;
|
|
14
|
+
private getListTotalCount;
|
|
15
|
+
getList<TModel extends AnyZodObject | undefined = undefined, TFieldOptions extends GetListFieldOptions<TModel> = GetListFieldOptions<TModel>, TSelectedFields extends readonly TFieldOptions[] | readonly ["*"] | undefined = undefined, TAsDict extends boolean | undefined = undefined, TReturnPaginationMeta extends boolean | undefined = undefined>({ resourceName, fields, filters, resourceModel, skip, limit, priority, asDict, params, returnPaginationMeta, }: {
|
|
14
16
|
resourceName: string;
|
|
15
17
|
fields?: TSelectedFields;
|
|
16
18
|
filters?: (string | string[])[][];
|
|
@@ -20,7 +22,8 @@ export declare class ERPNextResourceRequest {
|
|
|
20
22
|
priority?: number;
|
|
21
23
|
asDict?: TAsDict;
|
|
22
24
|
params?: Record<string, string>;
|
|
23
|
-
|
|
25
|
+
returnPaginationMeta?: TReturnPaginationMeta;
|
|
26
|
+
}): Promise<GetListReturnValue<TModel, TFieldOptions, TSelectedFields, TAsDict, TReturnPaginationMeta>>;
|
|
24
27
|
getById<TModel extends AnyZodObject | undefined = undefined>({ resourceName, resourceId, resourceModel, priority, }: {
|
|
25
28
|
resourceName: string;
|
|
26
29
|
resourceId: string;
|
|
@@ -45,7 +45,32 @@ class ERPNextResourceRequest {
|
|
|
45
45
|
};
|
|
46
46
|
return allParams;
|
|
47
47
|
};
|
|
48
|
-
|
|
48
|
+
buildListPaginationMeta({ totalCount, skip, limit, }) {
|
|
49
|
+
const pagination = { totalCount };
|
|
50
|
+
if (limit !== undefined && Number.isInteger(limit) && limit > 0) {
|
|
51
|
+
pagination.pageSize = limit;
|
|
52
|
+
pagination.page = Math.floor((skip ?? 0) / limit) + 1;
|
|
53
|
+
}
|
|
54
|
+
return pagination;
|
|
55
|
+
}
|
|
56
|
+
async getListTotalCount({ resourceName, filters, priority, params, }) {
|
|
57
|
+
const countSchema = zod_1.z
|
|
58
|
+
.object({
|
|
59
|
+
data: zod_1.z.array(zod_1.z.object({ "count(name)": zod_1.z.number() })),
|
|
60
|
+
})
|
|
61
|
+
.describe("Count Response");
|
|
62
|
+
const paramsString = (0, utils_1.paramsToString)({
|
|
63
|
+
...this.getParams(["count(name)"], filters, true, params),
|
|
64
|
+
});
|
|
65
|
+
const result = await this.temporalClient.executeERPNextRequestWorkflow(`GET-${resourceName}-List-count`, {
|
|
66
|
+
requestMethod: "GET",
|
|
67
|
+
resourceName,
|
|
68
|
+
responseValidationModel: countSchema,
|
|
69
|
+
params: paramsString,
|
|
70
|
+
}, "erpnext", priority);
|
|
71
|
+
return result.data[0]?.["count(name)"] ?? 0;
|
|
72
|
+
}
|
|
73
|
+
async getList({ resourceName, fields, filters, resourceModel, skip, limit, priority = 5, asDict, params, returnPaginationMeta, }) {
|
|
49
74
|
const erpNextFields = fields?.length
|
|
50
75
|
? fields
|
|
51
76
|
: ["name"]; // default field is name
|
|
@@ -89,7 +114,7 @@ class ERPNextResourceRequest {
|
|
|
89
114
|
? {}
|
|
90
115
|
: { limit_start: String(skip ?? loopSkip) }),
|
|
91
116
|
});
|
|
92
|
-
const result = await this.temporalClient.executeERPNextRequestWorkflow(`GET-${resourceName
|
|
117
|
+
const result = await this.temporalClient.executeERPNextRequestWorkflow(`GET-${resourceName}-List`, {
|
|
93
118
|
requestMethod: "GET",
|
|
94
119
|
resourceName,
|
|
95
120
|
responseValidationModel: schema,
|
|
@@ -99,7 +124,23 @@ class ERPNextResourceRequest {
|
|
|
99
124
|
results = [...results, ...currentResult];
|
|
100
125
|
loopSkip = loopSkip + loopLimit;
|
|
101
126
|
} while (autoPaginate && currentResult.length);
|
|
102
|
-
|
|
127
|
+
if (!returnPaginationMeta) {
|
|
128
|
+
return results;
|
|
129
|
+
}
|
|
130
|
+
const totalCount = await this.getListTotalCount({
|
|
131
|
+
resourceName,
|
|
132
|
+
priority,
|
|
133
|
+
...(filters !== undefined ? { filters } : {}),
|
|
134
|
+
...(params !== undefined ? { params } : {}),
|
|
135
|
+
});
|
|
136
|
+
return {
|
|
137
|
+
page: results,
|
|
138
|
+
pagination: this.buildListPaginationMeta({
|
|
139
|
+
totalCount,
|
|
140
|
+
...(skip !== undefined ? { skip } : {}),
|
|
141
|
+
...(limit !== undefined ? { limit } : {}),
|
|
142
|
+
}),
|
|
143
|
+
};
|
|
103
144
|
}
|
|
104
145
|
async getById({ resourceName, resourceId, resourceModel, priority = 5, }) {
|
|
105
146
|
if (!resourceId)
|
|
@@ -29,9 +29,8 @@ describe("stringifyFiltersForParams", () => {
|
|
|
29
29
|
test("adds context when filter serialization fails", () => {
|
|
30
30
|
const cyclicFilterValue = {};
|
|
31
31
|
cyclicFilterValue.self = cyclicFilterValue;
|
|
32
|
-
const filters = [
|
|
33
|
-
|
|
34
|
-
];
|
|
32
|
+
const filters = [["custom", "=", cyclicFilterValue]];
|
|
33
|
+
// @ts-expect-error - we want to test the error case, filters has wrong format here
|
|
35
34
|
expect(() => (0, resourceRequest_1.stringifyFiltersForParams)(filters)).toThrow("Failed to serialize ERPNext filters for query params");
|
|
36
35
|
});
|
|
37
36
|
test("passes typed getList filters as encoded JSON query params", async () => {
|
|
@@ -61,3 +60,91 @@ describe("stringifyFiltersForParams", () => {
|
|
|
61
60
|
expect((0, resourceRequest_1.stringifyFiltersForParams)(undefined)).toBeUndefined();
|
|
62
61
|
});
|
|
63
62
|
});
|
|
63
|
+
describe("ERPNextResourceRequest.getList returnPaginationMeta", () => {
|
|
64
|
+
const createResourceRequest = (listData, totalCount, mockOptions) => {
|
|
65
|
+
let listRequestCount = 0;
|
|
66
|
+
const executeERPNextRequestWorkflow = vi
|
|
67
|
+
.fn()
|
|
68
|
+
.mockImplementation((_workflowId, requestOptions) => {
|
|
69
|
+
const params = new URLSearchParams(requestOptions.params.slice(1));
|
|
70
|
+
const fields = params.get("fields");
|
|
71
|
+
if (fields?.includes("count(name)")) {
|
|
72
|
+
return Promise.resolve({ data: [{ "count(name)": totalCount }] });
|
|
73
|
+
}
|
|
74
|
+
if (mockOptions?.paginateOnce) {
|
|
75
|
+
listRequestCount += 1;
|
|
76
|
+
return Promise.resolve({
|
|
77
|
+
data: listRequestCount === 1 ? listData : [],
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return Promise.resolve({ data: listData });
|
|
81
|
+
});
|
|
82
|
+
return new resourceRequest_1.ERPNextResourceRequest({
|
|
83
|
+
executeERPNextRequestWorkflow,
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
test("returns a plain array by default", async () => {
|
|
87
|
+
const resourceRequest = createResourceRequest([{ name: "ITEM-0001" }], 42);
|
|
88
|
+
const result = await resourceRequest.getList({
|
|
89
|
+
resourceName: "Item",
|
|
90
|
+
fields: ["name"],
|
|
91
|
+
limit: 10,
|
|
92
|
+
skip: 0,
|
|
93
|
+
});
|
|
94
|
+
expect(result).toEqual([{ name: "ITEM-0001" }]);
|
|
95
|
+
});
|
|
96
|
+
test("wraps results with page and pagination when returnPaginationMeta is true", async () => {
|
|
97
|
+
const resourceRequest = createResourceRequest([{ name: "ITEM-0001" }, { name: "ITEM-0002" }], 42);
|
|
98
|
+
const filters = [["disabled", "=", "0"]];
|
|
99
|
+
const result = await resourceRequest.getList({
|
|
100
|
+
resourceName: "Item",
|
|
101
|
+
fields: ["name"],
|
|
102
|
+
filters,
|
|
103
|
+
limit: 10,
|
|
104
|
+
skip: 10,
|
|
105
|
+
returnPaginationMeta: true,
|
|
106
|
+
});
|
|
107
|
+
expect(result).toEqual({
|
|
108
|
+
page: [{ name: "ITEM-0001" }, { name: "ITEM-0002" }],
|
|
109
|
+
pagination: {
|
|
110
|
+
totalCount: 42,
|
|
111
|
+
pageSize: 10,
|
|
112
|
+
page: 2,
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
test("requests count(name) with the same filters as the list query", async () => {
|
|
117
|
+
const resourceRequest = createResourceRequest([], 7);
|
|
118
|
+
const filters = [["item_group", "=", "Products"]];
|
|
119
|
+
const executeERPNextRequestWorkflow = resourceRequest
|
|
120
|
+
.temporalClient.executeERPNextRequestWorkflow;
|
|
121
|
+
await resourceRequest.getList({
|
|
122
|
+
resourceName: "Item",
|
|
123
|
+
fields: ["name"],
|
|
124
|
+
filters,
|
|
125
|
+
limit: 5,
|
|
126
|
+
returnPaginationMeta: true,
|
|
127
|
+
});
|
|
128
|
+
const countCall = executeERPNextRequestWorkflow.mock.calls.find((call) => call[1].params.includes("count(name)"));
|
|
129
|
+
const listCall = executeERPNextRequestWorkflow.mock.calls.find((call) => !call[1].params.includes("count(name)"));
|
|
130
|
+
const countParams = new URLSearchParams((countCall?.[1]).params.slice(1));
|
|
131
|
+
const listParams = new URLSearchParams((listCall?.[1]).params.slice(1));
|
|
132
|
+
expect(countParams.get("fields")).toBe('["count(name)"]');
|
|
133
|
+
expect(countParams.get("filters")).toBe(listParams.get("filters"));
|
|
134
|
+
expect(JSON.parse(countParams.get("filters") ?? "")).toEqual(filters);
|
|
135
|
+
});
|
|
136
|
+
test("includes only totalCount when limit is not set", async () => {
|
|
137
|
+
const resourceRequest = createResourceRequest([{ name: "ITEM-0001" }], 3, {
|
|
138
|
+
paginateOnce: true,
|
|
139
|
+
});
|
|
140
|
+
const result = await resourceRequest.getList({
|
|
141
|
+
resourceName: "Item",
|
|
142
|
+
fields: ["name"],
|
|
143
|
+
returnPaginationMeta: true,
|
|
144
|
+
});
|
|
145
|
+
expect(result).toEqual({
|
|
146
|
+
page: [{ name: "ITEM-0001" }],
|
|
147
|
+
pagination: { totalCount: 3 },
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
});
|