@burdenoff/microfe-movethewheels 2026.1101.4 → 2026.1102.2
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/components/CustomerDeliveryUpdatesFields.js +57 -0
- package/dist/components/CustomerNotificationSettingsCard.js +368 -0
- package/dist/components/freight-cost/FreightCommercialFactEditor.js +46 -46
- package/dist/components/freight-cost/FreightCommercialHistory.js +7 -7
- package/dist/components/freight-cost/FreightDraftForm.js +20 -20
- package/dist/components/freight-invoice/InvoiceReviewPanel.js +255 -0
- package/dist/components/freight-invoice/InvoiceReviewQueue.js +93 -0
- package/dist/generated/freight-invoice-operations.d.ts +461 -0
- package/dist/generated/tracking-quality-operations.d.ts +275 -0
- package/dist/hooks/useCustomers.js +9 -3
- package/dist/hooks/useInvoiceComparisonRecovery.js +26 -26
- package/dist/hooks/useInvoiceReviewPermissions.js +14 -0
- package/dist/movethewheels/components/CustomerDeliveryUpdatesFields.d.ts +12 -0
- package/dist/movethewheels/components/CustomerNotificationSettingsCard.d.ts +3 -0
- package/dist/movethewheels/components/freight-invoice/InvoiceReviewPanel.d.ts +11 -0
- package/dist/movethewheels/components/freight-invoice/InvoiceReviewQueue.d.ts +22 -0
- package/dist/movethewheels/hooks/useInvoiceReviewPermissions.d.ts +5 -0
- package/dist/movethewheels/pages/FinancePage.d.ts +3 -1
- package/dist/movethewheels/types/index.d.ts +4 -0
- package/dist/movethewheels/utils/customerNotifications.d.ts +66 -0
- package/dist/movethewheels/utils/invoiceReview.d.ts +26 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/bell-ring.js +22 -0
- package/dist/pages/CustomerDetailsPage.js +279 -231
- package/dist/pages/FinancePage.js +109 -67
- package/dist/pages/FreightEstimatePage.js +89 -89
- package/dist/pages/FreightInvoicePage.js +138 -120
- package/dist/pages/SettingsPage.js +242 -238
- package/dist/src/generated/freight-invoice-operations.js +2100 -1
- package/dist/utils/customerNotifications.js +83 -0
- package/dist/utils/invoiceReview.js +84 -0
- package/package.json +4 -3
- package/src/generated/freight-invoice-operations.ts +245 -1
- package/src/generated/tracking-quality-operations.ts +304 -0
- package/src/movethewheels/components/CustomerDeliveryUpdatesFields.tsx +67 -0
- package/src/movethewheels/components/CustomerNotificationSettingsCard.tsx +465 -0
- package/src/movethewheels/components/freight-invoice/InvoiceReviewPanel.tsx +319 -0
- package/src/movethewheels/components/freight-invoice/InvoiceReviewQueue.tsx +96 -0
- package/src/movethewheels/hooks/useCustomers.ts +17 -0
- package/src/movethewheels/hooks/useInvoiceReviewPermissions.ts +25 -0
- package/src/movethewheels/pages/CustomerDetailsPage.tsx +39 -0
- package/src/movethewheels/pages/FinancePage.tsx +34 -2
- package/src/movethewheels/pages/FreightInvoicePage.tsx +44 -3
- package/src/movethewheels/pages/SettingsPage.tsx +221 -208
- package/src/movethewheels/types/index.ts +4 -0
- package/src/movethewheels/utils/customerNotifications.ts +209 -0
- package/src/movethewheels/utils/invoiceReview.ts +146 -0
- package/src/operations/freight-invoice/review.graphql +26 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Customer delivery notifications — client side of wspace-movethewheels-svc's
|
|
3
|
+
* customer notification settings, history and previews (BOFF-4402).
|
|
4
|
+
*
|
|
5
|
+
* The service decides what to send and when; this module holds the GraphQL
|
|
6
|
+
* documents, the labels and the pure draft helpers the Settings page uses.
|
|
7
|
+
* Kept free of React so the contract can be unit-tested.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type MtwCustomerNotificationEvent =
|
|
11
|
+
'BOOKED' | 'PICKED_UP' | 'OUT_FOR_DELIVERY' | 'DELIVERED' | 'DELAYED' | 'FAILED';
|
|
12
|
+
export type MtwCustomerNotificationChannel = 'EMAIL' | 'SMS';
|
|
13
|
+
export type MtwCustomerNotificationStatus = 'PENDING' | 'SENT' | 'FAILED' | 'SKIPPED';
|
|
14
|
+
|
|
15
|
+
export interface MtwCustomerNotificationSettings {
|
|
16
|
+
workspaceId: string;
|
|
17
|
+
enabled: boolean;
|
|
18
|
+
enabledAt: string | null;
|
|
19
|
+
emailEnabled: boolean;
|
|
20
|
+
smsEnabled: boolean;
|
|
21
|
+
events: MtwCustomerNotificationEvent[];
|
|
22
|
+
isCustomized: boolean;
|
|
23
|
+
updatedAt: string | null;
|
|
24
|
+
updatedBy: string | null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type MtwCustomerNotificationSettingsInput = Partial<
|
|
28
|
+
Pick<MtwCustomerNotificationSettings, 'enabled' | 'emailEnabled' | 'smsEnabled' | 'events'>
|
|
29
|
+
>;
|
|
30
|
+
|
|
31
|
+
export interface MtwCustomerNotification {
|
|
32
|
+
id: string;
|
|
33
|
+
shipmentId: string;
|
|
34
|
+
shipmentNumber: string | null;
|
|
35
|
+
customerId: string | null;
|
|
36
|
+
event: MtwCustomerNotificationEvent;
|
|
37
|
+
channel: MtwCustomerNotificationChannel;
|
|
38
|
+
status: MtwCustomerNotificationStatus;
|
|
39
|
+
recipient: string | null;
|
|
40
|
+
subject: string | null;
|
|
41
|
+
attempts: number;
|
|
42
|
+
lastError: string | null;
|
|
43
|
+
sentAt: string | null;
|
|
44
|
+
createdAt: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface MtwCustomerNotificationPreview {
|
|
48
|
+
event: MtwCustomerNotificationEvent;
|
|
49
|
+
subject: string;
|
|
50
|
+
emailText: string;
|
|
51
|
+
smsText: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Every event, in the order a shipment reaches them. */
|
|
55
|
+
export const CUSTOMER_NOTIFICATION_EVENTS: ReadonlyArray<{
|
|
56
|
+
value: MtwCustomerNotificationEvent;
|
|
57
|
+
label: string;
|
|
58
|
+
description: string;
|
|
59
|
+
}> = [
|
|
60
|
+
{ value: 'BOOKED', label: 'Booked', description: 'A shipment is created for the customer' },
|
|
61
|
+
{ value: 'PICKED_UP', label: 'Picked up', description: 'The shipment is collected' },
|
|
62
|
+
{
|
|
63
|
+
value: 'OUT_FOR_DELIVERY',
|
|
64
|
+
label: 'Out for delivery',
|
|
65
|
+
description: 'Tracking says the shipment is on its last leg',
|
|
66
|
+
},
|
|
67
|
+
{ value: 'DELIVERED', label: 'Delivered', description: 'The shipment is delivered' },
|
|
68
|
+
{
|
|
69
|
+
value: 'DELAYED',
|
|
70
|
+
label: 'Running late',
|
|
71
|
+
description: 'The risk monitor expects it to miss the promised delivery (once per promise)',
|
|
72
|
+
},
|
|
73
|
+
{ value: 'FAILED', label: 'Delivery failed', description: 'Delivery could not be completed' },
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
export function eventLabel(event: MtwCustomerNotificationEvent): string {
|
|
77
|
+
return CUSTOMER_NOTIFICATION_EVENTS.find((option) => option.value === event)?.label ?? event;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const STATUS_LABELS: Record<MtwCustomerNotificationStatus, string> = {
|
|
81
|
+
PENDING: 'Sending',
|
|
82
|
+
SENT: 'Sent',
|
|
83
|
+
FAILED: 'Failed',
|
|
84
|
+
SKIPPED: 'Not sent',
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/** Off, email only, every event — what a workspace sees until it saves. */
|
|
88
|
+
export function defaultNotificationSettings(workspaceId = ''): MtwCustomerNotificationSettings {
|
|
89
|
+
return {
|
|
90
|
+
workspaceId,
|
|
91
|
+
enabled: false,
|
|
92
|
+
enabledAt: null,
|
|
93
|
+
emailEnabled: true,
|
|
94
|
+
smsEnabled: false,
|
|
95
|
+
events: CUSTOMER_NOTIFICATION_EVENTS.map((option) => option.value),
|
|
96
|
+
isCustomized: false,
|
|
97
|
+
updatedAt: null,
|
|
98
|
+
updatedBy: null,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Events in canonical order without duplicates. */
|
|
103
|
+
export function orderedEvents(
|
|
104
|
+
events: readonly MtwCustomerNotificationEvent[]
|
|
105
|
+
): MtwCustomerNotificationEvent[] {
|
|
106
|
+
const wanted = new Set(events);
|
|
107
|
+
return CUSTOMER_NOTIFICATION_EVENTS.map((option) => option.value).filter((event) =>
|
|
108
|
+
wanted.has(event)
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Add or remove one event from a selection. */
|
|
113
|
+
export function toggleEvent(
|
|
114
|
+
events: readonly MtwCustomerNotificationEvent[],
|
|
115
|
+
event: MtwCustomerNotificationEvent,
|
|
116
|
+
on: boolean
|
|
117
|
+
): MtwCustomerNotificationEvent[] {
|
|
118
|
+
const next = new Set(events);
|
|
119
|
+
if (on) next.add(event);
|
|
120
|
+
else next.delete(event);
|
|
121
|
+
return orderedEvents([...next]);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Only the fields the draft changed, ready for the update mutation. */
|
|
125
|
+
export function changedNotificationSettings(
|
|
126
|
+
saved: MtwCustomerNotificationSettings,
|
|
127
|
+
draft: Pick<MtwCustomerNotificationSettings, 'enabled' | 'emailEnabled' | 'smsEnabled' | 'events'>
|
|
128
|
+
): MtwCustomerNotificationSettingsInput {
|
|
129
|
+
const changes: MtwCustomerNotificationSettingsInput = {};
|
|
130
|
+
if (draft.enabled !== saved.enabled) changes.enabled = draft.enabled;
|
|
131
|
+
if (draft.emailEnabled !== saved.emailEnabled) changes.emailEnabled = draft.emailEnabled;
|
|
132
|
+
if (draft.smsEnabled !== saved.smsEnabled) changes.smsEnabled = draft.smsEnabled;
|
|
133
|
+
const draftEvents = orderedEvents(draft.events);
|
|
134
|
+
const savedEvents = orderedEvents(saved.events);
|
|
135
|
+
if (draftEvents.join(',') !== savedEvents.join(',')) changes.events = draftEvents;
|
|
136
|
+
return changes;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Why the draft cannot be saved, or null. Switching on needs a channel and an event. */
|
|
140
|
+
export function notificationDraftError(
|
|
141
|
+
draft: Pick<MtwCustomerNotificationSettings, 'enabled' | 'emailEnabled' | 'smsEnabled' | 'events'>
|
|
142
|
+
): string | null {
|
|
143
|
+
if (!draft.enabled) return null;
|
|
144
|
+
if (!draft.emailEnabled && !draft.smsEnabled) return 'Choose email, SMS or both.';
|
|
145
|
+
if (draft.events.length === 0) return 'Choose at least one event to notify customers about.';
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const SETTINGS_FIELDS = `
|
|
150
|
+
workspaceId
|
|
151
|
+
enabled
|
|
152
|
+
enabledAt
|
|
153
|
+
emailEnabled
|
|
154
|
+
smsEnabled
|
|
155
|
+
events
|
|
156
|
+
isCustomized
|
|
157
|
+
updatedAt
|
|
158
|
+
updatedBy
|
|
159
|
+
`;
|
|
160
|
+
|
|
161
|
+
export const MTW_CUSTOMER_NOTIFICATION_SETTINGS_QUERY = `
|
|
162
|
+
query MtwCustomerNotificationSettings {
|
|
163
|
+
mtwCustomerNotificationSettings {${SETTINGS_FIELDS}}
|
|
164
|
+
}
|
|
165
|
+
`;
|
|
166
|
+
|
|
167
|
+
export const UPDATE_MTW_CUSTOMER_NOTIFICATION_SETTINGS_MUTATION = `
|
|
168
|
+
mutation UpdateMtwCustomerNotificationSettings($input: UpdateMtwCustomerNotificationSettingsInput!) {
|
|
169
|
+
updateMtwCustomerNotificationSettings(input: $input) {${SETTINGS_FIELDS}}
|
|
170
|
+
}
|
|
171
|
+
`;
|
|
172
|
+
|
|
173
|
+
export const MTW_CUSTOMER_NOTIFICATIONS_QUERY = `
|
|
174
|
+
query MtwCustomerNotifications(
|
|
175
|
+
$filter: MtwCustomerNotificationFilterInput
|
|
176
|
+
$pagination: PaginationInput
|
|
177
|
+
) {
|
|
178
|
+
mtwCustomerNotifications(filter: $filter, pagination: $pagination) {
|
|
179
|
+
total
|
|
180
|
+
hasMore
|
|
181
|
+
items {
|
|
182
|
+
id
|
|
183
|
+
shipmentId
|
|
184
|
+
shipmentNumber
|
|
185
|
+
customerId
|
|
186
|
+
event
|
|
187
|
+
channel
|
|
188
|
+
status
|
|
189
|
+
recipient
|
|
190
|
+
subject
|
|
191
|
+
attempts
|
|
192
|
+
lastError
|
|
193
|
+
sentAt
|
|
194
|
+
createdAt
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
`;
|
|
199
|
+
|
|
200
|
+
export const MTW_CUSTOMER_NOTIFICATION_PREVIEWS_QUERY = `
|
|
201
|
+
query MtwCustomerNotificationPreviews {
|
|
202
|
+
mtwCustomerNotificationPreviews {
|
|
203
|
+
event
|
|
204
|
+
subject
|
|
205
|
+
emailText
|
|
206
|
+
smsText
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
`;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Freight invoice review helpers (BOFF-10588): labels, the steps a viewer can
|
|
3
|
+
* take, and the exact mutation inputs. The server enforces every rule again;
|
|
4
|
+
* these only keep the page from offering a step that would be refused.
|
|
5
|
+
*/
|
|
6
|
+
import type {
|
|
7
|
+
DecideMtwFreightInvoiceInput,
|
|
8
|
+
HandOffMtwFreightInvoiceSettlementInput,
|
|
9
|
+
InvoiceReviewFragment,
|
|
10
|
+
MtwFreightInvoiceDecision,
|
|
11
|
+
MtwFreightInvoiceResolution,
|
|
12
|
+
MtwFreightInvoiceReviewAction,
|
|
13
|
+
MtwFreightInvoiceReviewStatus,
|
|
14
|
+
RecordMtwFreightInvoiceSupplierResponseInput,
|
|
15
|
+
ResolveMtwFreightInvoiceDisputeInput,
|
|
16
|
+
} from '../../generated/freight-invoice-operations';
|
|
17
|
+
|
|
18
|
+
export const REVIEW_STATUS_LABELS: Record<MtwFreightInvoiceReviewStatus, string> = {
|
|
19
|
+
AWAITING_REVIEW: 'Awaiting review',
|
|
20
|
+
ACCEPTED: 'Accepted',
|
|
21
|
+
ACCEPTED_WITH_VARIANCE: 'Accepted with variance',
|
|
22
|
+
DISPUTED: 'Disputed',
|
|
23
|
+
SUPPLIER_RESPONDED: 'Supplier responded',
|
|
24
|
+
SETTLEMENT_HANDED_OFF: 'Handed to Billing',
|
|
25
|
+
SUPERSEDED: 'Superseded by a correction',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const REVIEW_EVENT_LABELS: Record<InvoiceReviewFragment['events'][number]['kind'], string> =
|
|
29
|
+
{
|
|
30
|
+
DECISION: 'Review decision',
|
|
31
|
+
SUPPLIER_RESPONSE: 'Supplier response',
|
|
32
|
+
DISPUTE_RESOLUTION: 'Dispute resolved',
|
|
33
|
+
SETTLEMENT_HANDOFF: 'Handed to Billing for settlement',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** Statuses a reviewer can filter the queue by; SUPERSEDED never appears in the queue. */
|
|
37
|
+
export const REVIEW_QUEUE_STATUSES: MtwFreightInvoiceReviewStatus[] = [
|
|
38
|
+
'AWAITING_REVIEW',
|
|
39
|
+
'DISPUTED',
|
|
40
|
+
'SUPPLIER_RESPONDED',
|
|
41
|
+
'ACCEPTED',
|
|
42
|
+
'ACCEPTED_WITH_VARIANCE',
|
|
43
|
+
'SETTLEMENT_HANDED_OFF',
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
export interface ReviewPermissions {
|
|
47
|
+
canRead: boolean;
|
|
48
|
+
canUpdate: boolean;
|
|
49
|
+
canApprove: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const PERMISSION_FOR_ACTION: Record<MtwFreightInvoiceReviewAction, keyof ReviewPermissions> = {
|
|
53
|
+
DECIDE: 'canApprove',
|
|
54
|
+
RECORD_SUPPLIER_RESPONSE: 'canUpdate',
|
|
55
|
+
RESOLVE_DISPUTE: 'canApprove',
|
|
56
|
+
HAND_OFF_SETTLEMENT: 'canApprove',
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** Steps the server says are open (author separation included) that this viewer is also permitted to take. */
|
|
60
|
+
export function availableReviewActions(
|
|
61
|
+
review: Pick<InvoiceReviewFragment, 'allowedActions'>,
|
|
62
|
+
permissions: ReviewPermissions
|
|
63
|
+
): MtwFreightInvoiceReviewAction[] {
|
|
64
|
+
return review.allowedActions.filter((action) => permissions[PERMISSION_FOR_ACTION[action]]);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Why the author sees no decision: the server withholds it, so say so instead of showing nothing. */
|
|
68
|
+
export function authorNotice(
|
|
69
|
+
review: Pick<InvoiceReviewFragment, 'viewerIsAuthor' | 'status'>
|
|
70
|
+
): string | null {
|
|
71
|
+
if (!review.viewerIsAuthor) return null;
|
|
72
|
+
if (
|
|
73
|
+
review.status === 'AWAITING_REVIEW' ||
|
|
74
|
+
review.status === 'DISPUTED' ||
|
|
75
|
+
review.status === 'SUPPLIER_RESPONDED'
|
|
76
|
+
)
|
|
77
|
+
return 'You recorded this comparison, so another reviewer must accept, dispute or resolve it.';
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function text(value: string): string | null {
|
|
82
|
+
const trimmed = value.trim();
|
|
83
|
+
return trimmed ? trimmed : null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
interface Common {
|
|
87
|
+
review: Pick<InvoiceReviewFragment, 'shipmentId' | 'comparisonId' | 'sequence'>;
|
|
88
|
+
idempotencyKey: string;
|
|
89
|
+
}
|
|
90
|
+
function common({ review, idempotencyKey }: Common) {
|
|
91
|
+
return {
|
|
92
|
+
shipmentId: review.shipmentId,
|
|
93
|
+
comparisonId: review.comparisonId,
|
|
94
|
+
expectedSequence: review.sequence,
|
|
95
|
+
idempotencyKey,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export type DraftProblem = 'REASON_REQUIRED' | 'LINES_REQUIRED' | 'RESPONSE_REQUIRED';
|
|
100
|
+
export const DRAFT_PROBLEM_LABELS: Record<DraftProblem, string> = {
|
|
101
|
+
REASON_REQUIRED: 'Give a reason for a dispute, a variance or a resolution.',
|
|
102
|
+
LINES_REQUIRED: 'Choose at least one invoice line you dispute.',
|
|
103
|
+
RESPONSE_REQUIRED: "Record the supplier's response.",
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export function decisionInput(
|
|
107
|
+
base: Common,
|
|
108
|
+
decision: MtwFreightInvoiceDecision,
|
|
109
|
+
reason: string,
|
|
110
|
+
disputedLineIds: readonly string[]
|
|
111
|
+
): DecideMtwFreightInvoiceInput | DraftProblem {
|
|
112
|
+
const why = text(reason);
|
|
113
|
+
if (decision !== 'ACCEPTED' && !why) return 'REASON_REQUIRED';
|
|
114
|
+
if (decision === 'DISPUTED' && disputedLineIds.length === 0) return 'LINES_REQUIRED';
|
|
115
|
+
return {
|
|
116
|
+
...common(base),
|
|
117
|
+
decision,
|
|
118
|
+
reason: why,
|
|
119
|
+
disputedLineIds: decision === 'DISPUTED' ? [...new Set(disputedLineIds)] : [],
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function supplierResponseInput(
|
|
124
|
+
base: Common,
|
|
125
|
+
response: string
|
|
126
|
+
): RecordMtwFreightInvoiceSupplierResponseInput | DraftProblem {
|
|
127
|
+
const said = text(response);
|
|
128
|
+
return said ? { ...common(base), response: said } : 'RESPONSE_REQUIRED';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function resolutionInput(
|
|
132
|
+
base: Common,
|
|
133
|
+
resolution: MtwFreightInvoiceResolution,
|
|
134
|
+
reason: string
|
|
135
|
+
): ResolveMtwFreightInvoiceDisputeInput | DraftProblem {
|
|
136
|
+
const why = text(reason);
|
|
137
|
+
return why ? { ...common(base), resolution, reason: why } : 'REASON_REQUIRED';
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function handOffInput(base: Common, note: string): HandOffMtwFreightInvoiceSettlementInput {
|
|
141
|
+
return { ...common(base), note: text(note) };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function isDraftProblem(value: unknown): value is DraftProblem {
|
|
145
|
+
return typeof value === 'string' && value in DRAFT_PROBLEM_LABELS;
|
|
146
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
fragment InvoiceReview on MtwFreightInvoiceReview {
|
|
2
|
+
comparisonId shipmentId invoiceIdentityId revision latestRevision supersededByComparisonId
|
|
3
|
+
supplierReference supplierLabel invoiceReference currency { code minorUnitDigits }
|
|
4
|
+
knownInvoiceAmountMinor invoiceAmountComplete comparisonRecordedAt comparisonCreatedBy
|
|
5
|
+
status resolvedByCorrection sequence allowedActions viewerIsAuthor settlementReference
|
|
6
|
+
events { id sequence kind decision statusAfter reason disputedLineIds settlementReference recordedAt createdBy }
|
|
7
|
+
}
|
|
8
|
+
query InvoiceReviewExact($shipmentId: ID!, $comparisonId: ID!) {
|
|
9
|
+
mtwFreightInvoiceReview(shipmentId: $shipmentId, comparisonId: $comparisonId) { ...InvoiceReview }
|
|
10
|
+
}
|
|
11
|
+
query InvoiceReviewQueue($status: MtwFreightInvoiceReviewStatus, $pagination: MtwContainerPagination) {
|
|
12
|
+
mtwFreightInvoiceReviewPage(status: $status, pagination: $pagination) {
|
|
13
|
+
items { ...InvoiceReview } total limit offset hasMore nextOffset
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
query InvoiceReviewSummary {
|
|
17
|
+
mtwFreightInvoiceReviewSummary { total awaitingReview accepted acceptedWithVariance disputed supplierResponded settlementHandedOff }
|
|
18
|
+
}
|
|
19
|
+
mutation DecideInvoiceReview($input: DecideMtwFreightInvoiceInput!) { decideMtwFreightInvoice(input: $input) { ...InvoiceReview } }
|
|
20
|
+
mutation RecordInvoiceSupplierResponse($input: RecordMtwFreightInvoiceSupplierResponseInput!) {
|
|
21
|
+
recordMtwFreightInvoiceSupplierResponse(input: $input) { ...InvoiceReview }
|
|
22
|
+
}
|
|
23
|
+
mutation ResolveInvoiceDispute($input: ResolveMtwFreightInvoiceDisputeInput!) { resolveMtwFreightInvoiceDispute(input: $input) { ...InvoiceReview } }
|
|
24
|
+
mutation HandOffInvoiceSettlement($input: HandOffMtwFreightInvoiceSettlementInput!) {
|
|
25
|
+
handOffMtwFreightInvoiceSettlement(input: $input) { ...InvoiceReview }
|
|
26
|
+
}
|