Valor da fatura
-
+
{typeof limitAmount === "number" ? (
-
+
{formatCurrency(limitAmount)}
@@ -263,16 +333,45 @@ export function InvoiceSummaryCard({
-
+ {isPaid ? (
+
+ ) : (
+
+ {isPending
+ ? "Salvando..."
+ : actionLabelByStatus[invoiceStatus]}
+
+ }
+ />
+ )}
{isPaid ? (
);
}
+
+type PayInvoiceDialogProps = {
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+ isPending: boolean;
+ paymentDate: Date;
+ onPaymentDateChange: (date: Date) => void;
+ accountId: string;
+ onAccountChange: (accountId: string) => void;
+ accountOptions: PaymentAccountOption[];
+ onConfirm: () => void;
+ trigger: ReactNode;
+};
+
+function PayInvoiceDialog({
+ open,
+ onOpenChange,
+ isPending,
+ paymentDate,
+ onPaymentDateChange,
+ accountId,
+ onAccountChange,
+ accountOptions,
+ onConfirm,
+ trigger,
+}: PayInvoiceDialogProps) {
+ const paymentDateValue = paymentDate.toISOString().split("T")[0] ?? "";
+ const selectedAccount = accountOptions.find(
+ (option) => option.value === accountId,
+ );
+
+ return (
+
+ );
+}
diff --git a/src/features/transactions/actions/single-actions.ts b/src/features/transactions/actions/single-actions.ts
index abd0286..452e26d 100644
--- a/src/features/transactions/actions/single-actions.ts
+++ b/src/features/transactions/actions/single-actions.ts
@@ -42,6 +42,7 @@ import {
type UpdateInput,
updateSchema,
validateAllOwnership,
+ validateCardLimit,
} from "./core";
export async function createTransactionAction(
@@ -132,6 +133,20 @@ export async function createTransactionAction(
)} já estão pagas. Desfaça o pagamento antes de adicionar este lançamento.`,
} as ActionResult<{ ids: string[] }>;
}
+
+ if (data.transactionType === "Despesa") {
+ const limitCheck = await validateCardLimit({
+ userId: user.id,
+ cardId: data.cardId,
+ addAmount: Math.abs(data.amount),
+ });
+ if (!limitCheck.ok) {
+ return {
+ success: false,
+ error: limitCheck.error,
+ } as ActionResult<{ ids: string[] }>;
+ }
+ }
}
const inserted = await db
@@ -287,6 +302,22 @@ export async function updateTransactionAction(
}
}
+ if (
+ data.paymentMethod === "Cartão de crédito" &&
+ data.cardId &&
+ data.transactionType === "Despesa"
+ ) {
+ const limitCheck = await validateCardLimit({
+ userId: user.id,
+ cardId: data.cardId,
+ addAmount: Math.abs(data.amount),
+ excludeTransactionIds: [data.id],
+ });
+ if (!limitCheck.ok) {
+ return { success: false, error: limitCheck.error };
+ }
+ }
+
await db
.update(transactions)
.set({
@@ -582,7 +613,7 @@ export async function toggleTransactionSettlementAction(
const data = toggleSettlementSchema.parse(input);
const existing = await db.query.transactions.findFirst({
- columns: { id: true, paymentMethod: true },
+ columns: { id: true, paymentMethod: true, accountId: true },
where: and(
eq(transactions.id, data.id),
eq(transactions.userId, user.id),
@@ -601,18 +632,52 @@ export async function toggleTransactionSettlementAction(
}
const isBoleto = existing.paymentMethod === "Boleto";
+ const customPaymentDate =
+ isBoleto && data.value && data.paymentDate
+ ? parseLocalDateString(data.paymentDate)
+ : null;
const boletoPaymentDate = isBoleto
? data.value
- ? getBusinessTodayDate()
+ ? (customPaymentDate ?? getBusinessTodayDate())
: null
: null;
+ const shouldUpdateAccount =
+ isBoleto && data.value && data.paymentAccountId !== undefined;
+
+ if (shouldUpdateAccount && data.paymentAccountId) {
+ const paymentAccount = await db.query.financialAccounts.findFirst({
+ columns: { id: true },
+ where: and(
+ eq(financialAccounts.id, data.paymentAccountId),
+ eq(financialAccounts.userId, user.id),
+ ),
+ });
+
+ if (!paymentAccount) {
+ return {
+ success: false,
+ error: "Conta de pagamento não encontrada.",
+ };
+ }
+ }
+
+ const updatePayload: {
+ isSettled: boolean;
+ boletoPaymentDate: Date | null;
+ accountId?: string | null;
+ } = {
+ isSettled: data.value,
+ boletoPaymentDate,
+ };
+
+ if (shouldUpdateAccount) {
+ updatePayload.accountId = data.paymentAccountId ?? null;
+ }
+
await db
.update(transactions)
- .set({
- isSettled: data.value,
- boletoPaymentDate,
- })
+ .set(updatePayload)
.where(
and(eq(transactions.id, data.id), eq(transactions.userId, user.id)),
);
diff --git a/src/features/transactions/components/dialogs/transaction-details-dialog.tsx b/src/features/transactions/components/dialogs/transaction-details-dialog.tsx
index 8170f6f..2a755d2 100644
--- a/src/features/transactions/components/dialogs/transaction-details-dialog.tsx
+++ b/src/features/transactions/components/dialogs/transaction-details-dialog.tsx
@@ -64,6 +64,9 @@ export function TransactionDetailsDialog({
: 0;
const isBoleto = transaction.paymentMethod === "Boleto";
+ const shortTransactionId = `…${
+ transaction.id.split("-").at(-1) ?? transaction.id
+ }`;
const handleEdit = () => {
onOpenChange(false);
@@ -120,6 +123,12 @@ export function TransactionDetailsDialog({
Detalhes
+
+
{label}
- {value}
+
+ {value}
+
);
}
diff --git a/src/shared/lib/accounts/constants.ts b/src/shared/lib/accounts/constants.ts
index eb209f8..923a135 100644
--- a/src/shared/lib/accounts/constants.ts
+++ b/src/shared/lib/accounts/constants.ts
@@ -19,3 +19,15 @@ export const ACCOUNT_AUTO_INVOICE_NOTE_PREFIX = "AUTO_FATURA:";
export const buildInvoicePaymentNote = (cardId: string, period: string) =>
`${ACCOUNT_AUTO_INVOICE_NOTE_PREFIX}${cardId}:${period}`;
+
+export const INVOICE_ADJUSTMENT_NAME = "Ajuste de fatura";
+
+export const ACCOUNT_BALANCE_ADJUSTMENT_NAME = "Ajuste de saldo";
+
+export const REFUND_NOTE_PREFIX = "AUTO_REEMBOLSO:";
+
+export const buildRefundNote = (originalTransactionId: string) =>
+ `${REFUND_NOTE_PREFIX}${originalTransactionId}`;
+
+export const isRefundNote = (note: string | null | undefined) =>
+ note?.startsWith(REFUND_NOTE_PREFIX) ?? false;