@burdenoff/microfe-movethewheels 2026.1102.1 → 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/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/hooks/useInvoiceComparisonRecovery.js +26 -26
- package/dist/hooks/useInvoiceReviewPermissions.js +14 -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/utils/invoiceReview.d.ts +26 -0
- package/dist/pages/FinancePage.js +109 -67
- package/dist/pages/FreightEstimatePage.js +89 -89
- package/dist/pages/FreightInvoicePage.js +138 -120
- package/dist/src/generated/freight-invoice-operations.js +2100 -1
- package/dist/utils/invoiceReview.js +84 -0
- package/package.json +2 -2
- package/src/generated/freight-invoice-operations.ts +245 -1
- package/src/movethewheels/components/freight-invoice/InvoiceReviewPanel.tsx +319 -0
- package/src/movethewheels/components/freight-invoice/InvoiceReviewQueue.tsx +96 -0
- package/src/movethewheels/hooks/useInvoiceReviewPermissions.ts +25 -0
- package/src/movethewheels/pages/FinancePage.tsx +34 -2
- package/src/movethewheels/pages/FreightInvoicePage.tsx +44 -3
- package/src/movethewheels/utils/invoiceReview.ts +146 -0
- package/src/operations/freight-invoice/review.graphql +26 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Button as e } from "../ui.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { FreightSelect as t, FreightText as n } from "./FreightFields.js";
|
|
3
|
+
import { freightInstant as r, freightLocalTime as i } from "../../utils/freightDraft.js";
|
|
4
|
+
import { newCommercialFact as a } from "../../utils/freightCommercial.js";
|
|
5
5
|
import { useState as o } from "react";
|
|
6
6
|
import { Fragment as s, jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
7
7
|
//#region src/movethewheels/components/freight-cost/FreightCommercialFactEditor.tsx
|
|
8
|
-
function u({ slot:
|
|
9
|
-
let [p, m] = o(!1), h =
|
|
10
|
-
...
|
|
8
|
+
function u({ slot: r, value: i, onChange: s, evidence: u }) {
|
|
9
|
+
let [p, m] = o(!1), h = r[0].toUpperCase() + r.slice(1), g = (e) => s({
|
|
10
|
+
...i,
|
|
11
11
|
...e
|
|
12
12
|
});
|
|
13
13
|
return /* @__PURE__ */ l("fieldset", {
|
|
@@ -17,11 +17,11 @@ function u({ slot: i, value: a, onChange: s, evidence: u }) {
|
|
|
17
17
|
className: "px-1 font-semibold",
|
|
18
18
|
children: h
|
|
19
19
|
}),
|
|
20
|
-
/* @__PURE__ */ l(
|
|
20
|
+
/* @__PURE__ */ l(t, {
|
|
21
21
|
label: `${h} status`,
|
|
22
|
-
value:
|
|
22
|
+
value: i.status,
|
|
23
23
|
onChange: (e) => {
|
|
24
|
-
(e === "UNKNOWN" || e === "SOURCED" || e === "NOT_APPLICABLE") && (m(!1), s(
|
|
24
|
+
(e === "UNKNOWN" || e === "SOURCED" || e === "NOT_APPLICABLE") && (m(!1), s(a(e)));
|
|
25
25
|
},
|
|
26
26
|
children: [
|
|
27
27
|
/* @__PURE__ */ c("option", {
|
|
@@ -38,9 +38,9 @@ function u({ slot: i, value: a, onChange: s, evidence: u }) {
|
|
|
38
38
|
})
|
|
39
39
|
]
|
|
40
40
|
}),
|
|
41
|
-
|
|
41
|
+
i.status === "UNKNOWN" && !p && /* @__PURE__ */ l("div", {
|
|
42
42
|
className: "flex flex-wrap items-center gap-2",
|
|
43
|
-
children: [/* @__PURE__ */ c("p", { children:
|
|
43
|
+
children: [/* @__PURE__ */ c("p", { children: i.reason }), /* @__PURE__ */ l(e, {
|
|
44
44
|
type: "button",
|
|
45
45
|
variant: "outline",
|
|
46
46
|
onClick: () => m(!0),
|
|
@@ -51,31 +51,31 @@ function u({ slot: i, value: a, onChange: s, evidence: u }) {
|
|
|
51
51
|
]
|
|
52
52
|
})]
|
|
53
53
|
}),
|
|
54
|
-
(
|
|
54
|
+
(i.status === "NOT_APPLICABLE" || i.status === "UNKNOWN" && p) && /* @__PURE__ */ c(n, {
|
|
55
55
|
label: `${h} reason`,
|
|
56
|
-
value:
|
|
56
|
+
value: i.reason,
|
|
57
57
|
required: !0,
|
|
58
58
|
onChange: (e) => g({ reason: e })
|
|
59
59
|
}),
|
|
60
|
-
|
|
61
|
-
slot:
|
|
60
|
+
i.status === "SOURCED" && /* @__PURE__ */ c(d, {
|
|
61
|
+
slot: r,
|
|
62
62
|
label: h,
|
|
63
|
-
value:
|
|
63
|
+
value: i,
|
|
64
64
|
patch: g
|
|
65
65
|
}),
|
|
66
|
-
|
|
66
|
+
i.status !== "UNKNOWN" && /* @__PURE__ */ c(f, {
|
|
67
67
|
label: h,
|
|
68
|
-
value:
|
|
68
|
+
value: i,
|
|
69
69
|
patch: g,
|
|
70
70
|
evidence: u
|
|
71
71
|
})
|
|
72
72
|
]
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
|
-
function d({ slot: e, label:
|
|
75
|
+
function d({ slot: e, label: r, value: i, patch: a }) {
|
|
76
76
|
return /* @__PURE__ */ l(s, { children: [
|
|
77
|
-
(e === "origin" || e === "destination") && /* @__PURE__ */ l(s, { children: [/* @__PURE__ */ l(
|
|
78
|
-
label: `${
|
|
77
|
+
(e === "origin" || e === "destination") && /* @__PURE__ */ l(s, { children: [/* @__PURE__ */ l(t, {
|
|
78
|
+
label: `${r} label kind`,
|
|
79
79
|
value: i.kind,
|
|
80
80
|
onChange: (e) => a({ kind: e }),
|
|
81
81
|
children: [/* @__PURE__ */ c("option", {
|
|
@@ -91,32 +91,32 @@ function d({ slot: e, label: t, value: i, patch: a }) {
|
|
|
91
91
|
value: e,
|
|
92
92
|
children: e.replaceAll("_", " ").toLowerCase()
|
|
93
93
|
}, e))]
|
|
94
|
-
}), /* @__PURE__ */ c(
|
|
95
|
-
label: `${
|
|
94
|
+
}), /* @__PURE__ */ c(n, {
|
|
95
|
+
label: `${r} jurisdiction label (optional)`,
|
|
96
96
|
value: i.jurisdiction,
|
|
97
97
|
onChange: (e) => a({ jurisdiction: e })
|
|
98
98
|
})] }),
|
|
99
|
-
e === "carrier" && /* @__PURE__ */ c(
|
|
99
|
+
e === "carrier" && /* @__PURE__ */ c(n, {
|
|
100
100
|
label: "Carrier reference",
|
|
101
101
|
value: i.reference,
|
|
102
102
|
required: !0,
|
|
103
103
|
onChange: (e) => a({ reference: e })
|
|
104
104
|
}),
|
|
105
|
-
e !== "weight" && e !== "volume" && /* @__PURE__ */ c(
|
|
106
|
-
label: e === "cargo" ? "Cargo classification label" : e === "equipment" ? "Equipment type label" : `${
|
|
105
|
+
e !== "weight" && e !== "volume" && /* @__PURE__ */ c(n, {
|
|
106
|
+
label: e === "cargo" ? "Cargo classification label" : e === "equipment" ? "Equipment type label" : `${r} label`,
|
|
107
107
|
value: i.label,
|
|
108
108
|
required: !0,
|
|
109
109
|
onChange: (e) => a({ label: e })
|
|
110
110
|
}),
|
|
111
|
-
e === "cargo" && /* @__PURE__ */ c(
|
|
111
|
+
e === "cargo" && /* @__PURE__ */ c(n, {
|
|
112
112
|
label: "Cargo description",
|
|
113
113
|
value: i.description,
|
|
114
114
|
required: !0,
|
|
115
115
|
onChange: (e) => a({ description: e })
|
|
116
116
|
}),
|
|
117
117
|
(e === "weight" || e === "volume") && /* @__PURE__ */ l(s, { children: [
|
|
118
|
-
/* @__PURE__ */ c(
|
|
119
|
-
label: `${
|
|
118
|
+
/* @__PURE__ */ c(n, {
|
|
119
|
+
label: `${r} amount`,
|
|
120
120
|
value: i.amount,
|
|
121
121
|
required: !0,
|
|
122
122
|
onChange: (e) => a({
|
|
@@ -124,8 +124,8 @@ function d({ slot: e, label: t, value: i, patch: a }) {
|
|
|
124
124
|
zeroReason: e === "0" ? i.zeroReason : ""
|
|
125
125
|
})
|
|
126
126
|
}),
|
|
127
|
-
/* @__PURE__ */ l(
|
|
128
|
-
label: `${
|
|
127
|
+
/* @__PURE__ */ l(t, {
|
|
128
|
+
label: `${r} recorded unit`,
|
|
129
129
|
value: i.unit,
|
|
130
130
|
onChange: (e) => a({ unit: e }),
|
|
131
131
|
children: [/* @__PURE__ */ c("option", {
|
|
@@ -140,8 +140,8 @@ function d({ slot: e, label: t, value: i, patch: a }) {
|
|
|
140
140
|
children: e
|
|
141
141
|
}, e))]
|
|
142
142
|
}),
|
|
143
|
-
i.amount === "0" && /* @__PURE__ */ c(
|
|
144
|
-
label: `${
|
|
143
|
+
i.amount === "0" && /* @__PURE__ */ c(n, {
|
|
144
|
+
label: `${r} recorded zero reason`,
|
|
145
145
|
value: i.zeroReason,
|
|
146
146
|
required: !0,
|
|
147
147
|
onChange: (e) => a({ zeroReason: e })
|
|
@@ -151,7 +151,7 @@ function d({ slot: e, label: t, value: i, patch: a }) {
|
|
|
151
151
|
children: "Record the source unit. No conversion is inferred."
|
|
152
152
|
})
|
|
153
153
|
] }),
|
|
154
|
-
e === "equipment" && /* @__PURE__ */ c(
|
|
154
|
+
e === "equipment" && /* @__PURE__ */ c(n, {
|
|
155
155
|
label: "Equipment count",
|
|
156
156
|
value: i.count,
|
|
157
157
|
required: !0,
|
|
@@ -159,29 +159,29 @@ function d({ slot: e, label: t, value: i, patch: a }) {
|
|
|
159
159
|
})
|
|
160
160
|
] });
|
|
161
161
|
}
|
|
162
|
-
function f({ label: e, value: t, patch:
|
|
162
|
+
function f({ label: e, value: t, patch: a, evidence: o }) {
|
|
163
163
|
return /* @__PURE__ */ l(s, { children: [
|
|
164
164
|
/* @__PURE__ */ l("h4", {
|
|
165
165
|
className: "font-medium",
|
|
166
166
|
children: [e, " source declaration"]
|
|
167
167
|
}),
|
|
168
|
-
/* @__PURE__ */ c(
|
|
168
|
+
/* @__PURE__ */ c(n, {
|
|
169
169
|
label: `${e} source reference`,
|
|
170
170
|
value: t.sourceReference,
|
|
171
171
|
required: !0,
|
|
172
|
-
onChange: (e) =>
|
|
172
|
+
onChange: (e) => a({ sourceReference: e })
|
|
173
173
|
}),
|
|
174
|
-
/* @__PURE__ */ c(
|
|
174
|
+
/* @__PURE__ */ c(n, {
|
|
175
175
|
label: `${e} source revision`,
|
|
176
176
|
value: t.sourceRevision,
|
|
177
177
|
required: !0,
|
|
178
|
-
onChange: (e) =>
|
|
178
|
+
onChange: (e) => a({ sourceRevision: e })
|
|
179
179
|
}),
|
|
180
|
-
/* @__PURE__ */ c(
|
|
180
|
+
/* @__PURE__ */ c(n, {
|
|
181
181
|
label: `${e} observed at (local time, optional)`,
|
|
182
182
|
type: "datetime-local",
|
|
183
|
-
value: t.observedAt ?
|
|
184
|
-
onChange: (e) =>
|
|
183
|
+
value: t.observedAt ? i(t.observedAt) : "",
|
|
184
|
+
onChange: (e) => a({ observedAt: e ? r(e) : "" })
|
|
185
185
|
}),
|
|
186
186
|
/* @__PURE__ */ c("p", {
|
|
187
187
|
className: "text-sm",
|
|
@@ -192,14 +192,14 @@ function f({ label: e, value: t, patch: n, evidence: o }) {
|
|
|
192
192
|
children: "Choose 1–2 versions from the evidence selected below for this record. This declares the source; it does not verify its authority."
|
|
193
193
|
}),
|
|
194
194
|
o.map((e) => {
|
|
195
|
-
let
|
|
195
|
+
let n = t.evidenceLinks.some((t) => t.fileId === e.fileId && t.versionId === e.versionId);
|
|
196
196
|
return /* @__PURE__ */ l("label", {
|
|
197
197
|
className: "flex min-h-11 items-center gap-2 break-words text-sm",
|
|
198
198
|
children: [/* @__PURE__ */ c("input", {
|
|
199
199
|
type: "checkbox",
|
|
200
|
-
checked:
|
|
201
|
-
disabled: !
|
|
202
|
-
onChange: () =>
|
|
200
|
+
checked: n,
|
|
201
|
+
disabled: !n && t.evidenceLinks.length >= 2,
|
|
202
|
+
onChange: () => a({ evidenceLinks: n ? t.evidenceLinks.filter((t) => !(t.fileId === e.fileId && t.versionId === e.versionId)) : [...t.evidenceLinks, {
|
|
203
203
|
fileId: e.fileId,
|
|
204
204
|
versionId: e.versionId
|
|
205
205
|
}] })
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { freightLabel as e } from "../../utils/freightDraft.js";
|
|
2
|
+
import { commercialSlots as t } from "../../utils/freightCommercial.js";
|
|
3
3
|
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
4
4
|
//#region src/movethewheels/components/freight-cost/FreightCommercialHistory.tsx
|
|
5
5
|
function i({ capture: i }) {
|
|
@@ -19,17 +19,17 @@ function i({ capture: i }) {
|
|
|
19
19
|
}),
|
|
20
20
|
/* @__PURE__ */ n("div", {
|
|
21
21
|
className: "grid min-w-0 gap-3 md:grid-cols-2",
|
|
22
|
-
children:
|
|
23
|
-
let a = i[
|
|
22
|
+
children: t.map((t) => {
|
|
23
|
+
let a = i[t];
|
|
24
24
|
return /* @__PURE__ */ r("article", {
|
|
25
25
|
className: "space-y-2 rounded border p-3",
|
|
26
26
|
children: [
|
|
27
27
|
/* @__PURE__ */ r("h4", {
|
|
28
28
|
className: "font-medium",
|
|
29
29
|
children: [
|
|
30
|
-
t
|
|
30
|
+
e(t),
|
|
31
31
|
" — ",
|
|
32
|
-
|
|
32
|
+
e(a.status)
|
|
33
33
|
]
|
|
34
34
|
}),
|
|
35
35
|
a.reason && /* @__PURE__ */ n("p", { children: a.reason }),
|
|
@@ -61,7 +61,7 @@ function i({ capture: i }) {
|
|
|
61
61
|
] }, JSON.stringify([e.fileId, e.versionId])))]
|
|
62
62
|
})
|
|
63
63
|
]
|
|
64
|
-
},
|
|
64
|
+
}, t);
|
|
65
65
|
})
|
|
66
66
|
}),
|
|
67
67
|
/* @__PURE__ */ r("section", {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Button as e } from "../ui.js";
|
|
2
2
|
import { ContainerEvidencePicker as t } from "../ContainerEvidencePicker.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { FreightCurrency as n, FreightSelect as r, FreightText as i } from "./FreightFields.js";
|
|
4
|
+
import { freightInstant as a, freightLocalTime as o, newFreightFacts as s, newFreightLine as c } from "../../utils/freightDraft.js";
|
|
5
|
+
import { commercialCanonical as l, commercialCaptureInput as u } from "../../utils/freightCommercial.js";
|
|
6
6
|
import { FreightCommercialEditor as d } from "./FreightCommercialEditor.js";
|
|
7
7
|
import { FreightLineEditor as f } from "./FreightLineEditor.js";
|
|
8
8
|
import { FreightScopeEditor as p } from "./FreightScopeEditor.js";
|
|
@@ -12,7 +12,7 @@ import { useRef as g, useState as _ } from "react";
|
|
|
12
12
|
import { jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
13
13
|
//#region src/movethewheels/components/freight-cost/FreightDraftForm.tsx
|
|
14
14
|
function b({ disabled: b, shipmentId: x, onPreview: S, onSave: C }) {
|
|
15
|
-
let [w, T] = _(null), [E, D] = _(
|
|
15
|
+
let [w, T] = _(null), [E, D] = _(s), [O, k] = _([]), [A, j] = _(null), [M, N] = _(null), [P, F] = _(!1), I = g(!1), L = JSON.stringify(E), R = A?.body === L ? A.value : null, z = (e) => D({
|
|
16
16
|
...E,
|
|
17
17
|
...e
|
|
18
18
|
}), B = async () => {
|
|
@@ -40,8 +40,8 @@ function b({ disabled: b, shipmentId: x, onPreview: S, onSave: C }) {
|
|
|
40
40
|
}
|
|
41
41
|
N(null);
|
|
42
42
|
try {
|
|
43
|
-
if (w && w.reviewed !==
|
|
44
|
-
let e = w ?
|
|
43
|
+
if (w && w.reviewed !== l(w.selectedLegs)) throw Error("Explicitly review the commercial leg selection before saving.");
|
|
44
|
+
let e = w ? u(w.facts, w.selectedLegs, O) : void 0;
|
|
45
45
|
C(structuredClone(E), structuredClone(O), e).catch((e) => N(e instanceof Error ? e.message : "Unable to save this cost record."));
|
|
46
46
|
} catch (e) {
|
|
47
47
|
N(e instanceof Error ? e.message : "Review commercial facts before saving.");
|
|
@@ -59,13 +59,13 @@ function b({ disabled: b, shipmentId: x, onPreview: S, onSave: C }) {
|
|
|
59
59
|
/* @__PURE__ */ y("div", {
|
|
60
60
|
className: "grid gap-3 md:grid-cols-2",
|
|
61
61
|
children: [
|
|
62
|
-
/* @__PURE__ */ v(
|
|
62
|
+
/* @__PURE__ */ v(i, {
|
|
63
63
|
label: "Estimate title",
|
|
64
64
|
value: E.title,
|
|
65
65
|
required: !0,
|
|
66
66
|
onChange: (e) => z({ title: e })
|
|
67
67
|
}),
|
|
68
|
-
/* @__PURE__ */ y(
|
|
68
|
+
/* @__PURE__ */ y(r, {
|
|
69
69
|
label: "Record type",
|
|
70
70
|
value: E.kind,
|
|
71
71
|
onChange: (e) => {
|
|
@@ -79,13 +79,13 @@ function b({ disabled: b, shipmentId: x, onPreview: S, onSave: C }) {
|
|
|
79
79
|
children: "Reported quote"
|
|
80
80
|
})]
|
|
81
81
|
}),
|
|
82
|
-
/* @__PURE__ */ v(
|
|
82
|
+
/* @__PURE__ */ v(i, {
|
|
83
83
|
label: "Commercial source",
|
|
84
84
|
value: E.sourceReference,
|
|
85
85
|
required: !0,
|
|
86
86
|
onChange: (e) => z({ sourceReference: e })
|
|
87
87
|
}),
|
|
88
|
-
/* @__PURE__ */ v(
|
|
88
|
+
/* @__PURE__ */ v(i, {
|
|
89
89
|
label: "Source version",
|
|
90
90
|
value: E.sourceRevision,
|
|
91
91
|
required: !0,
|
|
@@ -93,7 +93,7 @@ function b({ disabled: b, shipmentId: x, onPreview: S, onSave: C }) {
|
|
|
93
93
|
})
|
|
94
94
|
]
|
|
95
95
|
}),
|
|
96
|
-
/* @__PURE__ */ v(
|
|
96
|
+
/* @__PURE__ */ v(i, {
|
|
97
97
|
label: "Shipment and services covered by this source",
|
|
98
98
|
value: E.commercialScope,
|
|
99
99
|
required: !0,
|
|
@@ -107,13 +107,13 @@ function b({ disabled: b, shipmentId: x, onPreview: S, onSave: C }) {
|
|
|
107
107
|
". They are recorded as exact UTC instants."
|
|
108
108
|
]
|
|
109
109
|
}),
|
|
110
|
-
/* @__PURE__ */ v(
|
|
110
|
+
/* @__PURE__ */ v(i, {
|
|
111
111
|
label: "Price applies at",
|
|
112
112
|
type: "datetime-local",
|
|
113
|
-
value:
|
|
113
|
+
value: o(E.pricingAt),
|
|
114
114
|
required: !0,
|
|
115
115
|
onChange: (e) => {
|
|
116
|
-
e && z({ pricingAt:
|
|
116
|
+
e && z({ pricingAt: a(e) });
|
|
117
117
|
}
|
|
118
118
|
}),
|
|
119
119
|
/* @__PURE__ */ y("section", {
|
|
@@ -137,7 +137,7 @@ function b({ disabled: b, shipmentId: x, onPreview: S, onSave: C }) {
|
|
|
137
137
|
type: "button",
|
|
138
138
|
variant: "outline",
|
|
139
139
|
disabled: E.lines.length >= 100,
|
|
140
|
-
onClick: () => z({ lines: [...E.lines,
|
|
140
|
+
onClick: () => z({ lines: [...E.lines, c()] }),
|
|
141
141
|
children: "Add cost item"
|
|
142
142
|
})
|
|
143
143
|
]
|
|
@@ -162,14 +162,14 @@ function b({ disabled: b, shipmentId: x, onPreview: S, onSave: C }) {
|
|
|
162
162
|
"quotedAt",
|
|
163
163
|
"validFrom",
|
|
164
164
|
"validUntil"
|
|
165
|
-
].map((e) => /* @__PURE__ */ v(
|
|
165
|
+
].map((e) => /* @__PURE__ */ v(i, {
|
|
166
166
|
label: e === "quotedAt" ? "Quote received at" : e === "validFrom" ? "Quote valid from" : "Quote valid until",
|
|
167
167
|
type: "datetime-local",
|
|
168
|
-
value: E[e] ?
|
|
169
|
-
onChange: (t) => z({ [e]: t ?
|
|
168
|
+
value: E[e] ? o(E[e]) : "",
|
|
169
|
+
onChange: (t) => z({ [e]: t ? a(t) : null })
|
|
170
170
|
}, e))]
|
|
171
171
|
}),
|
|
172
|
-
/* @__PURE__ */ y(
|
|
172
|
+
/* @__PURE__ */ y(r, {
|
|
173
173
|
label: "Reporting total",
|
|
174
174
|
value: E.reportingCurrency ? "RECORDED" : "UNKNOWN",
|
|
175
175
|
onChange: (e) => z({ reportingCurrency: e === "RECORDED" ? {
|
|
@@ -184,7 +184,7 @@ function b({ disabled: b, shipmentId: x, onPreview: S, onSave: C }) {
|
|
|
184
184
|
children: "Use a recorded currency convention"
|
|
185
185
|
})]
|
|
186
186
|
}),
|
|
187
|
-
E.reportingCurrency && /* @__PURE__ */ v(
|
|
187
|
+
E.reportingCurrency && /* @__PURE__ */ v(n, {
|
|
188
188
|
label: "Reporting currency convention",
|
|
189
189
|
value: E.reportingCurrency,
|
|
190
190
|
onChange: (e) => z({ reportingCurrency: e })
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { Button as e } from "../ui.js";
|
|
2
|
+
import { useConditionApi as t, useConditionQuery as n } from "../../hooks/useContainerCondition.js";
|
|
3
|
+
import { ReuseState as r, reuseCard as i } from "../ReuseControls.js";
|
|
4
|
+
import { FreightSelect as a } from "../freight-cost/FreightFields.js";
|
|
5
|
+
import { DecideInvoiceReviewDocument as o, HandOffInvoiceSettlementDocument as s, InvoiceReviewExactDocument as c, RecordInvoiceSupplierResponseDocument as l, ResolveInvoiceDisputeDocument as u } from "../../src/generated/freight-invoice-operations.js";
|
|
6
|
+
import { invoiceMoney as d } from "../../utils/invoiceComparison.js";
|
|
7
|
+
import { DRAFT_PROBLEM_LABELS as f, REVIEW_EVENT_LABELS as p, REVIEW_STATUS_LABELS as m, authorNotice as h, availableReviewActions as g, decisionInput as _, handOffInput as v, isDraftProblem as y, resolutionInput as b, supplierResponseInput as x } from "../../utils/invoiceReview.js";
|
|
8
|
+
import { useState as S } from "react";
|
|
9
|
+
import { Fragment as C, jsx as w, jsxs as T } from "react/jsx-runtime";
|
|
10
|
+
//#region src/movethewheels/components/freight-invoice/InvoiceReviewPanel.tsx
|
|
11
|
+
var E = "mt-1 min-h-24 w-full rounded border p-2";
|
|
12
|
+
function D({ record: e, permissions: t, onChanged: a }) {
|
|
13
|
+
let [o, s] = S(0), l = n(c, t.canRead ? {
|
|
14
|
+
shipmentId: e.shipmentId,
|
|
15
|
+
comparisonId: e.id
|
|
16
|
+
} : null, o);
|
|
17
|
+
if (!t.canRead) return /* @__PURE__ */ T("section", {
|
|
18
|
+
className: i,
|
|
19
|
+
"aria-label": "Invoice review",
|
|
20
|
+
children: [/* @__PURE__ */ w("h2", {
|
|
21
|
+
className: "text-lg font-semibold",
|
|
22
|
+
children: "Invoice review"
|
|
23
|
+
}), /* @__PURE__ */ w("p", { children: "Freight invoice read permission is required to see the review status of this invoice." })]
|
|
24
|
+
});
|
|
25
|
+
let u = l.data?.mtwFreightInvoiceReview;
|
|
26
|
+
return /* @__PURE__ */ T("section", {
|
|
27
|
+
className: i,
|
|
28
|
+
"aria-label": "Invoice review",
|
|
29
|
+
children: [
|
|
30
|
+
/* @__PURE__ */ w("h2", {
|
|
31
|
+
className: "text-lg font-semibold",
|
|
32
|
+
children: "Invoice review"
|
|
33
|
+
}),
|
|
34
|
+
/* @__PURE__ */ w(r, {
|
|
35
|
+
loading: l.loading,
|
|
36
|
+
error: l.error
|
|
37
|
+
}),
|
|
38
|
+
u && /* @__PURE__ */ w(O, {
|
|
39
|
+
review: u,
|
|
40
|
+
lines: e.facts.rows,
|
|
41
|
+
permissions: t,
|
|
42
|
+
onRecorded: () => {
|
|
43
|
+
s((e) => e + 1), a?.();
|
|
44
|
+
}
|
|
45
|
+
}, `${u.comparisonId}:${u.sequence}`)
|
|
46
|
+
]
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function O({ review: e, lines: t, permissions: n, onRecorded: r }) {
|
|
50
|
+
let i = g(e, n), a = h(e), o = (e) => t.find((t) => t.id === e)?.description ?? e;
|
|
51
|
+
return /* @__PURE__ */ T("div", {
|
|
52
|
+
className: "space-y-4",
|
|
53
|
+
children: [
|
|
54
|
+
/* @__PURE__ */ T("p", {
|
|
55
|
+
className: "font-medium",
|
|
56
|
+
role: "status",
|
|
57
|
+
children: ["Status: ", m[e.status]]
|
|
58
|
+
}),
|
|
59
|
+
/* @__PURE__ */ T("p", { children: [
|
|
60
|
+
"Revision ",
|
|
61
|
+
e.revision,
|
|
62
|
+
" of ",
|
|
63
|
+
e.latestRevision,
|
|
64
|
+
". Known invoice amount",
|
|
65
|
+
" ",
|
|
66
|
+
d(e.knownInvoiceAmountMinor, e.currency),
|
|
67
|
+
e.invoiceAmountComplete ? "" : " (some reported amounts are unknown and are not counted as zero)",
|
|
68
|
+
"."
|
|
69
|
+
] }),
|
|
70
|
+
e.status === "SUPERSEDED" && /* @__PURE__ */ T("p", { children: ["A newer correction replaces this revision; review continues on the latest revision.", e.resolvedByCorrection && " Its open dispute was resolved by that correction."] }),
|
|
71
|
+
e.settlementReference && /* @__PURE__ */ T("p", { children: [
|
|
72
|
+
"Handed to platform Billing for settlement under reference",
|
|
73
|
+
" ",
|
|
74
|
+
/* @__PURE__ */ w("span", {
|
|
75
|
+
className: "break-all font-mono",
|
|
76
|
+
children: e.settlementReference
|
|
77
|
+
}),
|
|
78
|
+
". This revision can no longer be corrected."
|
|
79
|
+
] }),
|
|
80
|
+
a && /* @__PURE__ */ w("p", { children: a }),
|
|
81
|
+
e.events.length > 0 && /* @__PURE__ */ w("ol", {
|
|
82
|
+
className: "space-y-2",
|
|
83
|
+
"aria-label": "Review history",
|
|
84
|
+
children: e.events.map((e) => /* @__PURE__ */ T("li", {
|
|
85
|
+
className: "rounded border p-3 text-sm",
|
|
86
|
+
children: [
|
|
87
|
+
/* @__PURE__ */ T("p", {
|
|
88
|
+
className: "font-medium",
|
|
89
|
+
children: [
|
|
90
|
+
e.sequence,
|
|
91
|
+
". ",
|
|
92
|
+
p[e.kind],
|
|
93
|
+
" —",
|
|
94
|
+
" ",
|
|
95
|
+
m[e.statusAfter]
|
|
96
|
+
]
|
|
97
|
+
}),
|
|
98
|
+
/* @__PURE__ */ T("p", { children: [
|
|
99
|
+
e.recordedAt,
|
|
100
|
+
" by ",
|
|
101
|
+
e.createdBy
|
|
102
|
+
] }),
|
|
103
|
+
e.reason && /* @__PURE__ */ w("p", {
|
|
104
|
+
className: "whitespace-pre-wrap break-words",
|
|
105
|
+
children: e.reason
|
|
106
|
+
}),
|
|
107
|
+
e.disputedLineIds.length > 0 && /* @__PURE__ */ T("p", { children: ["Disputed lines: ", e.disputedLineIds.map(o).join(", ")] })
|
|
108
|
+
]
|
|
109
|
+
}, e.id))
|
|
110
|
+
}),
|
|
111
|
+
i.length > 0 ? /* @__PURE__ */ w(A, {
|
|
112
|
+
review: e,
|
|
113
|
+
actions: i,
|
|
114
|
+
lines: t,
|
|
115
|
+
onRecorded: r
|
|
116
|
+
}) : e.status !== "SUPERSEDED" && e.status !== "SETTLEMENT_HANDED_OFF" && !a && /* @__PURE__ */ w("p", { children: "Your role cannot take the next review step on this invoice." })
|
|
117
|
+
]
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
var k = {
|
|
121
|
+
DECIDE: "Accept or dispute",
|
|
122
|
+
RECORD_SUPPLIER_RESPONSE: "Record supplier response",
|
|
123
|
+
RESOLVE_DISPUTE: "Resolve dispute",
|
|
124
|
+
HAND_OFF_SETTLEMENT: "Hand off to Billing"
|
|
125
|
+
};
|
|
126
|
+
function A({ review: n, actions: r, lines: i, onRecorded: c }) {
|
|
127
|
+
let p = t(), [m, h] = S(r[0]), [g, D] = S("ACCEPTED"), [O, A] = S("ACCEPTED"), [j, M] = S(""), [N, P] = S([]), [F, I] = S(() => crypto.randomUUID()), [L, R] = S(!1), [z, B] = S(null), V = (e) => (t) => {
|
|
128
|
+
e(t), I(crypto.randomUUID()), B(null);
|
|
129
|
+
}, H = {
|
|
130
|
+
review: n,
|
|
131
|
+
idempotencyKey: F
|
|
132
|
+
}, U = async () => {
|
|
133
|
+
let e = m === "DECIDE" ? {
|
|
134
|
+
document: o,
|
|
135
|
+
input: _(H, g, j, N)
|
|
136
|
+
} : m === "RECORD_SUPPLIER_RESPONSE" ? {
|
|
137
|
+
document: l,
|
|
138
|
+
input: x(H, j)
|
|
139
|
+
} : m === "RESOLVE_DISPUTE" ? {
|
|
140
|
+
document: u,
|
|
141
|
+
input: b(H, O, j)
|
|
142
|
+
} : {
|
|
143
|
+
document: s,
|
|
144
|
+
input: v(H, j)
|
|
145
|
+
};
|
|
146
|
+
if (y(e.input)) {
|
|
147
|
+
B(f[e.input]);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
R(!0), B(null);
|
|
151
|
+
try {
|
|
152
|
+
await p(e.document, { input: e.input }), c();
|
|
153
|
+
} catch (e) {
|
|
154
|
+
B(e instanceof Error ? e.message : "The review step was not recorded.");
|
|
155
|
+
} finally {
|
|
156
|
+
R(!1);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
return /* @__PURE__ */ T("form", {
|
|
160
|
+
className: "space-y-3 border-t pt-4",
|
|
161
|
+
"aria-label": "Next review step",
|
|
162
|
+
onSubmit: (e) => {
|
|
163
|
+
e.preventDefault(), U();
|
|
164
|
+
},
|
|
165
|
+
children: [
|
|
166
|
+
r.length > 1 && /* @__PURE__ */ w(a, {
|
|
167
|
+
label: "Review step",
|
|
168
|
+
value: m,
|
|
169
|
+
onChange: (e) => V(h)(e),
|
|
170
|
+
children: r.map((e) => /* @__PURE__ */ w("option", {
|
|
171
|
+
value: e,
|
|
172
|
+
children: k[e]
|
|
173
|
+
}, e))
|
|
174
|
+
}),
|
|
175
|
+
m === "DECIDE" && /* @__PURE__ */ T(C, { children: [/* @__PURE__ */ T(a, {
|
|
176
|
+
label: "Decision",
|
|
177
|
+
value: g,
|
|
178
|
+
onChange: (e) => V(D)(e),
|
|
179
|
+
children: [
|
|
180
|
+
/* @__PURE__ */ w("option", {
|
|
181
|
+
value: "ACCEPTED",
|
|
182
|
+
children: "Accept"
|
|
183
|
+
}),
|
|
184
|
+
/* @__PURE__ */ w("option", {
|
|
185
|
+
value: "ACCEPTED_WITH_VARIANCE",
|
|
186
|
+
children: "Accept with variance"
|
|
187
|
+
}),
|
|
188
|
+
/* @__PURE__ */ w("option", {
|
|
189
|
+
value: "DISPUTED",
|
|
190
|
+
children: "Dispute"
|
|
191
|
+
})
|
|
192
|
+
]
|
|
193
|
+
}), g === "DISPUTED" && /* @__PURE__ */ T("fieldset", {
|
|
194
|
+
className: "space-y-2",
|
|
195
|
+
children: [/* @__PURE__ */ w("legend", {
|
|
196
|
+
className: "text-sm font-medium",
|
|
197
|
+
children: "Disputed invoice lines"
|
|
198
|
+
}), i.map((e) => /* @__PURE__ */ T("label", {
|
|
199
|
+
className: "flex min-h-11 items-center gap-2 text-sm",
|
|
200
|
+
children: [
|
|
201
|
+
/* @__PURE__ */ w("input", {
|
|
202
|
+
type: "checkbox",
|
|
203
|
+
checked: N.includes(e.id),
|
|
204
|
+
onChange: (t) => V(P)(t.target.checked ? [...N, e.id] : N.filter((t) => t !== e.id))
|
|
205
|
+
}),
|
|
206
|
+
e.description,
|
|
207
|
+
" (",
|
|
208
|
+
d(e.amountMinor, e.currency),
|
|
209
|
+
")"
|
|
210
|
+
]
|
|
211
|
+
}, e.id))]
|
|
212
|
+
})] }),
|
|
213
|
+
m === "RESOLVE_DISPUTE" && /* @__PURE__ */ T(a, {
|
|
214
|
+
label: "Resolution",
|
|
215
|
+
value: O,
|
|
216
|
+
onChange: (e) => V(A)(e),
|
|
217
|
+
children: [/* @__PURE__ */ w("option", {
|
|
218
|
+
value: "ACCEPTED",
|
|
219
|
+
children: "Accept as invoiced"
|
|
220
|
+
}), /* @__PURE__ */ w("option", {
|
|
221
|
+
value: "ACCEPTED_WITH_VARIANCE",
|
|
222
|
+
children: "Accept with variance"
|
|
223
|
+
})]
|
|
224
|
+
}),
|
|
225
|
+
/* @__PURE__ */ T("label", {
|
|
226
|
+
className: "block text-sm",
|
|
227
|
+
children: [m === "RECORD_SUPPLIER_RESPONSE" ? "Supplier's response" : m === "HAND_OFF_SETTLEMENT" ? "Note for Billing (optional)" : "Reason", /* @__PURE__ */ w("textarea", {
|
|
228
|
+
className: E,
|
|
229
|
+
value: j,
|
|
230
|
+
maxLength: 1e3,
|
|
231
|
+
onChange: (e) => V(M)(e.target.value)
|
|
232
|
+
})]
|
|
233
|
+
}),
|
|
234
|
+
m === "RESOLVE_DISPUTE" && /* @__PURE__ */ w("p", {
|
|
235
|
+
className: "text-sm",
|
|
236
|
+
children: "A dispute can also close by recording a corrected invoice revision."
|
|
237
|
+
}),
|
|
238
|
+
m === "HAND_OFF_SETTLEMENT" && /* @__PURE__ */ w("p", {
|
|
239
|
+
className: "text-sm",
|
|
240
|
+
children: "Billing settles the invoice; MoveTheWheels keeps no payment ledger. After hand-off this revision can no longer be corrected."
|
|
241
|
+
}),
|
|
242
|
+
z && /* @__PURE__ */ w("p", {
|
|
243
|
+
role: "alert",
|
|
244
|
+
children: z
|
|
245
|
+
}),
|
|
246
|
+
/* @__PURE__ */ w(e, {
|
|
247
|
+
type: "submit",
|
|
248
|
+
disabled: L,
|
|
249
|
+
children: L ? "Recording…" : k[m]
|
|
250
|
+
})
|
|
251
|
+
]
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
//#endregion
|
|
255
|
+
export { D as InvoiceReviewPanel };
|