@hed-hog/finance 0.0.238 → 0.0.239
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/dto/reject-title.dto.d.ts +4 -0
- package/dist/dto/reject-title.dto.d.ts.map +1 -0
- package/dist/dto/reject-title.dto.js +22 -0
- package/dist/dto/reject-title.dto.js.map +1 -0
- package/dist/dto/reverse-settlement.dto.d.ts +4 -0
- package/dist/dto/reverse-settlement.dto.d.ts.map +1 -0
- package/dist/dto/reverse-settlement.dto.js +22 -0
- package/dist/dto/reverse-settlement.dto.js.map +1 -0
- package/dist/dto/settle-installment.dto.d.ts +12 -0
- package/dist/dto/settle-installment.dto.d.ts.map +1 -0
- package/dist/dto/settle-installment.dto.js +71 -0
- package/dist/dto/settle-installment.dto.js.map +1 -0
- package/dist/finance-data.controller.d.ts +13 -5
- package/dist/finance-data.controller.d.ts.map +1 -1
- package/dist/finance-installments.controller.d.ts +248 -12
- package/dist/finance-installments.controller.d.ts.map +1 -1
- package/dist/finance-installments.controller.js +92 -0
- package/dist/finance-installments.controller.js.map +1 -1
- package/dist/finance.service.d.ts +275 -17
- package/dist/finance.service.d.ts.map +1 -1
- package/dist/finance.service.js +666 -78
- package/dist/finance.service.js.map +1 -1
- package/hedhog/data/route.yaml +63 -0
- package/hedhog/frontend/app/accounts-payable/approvals/page.tsx.ejs +92 -12
- package/hedhog/frontend/app/accounts-payable/installments/[id]/page.tsx.ejs +355 -4
- package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +440 -16
- package/hedhog/frontend/app/accounts-receivable/installments/[id]/page.tsx.ejs +396 -49
- package/hedhog/frontend/app/accounts-receivable/installments/page.tsx.ejs +432 -14
- package/package.json +5 -5
- package/src/dto/reject-title.dto.ts +7 -0
- package/src/dto/reverse-settlement.dto.ts +7 -0
- package/src/dto/settle-installment.dto.ts +55 -0
- package/src/finance-installments.controller.ts +102 -0
- package/src/finance.service.ts +1007 -82
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { Page, PageHeader } from '@/components/entity-list';
|
|
4
|
+
import {
|
|
5
|
+
AlertDialog,
|
|
6
|
+
AlertDialogAction,
|
|
7
|
+
AlertDialogCancel,
|
|
8
|
+
AlertDialogContent,
|
|
9
|
+
AlertDialogDescription,
|
|
10
|
+
AlertDialogFooter,
|
|
11
|
+
AlertDialogHeader,
|
|
12
|
+
AlertDialogTitle,
|
|
13
|
+
AlertDialogTrigger,
|
|
14
|
+
} from '@/components/ui/alert-dialog';
|
|
4
15
|
import { AuditTimeline } from '@/components/ui/audit-timeline';
|
|
5
16
|
import { Badge } from '@/components/ui/badge';
|
|
6
17
|
import { Button } from '@/components/ui/button';
|
|
@@ -11,13 +22,38 @@ import {
|
|
|
11
22
|
CardHeader,
|
|
12
23
|
CardTitle,
|
|
13
24
|
} from '@/components/ui/card';
|
|
25
|
+
import {
|
|
26
|
+
Dialog,
|
|
27
|
+
DialogContent,
|
|
28
|
+
DialogDescription,
|
|
29
|
+
DialogFooter,
|
|
30
|
+
DialogHeader,
|
|
31
|
+
DialogTitle,
|
|
32
|
+
} from '@/components/ui/dialog';
|
|
14
33
|
import {
|
|
15
34
|
DropdownMenu,
|
|
16
35
|
DropdownMenuContent,
|
|
17
36
|
DropdownMenuItem,
|
|
18
37
|
DropdownMenuTrigger,
|
|
19
38
|
} from '@/components/ui/dropdown-menu';
|
|
39
|
+
import {
|
|
40
|
+
Form,
|
|
41
|
+
FormControl,
|
|
42
|
+
FormField,
|
|
43
|
+
FormItem,
|
|
44
|
+
FormLabel,
|
|
45
|
+
FormMessage,
|
|
46
|
+
} from '@/components/ui/form';
|
|
47
|
+
import { Input } from '@/components/ui/input';
|
|
48
|
+
import { InputMoney } from '@/components/ui/input-money';
|
|
20
49
|
import { Money } from '@/components/ui/money';
|
|
50
|
+
import {
|
|
51
|
+
Select,
|
|
52
|
+
SelectContent,
|
|
53
|
+
SelectItem,
|
|
54
|
+
SelectTrigger,
|
|
55
|
+
SelectValue,
|
|
56
|
+
} from '@/components/ui/select';
|
|
21
57
|
import { StatusBadge } from '@/components/ui/status-badge';
|
|
22
58
|
import {
|
|
23
59
|
Table,
|
|
@@ -30,27 +66,39 @@ import {
|
|
|
30
66
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
|
31
67
|
import { TagSelectorSheet } from '@/components/ui/tag-selector-sheet';
|
|
32
68
|
import { useApp } from '@hed-hog/next-app-provider';
|
|
69
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
33
70
|
import {
|
|
34
|
-
|
|
71
|
+
CheckCircle,
|
|
35
72
|
Download,
|
|
36
73
|
Edit,
|
|
37
74
|
FileText,
|
|
38
75
|
MoreHorizontal,
|
|
39
76
|
Send,
|
|
77
|
+
Undo,
|
|
40
78
|
} from 'lucide-react';
|
|
41
79
|
import { useTranslations } from 'next-intl';
|
|
42
80
|
import Link from 'next/link';
|
|
43
81
|
import { useParams } from 'next/navigation';
|
|
44
82
|
import { useEffect, useState } from 'react';
|
|
83
|
+
import { useForm } from 'react-hook-form';
|
|
84
|
+
import { z } from 'zod';
|
|
45
85
|
import { formatarData } from '../../../_lib/formatters';
|
|
46
86
|
import { useFinanceData } from '../../../_lib/use-finance-data';
|
|
47
87
|
|
|
88
|
+
const settleSchema = z.object({
|
|
89
|
+
installmentId: z.string().min(1, 'Parcela obrigatória'),
|
|
90
|
+
amount: z.number().min(0.01, 'Valor deve ser maior que zero'),
|
|
91
|
+
description: z.string().optional(),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
type SettleFormValues = z.infer<typeof settleSchema>;
|
|
95
|
+
|
|
48
96
|
export default function TituloReceberDetalhePage() {
|
|
49
97
|
const t = useTranslations('finance.ReceivableInstallmentDetailPage');
|
|
50
98
|
const { request, showToastHandler } = useApp();
|
|
51
99
|
const params = useParams<{ id: string }>();
|
|
52
100
|
const id = params?.id;
|
|
53
|
-
const { data } = useFinanceData();
|
|
101
|
+
const { data, refetch } = useFinanceData();
|
|
54
102
|
const {
|
|
55
103
|
titulosReceber,
|
|
56
104
|
pessoas,
|
|
@@ -63,9 +111,31 @@ export default function TituloReceberDetalhePage() {
|
|
|
63
111
|
|
|
64
112
|
const titulo = titulosReceber.find((t) => t.id === id);
|
|
65
113
|
|
|
114
|
+
const settleCandidates = (titulo?.parcelas || []).filter(
|
|
115
|
+
(parcela: any) =>
|
|
116
|
+
parcela.status === 'aberto' ||
|
|
117
|
+
parcela.status === 'parcial' ||
|
|
118
|
+
parcela.status === 'vencido'
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const settleForm = useForm<SettleFormValues>({
|
|
122
|
+
resolver: zodResolver(settleSchema),
|
|
123
|
+
defaultValues: {
|
|
124
|
+
installmentId: settleCandidates[0]?.id || '',
|
|
125
|
+
amount: Number(settleCandidates[0]?.valorAberto || 0),
|
|
126
|
+
description: '',
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
|
|
66
130
|
const [availableTags, setAvailableTags] = useState<any[]>([]);
|
|
67
131
|
const [selectedTagIds, setSelectedTagIds] = useState<string[]>([]);
|
|
68
132
|
const [isCreatingTag, setIsCreatingTag] = useState(false);
|
|
133
|
+
const [isApproving, setIsApproving] = useState(false);
|
|
134
|
+
const [isSettling, setIsSettling] = useState(false);
|
|
135
|
+
const [isSettleDialogOpen, setIsSettleDialogOpen] = useState(false);
|
|
136
|
+
const [reversingSettlementId, setReversingSettlementId] = useState<
|
|
137
|
+
string | null
|
|
138
|
+
>(null);
|
|
69
139
|
|
|
70
140
|
useEffect(() => {
|
|
71
141
|
setAvailableTags(tags || []);
|
|
@@ -79,6 +149,27 @@ export default function TituloReceberDetalhePage() {
|
|
|
79
149
|
setSelectedTagIds(Array.isArray(titulo.tags) ? titulo.tags : []);
|
|
80
150
|
}, [titulo]);
|
|
81
151
|
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
const firstCandidate = settleCandidates[0];
|
|
154
|
+
const nextInstallmentId = firstCandidate?.id || '';
|
|
155
|
+
const nextAmount = Number(firstCandidate?.valorAberto || 0);
|
|
156
|
+
const currentInstallmentId = settleForm.getValues('installmentId') || '';
|
|
157
|
+
const currentAmount = Number(settleForm.getValues('amount') || 0);
|
|
158
|
+
|
|
159
|
+
if (
|
|
160
|
+
currentInstallmentId === nextInstallmentId &&
|
|
161
|
+
currentAmount === nextAmount
|
|
162
|
+
) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
settleForm.reset({
|
|
167
|
+
installmentId: nextInstallmentId,
|
|
168
|
+
amount: nextAmount,
|
|
169
|
+
description: '',
|
|
170
|
+
});
|
|
171
|
+
}, [settleCandidates, settleForm]);
|
|
172
|
+
|
|
82
173
|
const canalBadge = {
|
|
83
174
|
boleto: {
|
|
84
175
|
label: t('channels.boleto'),
|
|
@@ -286,55 +377,267 @@ export default function TituloReceberDetalhePage() {
|
|
|
286
377
|
}
|
|
287
378
|
};
|
|
288
379
|
|
|
380
|
+
const canApprove = titulo.status === 'rascunho';
|
|
381
|
+
const canSettle = ['aprovado', 'aberto', 'parcial'].includes(titulo.status);
|
|
382
|
+
|
|
383
|
+
const getErrorMessage = (error: any, fallback: string) => {
|
|
384
|
+
const message = error?.response?.data?.message;
|
|
385
|
+
|
|
386
|
+
if (Array.isArray(message)) {
|
|
387
|
+
return message.join(', ');
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (typeof message === 'string' && message.trim()) {
|
|
391
|
+
return message;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return fallback;
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
const handleApprove = async () => {
|
|
398
|
+
if (!canApprove || isApproving) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
setIsApproving(true);
|
|
403
|
+
try {
|
|
404
|
+
await request({
|
|
405
|
+
url: `/finance/accounts-receivable/installments/${titulo.id}/approve`,
|
|
406
|
+
method: 'PATCH',
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
await refetch();
|
|
410
|
+
showToastHandler?.('success', 'Título aprovado com sucesso');
|
|
411
|
+
} catch (error) {
|
|
412
|
+
showToastHandler?.(
|
|
413
|
+
'error',
|
|
414
|
+
getErrorMessage(error, 'Não foi possível aprovar o título')
|
|
415
|
+
);
|
|
416
|
+
} finally {
|
|
417
|
+
setIsApproving(false);
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
const handleSettle = async (values: SettleFormValues) => {
|
|
422
|
+
if (!canSettle || isSettling) {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
setIsSettling(true);
|
|
427
|
+
try {
|
|
428
|
+
await request({
|
|
429
|
+
url: `/finance/accounts-receivable/installments/${titulo.id}/settlements`,
|
|
430
|
+
method: 'POST',
|
|
431
|
+
data: {
|
|
432
|
+
installment_id: Number(values.installmentId),
|
|
433
|
+
amount: values.amount,
|
|
434
|
+
description: values.description?.trim() || undefined,
|
|
435
|
+
},
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
await refetch();
|
|
439
|
+
setIsSettleDialogOpen(false);
|
|
440
|
+
showToastHandler?.('success', 'Recebimento registrado com sucesso');
|
|
441
|
+
} catch (error) {
|
|
442
|
+
showToastHandler?.(
|
|
443
|
+
'error',
|
|
444
|
+
getErrorMessage(error, 'Não foi possível registrar o recebimento')
|
|
445
|
+
);
|
|
446
|
+
} finally {
|
|
447
|
+
setIsSettling(false);
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
const handleReverseSettlement = async (settlementId: string) => {
|
|
452
|
+
if (!settlementId || reversingSettlementId) {
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
setReversingSettlementId(settlementId);
|
|
457
|
+
try {
|
|
458
|
+
await request({
|
|
459
|
+
url: `/finance/accounts-receivable/installments/${titulo.id}/settlements/${settlementId}/reverse`,
|
|
460
|
+
method: 'PATCH',
|
|
461
|
+
data: {},
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
await refetch();
|
|
465
|
+
showToastHandler?.('success', 'Estorno realizado com sucesso');
|
|
466
|
+
} catch (error) {
|
|
467
|
+
showToastHandler?.(
|
|
468
|
+
'error',
|
|
469
|
+
getErrorMessage(error, 'Não foi possível estornar o recebimento')
|
|
470
|
+
);
|
|
471
|
+
} finally {
|
|
472
|
+
setReversingSettlementId(null);
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
|
|
289
476
|
return (
|
|
290
477
|
<Page>
|
|
291
|
-
<
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
<
|
|
313
|
-
<
|
|
314
|
-
<
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
<
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
478
|
+
<PageHeader
|
|
479
|
+
title={titulo.documento}
|
|
480
|
+
description={titulo.descricao}
|
|
481
|
+
breadcrumbs={[
|
|
482
|
+
{ label: t('breadcrumbs.home'), href: '/' },
|
|
483
|
+
{ label: t('breadcrumbs.finance'), href: '/finance' },
|
|
484
|
+
{
|
|
485
|
+
label: t('breadcrumbs.receivables'),
|
|
486
|
+
href: '/finance/accounts-receivable/installments',
|
|
487
|
+
},
|
|
488
|
+
{ label: titulo.documento },
|
|
489
|
+
]}
|
|
490
|
+
actions={
|
|
491
|
+
<div className="flex items-center gap-2">
|
|
492
|
+
<DropdownMenu>
|
|
493
|
+
<DropdownMenuTrigger asChild>
|
|
494
|
+
<Button variant="outline">
|
|
495
|
+
<MoreHorizontal className="mr-2 h-4 w-4" />
|
|
496
|
+
{t('actions.title')}
|
|
497
|
+
</Button>
|
|
498
|
+
</DropdownMenuTrigger>
|
|
499
|
+
<DropdownMenuContent align="end">
|
|
500
|
+
<DropdownMenuItem>
|
|
501
|
+
<Edit className="mr-2 h-4 w-4" />
|
|
502
|
+
{t('actions.edit')}
|
|
503
|
+
</DropdownMenuItem>
|
|
504
|
+
<DropdownMenuItem
|
|
505
|
+
disabled={!canApprove || isApproving}
|
|
506
|
+
onClick={() => void handleApprove()}
|
|
507
|
+
>
|
|
508
|
+
<CheckCircle className="mr-2 h-4 w-4" />
|
|
509
|
+
Aprovar
|
|
510
|
+
</DropdownMenuItem>
|
|
511
|
+
<DropdownMenuItem
|
|
512
|
+
disabled={!canSettle || settleCandidates.length === 0}
|
|
513
|
+
onClick={() => setIsSettleDialogOpen(true)}
|
|
514
|
+
>
|
|
515
|
+
<Download className="mr-2 h-4 w-4" />
|
|
516
|
+
{t('actions.registerReceipt')}
|
|
517
|
+
</DropdownMenuItem>
|
|
518
|
+
<DropdownMenuItem>
|
|
519
|
+
<Send className="mr-2 h-4 w-4" />
|
|
520
|
+
{t('actions.sendCollection')}
|
|
521
|
+
</DropdownMenuItem>
|
|
522
|
+
</DropdownMenuContent>
|
|
523
|
+
</DropdownMenu>
|
|
524
|
+
|
|
525
|
+
<Dialog
|
|
526
|
+
open={isSettleDialogOpen}
|
|
527
|
+
onOpenChange={setIsSettleDialogOpen}
|
|
528
|
+
>
|
|
529
|
+
<DialogContent>
|
|
530
|
+
<DialogHeader>
|
|
531
|
+
<DialogTitle>Registrar recebimento</DialogTitle>
|
|
532
|
+
<DialogDescription>
|
|
533
|
+
Informe a parcela e o valor para baixa parcial ou total.
|
|
534
|
+
</DialogDescription>
|
|
535
|
+
</DialogHeader>
|
|
536
|
+
|
|
537
|
+
<Form {...settleForm}>
|
|
538
|
+
<form
|
|
539
|
+
className="space-y-4"
|
|
540
|
+
onSubmit={settleForm.handleSubmit(handleSettle)}
|
|
541
|
+
>
|
|
542
|
+
<FormField
|
|
543
|
+
control={settleForm.control}
|
|
544
|
+
name="installmentId"
|
|
545
|
+
render={({ field }) => (
|
|
546
|
+
<FormItem>
|
|
547
|
+
<FormLabel>Parcela</FormLabel>
|
|
548
|
+
<Select
|
|
549
|
+
value={field.value}
|
|
550
|
+
onValueChange={(value) => {
|
|
551
|
+
field.onChange(value);
|
|
552
|
+
|
|
553
|
+
const selected = settleCandidates.find(
|
|
554
|
+
(parcela: any) => parcela.id === value
|
|
555
|
+
);
|
|
556
|
+
|
|
557
|
+
if (selected) {
|
|
558
|
+
settleForm.setValue(
|
|
559
|
+
'amount',
|
|
560
|
+
Number(selected.valorAberto || 0),
|
|
561
|
+
{ shouldValidate: true }
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
}}
|
|
565
|
+
>
|
|
566
|
+
<FormControl>
|
|
567
|
+
<SelectTrigger>
|
|
568
|
+
<SelectValue placeholder="Selecione" />
|
|
569
|
+
</SelectTrigger>
|
|
570
|
+
</FormControl>
|
|
571
|
+
<SelectContent>
|
|
572
|
+
{settleCandidates.map((parcela: any) => (
|
|
573
|
+
<SelectItem key={parcela.id} value={parcela.id}>
|
|
574
|
+
Parcela {parcela.numero} - em aberto:{' '}
|
|
575
|
+
{new Intl.NumberFormat('pt-BR', {
|
|
576
|
+
style: 'currency',
|
|
577
|
+
currency: 'BRL',
|
|
578
|
+
}).format(Number(parcela.valorAberto || 0))}
|
|
579
|
+
</SelectItem>
|
|
580
|
+
))}
|
|
581
|
+
</SelectContent>
|
|
582
|
+
</Select>
|
|
583
|
+
<FormMessage />
|
|
584
|
+
</FormItem>
|
|
585
|
+
)}
|
|
586
|
+
/>
|
|
587
|
+
|
|
588
|
+
<FormField
|
|
589
|
+
control={settleForm.control}
|
|
590
|
+
name="amount"
|
|
591
|
+
render={({ field }) => (
|
|
592
|
+
<FormItem>
|
|
593
|
+
<FormLabel>Valor</FormLabel>
|
|
594
|
+
<FormControl>
|
|
595
|
+
<InputMoney
|
|
596
|
+
value={Number(field.value || 0)}
|
|
597
|
+
onValueChange={(value) => {
|
|
598
|
+
field.onChange(Number(value || 0));
|
|
599
|
+
}}
|
|
600
|
+
/>
|
|
601
|
+
</FormControl>
|
|
602
|
+
<FormMessage />
|
|
603
|
+
</FormItem>
|
|
604
|
+
)}
|
|
605
|
+
/>
|
|
606
|
+
|
|
607
|
+
<FormField
|
|
608
|
+
control={settleForm.control}
|
|
609
|
+
name="description"
|
|
610
|
+
render={({ field }) => (
|
|
611
|
+
<FormItem>
|
|
612
|
+
<FormLabel>Descrição (opcional)</FormLabel>
|
|
613
|
+
<FormControl>
|
|
614
|
+
<Input {...field} value={field.value || ''} />
|
|
615
|
+
</FormControl>
|
|
616
|
+
<FormMessage />
|
|
617
|
+
</FormItem>
|
|
618
|
+
)}
|
|
619
|
+
/>
|
|
620
|
+
|
|
621
|
+
<DialogFooter>
|
|
622
|
+
<Button
|
|
623
|
+
type="button"
|
|
624
|
+
variant="outline"
|
|
625
|
+
disabled={isSettling}
|
|
626
|
+
onClick={() => setIsSettleDialogOpen(false)}
|
|
627
|
+
>
|
|
628
|
+
Cancelar
|
|
629
|
+
</Button>
|
|
630
|
+
<Button type="submit" disabled={isSettling}>
|
|
631
|
+
Confirmar recebimento
|
|
632
|
+
</Button>
|
|
633
|
+
</DialogFooter>
|
|
634
|
+
</form>
|
|
635
|
+
</Form>
|
|
636
|
+
</DialogContent>
|
|
637
|
+
</Dialog>
|
|
638
|
+
</div>
|
|
639
|
+
}
|
|
640
|
+
/>
|
|
338
641
|
|
|
339
642
|
<div className="grid gap-6 lg:grid-cols-3">
|
|
340
643
|
<Card className="lg:col-span-2">
|
|
@@ -537,6 +840,7 @@ export default function TituloReceberDetalhePage() {
|
|
|
537
840
|
</TableHead>
|
|
538
841
|
<TableHead>{t('receiptsTable.account')}</TableHead>
|
|
539
842
|
<TableHead>{t('receiptsTable.method')}</TableHead>
|
|
843
|
+
<TableHead className="text-right">Ações</TableHead>
|
|
540
844
|
</TableRow>
|
|
541
845
|
</TableHeader>
|
|
542
846
|
<TableBody>
|
|
@@ -559,6 +863,49 @@ export default function TituloReceberDetalhePage() {
|
|
|
559
863
|
<TableCell className="capitalize">
|
|
560
864
|
{liq.metodo}
|
|
561
865
|
</TableCell>
|
|
866
|
+
<TableCell className="text-right">
|
|
867
|
+
<AlertDialog>
|
|
868
|
+
<AlertDialogTrigger asChild>
|
|
869
|
+
<Button
|
|
870
|
+
variant="outline"
|
|
871
|
+
size="sm"
|
|
872
|
+
disabled={
|
|
873
|
+
!liq.settlementId ||
|
|
874
|
+
liq.status === 'reversed' ||
|
|
875
|
+
!!reversingSettlementId
|
|
876
|
+
}
|
|
877
|
+
>
|
|
878
|
+
<Undo className="mr-2 h-4 w-4" />
|
|
879
|
+
Estornar
|
|
880
|
+
</Button>
|
|
881
|
+
</AlertDialogTrigger>
|
|
882
|
+
<AlertDialogContent>
|
|
883
|
+
<AlertDialogHeader>
|
|
884
|
+
<AlertDialogTitle>
|
|
885
|
+
Confirmar estorno
|
|
886
|
+
</AlertDialogTitle>
|
|
887
|
+
<AlertDialogDescription>
|
|
888
|
+
Esta ação estorna o recebimento e
|
|
889
|
+
recalcula saldos e status.
|
|
890
|
+
</AlertDialogDescription>
|
|
891
|
+
</AlertDialogHeader>
|
|
892
|
+
<AlertDialogFooter>
|
|
893
|
+
<AlertDialogCancel>
|
|
894
|
+
Cancelar
|
|
895
|
+
</AlertDialogCancel>
|
|
896
|
+
<AlertDialogAction
|
|
897
|
+
onClick={() =>
|
|
898
|
+
void handleReverseSettlement(
|
|
899
|
+
String(liq.settlementId)
|
|
900
|
+
)
|
|
901
|
+
}
|
|
902
|
+
>
|
|
903
|
+
Confirmar estorno
|
|
904
|
+
</AlertDialogAction>
|
|
905
|
+
</AlertDialogFooter>
|
|
906
|
+
</AlertDialogContent>
|
|
907
|
+
</AlertDialog>
|
|
908
|
+
</TableCell>
|
|
562
909
|
</TableRow>
|
|
563
910
|
);
|
|
564
911
|
})
|