@markwharton/liquidplanner 3.2.2 → 3.2.3
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/README.md +6 -5
- package/dist/client.d.ts +15 -2
- package/dist/client.js +70 -3
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +47 -0
- package/dist/types.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,11 +43,11 @@ await client.createTimesheetEntry({
|
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
// Query existing entries for a date
|
|
46
|
-
const tsResult = await client.getTimesheetEntries('2026-01-29'
|
|
47
|
-
if (tsResult.ok) console.log(tsResult.data); //
|
|
46
|
+
const tsResult = await client.getTimesheetEntries('2026-01-29');
|
|
47
|
+
if (tsResult.ok) console.log(tsResult.data); // LPTimesheetEntries (grouped by member)
|
|
48
48
|
|
|
49
49
|
// Update an existing entry (accumulate hours)
|
|
50
|
-
const existing = tsResult.data
|
|
50
|
+
const existing = tsResult.data!.members[0].entries[0];
|
|
51
51
|
await client.updateTimesheetEntry(existing.id, existing, {
|
|
52
52
|
hours: existing.hours + 1.5,
|
|
53
53
|
note: 'Additional work'
|
|
@@ -114,7 +114,7 @@ All methods return `Result<T>` — see [api-core Result Pattern](../../README.md
|
|
|
114
114
|
| `findItems(options)` | `LPFindItemsOptions` | `Result<LPItem[]>` |
|
|
115
115
|
| `getChildren(parentId, options?)` | `number, { itemType? }?` | `Result<LPItem[]>` |
|
|
116
116
|
| `getWorkspaceTree()` | — | `Result<LPWorkspaceTree>` |
|
|
117
|
-
| `getAssignments(memberId)` | `number
|
|
117
|
+
| `getAssignments(memberId?)` | `number?` | `Result<LPAssignments>` |
|
|
118
118
|
| `clearCache()` | — | `void` |
|
|
119
119
|
| `invalidateTimesheetCache()` | — | `void` |
|
|
120
120
|
| `invalidateTreeCache()` | — | `void` |
|
|
@@ -123,9 +123,10 @@ All methods return `Result<T>` — see [api-core Result Pattern](../../README.md
|
|
|
123
123
|
| `invalidateCostCodeCache()` | — | `void` |
|
|
124
124
|
| `getCostCodes()` | — | `Result<LPCostCode[]>` |
|
|
125
125
|
| `createTimesheetEntry(entry)` | `LPTimesheetEntry` | `LPSyncResult` |
|
|
126
|
-
| `getTimesheetEntries(date,
|
|
126
|
+
| `getTimesheetEntries(date, options?)` | `string \| string[], LPTimesheetOptions?` | `Result<LPTimesheetEntries>` |
|
|
127
127
|
| `updateTimesheetEntry(entryId, existing, updates)` | `number, LPTimesheetEntry, Partial<LPTimesheetEntry>` | `LPSyncResult` |
|
|
128
128
|
| `upsertTimesheetEntry(entry, options?)` | `LPTimesheetEntry, LPUpsertOptions?` | `LPSyncResult` |
|
|
129
|
+
| `getWeeklySummary(dates, options?)` | `string[], LPWeeklySummaryOptions?` | `Result<LPWeeklySummary>` |
|
|
129
130
|
|
|
130
131
|
### Workflow: `resolveTaskToAssignment`
|
|
131
132
|
|
package/dist/client.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @see https://api-docs.liquidplanner.com/
|
|
8
8
|
*/
|
|
9
9
|
import type { Result } from '@markwharton/api-core';
|
|
10
|
-
import type { LPConfig, LPWorkspace, LPMember, LPItem, LPCostCode, LPSyncResult, LPTimesheetEntry, LPTimesheetOptions, LPUpsertOptions, LPAncestor, LPFindItemsOptions, LPWorkspaceTree, LPAssignments } from './types.js';
|
|
10
|
+
import type { LPConfig, LPWorkspace, LPMember, LPItem, LPCostCode, LPSyncResult, LPTimesheetEntry, LPTimesheetOptions, LPUpsertOptions, LPTimesheetEntries, LPAncestor, LPFindItemsOptions, LPWorkspaceTree, LPAssignments, LPWeeklySummaryOptions, LPWeeklySummary } from './types.js';
|
|
11
11
|
/**
|
|
12
12
|
* LiquidPlanner API Client
|
|
13
13
|
*
|
|
@@ -204,7 +204,7 @@ export declare class LPClient {
|
|
|
204
204
|
* @param date - Date(s) in YYYY-MM-DD format (string or array)
|
|
205
205
|
* @param options - Optional filters (itemId, memberId)
|
|
206
206
|
*/
|
|
207
|
-
getTimesheetEntries(date: string | string[], options?: LPTimesheetOptions): Promise<Result<
|
|
207
|
+
getTimesheetEntries(date: string | string[], options?: LPTimesheetOptions): Promise<Result<LPTimesheetEntries>>;
|
|
208
208
|
/**
|
|
209
209
|
* Update an existing timesheet entry
|
|
210
210
|
*
|
|
@@ -260,4 +260,17 @@ export declare class LPClient {
|
|
|
260
260
|
* @param options - Upsert options (accumulate defaults to true)
|
|
261
261
|
*/
|
|
262
262
|
upsertTimesheetEntry(entry: LPTimesheetEntry, options?: LPUpsertOptions): Promise<LPSyncResult>;
|
|
263
|
+
/**
|
|
264
|
+
* Get a weekly summary with per-member time breakdowns.
|
|
265
|
+
*
|
|
266
|
+
* Aggregates timesheet entries by member and date. Optionally includes
|
|
267
|
+
* assignments and late items when specified via the `include` option.
|
|
268
|
+
*
|
|
269
|
+
* Fetches are parallelized — timesheet entries are always fetched, while
|
|
270
|
+
* assignments and late items are conditionally fetched based on `include`.
|
|
271
|
+
*
|
|
272
|
+
* @param dates - Array of date strings (YYYY-MM-DD) for the week
|
|
273
|
+
* @param options - Optional memberId filter and detail sections to include
|
|
274
|
+
*/
|
|
275
|
+
getWeeklySummary(dates: string[], options?: LPWeeklySummaryOptions): Promise<Result<LPWeeklySummary>>;
|
|
263
276
|
}
|
package/dist/client.js
CHANGED
|
@@ -640,7 +640,7 @@ export class LPClient {
|
|
|
640
640
|
if (memberId) {
|
|
641
641
|
baseUrl += `&${filterIs('userId', memberId)}`;
|
|
642
642
|
}
|
|
643
|
-
|
|
643
|
+
const flatResult = await paginatedFetch({
|
|
644
644
|
fetchFn: (url) => this.fetch(url),
|
|
645
645
|
baseUrl,
|
|
646
646
|
transform: (data) => data.map(entry => ({
|
|
@@ -653,6 +653,23 @@ export class LPClient {
|
|
|
653
653
|
userId: entry.userId,
|
|
654
654
|
})),
|
|
655
655
|
});
|
|
656
|
+
if (!flatResult.ok)
|
|
657
|
+
return err(flatResult.error, flatResult.status);
|
|
658
|
+
// Group by member (same pattern as getAssignments)
|
|
659
|
+
const memberMap = new Map();
|
|
660
|
+
for (const entry of flatResult.data) {
|
|
661
|
+
const uid = entry.userId ?? 0;
|
|
662
|
+
let entries = memberMap.get(uid);
|
|
663
|
+
if (!entries) {
|
|
664
|
+
entries = [];
|
|
665
|
+
memberMap.set(uid, entries);
|
|
666
|
+
}
|
|
667
|
+
entries.push(entry);
|
|
668
|
+
}
|
|
669
|
+
const members = [...memberMap.entries()]
|
|
670
|
+
.sort(([a], [b]) => a - b)
|
|
671
|
+
.map(([memberId, entries]) => ({ memberId, entries }));
|
|
672
|
+
return ok({ members });
|
|
656
673
|
});
|
|
657
674
|
}
|
|
658
675
|
/**
|
|
@@ -734,10 +751,11 @@ export class LPClient {
|
|
|
734
751
|
if (!fetchResult.ok) {
|
|
735
752
|
return { ok: false, error: fetchResult.error, status: fetchResult.status };
|
|
736
753
|
}
|
|
737
|
-
// Find matching entry
|
|
754
|
+
// Find matching entry from grouped result
|
|
738
755
|
// If no costCodeId specified, match any entry (LP uses assignment's default)
|
|
739
756
|
// If costCodeId specified, match exactly
|
|
740
|
-
const
|
|
757
|
+
const allEntries = fetchResult.data?.members.flatMap(m => m.entries) ?? [];
|
|
758
|
+
const existingEntry = allEntries.find((e) => {
|
|
741
759
|
if (entry.costCodeId === undefined || entry.costCodeId === null) {
|
|
742
760
|
return true;
|
|
743
761
|
}
|
|
@@ -756,4 +774,53 @@ export class LPClient {
|
|
|
756
774
|
// No existing entry, create new
|
|
757
775
|
return this.createTimesheetEntry(entry);
|
|
758
776
|
}
|
|
777
|
+
// ============================================================================
|
|
778
|
+
// Weekly Summary
|
|
779
|
+
// ============================================================================
|
|
780
|
+
/**
|
|
781
|
+
* Get a weekly summary with per-member time breakdowns.
|
|
782
|
+
*
|
|
783
|
+
* Aggregates timesheet entries by member and date. Optionally includes
|
|
784
|
+
* assignments and late items when specified via the `include` option.
|
|
785
|
+
*
|
|
786
|
+
* Fetches are parallelized — timesheet entries are always fetched, while
|
|
787
|
+
* assignments and late items are conditionally fetched based on `include`.
|
|
788
|
+
*
|
|
789
|
+
* @param dates - Array of date strings (YYYY-MM-DD) for the week
|
|
790
|
+
* @param options - Optional memberId filter and detail sections to include
|
|
791
|
+
*/
|
|
792
|
+
async getWeeklySummary(dates, options) {
|
|
793
|
+
const { memberId, include = [] } = options || {};
|
|
794
|
+
const includeSet = new Set(include);
|
|
795
|
+
// Parallel fetch: timesheet (always) + optional sections
|
|
796
|
+
const [timesheetResult, assignmentsResult, lateItemsResult] = await Promise.all([
|
|
797
|
+
this.getTimesheetEntries(dates, memberId ? { memberId } : undefined),
|
|
798
|
+
includeSet.has('assignments') ? this.getAssignments(memberId) : Promise.resolve(null),
|
|
799
|
+
includeSet.has('lateItems')
|
|
800
|
+
? this.findItems({ itemType: 'tasks', taskStatusGroupNot: 'done', late: true })
|
|
801
|
+
: Promise.resolve(null),
|
|
802
|
+
]);
|
|
803
|
+
if (!timesheetResult.ok)
|
|
804
|
+
return err(timesheetResult.error, timesheetResult.status);
|
|
805
|
+
if (assignmentsResult && !assignmentsResult.ok)
|
|
806
|
+
return err(assignmentsResult.error, assignmentsResult.status);
|
|
807
|
+
if (lateItemsResult && !lateItemsResult.ok)
|
|
808
|
+
return err(lateItemsResult.error, lateItemsResult.status);
|
|
809
|
+
// Build per-member weekly summaries from grouped timesheet entries
|
|
810
|
+
const members = timesheetResult.data.members.map(({ memberId, entries }) => {
|
|
811
|
+
const timeSummary = {};
|
|
812
|
+
let totalHours = 0;
|
|
813
|
+
for (const entry of entries) {
|
|
814
|
+
timeSummary[entry.date] = (timeSummary[entry.date] || 0) + entry.hours;
|
|
815
|
+
totalHours += entry.hours;
|
|
816
|
+
}
|
|
817
|
+
return { memberId, timeSummary, totalHours, entries };
|
|
818
|
+
});
|
|
819
|
+
const result = { members };
|
|
820
|
+
if (assignmentsResult)
|
|
821
|
+
result.assignments = assignmentsResult.data;
|
|
822
|
+
if (lateItemsResult)
|
|
823
|
+
result.lateItems = lateItemsResult.data;
|
|
824
|
+
return ok(result);
|
|
825
|
+
}
|
|
759
826
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
*/
|
|
31
31
|
export { LPClient } from './client.js';
|
|
32
32
|
export { resolveTaskToAssignment } from './workflows.js';
|
|
33
|
-
export type { LPConfig, LPCacheConfig, LPRetryConfig, LPItemType, LPUserType, LPHierarchyItem, LPItem, LPAncestor, LPWorkspace, LPMember, LPCostCode, LPSyncResult, LPTimesheetEntry, LPTaskResolution, LPTimesheetOptions, LPUpsertOptions, LPAssignment, LPItemRef, LPNormalizedAssignment, LPMemberAssignments, LPAssignments, LPFindItemsOptions, LPTreeNode, LPWorkspaceTree, } from './types.js';
|
|
33
|
+
export type { LPConfig, LPCacheConfig, LPRetryConfig, LPItemType, LPUserType, LPHierarchyItem, LPItem, LPAncestor, LPWorkspace, LPMember, LPCostCode, LPSyncResult, LPTimesheetEntry, LPTaskResolution, LPTimesheetOptions, LPUpsertOptions, LPMemberTimesheetEntries, LPTimesheetEntries, LPAssignment, LPItemRef, LPNormalizedAssignment, LPMemberAssignments, LPAssignments, LPWeeklySummaryOptions, LPMemberWeeklySummary, LPWeeklySummary, LPFindItemsOptions, LPTreeNode, LPWorkspaceTree, } from './types.js';
|
|
34
34
|
export type { AccessTier } from './types.js';
|
|
35
35
|
export { METHOD_TIERS, ENTITIES } from './types.js';
|
|
36
36
|
export { ok, err, getErrorMessage, normalizeEnum, TTLCache, MemoryCacheStore, LayeredCache } from '@markwharton/api-core';
|
package/dist/types.d.ts
CHANGED
|
@@ -265,6 +265,20 @@ export interface LPUpsertOptions {
|
|
|
265
265
|
*/
|
|
266
266
|
accumulate?: boolean;
|
|
267
267
|
}
|
|
268
|
+
/**
|
|
269
|
+
* One member's timesheet entries in the grouped result
|
|
270
|
+
*/
|
|
271
|
+
export interface LPMemberTimesheetEntries {
|
|
272
|
+
memberId: number;
|
|
273
|
+
entries: LPTimesheetEntry[];
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Timesheet entries grouped by member.
|
|
277
|
+
* One member entry when memberId specified, all members when omitted.
|
|
278
|
+
*/
|
|
279
|
+
export interface LPTimesheetEntries {
|
|
280
|
+
members: LPMemberTimesheetEntries[];
|
|
281
|
+
}
|
|
268
282
|
/**
|
|
269
283
|
* Assignment with resolved parent context
|
|
270
284
|
*
|
|
@@ -360,6 +374,39 @@ export interface LPAssignments {
|
|
|
360
374
|
/** Total items in the workspace tree snapshot */
|
|
361
375
|
treeItemCount: number;
|
|
362
376
|
}
|
|
377
|
+
/**
|
|
378
|
+
* Options for getWeeklySummary
|
|
379
|
+
*/
|
|
380
|
+
export interface LPWeeklySummaryOptions {
|
|
381
|
+
/** Filter to a specific member. When omitted, returns all members. */
|
|
382
|
+
memberId?: number;
|
|
383
|
+
/** Optional detail sections to include alongside the time summary */
|
|
384
|
+
include?: ('assignments' | 'lateItems')[];
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* One member's weekly summary with time breakdown by date
|
|
388
|
+
*/
|
|
389
|
+
export interface LPMemberWeeklySummary {
|
|
390
|
+
memberId: number;
|
|
391
|
+
/** Total hours per date (date string → hours) */
|
|
392
|
+
timeSummary: Record<string, number>;
|
|
393
|
+
/** Sum of all hours across all dates */
|
|
394
|
+
totalHours: number;
|
|
395
|
+
/** Raw timesheet entries for this member */
|
|
396
|
+
entries: LPTimesheetEntry[];
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Weekly summary with per-member time breakdowns and optional detail sections.
|
|
400
|
+
* Aggregates timesheet entries, assignments, and late items into a single result.
|
|
401
|
+
*/
|
|
402
|
+
export interface LPWeeklySummary {
|
|
403
|
+
/** Per-member weekly summaries */
|
|
404
|
+
members: LPMemberWeeklySummary[];
|
|
405
|
+
/** Assignments data (when 'assignments' included) */
|
|
406
|
+
assignments?: LPAssignments;
|
|
407
|
+
/** Late items (when 'lateItems' included) */
|
|
408
|
+
lateItems?: LPItem[];
|
|
409
|
+
}
|
|
363
410
|
/**
|
|
364
411
|
* Options for querying items with LP API filters
|
|
365
412
|
*
|
package/dist/types.js
CHANGED
|
@@ -26,6 +26,7 @@ export const METHOD_TIERS = {
|
|
|
26
26
|
getTimesheetEntries: 'standard',
|
|
27
27
|
updateTimesheetEntry: 'standard',
|
|
28
28
|
upsertTimesheetEntry: 'standard',
|
|
29
|
+
getWeeklySummary: 'standard',
|
|
29
30
|
};
|
|
30
31
|
// ============================================================================
|
|
31
32
|
// Entity Registry
|