umwd-components 0.1.640 → 0.1.641

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.
@@ -1 +1,2 @@
1
- export declare function confirmationService(path: string, ids: number[]): Promise<any>;
1
+ import { InvoiceOverwrites } from "../../../types/e-commerce/invoice/types";
2
+ export declare function confirmationService(path: string, ids: number[], overwrites?: object | InvoiceOverwrites): Promise<any>;
@@ -65,3 +65,15 @@ export interface Invoice {
65
65
  customer?: number;
66
66
  comments?: string;
67
67
  }
68
+ export interface InvoiceOverwrites {
69
+ lines?: {
70
+ quantity?: number;
71
+ vat_rate?: number;
72
+ price_excl_vat?: number;
73
+ price_incl_vat?: number;
74
+ }[];
75
+ customer_internal_reference?: string;
76
+ total_excl_vat?: number;
77
+ VAT_total?: number;
78
+ total_incl_vat?: number;
79
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "umwd-components",
3
- "version": "0.1.640",
3
+ "version": "0.1.641",
4
4
  "description": "UMWD Component library",
5
5
  "main": "dist/src/index.js",
6
6
  "module": "dist/src/index.js",
@@ -37,6 +37,8 @@ import {
37
37
  List,
38
38
  ListItem,
39
39
  TextField,
40
+ DialogContentText,
41
+ CircularProgress,
40
42
  } from "@mui/material";
41
43
  import NumbersIcon from "@mui/icons-material/Numbers";
42
44
  import AssignmentReturnIcon from "@mui/icons-material/AssignmentReturn";
@@ -45,6 +47,11 @@ import BusinessIcon from "@mui/icons-material/Business";
45
47
  import DescriptionIcon from "@mui/icons-material/Description";
46
48
  import UpdateIcon from "@mui/icons-material/Update";
47
49
  import { IroStatusIndicator } from "./IroStatusIndicator";
50
+ import { InvoiceOverwrites } from "../../../types/e-commerce/invoice/types";
51
+ import { Iro } from "../../../types/e-commerce/iro/types";
52
+ import { queryAllProducts } from "../../../data/loaders/e-commerce/queryAllProducts";
53
+ import qs from "qs";
54
+ import { Product } from "../../../types/e-commerce/product/types";
48
55
 
49
56
  const INITIAL_STATE = {
50
57
  zodErrors: null,
@@ -53,18 +60,305 @@ const INITIAL_STATE = {
53
60
  message: null,
54
61
  };
55
62
 
63
+ function OverwritesDialog({
64
+ open,
65
+ handleClose,
66
+ overwrites,
67
+ setOverwrites,
68
+ iro,
69
+ }: {
70
+ open: boolean;
71
+ handleClose: () => void;
72
+ overwrites: InvoiceOverwrites;
73
+ setOverwrites: (overwrites: InvoiceOverwrites) => void;
74
+ iro: Iro;
75
+ }) {
76
+ const [loading, setLoading] = useState(false);
77
+
78
+ // Initialize overwrites values when dialog opens
79
+ useEffect(() => {
80
+ const fetchProducts = async () => {
81
+ setLoading(true);
82
+ const query = qs.stringify(
83
+ {
84
+ filters: {
85
+ product_number: {
86
+ $in: iro.iro_items.data.map(
87
+ (item) => item.product?.product_number?.replace(/^r-/, "") || ""
88
+ ),
89
+ },
90
+ },
91
+ populate: {
92
+ price: true,
93
+ },
94
+ },
95
+ { encodeValuesOnly: true }
96
+ );
97
+ const correspondingProductsData = await queryAllProducts(query);
98
+ const correspondingProducts = correspondingProductsData.data.map(
99
+ (product: Product) => {
100
+ return {
101
+ id: product.id,
102
+ product_number: product.product_number,
103
+ price: product.price,
104
+ };
105
+ }
106
+ );
107
+
108
+ console.log("correspondingProducts", correspondingProducts);
109
+ setLoading(false);
110
+
111
+ // Calculate totals from the IRO items
112
+ let totalExclVat = 0;
113
+ let totalInclVat = 0;
114
+
115
+ // Create line overwrites for each item
116
+ const lineOverwrites = iro.iro_items.data.map((item, index) => {
117
+ const product = item.product;
118
+ const correspondingProduct = correspondingProducts.find(
119
+ (p: Product) =>
120
+ p.product_number === product?.product_number?.replace(/^r-/, "") ||
121
+ ""
122
+ );
123
+ // For return products, typically the product_number starts with r-
124
+ // The original product should have the same number without the r-
125
+ let price_excl_vat = 0;
126
+ let price_incl_vat = 0;
127
+ let vat_rate = 0;
128
+
129
+ if (correspondingProduct && correspondingProduct.price) {
130
+ price_excl_vat = correspondingProduct.price.price || 0;
131
+ price_incl_vat = correspondingProduct.price.price_incl_vat || 0;
132
+ vat_rate = correspondingProduct.price.vat_rate || 0;
133
+
134
+ // Add to totals
135
+ const itemTotalExclVat = price_excl_vat * item.returned_quantity;
136
+ const itemTotalInclVat = price_incl_vat * item.returned_quantity;
137
+
138
+ totalExclVat += itemTotalExclVat;
139
+ totalInclVat += itemTotalInclVat;
140
+ }
141
+
142
+ return {
143
+ vat_rate,
144
+ price_excl_vat,
145
+ price_incl_vat,
146
+ quantity: item.returned_quantity,
147
+ };
148
+ });
149
+
150
+ // Set the calculated values to the overwrites
151
+ setOverwrites({
152
+ customer_internal_reference: iro.customer_reference || "",
153
+ total_excl_vat: parseFloat(totalExclVat.toFixed(2)),
154
+ total_incl_vat: parseFloat(totalInclVat.toFixed(2)),
155
+ lines: lineOverwrites,
156
+ });
157
+ };
158
+
159
+ if (open && iro?.iro_items?.data?.length > 0) {
160
+ fetchProducts();
161
+ }
162
+ }, [open, iro]);
163
+
164
+ return (
165
+ <Dialog open={open} fullWidth maxWidth="lg">
166
+ <DialogTitle>Check or overwrite value for the return payment</DialogTitle>
167
+ <DialogContent>
168
+ <Stack spacing={2} alignItems="left" sx={{ px: 0 }}>
169
+ <DialogContentText>
170
+ This is very important because these values will be used for
171
+ automated invoice generation
172
+ </DialogContentText>
173
+ <DialogContentText variant="h5">Overwrites</DialogContentText>
174
+ {loading ? (
175
+ <CircularProgress />
176
+ ) : (
177
+ <Stack spacing={2}>
178
+ <TextField
179
+ label="Customer internal reference"
180
+ name="customer_internal_reference"
181
+ fullWidth
182
+ variant="outlined"
183
+ value={overwrites.customer_internal_reference}
184
+ onChange={(e) =>
185
+ setOverwrites({
186
+ ...overwrites,
187
+ customer_internal_reference: e.target.value,
188
+ })
189
+ }
190
+ />
191
+ <TextField
192
+ label="Total excl. VAT"
193
+ name="total_excl_vat"
194
+ type="number"
195
+ fullWidth
196
+ variant="outlined"
197
+ value={overwrites.total_excl_vat}
198
+ onChange={(e) =>
199
+ setOverwrites({
200
+ ...overwrites,
201
+ total_excl_vat: parseFloat(e.target.value),
202
+ })
203
+ }
204
+ />
205
+ <TextField
206
+ label="Total incl. VAT"
207
+ name="total_incl_vat"
208
+ type="number"
209
+ fullWidth
210
+ variant="outlined"
211
+ value={overwrites.total_incl_vat}
212
+ onChange={(e) =>
213
+ setOverwrites({
214
+ ...overwrites,
215
+ total_incl_vat: parseFloat(e.target.value),
216
+ })
217
+ }
218
+ />
219
+ </Stack>
220
+ )}
221
+ {iro?.iro_items?.data?.length > 0 && (
222
+ <>
223
+ <DialogContentText variant="h5">Items</DialogContentText>
224
+
225
+ {iro.iro_items.data.map((item, index) => {
226
+ return (
227
+ <Stack
228
+ spacing={2}
229
+ direction="row"
230
+ alignItems="center"
231
+ sx={{ px: 2 }}
232
+ key={item.id}
233
+ >
234
+ <Typography sx={{ minWidth: 100 }}>
235
+ {item.line_item_number}
236
+ </Typography>
237
+ <Typography sx={{ minWidth: 150 }}>
238
+ {item.product?.product_number}
239
+ </Typography>
240
+ <Typography sx={{ minWidth: 200 }}>
241
+ {item.product?.title}
242
+ </Typography>
243
+
244
+ <TextField
245
+ size="small"
246
+ label="VAT %"
247
+ name="vat_rate"
248
+ type="number"
249
+ sx={{ width: 100 }}
250
+ value={overwrites?.lines?.[index]?.vat_rate ?? ""}
251
+ onChange={(e) => {
252
+ const newLines = [...(overwrites.lines || [])];
253
+ newLines[index] = {
254
+ ...newLines[index],
255
+ vat_rate: parseFloat(e.target.value),
256
+ };
257
+ setOverwrites({
258
+ ...overwrites,
259
+ lines: newLines,
260
+ });
261
+ }}
262
+ />
263
+
264
+ <TextField
265
+ size="small"
266
+ label="Price excl."
267
+ name="price_excl_vat"
268
+ type="number"
269
+ sx={{ width: 120 }}
270
+ value={overwrites?.lines?.[index]?.price_excl_vat ?? ""}
271
+ onChange={(e) => {
272
+ const newLines = [...(overwrites.lines || [])];
273
+ newLines[index] = {
274
+ ...newLines[index],
275
+ price_excl_vat: parseFloat(e.target.value),
276
+ };
277
+ setOverwrites({
278
+ ...overwrites,
279
+ lines: newLines,
280
+ });
281
+ }}
282
+ />
283
+
284
+ <TextField
285
+ size="small"
286
+ label="Price incl."
287
+ name="price_incl_vat"
288
+ type="number"
289
+ sx={{ width: 120 }}
290
+ value={overwrites?.lines?.[index]?.price_incl_vat ?? ""}
291
+ onChange={(e) => {
292
+ const newLines = [...(overwrites.lines || [])];
293
+ newLines[index] = {
294
+ ...newLines[index],
295
+ price_incl_vat: parseFloat(e.target.value),
296
+ };
297
+ setOverwrites({
298
+ ...overwrites,
299
+ lines: newLines,
300
+ });
301
+ }}
302
+ />
303
+
304
+ <TextField
305
+ size="small"
306
+ label="Qty"
307
+ name="quantity"
308
+ type="number"
309
+ sx={{ width: 80 }}
310
+ value={overwrites?.lines?.[index]?.quantity ?? ""}
311
+ onChange={(e) => {
312
+ const newLines = [...(overwrites.lines || [])];
313
+ newLines[index] = {
314
+ ...newLines[index],
315
+ quantity: parseFloat(e.target.value),
316
+ };
317
+ setOverwrites({
318
+ ...overwrites,
319
+ lines: newLines,
320
+ });
321
+ }}
322
+ />
323
+ </Stack>
324
+ );
325
+ })}
326
+ </>
327
+ )}
328
+ </Stack>
329
+ </DialogContent>
330
+ <DialogActions>
331
+ <Button
332
+ variant="contained"
333
+ onClick={(e) => {
334
+ confirmationService("iros", [iro.id], overwrites);
335
+ handleClose();
336
+ }}
337
+ >
338
+ Confirm
339
+ </Button>
340
+ <Button variant="contained" onClick={handleClose}>
341
+ Cancel
342
+ </Button>
343
+ </DialogActions>
344
+ </Dialog>
345
+ );
346
+ }
347
+
56
348
  function ConfirmFormDialog({
57
349
  open,
58
350
  handleClose,
59
351
  orderID,
60
352
  currentStatus,
61
353
  revalidateCallback,
354
+ openOverwritesDialog,
62
355
  }: {
63
356
  open: boolean;
64
357
  handleClose: () => void;
65
358
  orderID: number;
66
359
  currentStatus: "requested" | "finalising_process";
67
360
  revalidateCallback?: () => void;
361
+ openOverwritesDialog?: () => void;
68
362
  }) {
69
363
  return (
70
364
  <Dialog open={open}>
@@ -83,6 +377,11 @@ function ConfirmFormDialog({
83
377
  ? "By confirming the return you will update it's status from requested to returning"
84
378
  : "By confirming the return you will update it's status from finalising_process to done"}
85
379
  </ListItem>
380
+ {currentStatus === "finalising_process" && (
381
+ <ListItem>
382
+ Confirming this order will automatically create an invoice
383
+ </ListItem>
384
+ )}
86
385
  </List>
87
386
  <Typography>Current status: {currentStatus}</Typography>
88
387
  </Stack>
@@ -91,9 +390,14 @@ function ConfirmFormDialog({
91
390
  <Button
92
391
  variant="contained"
93
392
  onClick={(e) => {
94
- confirmationService("iros", [orderID]);
95
- revalidateCallback && revalidateCallback();
96
- handleClose();
393
+ if (currentStatus === "finalising_process") {
394
+ openOverwritesDialog && openOverwritesDialog();
395
+ handleClose();
396
+ } else if (currentStatus === "requested") {
397
+ confirmationService("iros", [orderID]);
398
+ revalidateCallback && revalidateCallback();
399
+ handleClose();
400
+ }
97
401
  }}
98
402
  >
99
403
  Yes
@@ -196,6 +500,14 @@ export default function TextualManageIROForm({
196
500
 
197
501
  const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);
198
502
  const [cancelDialogOpen, setCancelDialogOpen] = useState(false);
503
+ const [overwritesDialogOpen, setOverwritesDialogOpen] = useState(false);
504
+
505
+ const [overwrites, setOverwrites] = useState<InvoiceOverwrites>({
506
+ customer_internal_reference: "",
507
+ total_excl_vat: 0,
508
+ total_incl_vat: 0,
509
+ lines: [],
510
+ });
199
511
 
200
512
  const [showing, setShowing] = useState<
201
513
  ("received" | "registered" | "released" | "reports")[]
@@ -278,6 +590,7 @@ export default function TextualManageIROForm({
278
590
  }, [formState]);
279
591
 
280
592
  useEffect(() => {
593
+ console.log("data", data);
281
594
  if (data.iro_items?.data) {
282
595
  setItems(data.iro_items.data ? data.iro_items.data : []);
283
596
  }
@@ -455,6 +768,14 @@ export default function TextualManageIROForm({
455
768
  orderID={data.id}
456
769
  currentStatus={data.status}
457
770
  revalidateCallback={revalidateCallback}
771
+ openOverwritesDialog={() => setOverwritesDialogOpen(true)}
772
+ />
773
+ <OverwritesDialog
774
+ open={overwritesDialogOpen}
775
+ handleClose={() => setOverwritesDialogOpen(false)}
776
+ overwrites={overwrites}
777
+ setOverwrites={setOverwrites}
778
+ iro={data}
458
779
  />
459
780
  <CancelIroDialog
460
781
  open={cancelDialogOpen}
@@ -2,9 +2,14 @@
2
2
 
3
3
  import { getAuthToken } from "../get-token";
4
4
  import { getStrapiURL } from "../../../lib/utils"; //"@/lib/utils";
5
+ import { InvoiceOverwrites } from "../../../types/e-commerce/invoice/types";
5
6
 
6
7
  // TODO move to common services
7
- export async function confirmationService(path: string, ids: number[]) {
8
+ export async function confirmationService(
9
+ path: string,
10
+ ids: number[],
11
+ overwrites: object | InvoiceOverwrites = {}
12
+ ) {
8
13
  const authToken = await getAuthToken();
9
14
 
10
15
  const baseUrl = getStrapiURL();
@@ -13,7 +18,9 @@ export async function confirmationService(path: string, ids: number[]) {
13
18
 
14
19
  if (!authToken) throw new Error("No auth token found");
15
20
 
16
- const payload = JSON.stringify({ data: { selectedIds: [...ids] } });
21
+ const payload = JSON.stringify({
22
+ data: { selectedIds: [...ids], overwrites },
23
+ });
17
24
 
18
25
  console.log("payload", payload);
19
26
  console.log("url", url);
@@ -69,3 +69,19 @@ export interface Invoice {
69
69
  customer?: number;
70
70
  comments?: string;
71
71
  }
72
+
73
+ // TODO a copy of this exists in the umwd project, consider moving to a common place
74
+ // TODO consider moving to a common place, this is used in the umwd project as well
75
+ // /cms/api/e-commerce/services/invoice/confirm
76
+ export interface InvoiceOverwrites {
77
+ lines?: {
78
+ quantity?: number;
79
+ vat_rate?: number;
80
+ price_excl_vat?: number;
81
+ price_incl_vat?: number;
82
+ }[];
83
+ customer_internal_reference?: string;
84
+ total_excl_vat?: number;
85
+ VAT_total?: number;
86
+ total_incl_vat?: number;
87
+ }