@fayz-ai/plugin-financial 0.18.3 → 0.19.0
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/AttachmentViewer.d.ts +8 -0
- package/dist/components/AttachmentViewer.d.ts.map +1 -0
- package/dist/components/BankPicker.d.ts +25 -0
- package/dist/components/BankPicker.d.ts.map +1 -0
- package/dist/components/CnabRemittanceModal.d.ts.map +1 -1
- package/dist/components/InstallmentSource.d.ts.map +1 -1
- package/dist/components/ReceivePaymentModal.d.ts +14 -3
- package/dist/components/ReceivePaymentModal.d.ts.map +1 -1
- package/dist/components/ReconcileFlow.d.ts.map +1 -1
- package/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/data/registries.d.ts.map +1 -1
- package/dist/data/supabase.d.ts.map +1 -1
- package/dist/data/tables.d.ts +7 -0
- package/dist/data/tables.d.ts.map +1 -1
- package/dist/data/types.d.ts +26 -1
- package/dist/data/types.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1629 -411
- package/dist/index.js.map +1 -1
- package/dist/lib/br-banks.d.ts +13 -0
- package/dist/lib/br-banks.d.ts.map +1 -0
- package/dist/lib/cnab240.d.ts +106 -0
- package/dist/lib/cnab240.d.ts.map +1 -0
- package/dist/lib/invoice-list.d.ts +15 -0
- package/dist/lib/invoice-list.d.ts.map +1 -1
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/pt-BR.d.ts.map +1 -1
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/types.d.ts +94 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/views/ClientAccountPanel.d.ts.map +1 -1
- package/dist/views/InvoiceDetailView.d.ts.map +1 -1
- package/dist/views/InvoiceFormView.d.ts +18 -1
- package/dist/views/InvoiceFormView.d.ts.map +1 -1
- package/dist/views/InvoiceListView.d.ts +3 -2
- package/dist/views/InvoiceListView.d.ts.map +1 -1
- package/dist/views/PayablesView.d.ts.map +1 -1
- package/dist/views/ReceivablesView.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,15 +1,94 @@
|
|
|
1
1
|
import * as React23 from 'react';
|
|
2
|
-
import React23__default, { useState, useEffect,
|
|
3
|
-
import { recordRailFields, RECORD_GROUP, activeFacet, createConnectionStore, connectionSettings, connectionStatus, settingsStatus, connectionRuns, registerTranslations, getSupabaseClientOptional, createSafeDataProvider, registerMessageDefaults, reportDataError, useTranslation, createPendingCommand, useUnitMode, useDataChanged, countByTenant, tl, resolveDataProvider, getActiveTenantId, emitDataChanged, exportCSV
|
|
4
|
-
import { ModalTask, toast, Button, useInModalTaskHost,
|
|
2
|
+
import React23__default, { useState, useEffect, useMemo, useRef, useCallback } from 'react';
|
|
3
|
+
import { recordRailFields, RECORD_GROUP, activeFacet, createConnectionStore, connectionSettings, connectionStatus, settingsStatus, connectionRuns, registerTranslations, getSupabaseClientOptional, createSafeDataProvider, registerMessageDefaults, reportDataError, useTranslation, createPendingCommand, useUnitMode, useActiveUnitId, useDataChanged, countByTenant, tl, resolveDataProvider, getActiveTenantId, emitDataChanged, exportCSV } from '@fayz-ai/core';
|
|
4
|
+
import { INLINE_LINK_TONE, SearchSelect, ModalTask, toast, Button, useInModalTaskHost, useSaveBar, DatePicker, tourAnchor, SubpageHeader, defineKpiWidget, defineChartWidget, defineCustomWidget, defineTableWidget, Modal, ModalContent, ModalHeader, ModalTitle, ModalDescription, ModalBody, DataTable, Card, CardContent, Skeleton, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, MultiSelect, PageTransition, ModulePage, Badge, TooltipProvider, ConfirmDialog, CurrencyInput, Input, PageHeaderActions, Hint, ModalFooter, PageTabs, Tooltip, TooltipTrigger, TooltipContent, Checkbox, DashboardCanvas, ComingSoon, StatTiles, ListView, DateRangeControl, FilterPanel, FilterPanelField, SegmentedControl, RequiredFilter, FilterMenuRow, ModalFlow, Switch, WizardSteps, KpiCard, ChartWidget, CardHeader, CardTitle, TableWidget, useDashboardNavigate, GoldCard } from '@fayz-ai/ui';
|
|
5
5
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
6
|
-
import { createPluginContext, fayzConnectorOAuth, formatCurrency, RightRailPage, createCrudPage, PluginSettingsPanel, dedup, useUnitStore, OrderDetailSkeleton, PageActions, OrderDetailBlock, OrderDetailView, PersonLink, useTenantPluginSettings, useModuleNavigation, usePluginOnboardingGate,
|
|
7
|
-
import { CircleDollarSign, FileText, TriangleAlert, Loader2, Check, X, Ban, CircleDashed,
|
|
6
|
+
import { createPluginContext, fayzConnectorOAuth, formatCurrency, RightRailPage, createCrudPage, PluginSettingsPanel, dedup, useUnitFilter, useLimitGuard, usePermissionOptional, useLookupCreate, OrderFormSkeleton, OrderFormBlock, OrderFormView, OrderFormItemsCard, OrderFormItemRow, OrderFormAddItemStep, useUnitStore, OrderDetailSkeleton, PageActions, OrderDetailBlock, OrderDetailView, PersonLink, useTenantPluginSettings, useModuleNavigation, usePluginOnboardingGate, useModuleNavAccess, PluginOnboardingGateSkeleton, ModuleActionBar, SettingsGroup, ToggleRow, createArchetypeLookup, invalidateLimit, useAgentSurface, parseViewId, EntitlementGate, UpgradePrompt, useChatStore, adminNavigateTo, revealPage, useAuth, GetStarted, resolveEntityRoute, PermissionGate, createOneShotAsk, shrinkForVision, toInlineFile, askAI, requestChatUpload } from '@fayz-ai/admin';
|
|
7
|
+
import { Landmark, CircleDollarSign, FileText, TriangleAlert, Loader2, Check, X, MoreVertical, Ban, CircleCheckBig, CircleDashed, Paperclip, Camera, DollarSign, CircleAlert, CircleEllipsis, Pencil, Receipt, Gift, Briefcase, Package, CalendarDays, ExternalLink, LayoutList, Layers, FileCheck, ArrowRightLeft, CreditCard, QrCode, Banknote, Undo2, ChevronRight, Wallet, Plus, AlertTriangle, Filter, TrendingUp, Copy, Trash2, Search, ChevronsUpDown, ChevronsDownUp, Building2, ChevronDown, Calendar, User, Unlink, Eye, Inbox, Clock, ClipboardCheck, BookOpen, Info, FolderOpen, Folder, RotateCcw, Calculator, Square, Play, ChevronUp, Printer, CalendarClock, BadgeCheck, Target, Settings, ArrowRight, Users, RefreshCw, Download, Sparkles, Link2, FileSearch, CheckCheck, Pause, ShoppingCart, Car, Home, Utensils, Zap, Film, HeartPulse, Dumbbell, Music, ShoppingBag, ArrowLeft, FileDown, ArrowUpRight, ArrowDownRight, EyeOff } from 'lucide-react';
|
|
8
8
|
import { createPortal } from 'react-dom';
|
|
9
9
|
import { createStore as createStore$1 } from 'zustand/vanilla';
|
|
10
10
|
import { useStore } from 'zustand';
|
|
11
11
|
|
|
12
12
|
// src/index.ts
|
|
13
|
+
|
|
14
|
+
// src/config.ts
|
|
15
|
+
var DEFAULT_LABELS = {
|
|
16
|
+
pageTitle: "Financial",
|
|
17
|
+
pageSubtitle: "Financial overview and cash flow management",
|
|
18
|
+
summary: "Summary",
|
|
19
|
+
payables: "Accounts Payable",
|
|
20
|
+
payablesNew: "New",
|
|
21
|
+
payablesList: "List",
|
|
22
|
+
payablesRecurring: "Recurring Expenses",
|
|
23
|
+
receivables: "Accounts Receivable",
|
|
24
|
+
receivablesNew: "New",
|
|
25
|
+
receivablesList: "List",
|
|
26
|
+
cashRegisters: "Cash Registers",
|
|
27
|
+
statements: "Statements",
|
|
28
|
+
commissions: "Commissions",
|
|
29
|
+
commissionsOverview: "Overview",
|
|
30
|
+
commissionsRules: "Rules",
|
|
31
|
+
cards: "Cards",
|
|
32
|
+
goals: "Metas",
|
|
33
|
+
giftCards: "Gift Cards",
|
|
34
|
+
cardsOverview: "Overview",
|
|
35
|
+
cardsReconciliation: "Reconciliation",
|
|
36
|
+
notasFiscais: "Fiscal Invoices"
|
|
37
|
+
};
|
|
38
|
+
var DEFAULT_CURRENCY = { code: "BRL", locale: "pt-BR", symbol: "R$" };
|
|
39
|
+
var DEFAULT_ITEM_TYPES = [
|
|
40
|
+
{ value: "service", label: "Service", icon: "Briefcase" },
|
|
41
|
+
// O pacote é o terceiro catálogo da casa que atende: um bloco de sessões
|
|
42
|
+
// vendido de uma vez. Só aparece onde há `entityLookups.package` instalado —
|
|
43
|
+
// e, dentro dessas, só onde o catálogo tem linhas (useStockedItemTypes).
|
|
44
|
+
{ value: "package", label: "Package", icon: "PackageCheck" },
|
|
45
|
+
{ value: "product", label: "Product", icon: "Package" },
|
|
46
|
+
{ value: "other", label: "Other", icon: "MoreHorizontal" }
|
|
47
|
+
];
|
|
48
|
+
function resolveConfig(options) {
|
|
49
|
+
return {
|
|
50
|
+
modules: {
|
|
51
|
+
payables: options?.modules?.payables !== false,
|
|
52
|
+
receivables: options?.modules?.receivables !== false,
|
|
53
|
+
cashRegisters: options?.modules?.cashRegisters !== false,
|
|
54
|
+
statements: options?.modules?.statements !== false,
|
|
55
|
+
// Era opt-in porque a comissão era um cadastro que nada lia: o item de
|
|
56
|
+
// menu só podia levar a uma tela vazia. Desde 009 existe apuração e desde
|
|
57
|
+
// 012 existe borderô, e a tela mostra o que eles produziram — então o
|
|
58
|
+
// motivo do opt-in acabou junto com a tela vazia.
|
|
59
|
+
commissions: options?.modules?.commissions !== false,
|
|
60
|
+
cards: options?.modules?.cards !== false,
|
|
61
|
+
goals: options?.modules?.goals !== false,
|
|
62
|
+
// Ligado como as outras: quando a loja não está instalada a própria tela
|
|
63
|
+
// diz isso, então o item de menu nunca leva a um beco.
|
|
64
|
+
giftCards: options?.modules?.giftCards !== false,
|
|
65
|
+
// Opt-in: only shown when an app explicitly enables reconciliation (bank connector present).
|
|
66
|
+
reconciliation: options?.modules?.reconciliation === true,
|
|
67
|
+
// Opt-in: only shown when an app explicitly enables fiscal-invoice tracking.
|
|
68
|
+
// LIGADA como as outras (`!== false`), e não opt-in. Estava em
|
|
69
|
+
// `=== true`: quem não passasse a opção nunca via a tela, e o V1 tem
|
|
70
|
+
// Notas Fiscais no menu do Financeiro desde sempre. A tela se explica
|
|
71
|
+
// sozinha quando o provider não expõe notas, então ligá-la não deixa
|
|
72
|
+
// ninguém diante de um beco.
|
|
73
|
+
notasFiscais: options?.modules?.notasFiscais !== false
|
|
74
|
+
},
|
|
75
|
+
labels: { ...DEFAULT_LABELS, ...options?.labels },
|
|
76
|
+
currency: { ...DEFAULT_CURRENCY, ...options?.currency },
|
|
77
|
+
moduleNav: options?.moduleNav,
|
|
78
|
+
itemTypes: options?.itemTypes ?? DEFAULT_ITEM_TYPES,
|
|
79
|
+
enableServiceExecution: options?.enableServiceExecution ?? false,
|
|
80
|
+
contactEntity: {
|
|
81
|
+
archetypeKind: options?.contactEntity?.archetypeKind ?? "client",
|
|
82
|
+
label: options?.contactEntity?.label ?? "Contact"
|
|
83
|
+
},
|
|
84
|
+
locations: options?.locations ?? [],
|
|
85
|
+
entityLookups: options?.entityLookups ?? {},
|
|
86
|
+
contactLookup: options?.contactLookup,
|
|
87
|
+
fiscalProvider: options?.fiscalProvider,
|
|
88
|
+
onBookingClick: options?.onBookingClick,
|
|
89
|
+
navFeatureMap: { ...options?.navFeatureMap }
|
|
90
|
+
};
|
|
91
|
+
}
|
|
13
92
|
function PettyCashView() {
|
|
14
93
|
return /* @__PURE__ */ jsx(
|
|
15
94
|
ComingSoon,
|
|
@@ -388,7 +467,14 @@ var T = {
|
|
|
388
467
|
marginByMonth: "v_financial_margin_by_month",
|
|
389
468
|
recurringBills: "plg_financial_recurring_bills",
|
|
390
469
|
recurringOccurrences: "plg_financial_recurring_bill_occurrences",
|
|
391
|
-
discountsGiven: "v_financial_discounts_given"
|
|
470
|
+
discountsGiven: "v_financial_discounts_given",
|
|
471
|
+
/** O arquivo de remessa que já saiu para o banco, e o pagamento dentro dele (080). */
|
|
472
|
+
cnabBatches: "plg_financial_cnab_batches",
|
|
473
|
+
cnabItems: "plg_financial_cnab_items",
|
|
474
|
+
/** Para onde o dinheiro SAI — a conta de quem a casa paga, nunca conta do razão (149). */
|
|
475
|
+
payeeAccounts: "payee_accounts",
|
|
476
|
+
/** A empresa pagadora da unidade: é o CNPJ dela que vai no cabeçalho do CNAB. */
|
|
477
|
+
unitCompanies: "v_unit_company_legal_entities"
|
|
392
478
|
};
|
|
393
479
|
|
|
394
480
|
// src/lib/accounts.ts
|
|
@@ -865,83 +951,685 @@ function createIdempotencyKey(scope) {
|
|
|
865
951
|
}
|
|
866
952
|
return `${scope}:${suffix}`;
|
|
867
953
|
}
|
|
954
|
+
|
|
955
|
+
// src/lib/cnab240.ts
|
|
956
|
+
var onlyDigits = (s) => (s || "").replace(/\D/g, "");
|
|
957
|
+
var padL = (v, n, c = "0") => String(v ?? "").slice(0, n).padStart(n, c);
|
|
958
|
+
var padR = (v, n, c = " ") => String(v ?? "").slice(0, n).padEnd(n, c);
|
|
959
|
+
var ascii = (s) => (s || "").normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^A-Za-z0-9 .,&'/()-]/g, " ").toUpperCase();
|
|
960
|
+
var num = (v, n) => padL(onlyDigits(String(v ?? "0")), n);
|
|
961
|
+
var alpha = (v, n) => padR(ascii(String(v ?? "")), n);
|
|
962
|
+
var money = (v, n = 15) => padL(Math.round((Number(v) || 0) * 100).toString(), n);
|
|
963
|
+
var dateYMD = (s) => {
|
|
964
|
+
if (!s) return "00000000";
|
|
965
|
+
const d = /* @__PURE__ */ new Date(s + (s.length === 10 ? "T00:00:00" : ""));
|
|
966
|
+
if (Number.isNaN(d.getTime())) return "00000000";
|
|
967
|
+
return `${padL(d.getDate(), 2)}${padL(d.getMonth() + 1, 2)}${padL(d.getFullYear(), 4)}`;
|
|
968
|
+
};
|
|
969
|
+
var stampDate = (d) => `${padL(d.getDate(), 2)}${padL(d.getMonth() + 1, 2)}${padL(d.getFullYear(), 4)}`;
|
|
970
|
+
var stampTime = (d) => `${padL(d.getHours(), 2)}${padL(d.getMinutes(), 2)}${padL(d.getSeconds(), 2)}`;
|
|
971
|
+
var finalize = (s) => s.length < 240 ? s.padEnd(240, " ") : s.slice(0, 240);
|
|
972
|
+
var inscriptionType = (document2) => onlyDigits(document2).length > 11 ? "2" : "1";
|
|
973
|
+
var serviceTypeFor = (form) => form === "tributo" ? "22" : "20";
|
|
974
|
+
var settlementFor = (form, payeeBank, ourBank) => {
|
|
975
|
+
if (form === "pix") return "45";
|
|
976
|
+
if (form === "boleto") return "30";
|
|
977
|
+
if (form === "tributo") return "11";
|
|
978
|
+
if (form === "ted") {
|
|
979
|
+
return payeeBank && ourBank && onlyDigits(payeeBank) === onlyDigits(ourBank) ? "01" : "41";
|
|
980
|
+
}
|
|
981
|
+
return "01";
|
|
982
|
+
};
|
|
983
|
+
var clearingHouseFor = (form, payeeBank, ourBank) => {
|
|
984
|
+
if (form !== "ted") return "000";
|
|
985
|
+
return payeeBank && ourBank && onlyDigits(payeeBank) === onlyDigits(ourBank) ? "000" : "018";
|
|
986
|
+
};
|
|
987
|
+
var pixInitiationCodeFor = (pixKeyType, pixKey) => {
|
|
988
|
+
const type = (pixKeyType || "").toLowerCase();
|
|
989
|
+
const key = String(pixKey || "").trim();
|
|
990
|
+
const digits2 = onlyDigits(key);
|
|
991
|
+
switch (type) {
|
|
992
|
+
case "phone":
|
|
993
|
+
case "telefone":
|
|
994
|
+
return "01";
|
|
995
|
+
case "email":
|
|
996
|
+
return "02";
|
|
997
|
+
case "cpf":
|
|
998
|
+
case "cnpj":
|
|
999
|
+
return "03";
|
|
1000
|
+
case "random":
|
|
1001
|
+
case "aleatoria":
|
|
1002
|
+
case "evp":
|
|
1003
|
+
return "04";
|
|
1004
|
+
case "bank":
|
|
1005
|
+
case "dados_bancarios":
|
|
1006
|
+
return "05";
|
|
1007
|
+
default:
|
|
1008
|
+
if (digits2.length === 11 || digits2.length === 14) return "03";
|
|
1009
|
+
if (key.includes("@")) return "02";
|
|
1010
|
+
if (key.startsWith("+") || digits2.length >= 10) return "01";
|
|
1011
|
+
if (key) return "04";
|
|
1012
|
+
return "05";
|
|
1013
|
+
}
|
|
1014
|
+
};
|
|
1015
|
+
var initiationFieldFor = (form, payee) => form === "pix" ? `${pixInitiationCodeFor(payee.pixKeyType, payee.pixKey)} ` : "03 ";
|
|
1016
|
+
var pixKeyValueFor = (pixKey, initiationCode) => {
|
|
1017
|
+
const key = String(pixKey || "").trim();
|
|
1018
|
+
if (initiationCode === "01" || initiationCode === "03") return onlyDigits(key);
|
|
1019
|
+
if (initiationCode === "02") return key.toLowerCase();
|
|
1020
|
+
return key;
|
|
1021
|
+
};
|
|
1022
|
+
var pixAlpha = (v, n) => padR(
|
|
1023
|
+
String(v ?? "").normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^\x20-\x7E]/g, " "),
|
|
1024
|
+
n
|
|
1025
|
+
);
|
|
1026
|
+
function linhaDigitavelToBarcode(input) {
|
|
1027
|
+
const d = onlyDigits(input || "");
|
|
1028
|
+
if (d.length === 44) return d;
|
|
1029
|
+
if (d.length === 47) {
|
|
1030
|
+
const c1 = d.substring(0, 9);
|
|
1031
|
+
const c2 = d.substring(10, 20);
|
|
1032
|
+
const c3 = d.substring(21, 31);
|
|
1033
|
+
const dv = d.substring(32, 33);
|
|
1034
|
+
const vv = d.substring(33, 47);
|
|
1035
|
+
return c1.substring(0, 4) + dv + vv + c1.substring(4, 9) + c2 + c3;
|
|
1036
|
+
}
|
|
1037
|
+
if (d.length === 48) {
|
|
1038
|
+
return d.substring(0, 11) + d.substring(12, 23) + d.substring(24, 35) + d.substring(36, 47);
|
|
1039
|
+
}
|
|
1040
|
+
return d;
|
|
1041
|
+
}
|
|
1042
|
+
function headerArquivo(ctx2, sequence, at) {
|
|
1043
|
+
return finalize(
|
|
1044
|
+
num(ctx2.bank.code, 3) + "00000" + padR("", 9) + inscriptionType(ctx2.company.document) + num(ctx2.company.document, 14) + padR(ctx2.bank.agreement || "", 20) + num(ctx2.bank.agency, 5) + alpha(ctx2.bank.agencyDigit || "", 1) + num(ctx2.bank.account, 12) + alpha(ctx2.bank.accountDigit || "", 1) + alpha("", 1) + alpha(ctx2.company.name, 30) + alpha(ctx2.bank.name, 30) + padR("", 10) + "1" + stampDate(at) + stampTime(at) + num(sequence, 6) + "10300000" + padR("", 20) + padR("", 20) + padR("", 29)
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
function trailerArquivo(lotes, records) {
|
|
1048
|
+
return finalize("99999999" + padR("", 9) + num(lotes, 6) + num(records, 6) + num(0, 6) + padR("", 205));
|
|
1049
|
+
}
|
|
1050
|
+
function headerLote(ctx2, lote, form, settlement) {
|
|
1051
|
+
const agreement = form === "pix" ? "" : ctx2.bank.agreement || "";
|
|
1052
|
+
return finalize(
|
|
1053
|
+
num(ctx2.bank.code, 3) + num(lote, 4) + "1C" + serviceTypeFor(form) + settlement + "046 " + inscriptionType(ctx2.company.document) + num(ctx2.company.document, 14) + padR(agreement, 20) + num(ctx2.bank.agency, 5) + alpha(ctx2.bank.agencyDigit || "", 1) + num(ctx2.bank.account, 12) + alpha(ctx2.bank.accountDigit || "", 1) + alpha("", 1) + alpha(ctx2.company.name, 30) + alpha("", 40) + alpha("", 30) + num("", 5) + alpha("", 15) + alpha("", 20) + num("", 8) + alpha("", 2) + " " + padR("", 6) + padR("", 10)
|
|
1054
|
+
);
|
|
1055
|
+
}
|
|
1056
|
+
function trailerLote(lote, records, total) {
|
|
1057
|
+
return finalize(
|
|
1058
|
+
"000" + num(lote, 4) + "5" + padR("", 9) + num(records, 6) + money(total, 18) + num(0, 18) + padR("", 6) + padR("", 165) + padR("", 10)
|
|
1059
|
+
);
|
|
1060
|
+
}
|
|
1061
|
+
function segmentoA(ctx2, lote, index, form, item, docNumber) {
|
|
1062
|
+
const payee = item.payee;
|
|
1063
|
+
const byPixKey = form === "pix" && !!payee.pixKey;
|
|
1064
|
+
const favBank = byPixKey ? "0" : payee.bankCode || "0";
|
|
1065
|
+
const favAgency = byPixKey ? "0" : payee.agency || "0";
|
|
1066
|
+
const favAgencyDigit = byPixKey ? "0" : payee.agencyDigit || "0";
|
|
1067
|
+
const favAccount = byPixKey ? "0" : payee.account || "0";
|
|
1068
|
+
const favAccountDigit = byPixKey ? "0" : payee.accountDigit || "0";
|
|
1069
|
+
return finalize(
|
|
1070
|
+
num(ctx2.bank.code, 3) + num(lote, 4) + "3" + num(index, 5) + "A000" + clearingHouseFor(form, payee.bankCode, ctx2.bank.code) + num(favBank, 3) + num(favAgency, 5) + num(favAgencyDigit, 1) + num(favAccount, 12) + num(favAccountDigit, 1) + alpha("", 1) + alpha(payee.name, 30) + alpha(`DOC${docNumber}`, 20) + dateYMD(item.paymentDate) + "BRL" + num(0, 15) + money(item.amount, 15) + alpha("", 20) + num(0, 8) + money(0, 15) + num(byPixKey ? "0" : docNumber, 14) + num("0", 8) + num(byPixKey ? "0" : "01", 2) + padR("", 29) + padR("", 10)
|
|
1071
|
+
);
|
|
1072
|
+
}
|
|
1073
|
+
function segmentoB(ctx2, lote, index, form, item) {
|
|
1074
|
+
const payee = item.payee;
|
|
1075
|
+
const byPixKey = form === "pix" && !!payee.pixKey;
|
|
1076
|
+
const initiation = pixInitiationCodeFor(payee.pixKeyType, payee.pixKey);
|
|
1077
|
+
const identifier = byPixKey && initiation === "03" ? payee.pixKey : payee.document;
|
|
1078
|
+
const header = num(ctx2.bank.code, 3) + num(lote, 4) + "3" + num(index, 5) + "B" + initiationFieldFor(form, payee);
|
|
1079
|
+
if (byPixKey) {
|
|
1080
|
+
return finalize(
|
|
1081
|
+
header + inscriptionType(identifier || "") + num(identifier, 14) + pixAlpha("", 35) + pixAlpha("", 60) + pixAlpha(initiation === "03" ? "" : pixKeyValueFor(payee.pixKey, initiation), 99) + padR("", 6) + num(0, 8)
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
1084
|
+
return finalize(
|
|
1085
|
+
header + inscriptionType(payee.document) + num(payee.document, 14) + alpha(payee.street || "", 30) + num(payee.number || "0", 5) + alpha(payee.complement || "", 15) + alpha(payee.district || "", 15) + alpha(payee.city || "", 20) + num(payee.postalCode || "0", 8) + alpha(payee.state || "", 2) + dateYMD(item.paymentDate) + money(item.amount, 15) + money(0, 15) + money(0, 15) + money(0, 15) + money(0, 15) + padR("", 99)
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
function segmentoJ(ctx2, lote, index, item, docNumber) {
|
|
1089
|
+
return finalize(
|
|
1090
|
+
num(ctx2.bank.code, 3) + num(lote, 4) + "3" + num(index, 5) + "J000" + padL(linhaDigitavelToBarcode(item.barcode || ""), 44) + alpha(item.payee.name, 30) + dateYMD(item.paymentDate) + money(item.amount, 15) + money(0, 15) + money(0, 15) + dateYMD(item.paymentDate) + money(item.amount, 15) + num("0", 15) + num(docNumber, 20) + num("0", 20) + padR("", 8) + padR("", 10)
|
|
1091
|
+
);
|
|
1092
|
+
}
|
|
1093
|
+
function segmentoJ52(ctx2, lote, index, item, docNumber) {
|
|
1094
|
+
const payerDoc = onlyDigits(ctx2.company.document);
|
|
1095
|
+
const payeeDoc = onlyDigits(item.payee.document);
|
|
1096
|
+
return finalize(
|
|
1097
|
+
num(ctx2.bank.code, 3) + num(lote, 4) + "3" + num(index, 5) + "J 0052" + inscriptionType(payerDoc) + num(payerDoc, 15) + alpha(ctx2.company.name, 40) + inscriptionType(payeeDoc) + num(payeeDoc, 15) + alpha(item.payee.name, 40) + padR("", 56) + num(docNumber, 53)
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
function segmentoO(ctx2, lote, index, item, docNumber) {
|
|
1101
|
+
return finalize(
|
|
1102
|
+
num(ctx2.bank.code, 3) + num(lote, 4) + "3" + num(index, 5) + "O000" + padL(linhaDigitavelToBarcode(item.barcode || ""), 44) + alpha(item.payee.name, 30) + dateYMD(item.paymentDate) + dateYMD(item.paymentDate) + money(item.amount, 15) + alpha(`DOC${docNumber}`, 20) + padR("", 6) + padR("", 33)
|
|
1103
|
+
);
|
|
1104
|
+
}
|
|
1105
|
+
function groupItems(items, ourBankCode) {
|
|
1106
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1107
|
+
for (const item of items) {
|
|
1108
|
+
let settlement = settlementFor(item.paymentForm, item.payee.bankCode, ourBankCode);
|
|
1109
|
+
if (item.paymentForm === "boleto") {
|
|
1110
|
+
const barcode = linhaDigitavelToBarcode(item.barcode || "");
|
|
1111
|
+
settlement = barcode.substring(0, 3) === padL(onlyDigits(ourBankCode), 3) ? "30" : "31";
|
|
1112
|
+
}
|
|
1113
|
+
const key = `${item.paymentForm}:${settlement}`;
|
|
1114
|
+
const group = groups.get(key) ?? { form: item.paymentForm, settlement, list: [] };
|
|
1115
|
+
group.list.push(item);
|
|
1116
|
+
groups.set(key, group);
|
|
1117
|
+
}
|
|
1118
|
+
return [...groups.values()];
|
|
1119
|
+
}
|
|
1120
|
+
var docNumberFor = (sequence, index) => sequence * 1e4 + index + 1;
|
|
1121
|
+
function generateCnab240(input) {
|
|
1122
|
+
if (!input.items.length) throw new Error("Uma remessa sem itens n\xE3o \xE9 um arquivo.");
|
|
1123
|
+
const at = input.generatedAt ?? /* @__PURE__ */ new Date();
|
|
1124
|
+
const ctx2 = { company: input.company, bank: input.bank };
|
|
1125
|
+
const lines = [headerArquivo(ctx2, input.sequence, at)];
|
|
1126
|
+
const groups = groupItems(input.items, input.bank.code);
|
|
1127
|
+
const batches = [];
|
|
1128
|
+
let lote = 0;
|
|
1129
|
+
let totalRecords = 1;
|
|
1130
|
+
let line = 0;
|
|
1131
|
+
for (const group of groups) {
|
|
1132
|
+
lote += 1;
|
|
1133
|
+
lines.push(headerLote(ctx2, lote, group.form, group.settlement));
|
|
1134
|
+
let loteRecords = 1;
|
|
1135
|
+
let loteTotal = 0;
|
|
1136
|
+
let index = 0;
|
|
1137
|
+
for (const item of group.list) {
|
|
1138
|
+
const docNumber = docNumberFor(input.sequence, line);
|
|
1139
|
+
line += 1;
|
|
1140
|
+
loteTotal += Number(item.amount) || 0;
|
|
1141
|
+
if (group.form === "boleto") {
|
|
1142
|
+
lines.push(segmentoJ(ctx2, lote, index += 1, item, docNumber));
|
|
1143
|
+
lines.push(segmentoJ52(ctx2, lote, index += 1, item, docNumber));
|
|
1144
|
+
loteRecords += 2;
|
|
1145
|
+
} else if (group.form === "tributo") {
|
|
1146
|
+
lines.push(segmentoO(ctx2, lote, index += 1, item, docNumber));
|
|
1147
|
+
loteRecords += 1;
|
|
1148
|
+
} else {
|
|
1149
|
+
lines.push(segmentoA(ctx2, lote, index += 1, group.form, item, docNumber));
|
|
1150
|
+
lines.push(segmentoB(ctx2, lote, index += 1, group.form, item));
|
|
1151
|
+
loteRecords += 2;
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
loteRecords += 1;
|
|
1155
|
+
lines.push(trailerLote(lote, loteRecords, loteTotal));
|
|
1156
|
+
totalRecords += loteRecords;
|
|
1157
|
+
batches.push({ form: group.form, count: group.list.length, total: loteTotal });
|
|
1158
|
+
}
|
|
1159
|
+
totalRecords += 1;
|
|
1160
|
+
lines.push(trailerArquivo(lote, totalRecords));
|
|
1161
|
+
const totalAmount = input.items.reduce((sum, item) => sum + (Number(item.amount) || 0), 0);
|
|
1162
|
+
const bankLabel = String(parseInt(onlyDigits(input.bank.code) || "0", 10));
|
|
1163
|
+
return {
|
|
1164
|
+
fileName: `CI240_${bankLabel}_${String(input.sequence).padStart(4, "0")}.REM`,
|
|
1165
|
+
content: lines.join("\r\n") + "\r\n",
|
|
1166
|
+
layout: `febraban-${padL(onlyDigits(input.bank.code), 3)}`,
|
|
1167
|
+
totalAmount,
|
|
1168
|
+
itemsCount: input.items.length,
|
|
1169
|
+
batches
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
function cnabPayeeGaps(items) {
|
|
1173
|
+
const gaps = [];
|
|
1174
|
+
items.forEach((item, index) => {
|
|
1175
|
+
const payee = item.payee;
|
|
1176
|
+
const name = payee.name || `Favorecido #${index + 1}`;
|
|
1177
|
+
const push = (field, message) => gaps.push({ reference: item.reference, name, field, message });
|
|
1178
|
+
if (!onlyDigits(payee.document)) push("document", "CPF/CNPJ");
|
|
1179
|
+
if (item.paymentForm === "pix") {
|
|
1180
|
+
if (!payee.pixKey?.trim()) push("pixKey", "chave PIX");
|
|
1181
|
+
else if (payee.pixKey.trim().toUpperCase().startsWith("00020")) {
|
|
1182
|
+
push("pixKeyTypeEmv", "o campo tem um PIX Copia-e-Cola (EMV), que o CNAB 240 n\xE3o aceita \u2014 informe a chave real");
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
if (item.paymentForm === "ted" && (!payee.bankCode?.trim() || !payee.account?.trim())) {
|
|
1186
|
+
push("bankDetails", "banco, ag\xEAncia e conta");
|
|
1187
|
+
}
|
|
1188
|
+
if (item.paymentForm === "boleto" || item.paymentForm === "tributo") {
|
|
1189
|
+
const barcode = linhaDigitavelToBarcode(item.barcode || "");
|
|
1190
|
+
if (barcode.length !== 44) push("barcode", "c\xF3digo de barras ou linha digit\xE1vel");
|
|
1191
|
+
}
|
|
1192
|
+
});
|
|
1193
|
+
return gaps;
|
|
1194
|
+
}
|
|
1195
|
+
function suggestPaymentForm(barcode) {
|
|
1196
|
+
const raw = (barcode || "").trim();
|
|
1197
|
+
const digits2 = onlyDigits(raw);
|
|
1198
|
+
if (/^\d+$/.test(raw) && digits2.length >= 44 && digits2.length <= 48) return "boleto";
|
|
1199
|
+
return "pix";
|
|
1200
|
+
}
|
|
1201
|
+
function inferPixKeyType(key) {
|
|
1202
|
+
const raw = key.trim();
|
|
1203
|
+
const digits2 = onlyDigits(raw);
|
|
1204
|
+
if (raw.toUpperCase().startsWith("00020")) return "aleatoria";
|
|
1205
|
+
if (digits2.length === 11 && !raw.includes("@")) return "cpf";
|
|
1206
|
+
if (digits2.length === 14) return "cnpj";
|
|
1207
|
+
if (raw.includes("@")) return "email";
|
|
1208
|
+
if (digits2.length >= 10 && digits2.length <= 13) return "telefone";
|
|
1209
|
+
return "aleatoria";
|
|
1210
|
+
}
|
|
1211
|
+
function splitAccountDigit(value, digit) {
|
|
1212
|
+
if (digit.trim()) return [value.trim(), digit.trim()];
|
|
1213
|
+
const match = value.trim().match(/^(\d+)\s*[-/.]\s*([0-9xX])$/);
|
|
1214
|
+
return match ? [match[1], match[2].toUpperCase()] : [value.trim(), ""];
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
// src/lib/br-banks.ts
|
|
1218
|
+
var LOGO_BASE = "https://cdn.jsdelivr.net/gh/Tgentil/Bancos-em-SVG@fe1d43f0cf379135bd01c987bc147a04fdf48c6d";
|
|
1219
|
+
var logo = (path) => `${LOGO_BASE}/${path.split("/").map(encodeURIComponent).join("/")}`;
|
|
1220
|
+
var BR_BANKS = [
|
|
1221
|
+
{ code: "001", name: "Banco do Brasil", aliases: ["bb"], logo: logo("Banco do Brasil S.A/banco-do-brasil-sem-fundo.svg") },
|
|
1222
|
+
{ code: "341", name: "Ita\xFA", aliases: ["itau", "ita\xFA unibanco"], logo: logo("Ita\xFA Unibanco S.A/itau.svg") },
|
|
1223
|
+
{ code: "237", name: "Bradesco", logo: logo("Bradesco S.A/bradesco.svg") },
|
|
1224
|
+
{ code: "104", name: "Caixa Econ\xF4mica Federal", aliases: ["caixa", "cef"], logo: logo("Caixa Econ\xF4mica Federal/caixa-economica-federal-1.svg") },
|
|
1225
|
+
{ code: "033", name: "Santander", logo: logo("Banco Santander Brasil S.A/banco-santander-logo.svg") },
|
|
1226
|
+
{ code: "260", name: "Nubank", aliases: ["nu pagamentos"], logo: logo("Nu Pagamentos S.A/nubank-logo-2021.svg") },
|
|
1227
|
+
{ code: "077", name: "Inter", aliases: ["banco inter"], logo: logo("Banco Inter S.A/inter.svg") },
|
|
1228
|
+
{ code: "336", name: "C6 Bank", aliases: ["c6"], logo: logo("Banco C6 S.A/c6 bank.svg") },
|
|
1229
|
+
{ code: "208", name: "BTG Pactual", aliases: ["btg"], logo: logo("Banco BTG Pacutal/btg-pactual.svg") },
|
|
1230
|
+
{ code: "756", name: "Sicoob", logo: logo("Sicoob/sicoob-vector-logo.svg") },
|
|
1231
|
+
{ code: "748", name: "Sicredi", logo: logo("Sicredi/logo-svg2.svg") },
|
|
1232
|
+
{ code: "290", name: "PagBank", aliases: ["pagseguro"], logo: logo("PagSeguro Internet S.A/logo-pagbank.svg") },
|
|
1233
|
+
{ code: "197", name: "Stone", logo: logo("Stone Pagamentos S.A/stone.svg") },
|
|
1234
|
+
{ code: "323", name: "Mercado Pago", logo: logo("Mercado Pago/mercado-pago.svg") },
|
|
1235
|
+
{ code: "542", name: "InfinitePay", aliases: ["cloudwalk"], logo: logo("InfinitePay/InfitePay.svg") },
|
|
1236
|
+
{ code: "403", name: "Cora", logo: logo("Cora Sociedade Credito Direto S.A/icone-cora-rosa-pequeno.svg") },
|
|
1237
|
+
{ code: "380", name: "PicPay", logo: logo("PicPay/Logo-PicPay.svg") },
|
|
1238
|
+
{ code: "364", name: "Ef\xED", aliases: ["gerencianet", "efi bank"], logo: logo("Ef\xED - Gerencianet/logo-efi-bank-laranja.svg") },
|
|
1239
|
+
{ code: "461", name: "Asaas", logo: logo("Asaas IP S.A/header-logo-azul.svg") },
|
|
1240
|
+
{ code: "422", name: "Safra", logo: logo("Banco Safra S.A/logo-safra.svg") },
|
|
1241
|
+
{ code: "212", name: "Banco Original", aliases: ["original"], logo: logo("Banco Original S.A/banco-original-logo-verde.svg") },
|
|
1242
|
+
{ code: "655", name: "Banco BV", aliases: ["votorantim", "bv"], logo: logo("Banco Votorantim/banco-bv-logo.svg") },
|
|
1243
|
+
{ code: "041", name: "Banrisul", logo: logo("Banrisul/banrisul-logo-2023.svg") },
|
|
1244
|
+
{ code: "070", name: "BRB", aliases: ["banco de bras\xEDlia"], logo: logo("BRB - Banco de Brasilia/brb-logo.svg") },
|
|
1245
|
+
{ code: "136", name: "Unicred", logo: logo("Unicred/verde.svg") },
|
|
1246
|
+
{ code: "133", name: "Cresol", logo: logo("Cresol/Icone-original.svg") },
|
|
1247
|
+
{ code: "085", name: "Ailos", logo: logo("Ailos/logo-ailos.svg") },
|
|
1248
|
+
{ code: "004", name: "Banco do Nordeste", aliases: ["bnb"], logo: logo("Banco do Nordeste do Brasil S.A/Logo_BNB.svg") },
|
|
1249
|
+
{ code: "021", name: "Banestes", logo: logo("Banco do Estado do Espirito Santo/banestes.svg") },
|
|
1250
|
+
{ code: "037", name: "Banpar\xE1", logo: logo("Banco do Estado do Para/Logo_do_Banpar\xE1.svg") },
|
|
1251
|
+
{ code: "047", name: "Banese", logo: logo("Banco do Estado do Sergipe/logo banese.svg") },
|
|
1252
|
+
{ code: "389", name: "Mercantil do Brasil", aliases: ["mercantil"], logo: logo("Banco Mercantil do Brasil S.A/banco-mercantil-novo-azul.svg") },
|
|
1253
|
+
{ code: "318", name: "BMG", logo: logo("Banco BMG/banco-bmg-logo.svg") },
|
|
1254
|
+
{ code: "707", name: "Daycoval", logo: logo("Banco Daycoval/logo-Daycoval.svg") },
|
|
1255
|
+
{ code: "246", name: "ABC Brasil", logo: logo("ABC Brasil/logoabc.svg") },
|
|
1256
|
+
{ code: "218", name: "BS2", logo: logo("Banco BS2 S.A/Banco_BS2.svg") },
|
|
1257
|
+
{ code: "102", name: "XP", aliases: ["xp investimentos"], logo: logo("XP Investimentos/xp-investimentos-logo.svg") },
|
|
1258
|
+
{ code: "536", name: "Neon", logo: logo("Neon/header-logo-neon.svg") }
|
|
1259
|
+
];
|
|
1260
|
+
var norm = (s) => s.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().trim();
|
|
1261
|
+
var digits = (s) => s.replace(/\D/g, "");
|
|
1262
|
+
function findBrBank(code, name) {
|
|
1263
|
+
const c = digits(code ?? "");
|
|
1264
|
+
if (c) {
|
|
1265
|
+
const byCode = BR_BANKS.find((b) => Number(b.code) === Number(c));
|
|
1266
|
+
if (byCode) return byCode;
|
|
1267
|
+
}
|
|
1268
|
+
const n = norm(name ?? "");
|
|
1269
|
+
if (!n) return void 0;
|
|
1270
|
+
return BR_BANKS.find((b) => norm(b.name) === n || b.aliases?.some((a) => norm(a) === n));
|
|
1271
|
+
}
|
|
1272
|
+
function searchBrBanks(query) {
|
|
1273
|
+
const q = norm(query);
|
|
1274
|
+
if (!q) return BR_BANKS;
|
|
1275
|
+
const qd = digits(q);
|
|
1276
|
+
return BR_BANKS.filter(
|
|
1277
|
+
(b) => qd && qd === q && (b.code.startsWith(qd) || b.code.replace(/^0+/, "").startsWith(qd)) || norm(b.name).includes(q) || b.aliases?.some((a) => norm(a).includes(q))
|
|
1278
|
+
);
|
|
1279
|
+
}
|
|
1280
|
+
function BankLogo({ bank, size = 20 }) {
|
|
1281
|
+
const [broken, setBroken] = useState(false);
|
|
1282
|
+
if (!bank?.logo || broken) {
|
|
1283
|
+
return /* @__PURE__ */ jsx(Landmark, { className: "text-muted-foreground", style: { width: size * 0.8, height: size * 0.8 }, "aria-hidden": true });
|
|
1284
|
+
}
|
|
1285
|
+
return /* @__PURE__ */ jsx(
|
|
1286
|
+
"img",
|
|
1287
|
+
{
|
|
1288
|
+
src: bank.logo,
|
|
1289
|
+
alt: "",
|
|
1290
|
+
width: size,
|
|
1291
|
+
height: size,
|
|
1292
|
+
loading: "lazy",
|
|
1293
|
+
onError: () => setBroken(true),
|
|
1294
|
+
className: "shrink-0 object-contain",
|
|
1295
|
+
style: { width: size, height: size }
|
|
1296
|
+
}
|
|
1297
|
+
);
|
|
1298
|
+
}
|
|
1299
|
+
var fromCatalog = (entry) => entry.compe ? { code: entry.compe, name: entry.name, logo: entry.logoUrl } : null;
|
|
1300
|
+
function BankPicker({ code, name, search, onChange }) {
|
|
1301
|
+
const known = findBrBank(code, name);
|
|
1302
|
+
const toOption = (b) => ({
|
|
1303
|
+
id: b.code,
|
|
1304
|
+
label: b.name,
|
|
1305
|
+
subtitle: b.code,
|
|
1306
|
+
icon: /* @__PURE__ */ jsx(BankLogo, { bank: b }),
|
|
1307
|
+
data: b
|
|
1308
|
+
});
|
|
1309
|
+
const onSearch = async (query) => {
|
|
1310
|
+
const local = searchBrBanks(query);
|
|
1311
|
+
if (!search) return local.map(toOption);
|
|
1312
|
+
try {
|
|
1313
|
+
const remote = (await search(query)).map(fromCatalog).filter(Boolean);
|
|
1314
|
+
if (!remote.length) return local.map(toOption);
|
|
1315
|
+
const merged = remote.map((bank) => findBrBank(bank.code) ?? bank);
|
|
1316
|
+
const seen = new Set(merged.map((bank) => bank.code));
|
|
1317
|
+
return [...merged, ...local.filter((bank) => !seen.has(bank.code))].map(toOption);
|
|
1318
|
+
} catch {
|
|
1319
|
+
return local.map(toOption);
|
|
1320
|
+
}
|
|
1321
|
+
};
|
|
1322
|
+
return /* @__PURE__ */ jsx(
|
|
1323
|
+
SearchSelect,
|
|
1324
|
+
{
|
|
1325
|
+
value: name ? known?.code ?? `name:${name}` : "",
|
|
1326
|
+
displayValue: name ?? "",
|
|
1327
|
+
leading: name ? /* @__PURE__ */ jsx(BankLogo, { bank: known, size: 18 }) : void 0,
|
|
1328
|
+
placeholder: "Busque pelo nome ou n\xFAmero",
|
|
1329
|
+
minChars: 0,
|
|
1330
|
+
loadOnOpen: true,
|
|
1331
|
+
onSearch,
|
|
1332
|
+
onChange: (_id, option) => {
|
|
1333
|
+
const b = option?.data;
|
|
1334
|
+
onChange(b ? { code: b.code, name: b.name } : { code: "", name: "" });
|
|
1335
|
+
},
|
|
1336
|
+
allowCreate: true,
|
|
1337
|
+
createLabel: "Usar",
|
|
1338
|
+
clearLabel: "Limpar",
|
|
1339
|
+
onCreate: (q) => onChange({ code: code ?? "", name: q.trim() }),
|
|
1340
|
+
noResultsLabel: "Nenhum banco com esse nome",
|
|
1341
|
+
debounce: 0
|
|
1342
|
+
}
|
|
1343
|
+
);
|
|
1344
|
+
}
|
|
868
1345
|
var STEPS = [
|
|
869
1346
|
{ id: "account", label: "Conta banc\xE1ria" },
|
|
870
1347
|
{ id: "payments", label: "Pagamentos" },
|
|
871
1348
|
{ id: "beneficiaries", label: "Favorecidos" },
|
|
872
1349
|
{ id: "review", label: "Revis\xE3o" }
|
|
873
1350
|
];
|
|
1351
|
+
var FORM_LABEL = {
|
|
1352
|
+
pix: "PIX",
|
|
1353
|
+
ted: "TED/DOC",
|
|
1354
|
+
boleto: "Boleto",
|
|
1355
|
+
tributo: "Tributo"
|
|
1356
|
+
};
|
|
1357
|
+
var PIX_KEY_TYPES = [
|
|
1358
|
+
{ value: "cpf", label: "CPF" },
|
|
1359
|
+
{ value: "cnpj", label: "CNPJ" },
|
|
1360
|
+
{ value: "email", label: "E-mail" },
|
|
1361
|
+
{ value: "telefone", label: "Telefone" },
|
|
1362
|
+
{ value: "aleatoria", label: "Aleat\xF3ria" }
|
|
1363
|
+
];
|
|
1364
|
+
var todayISO = () => {
|
|
1365
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
1366
|
+
return `${now2.getFullYear()}-${String(now2.getMonth() + 1).padStart(2, "0")}-${String(now2.getDate()).padStart(2, "0")}`;
|
|
1367
|
+
};
|
|
874
1368
|
function textMetadata(account, key) {
|
|
875
1369
|
const value = account.metadata?.[key];
|
|
876
1370
|
return typeof value === "string" ? value : "";
|
|
877
1371
|
}
|
|
878
1372
|
function draftFromAccount(account) {
|
|
1373
|
+
const [agencyNumber, agencyDigit] = splitAccountDigit(account.agencyNumber || "", textMetadata(account, "agencyDigit"));
|
|
1374
|
+
const [accountNumber, accountDigit] = splitAccountDigit(account.accountNumber || "", textMetadata(account, "accountDigit"));
|
|
879
1375
|
return {
|
|
880
|
-
bankName: account.bankName
|
|
881
|
-
bankCode: textMetadata(account, "bankCode"),
|
|
882
|
-
agencyNumber
|
|
883
|
-
agencyDigit
|
|
884
|
-
accountNumber
|
|
885
|
-
accountDigit
|
|
1376
|
+
bankName: account.bankName || "",
|
|
1377
|
+
bankCode: account.bankCode || textMetadata(account, "bankCode"),
|
|
1378
|
+
agencyNumber,
|
|
1379
|
+
agencyDigit,
|
|
1380
|
+
accountNumber,
|
|
1381
|
+
accountDigit,
|
|
886
1382
|
agreementCode: textMetadata(account, "cnabAgreementCode")
|
|
887
1383
|
};
|
|
888
1384
|
}
|
|
889
|
-
function missingFields(draft) {
|
|
1385
|
+
function missingFields(draft, companyDocument) {
|
|
890
1386
|
return [
|
|
891
1387
|
!draft.bankCode.trim() && "c\xF3digo do banco",
|
|
892
1388
|
!draft.agencyNumber.trim() && "ag\xEAncia",
|
|
893
|
-
!draft.accountNumber.trim() && "conta"
|
|
1389
|
+
!draft.accountNumber.trim() && "conta",
|
|
1390
|
+
!companyDocument.replace(/\D/g, "") && "CNPJ da empresa"
|
|
894
1391
|
].filter(Boolean);
|
|
895
1392
|
}
|
|
1393
|
+
var emptyPayee = (invoice) => ({
|
|
1394
|
+
contactId: invoice.contactId ?? "",
|
|
1395
|
+
name: invoice.contactName || invoice.itemsSummary || "Sem favorecido",
|
|
1396
|
+
document: ""
|
|
1397
|
+
});
|
|
1398
|
+
function lineFromInvoice(invoice, payee) {
|
|
1399
|
+
const base = payee ?? emptyPayee(invoice);
|
|
1400
|
+
const barcode = typeof invoice.metadata?.barcode === "string" ? invoice.metadata.barcode : "";
|
|
1401
|
+
const paymentForm = suggestPaymentForm(barcode);
|
|
1402
|
+
const resolved = { ...base, name: base.name || invoice.contactName || "Sem favorecido" };
|
|
1403
|
+
if (!resolved.pixKey?.trim() && resolved.document.replace(/\D/g, "")) {
|
|
1404
|
+
resolved.pixKey = resolved.document;
|
|
1405
|
+
resolved.pixKeyType = inferPixKeyType(resolved.document);
|
|
1406
|
+
} else if (resolved.pixKey?.trim() && !resolved.pixKeyType?.trim()) {
|
|
1407
|
+
resolved.pixKeyType = inferPixKeyType(resolved.pixKey);
|
|
1408
|
+
}
|
|
1409
|
+
const today5 = todayISO();
|
|
1410
|
+
return {
|
|
1411
|
+
invoice,
|
|
1412
|
+
paymentForm,
|
|
1413
|
+
// Paying on a date already past is a rejected line; the due date only wins
|
|
1414
|
+
// while it is still ahead.
|
|
1415
|
+
paymentDate: invoice.dueDate && invoice.dueDate >= today5 ? invoice.dueDate : today5,
|
|
1416
|
+
barcode: paymentForm === "boleto" ? barcode : "",
|
|
1417
|
+
payee: resolved
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
var openAmount = (invoice) => Math.max(0, invoice.totalAmount - invoice.paidAmount);
|
|
896
1421
|
function CnabRemittanceModal({ invoices, bankAccounts, provider, onClose }) {
|
|
897
1422
|
const { currency } = useFinancialConfig();
|
|
898
1423
|
const [step, setStep] = React23__default.useState(0);
|
|
899
1424
|
const [bankAccountId, setBankAccountId] = React23__default.useState("");
|
|
900
1425
|
const [draft, setDraft] = React23__default.useState(null);
|
|
901
|
-
const [
|
|
902
|
-
const [
|
|
1426
|
+
const [context, setContext] = React23__default.useState(null);
|
|
1427
|
+
const [companyName, setCompanyName] = React23__default.useState("");
|
|
1428
|
+
const [companyDocument, setCompanyDocument] = React23__default.useState("");
|
|
1429
|
+
const [lines, setLines] = React23__default.useState([]);
|
|
1430
|
+
const [companyUnsaved, setCompanyUnsaved] = React23__default.useState(false);
|
|
1431
|
+
const [loadingContext, setLoadingContext] = React23__default.useState(false);
|
|
1432
|
+
const [busy, setBusy] = React23__default.useState(false);
|
|
1433
|
+
const [error, setError] = React23__default.useState("");
|
|
903
1434
|
const eligibleAccounts2 = bankAccounts.filter((account2) => account2.accountType === "bank" && account2.isActive);
|
|
904
|
-
const total = invoices.reduce((sum, invoice) => sum +
|
|
1435
|
+
const total = invoices.reduce((sum, invoice) => sum + openAmount(invoice), 0);
|
|
905
1436
|
const account = eligibleAccounts2.find((candidate) => candidate.id === bankAccountId);
|
|
906
|
-
const missingBankData = draft ? missingFields(draft) : [];
|
|
907
|
-
const
|
|
1437
|
+
const missingBankData = draft ? missingFields(draft, companyDocument) : [];
|
|
1438
|
+
const items = React23__default.useMemo(() => lines.map((line) => ({
|
|
1439
|
+
reference: line.invoice.id,
|
|
1440
|
+
amount: openAmount(line.invoice),
|
|
1441
|
+
paymentDate: line.paymentDate,
|
|
1442
|
+
paymentForm: line.paymentForm,
|
|
1443
|
+
barcode: line.barcode,
|
|
1444
|
+
payee: {
|
|
1445
|
+
name: line.payee.name,
|
|
1446
|
+
document: line.payee.document,
|
|
1447
|
+
bankCode: line.payee.bankCode,
|
|
1448
|
+
agency: line.payee.agencyNumber,
|
|
1449
|
+
agencyDigit: line.payee.agencyDigit,
|
|
1450
|
+
account: line.payee.accountNumber,
|
|
1451
|
+
accountDigit: line.payee.accountDigit,
|
|
1452
|
+
pixKey: line.payee.pixKey,
|
|
1453
|
+
pixKeyType: line.payee.pixKeyType,
|
|
1454
|
+
street: line.payee.street,
|
|
1455
|
+
number: line.payee.number,
|
|
1456
|
+
complement: line.payee.complement,
|
|
1457
|
+
district: line.payee.district,
|
|
1458
|
+
city: line.payee.city,
|
|
1459
|
+
state: line.payee.state,
|
|
1460
|
+
postalCode: line.payee.postalCode
|
|
1461
|
+
}
|
|
1462
|
+
})), [lines]);
|
|
1463
|
+
const gaps = React23__default.useMemo(() => cnabPayeeGaps(items), [items]);
|
|
1464
|
+
const batches = React23__default.useMemo(() => {
|
|
1465
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
1466
|
+
for (const line of lines) {
|
|
1467
|
+
const current = grouped.get(line.paymentForm) ?? { count: 0, total: 0 };
|
|
1468
|
+
current.count += 1;
|
|
1469
|
+
current.total += openAmount(line.invoice);
|
|
1470
|
+
grouped.set(line.paymentForm, current);
|
|
1471
|
+
}
|
|
1472
|
+
return [...grouped.entries()];
|
|
1473
|
+
}, [lines]);
|
|
1474
|
+
const selectAccount = async (id) => {
|
|
908
1475
|
const selected = eligibleAccounts2.find((candidate) => candidate.id === id);
|
|
909
1476
|
setBankAccountId(id);
|
|
910
1477
|
setDraft(selected ? draftFromAccount(selected) : null);
|
|
911
|
-
|
|
1478
|
+
setError("");
|
|
1479
|
+
if (!selected || !provider.getCnabContext) {
|
|
1480
|
+
setLines(invoices.map((invoice) => lineFromInvoice(invoice, void 0)));
|
|
1481
|
+
return;
|
|
1482
|
+
}
|
|
1483
|
+
setLoadingContext(true);
|
|
1484
|
+
try {
|
|
1485
|
+
const loaded = await provider.getCnabContext({
|
|
1486
|
+
contactIds: invoices.map((invoice) => invoice.contactId ?? "").filter(Boolean),
|
|
1487
|
+
bankAccountId: id,
|
|
1488
|
+
unitId: selected.unitId
|
|
1489
|
+
});
|
|
1490
|
+
setContext(loaded);
|
|
1491
|
+
setCompanyName(loaded.company.name);
|
|
1492
|
+
setCompanyDocument(loaded.company.document);
|
|
1493
|
+
const byContact = new Map(loaded.payees.map((payee) => [payee.contactId, payee]));
|
|
1494
|
+
setLines(invoices.map((invoice) => lineFromInvoice(invoice, byContact.get(invoice.contactId ?? ""))));
|
|
1495
|
+
} catch (cause) {
|
|
1496
|
+
setError(cause instanceof Error ? cause.message : "N\xE3o foi poss\xEDvel carregar os favorecidos.");
|
|
1497
|
+
setLines(invoices.map((invoice) => lineFromInvoice(invoice, void 0)));
|
|
1498
|
+
} finally {
|
|
1499
|
+
setLoadingContext(false);
|
|
1500
|
+
}
|
|
912
1501
|
};
|
|
913
1502
|
const updateDraft = (key, value) => {
|
|
914
1503
|
setDraft((current) => current ? { ...current, [key]: value } : current);
|
|
915
|
-
|
|
1504
|
+
setError("");
|
|
1505
|
+
};
|
|
1506
|
+
const updateLine = (index, patch) => setLines((current) => current.map((line, i) => i === index ? { ...line, ...patch } : line));
|
|
1507
|
+
const updatePayee = (index, patch) => setLines((current) => current.map((line, i) => i === index ? { ...line, payee: { ...line.payee, ...patch } } : line));
|
|
1508
|
+
const saveBankDraft = async () => {
|
|
1509
|
+
if (!account || !draft) return;
|
|
1510
|
+
await provider.updateBankAccount(account.id, {
|
|
1511
|
+
bankCode: draft.bankCode.trim(),
|
|
1512
|
+
bankName: draft.bankName.trim() || void 0,
|
|
1513
|
+
agencyNumber: draft.agencyNumber.trim(),
|
|
1514
|
+
accountNumber: draft.accountNumber.trim(),
|
|
1515
|
+
metadata: {
|
|
1516
|
+
...account.metadata,
|
|
1517
|
+
agencyDigit: draft.agencyDigit.trim(),
|
|
1518
|
+
accountDigit: draft.accountDigit.trim(),
|
|
1519
|
+
cnabAgreementCode: draft.agreementCode.trim()
|
|
1520
|
+
}
|
|
1521
|
+
});
|
|
1522
|
+
};
|
|
1523
|
+
const saveCompany = async () => {
|
|
1524
|
+
if (!provider.saveCompany) return;
|
|
1525
|
+
const loaded = context?.company;
|
|
1526
|
+
const unchanged = loaded && loaded.document.replace(/\D/g, "") === companyDocument.replace(/\D/g, "") && loaded.name === companyName;
|
|
1527
|
+
if (unchanged) return;
|
|
1528
|
+
try {
|
|
1529
|
+
const saved = await provider.saveCompany({
|
|
1530
|
+
id: loaded?.id,
|
|
1531
|
+
unitId: account?.unitId,
|
|
1532
|
+
name: companyName,
|
|
1533
|
+
document: companyDocument
|
|
1534
|
+
});
|
|
1535
|
+
setContext((current) => current ? { ...current, company: { ...current.company, id: saved.id, name: companyName, document: companyDocument } } : current);
|
|
1536
|
+
} catch {
|
|
1537
|
+
setCompanyUnsaved(true);
|
|
1538
|
+
}
|
|
916
1539
|
};
|
|
917
1540
|
const advance = async () => {
|
|
918
|
-
|
|
919
|
-
|
|
1541
|
+
setError("");
|
|
1542
|
+
if (step === 0) {
|
|
1543
|
+
if (!account || !draft || missingBankData.length > 0) return;
|
|
1544
|
+
setBusy(true);
|
|
1545
|
+
try {
|
|
1546
|
+
await saveBankDraft();
|
|
1547
|
+
await saveCompany();
|
|
1548
|
+
setStep(1);
|
|
1549
|
+
} catch (cause) {
|
|
1550
|
+
setError(cause instanceof Error ? cause.message : "N\xE3o foi poss\xEDvel salvar os dados banc\xE1rios.");
|
|
1551
|
+
} finally {
|
|
1552
|
+
setBusy(false);
|
|
1553
|
+
}
|
|
920
1554
|
return;
|
|
921
1555
|
}
|
|
922
|
-
if (
|
|
923
|
-
|
|
924
|
-
|
|
1556
|
+
if (step === 2 && gaps.length > 0) return;
|
|
1557
|
+
setStep((value) => Math.min(value + 1, 3));
|
|
1558
|
+
};
|
|
1559
|
+
const generate = async () => {
|
|
1560
|
+
if (!account || !draft) return;
|
|
1561
|
+
setBusy(true);
|
|
1562
|
+
setError("");
|
|
925
1563
|
try {
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
1564
|
+
const sequence = context?.nextSequence ?? 1;
|
|
1565
|
+
const file = generateCnab240({
|
|
1566
|
+
sequence,
|
|
1567
|
+
company: { name: companyName, document: companyDocument },
|
|
1568
|
+
bank: {
|
|
1569
|
+
code: draft.bankCode,
|
|
1570
|
+
name: draft.bankName,
|
|
1571
|
+
agency: draft.agencyNumber,
|
|
1572
|
+
agencyDigit: draft.agencyDigit,
|
|
1573
|
+
account: draft.accountNumber,
|
|
1574
|
+
accountDigit: draft.accountDigit,
|
|
1575
|
+
agreement: draft.agreementCode
|
|
1576
|
+
},
|
|
1577
|
+
items
|
|
937
1578
|
});
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
1579
|
+
if (provider.saveCnabPayees) {
|
|
1580
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1581
|
+
const payload = lines.filter((line) => {
|
|
1582
|
+
if (!line.payee.contactId || seen.has(line.payee.contactId)) return false;
|
|
1583
|
+
seen.add(line.payee.contactId);
|
|
1584
|
+
return true;
|
|
1585
|
+
}).map((line) => ({
|
|
1586
|
+
contactId: line.payee.contactId,
|
|
1587
|
+
payeeAccountId: line.payee.payeeAccountId,
|
|
1588
|
+
document: line.payee.document,
|
|
1589
|
+
bankCode: line.payee.bankCode,
|
|
1590
|
+
bankName: line.payee.bankName,
|
|
1591
|
+
agencyNumber: line.payee.agencyNumber,
|
|
1592
|
+
agencyDigit: line.payee.agencyDigit,
|
|
1593
|
+
accountNumber: line.payee.accountNumber,
|
|
1594
|
+
accountDigit: line.payee.accountDigit,
|
|
1595
|
+
accountType: line.payee.accountType,
|
|
1596
|
+
pixKey: line.payee.pixKey,
|
|
1597
|
+
pixKeyType: line.payee.pixKeyType
|
|
1598
|
+
}));
|
|
1599
|
+
if (payload.length) await provider.saveCnabPayees(payload);
|
|
1600
|
+
}
|
|
1601
|
+
if (provider.recordCnabBatch) {
|
|
1602
|
+
await provider.recordCnabBatch({
|
|
1603
|
+
bankAccountId: account.id,
|
|
1604
|
+
unitId: account.unitId,
|
|
1605
|
+
layout: file.layout,
|
|
1606
|
+
sequenceNumber: sequence,
|
|
1607
|
+
totalAmount: file.totalAmount,
|
|
1608
|
+
fileName: file.fileName,
|
|
1609
|
+
fileContent: file.content,
|
|
1610
|
+
items: lines.map((line) => ({
|
|
1611
|
+
movementId: line.invoice.id,
|
|
1612
|
+
paymentForm: line.paymentForm,
|
|
1613
|
+
paymentDate: line.paymentDate,
|
|
1614
|
+
amount: openAmount(line.invoice)
|
|
1615
|
+
}))
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
const blob = new Blob([file.content], { type: "text/plain;charset=us-ascii" });
|
|
1619
|
+
const url = URL.createObjectURL(blob);
|
|
1620
|
+
const anchor = document.createElement("a");
|
|
1621
|
+
anchor.href = url;
|
|
1622
|
+
anchor.download = file.fileName;
|
|
1623
|
+
anchor.click();
|
|
1624
|
+
URL.revokeObjectURL(url);
|
|
1625
|
+
onClose();
|
|
1626
|
+
} catch (cause) {
|
|
1627
|
+
setError(cause instanceof Error ? cause.message : "N\xE3o foi poss\xEDvel gerar o arquivo.");
|
|
941
1628
|
} finally {
|
|
942
|
-
|
|
1629
|
+
setBusy(false);
|
|
943
1630
|
}
|
|
944
1631
|
};
|
|
1632
|
+
const canAdvance = step === 0 ? !!account && !!draft && missingBankData.length === 0 && !loadingContext : step === 2 ? gaps.length === 0 : true;
|
|
945
1633
|
return createPortal(
|
|
946
1634
|
/* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
|
|
947
1635
|
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/50 backdrop-blur-[2px]", onClick: onClose }),
|
|
@@ -983,82 +1671,203 @@ function CnabRemittanceModal({ invoices, bankAccounts, provider, onClose }) {
|
|
|
983
1671
|
id: candidate.id,
|
|
984
1672
|
label: candidate.name,
|
|
985
1673
|
subtitle: [candidate.bankName, candidate.agencyNumber && `Ag ${candidate.agencyNumber}`, candidate.accountNumber && `Cc ${candidate.accountNumber}`].filter(Boolean).join(" \xB7 "),
|
|
986
|
-
icon: /* @__PURE__ */ jsx(
|
|
1674
|
+
icon: /* @__PURE__ */ jsx(BankLogo, { bank: findBrBank(candidate.bankCode || textMetadata(candidate, "bankCode"), candidate.bankName) })
|
|
987
1675
|
}));
|
|
988
1676
|
},
|
|
989
|
-
onChange: (id) => selectAccount(id)
|
|
1677
|
+
onChange: (id) => void selectAccount(id)
|
|
990
1678
|
}
|
|
991
1679
|
),
|
|
992
|
-
draft ? /* @__PURE__ */ jsxs(
|
|
993
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
994
|
-
/* @__PURE__ */
|
|
995
|
-
|
|
1680
|
+
draft ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1681
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded-lg border bg-muted/20 p-4", children: [
|
|
1682
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1683
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Empresa pagadora" }),
|
|
1684
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: companyUnsaved ? "Vale para este arquivo: seu perfil n\xE3o pode gravar o cadastro da empresa. Pe\xE7a a um respons\xE1vel para cadastr\xE1-la." : context?.company.id ? "\xC9 este CNPJ que vai no cabe\xE7alho do arquivo." : "Esta unidade ainda n\xE3o tem empresa cadastrada. O que voc\xEA informar aqui vira o cadastro dela, e a pr\xF3xima remessa j\xE1 abre preenchida." })
|
|
1685
|
+
] }),
|
|
1686
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [
|
|
1687
|
+
/* @__PURE__ */ jsx(Field, { label: "Raz\xE3o social / nome", value: companyName, onChange: setCompanyName }),
|
|
1688
|
+
/* @__PURE__ */ jsx(Field, { label: "CNPJ", required: true, value: companyDocument, placeholder: "00.000.000/0000-00", onChange: setCompanyDocument })
|
|
1689
|
+
] })
|
|
996
1690
|
] }),
|
|
997
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
998
|
-
/* @__PURE__ */
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
/* @__PURE__ */ jsx(Field, { label: "Ag\xEAncia", required: true, value: draft.agencyNumber, onChange: (value) => updateDraft("agencyNumber", value), className: "col-span-2" }),
|
|
1002
|
-
/* @__PURE__ */ jsx(Field, { label: "D\xEDgito", value: draft.agencyDigit, onChange: (value) => updateDraft("agencyDigit", value) })
|
|
1691
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded-lg border bg-muted/20 p-4", children: [
|
|
1692
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1693
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Dados da conta para a remessa" }),
|
|
1694
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Preencha uma vez; os pr\xF3ximos arquivos reutilizam esta configura\xE7\xE3o." })
|
|
1003
1695
|
] }),
|
|
1004
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-
|
|
1005
|
-
/* @__PURE__ */
|
|
1006
|
-
|
|
1696
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [
|
|
1697
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5 sm:col-span-2", children: [
|
|
1698
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-medium", children: [
|
|
1699
|
+
"Banco",
|
|
1700
|
+
/* @__PURE__ */ jsx("span", { className: "text-destructive", children: " *" })
|
|
1701
|
+
] }),
|
|
1702
|
+
/* @__PURE__ */ jsx(
|
|
1703
|
+
BankPicker,
|
|
1704
|
+
{
|
|
1705
|
+
code: draft.bankCode,
|
|
1706
|
+
name: draft.bankName,
|
|
1707
|
+
search: provider.searchBanks ? (query) => provider.searchBanks(query) : void 0,
|
|
1708
|
+
onChange: ({ code, name }) => setDraft((current) => current ? { ...current, bankCode: code || current.bankCode, bankName: name } : current)
|
|
1709
|
+
}
|
|
1710
|
+
),
|
|
1711
|
+
draft.bankCode ? /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
1712
|
+
"C\xF3digo ",
|
|
1713
|
+
draft.bankCode
|
|
1714
|
+
] }) : null
|
|
1715
|
+
] }),
|
|
1716
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-2", children: [
|
|
1717
|
+
/* @__PURE__ */ jsx(Field, { label: "Ag\xEAncia", required: true, value: draft.agencyNumber, onChange: (value) => updateDraft("agencyNumber", value), className: "col-span-2" }),
|
|
1718
|
+
/* @__PURE__ */ jsx(Field, { label: "D\xEDgito", value: draft.agencyDigit, onChange: (value) => updateDraft("agencyDigit", value) })
|
|
1719
|
+
] }),
|
|
1720
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-2", children: [
|
|
1721
|
+
/* @__PURE__ */ jsx(Field, { label: "Conta", required: true, value: draft.accountNumber, onChange: (value) => updateDraft("accountNumber", value), className: "col-span-2" }),
|
|
1722
|
+
/* @__PURE__ */ jsx(Field, { label: "D\xEDgito", value: draft.accountDigit, onChange: (value) => updateDraft("accountDigit", value) })
|
|
1723
|
+
] }),
|
|
1724
|
+
/* @__PURE__ */ jsx(Field, { label: "Conv\xEAnio / c\xF3digo da empresa", value: draft.agreementCode, placeholder: "Opcional para alguns bancos", onChange: (value) => updateDraft("agreementCode", value), className: "sm:col-span-2" })
|
|
1007
1725
|
] }),
|
|
1008
|
-
/* @__PURE__ */
|
|
1009
|
-
] }),
|
|
1010
|
-
missingBankData.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "flex gap-2 rounded-lg border border-warning/30 bg-warning/10 p-3 text-sm", children: [
|
|
1011
|
-
/* @__PURE__ */ jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-warning" }),
|
|
1012
|
-
/* @__PURE__ */ jsxs("p", { children: [
|
|
1726
|
+
missingBankData.length > 0 ? /* @__PURE__ */ jsxs(Warning, { children: [
|
|
1013
1727
|
"Complete ",
|
|
1014
1728
|
missingBankData.join(", "),
|
|
1015
1729
|
" para continuar."
|
|
1016
|
-
] })
|
|
1017
|
-
] })
|
|
1018
|
-
saveError ? /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm text-destructive", children: saveError }) : null
|
|
1730
|
+
] }) : null
|
|
1731
|
+
] })
|
|
1019
1732
|
] }) : null,
|
|
1020
1733
|
eligibleAccounts2.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Cadastre uma conta banc\xE1ria ativa para iniciar uma remessa." }) : null
|
|
1021
1734
|
] }) : null,
|
|
1022
1735
|
step === 1 ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1023
1736
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1024
1737
|
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Pagamentos selecionados" }),
|
|
1025
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "
|
|
1738
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Defina a forma de pagamento e a data de pagamento de cada parcela." })
|
|
1026
1739
|
] }),
|
|
1027
|
-
|
|
1028
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
1029
|
-
/* @__PURE__ */ jsx("p", { className: "truncate font-medium", children:
|
|
1740
|
+
lines.map((line, index) => /* @__PURE__ */ jsxs("div", { className: "grid items-center gap-3 rounded-lg border p-3 sm:grid-cols-12", children: [
|
|
1741
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 sm:col-span-4", children: [
|
|
1742
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium", children: line.payee.name }),
|
|
1030
1743
|
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
1031
|
-
"
|
|
1032
|
-
invoice.dueDate || "\u2014",
|
|
1033
|
-
invoice.fiscalNumber ? ` \xB7
|
|
1744
|
+
"Venc.: ",
|
|
1745
|
+
line.invoice.dueDate || "\u2014",
|
|
1746
|
+
line.invoice.fiscalNumber ? ` \xB7 NF ${line.invoice.fiscalNumber}` : ""
|
|
1034
1747
|
] })
|
|
1035
1748
|
] }),
|
|
1036
|
-
/* @__PURE__ */ jsx("
|
|
1037
|
-
|
|
1749
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-semibold tabular-nums sm:col-span-2", children: formatCurrency(openAmount(line.invoice), currency) }),
|
|
1750
|
+
/* @__PURE__ */ jsx("div", { className: "sm:col-span-3", children: /* @__PURE__ */ jsxs(Select, { value: line.paymentForm, onValueChange: (value) => updateLine(index, { paymentForm: value }), children: [
|
|
1751
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-9", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
1752
|
+
/* @__PURE__ */ jsx(SelectContent, { children: Object.keys(FORM_LABEL).map((form) => /* @__PURE__ */ jsx(SelectItem, { value: form, children: FORM_LABEL[form] }, form)) })
|
|
1753
|
+
] }) }),
|
|
1754
|
+
/* @__PURE__ */ jsx("div", { className: "sm:col-span-3", children: /* @__PURE__ */ jsx(DatePicker, { value: line.paymentDate, format: "numeric", onChange: (value) => updateLine(index, { paymentDate: value }) }) })
|
|
1755
|
+
] }, line.invoice.id))
|
|
1756
|
+
] }) : null,
|
|
1757
|
+
step === 2 ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1758
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1759
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Dados dos favorecidos" }),
|
|
1760
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Confirme ou preencha o que falta. O que voc\xEA informar \xE9 salvo no cadastro para o pr\xF3ximo arquivo." })
|
|
1761
|
+
] }),
|
|
1762
|
+
lines.map((line, index) => /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded-lg border p-3", children: [
|
|
1763
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
|
|
1764
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm font-medium", children: [
|
|
1765
|
+
line.payee.name,
|
|
1766
|
+
/* @__PURE__ */ jsx("span", { className: "ml-2 rounded-full border px-2 py-0.5 text-xs font-normal text-muted-foreground", children: FORM_LABEL[line.paymentForm] })
|
|
1767
|
+
] }),
|
|
1768
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm font-semibold tabular-nums", children: formatCurrency(openAmount(line.invoice), currency) })
|
|
1769
|
+
] }),
|
|
1770
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-3 sm:grid-cols-12", children: [
|
|
1771
|
+
/* @__PURE__ */ jsx(Field, { label: "CPF/CNPJ", required: true, value: line.payee.document, onChange: (value) => updatePayee(index, { document: value }), className: "sm:col-span-4" }),
|
|
1772
|
+
line.paymentForm === "pix" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1773
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5 sm:col-span-3", children: [
|
|
1774
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium", children: "Tipo da chave" }),
|
|
1775
|
+
/* @__PURE__ */ jsxs(Select, { value: line.payee.pixKeyType || "", onValueChange: (value) => updatePayee(index, { pixKeyType: value }), children: [
|
|
1776
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-9", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Tipo" }) }),
|
|
1777
|
+
/* @__PURE__ */ jsx(SelectContent, { children: PIX_KEY_TYPES.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.value, children: option.label }, option.value)) })
|
|
1778
|
+
] })
|
|
1779
|
+
] }),
|
|
1780
|
+
/* @__PURE__ */ jsx(Field, { label: "Chave PIX", required: true, value: line.payee.pixKey || "", onChange: (value) => updatePayee(index, { pixKey: value, pixKeyType: line.payee.pixKeyType || inferPixKeyType(value) }), className: "sm:col-span-5" })
|
|
1781
|
+
] }) : null,
|
|
1782
|
+
line.paymentForm === "ted" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1783
|
+
/* @__PURE__ */ jsx(Field, { label: "Banco", required: true, value: line.payee.bankCode || "", onChange: (value) => updatePayee(index, { bankCode: value }), className: "sm:col-span-2" }),
|
|
1784
|
+
/* @__PURE__ */ jsx(Field, { label: "Ag\xEAncia", required: true, value: line.payee.agencyNumber || "", onChange: (value) => updatePayee(index, { agencyNumber: value }), className: "sm:col-span-2" }),
|
|
1785
|
+
/* @__PURE__ */ jsx(Field, { label: "DV", value: line.payee.agencyDigit || "", onChange: (value) => updatePayee(index, { agencyDigit: value }), className: "sm:col-span-1" }),
|
|
1786
|
+
/* @__PURE__ */ jsx(Field, { label: "Conta", required: true, value: line.payee.accountNumber || "", onChange: (value) => updatePayee(index, { accountNumber: value }), className: "sm:col-span-2" }),
|
|
1787
|
+
/* @__PURE__ */ jsx(Field, { label: "DV", value: line.payee.accountDigit || "", onChange: (value) => updatePayee(index, { accountDigit: value }), className: "sm:col-span-1" })
|
|
1788
|
+
] }) : null,
|
|
1789
|
+
line.paymentForm === "boleto" || line.paymentForm === "tributo" ? /* @__PURE__ */ jsx(Field, { label: "C\xF3digo de barras / linha digit\xE1vel", required: true, value: line.barcode, onChange: (value) => updateLine(index, { barcode: value }), className: "sm:col-span-8" }) : null
|
|
1790
|
+
] })
|
|
1791
|
+
] }, line.invoice.id)),
|
|
1792
|
+
gaps.length > 0 ? /* @__PURE__ */ jsxs(Warning, { children: [
|
|
1793
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Falta preencher:" }),
|
|
1794
|
+
/* @__PURE__ */ jsxs("ul", { className: "mt-1 space-y-0.5", children: [
|
|
1795
|
+
gaps.slice(0, 5).map((gap, index) => /* @__PURE__ */ jsxs("li", { children: [
|
|
1796
|
+
gap.name,
|
|
1797
|
+
": ",
|
|
1798
|
+
gap.message
|
|
1799
|
+
] }, `${gap.reference}-${gap.field}-${index}`)),
|
|
1800
|
+
gaps.length > 5 ? /* @__PURE__ */ jsxs("li", { className: "text-muted-foreground", children: [
|
|
1801
|
+
"e mais ",
|
|
1802
|
+
gaps.length - 5,
|
|
1803
|
+
"\u2026"
|
|
1804
|
+
] }) : null
|
|
1805
|
+
] })
|
|
1806
|
+
] }) : null
|
|
1038
1807
|
] }) : null,
|
|
1039
|
-
step
|
|
1040
|
-
/* @__PURE__ */ jsx("
|
|
1041
|
-
/* @__PURE__ */
|
|
1042
|
-
|
|
1043
|
-
|
|
1808
|
+
step === 3 ? /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
1809
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Resumo da remessa" }),
|
|
1810
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-3 text-sm sm:grid-cols-2", children: [
|
|
1811
|
+
/* @__PURE__ */ jsx(Summary, { label: "Banco pagador", value: `${draft?.bankCode ?? ""} \u2014 ${draft?.bankName || "\u2014"}` }),
|
|
1812
|
+
/* @__PURE__ */ jsx(Summary, { label: "Conta", value: `Ag ${draft?.agencyNumber ?? ""} / Cc ${draft?.accountNumber ?? ""}${draft?.accountDigit ? `-${draft.accountDigit}` : ""}` }),
|
|
1813
|
+
/* @__PURE__ */ jsx(Summary, { label: "Empresa", value: `${companyName || "\u2014"} (${companyDocument || "\u2014"})` }),
|
|
1814
|
+
/* @__PURE__ */ jsx(Summary, { label: "Total", value: formatCurrency(total, currency), strong: true }),
|
|
1815
|
+
/* @__PURE__ */ jsx(Summary, { label: "N\xBA da remessa (NSA)", value: String(context?.nextSequence ?? 1) })
|
|
1816
|
+
] }),
|
|
1817
|
+
/* @__PURE__ */ jsxs("div", { className: "border-t pt-3", children: [
|
|
1818
|
+
/* @__PURE__ */ jsx("h4", { className: "mb-2 text-sm font-medium", children: "Lotes" }),
|
|
1819
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-1 text-sm", children: batches.map(([form, group]) => /* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
|
|
1820
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1821
|
+
FORM_LABEL[form],
|
|
1822
|
+
" \u2014 ",
|
|
1823
|
+
group.count,
|
|
1824
|
+
" t\xEDtulo(s)"
|
|
1825
|
+
] }),
|
|
1826
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium tabular-nums", children: formatCurrency(group.total, currency) })
|
|
1827
|
+
] }, form)) })
|
|
1828
|
+
] }),
|
|
1829
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Ao gerar, o arquivo .REM \xE9 baixado, a remessa \xE9 registrada no hist\xF3rico e os dados informados s\xE3o salvos nos cadastros. O status das contas n\xE3o muda." })
|
|
1830
|
+
] }) : null,
|
|
1831
|
+
error ? /* @__PURE__ */ jsx("p", { role: "alert", className: "mt-4 text-sm text-destructive", children: error }) : null
|
|
1044
1832
|
] }),
|
|
1045
1833
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-t px-5 py-4", children: [
|
|
1046
|
-
/* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => step === 0 ? onClose() : setStep((value) => value - 1), disabled:
|
|
1834
|
+
/* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => step === 0 ? onClose() : setStep((value) => value - 1), disabled: busy, children: [
|
|
1047
1835
|
step > 0 ? /* @__PURE__ */ jsx(ArrowLeft, { className: "h-4 w-4" }) : null,
|
|
1048
1836
|
step === 0 ? "Cancelar" : "Voltar"
|
|
1049
1837
|
] }),
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1838
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1839
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
1840
|
+
invoices.length,
|
|
1841
|
+
" parcela(s) selecionada(s)"
|
|
1842
|
+
] }),
|
|
1843
|
+
step < 3 ? /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: () => void advance(), disabled: busy || !canAdvance, children: [
|
|
1844
|
+
busy || loadingContext ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : null,
|
|
1845
|
+
step === 0 ? "Salvar e avan\xE7ar" : "Avan\xE7ar",
|
|
1846
|
+
" ",
|
|
1847
|
+
/* @__PURE__ */ jsx(ArrowRight, { className: "h-4 w-4" })
|
|
1848
|
+
] }) : /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: () => void generate(), disabled: busy || gaps.length > 0, children: [
|
|
1849
|
+
busy ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(FileDown, { className: "h-4 w-4" }),
|
|
1850
|
+
" Gerar arquivo .REM"
|
|
1851
|
+
] })
|
|
1852
|
+
] })
|
|
1056
1853
|
] })
|
|
1057
1854
|
] })
|
|
1058
1855
|
] }),
|
|
1059
1856
|
document.body
|
|
1060
1857
|
);
|
|
1061
1858
|
}
|
|
1859
|
+
function Warning({ children }) {
|
|
1860
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex gap-2 rounded-lg border border-warning/30 bg-warning/10 p-3 text-sm", children: [
|
|
1861
|
+
/* @__PURE__ */ jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-warning" }),
|
|
1862
|
+
/* @__PURE__ */ jsx("div", { children })
|
|
1863
|
+
] });
|
|
1864
|
+
}
|
|
1865
|
+
function Summary({ label, value, strong }) {
|
|
1866
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
1867
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: label }),
|
|
1868
|
+
/* @__PURE__ */ jsx("div", { className: strong ? "text-lg font-semibold" : "font-medium", children: value })
|
|
1869
|
+
] });
|
|
1870
|
+
}
|
|
1062
1871
|
function Field({ label, value, onChange, required, placeholder, className = "" }) {
|
|
1063
1872
|
return /* @__PURE__ */ jsxs("label", { className: `block space-y-1.5 ${className}`, children: [
|
|
1064
1873
|
/* @__PURE__ */ jsxs("span", { className: "text-xs font-medium", children: [
|
|
@@ -1068,6 +1877,98 @@ function Field({ label, value, onChange, required, placeholder, className = "" }
|
|
|
1068
1877
|
/* @__PURE__ */ jsx("input", { value, onChange: (event) => onChange(event.target.value), placeholder, className: "h-9 w-full rounded-input border bg-background px-3 text-sm outline-none focus:border-primary focus:ring-2 focus:ring-primary/15" })
|
|
1069
1878
|
] });
|
|
1070
1879
|
}
|
|
1880
|
+
function AttachmentViewer({ attachments, index, onIndexChange, onClose }) {
|
|
1881
|
+
if (!attachments[index]) return null;
|
|
1882
|
+
const doc = attachments[index];
|
|
1883
|
+
const ehImagem = (doc.mimeType ?? "").startsWith("image/");
|
|
1884
|
+
const ehPdf = (doc.mimeType ?? "") === "application/pdf";
|
|
1885
|
+
const irPara = (delta) => onIndexChange((index + delta + attachments.length) % attachments.length);
|
|
1886
|
+
return createPortal(
|
|
1887
|
+
/* @__PURE__ */ jsx(
|
|
1888
|
+
"div",
|
|
1889
|
+
{
|
|
1890
|
+
"data-modal-passthrough": true,
|
|
1891
|
+
className: "pointer-events-auto fixed inset-0 z-[200] flex flex-col bg-black/80 p-4",
|
|
1892
|
+
onClick: () => onClose(),
|
|
1893
|
+
role: "dialog",
|
|
1894
|
+
"aria-modal": "true",
|
|
1895
|
+
"aria-label": doc.fileName,
|
|
1896
|
+
children: /* @__PURE__ */ jsxs(
|
|
1897
|
+
"div",
|
|
1898
|
+
{
|
|
1899
|
+
className: "mx-auto flex w-full max-w-4xl flex-1 flex-col gap-3 overflow-hidden",
|
|
1900
|
+
onClick: (e) => e.stopPropagation(),
|
|
1901
|
+
children: [
|
|
1902
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-white", children: [
|
|
1903
|
+
/* @__PURE__ */ jsx(Paperclip, { className: "h-4 w-4 shrink-0 opacity-70", "aria-hidden": true }),
|
|
1904
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
1905
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium", title: doc.fileName, children: doc.fileName }),
|
|
1906
|
+
/* @__PURE__ */ jsxs("p", { className: "text-[11px] opacity-60", children: [
|
|
1907
|
+
attachments.length > 1 ? `${index + 1} de ${attachments.length}` : null,
|
|
1908
|
+
doc.fileSize ? `${attachments.length > 1 ? " \xB7 " : ""}${Math.round(doc.fileSize / 1024)} KB` : null
|
|
1909
|
+
] })
|
|
1910
|
+
] }),
|
|
1911
|
+
doc.url ? /* @__PURE__ */ jsxs(
|
|
1912
|
+
"a",
|
|
1913
|
+
{
|
|
1914
|
+
href: doc.url,
|
|
1915
|
+
target: "_blank",
|
|
1916
|
+
rel: "noreferrer",
|
|
1917
|
+
className: "inline-flex shrink-0 items-center gap-1 rounded-lg border border-white/20 px-2.5 py-1.5 text-xs font-medium text-white hover:bg-white/10",
|
|
1918
|
+
children: [
|
|
1919
|
+
/* @__PURE__ */ jsx(ExternalLink, { className: "h-3.5 w-3.5", "aria-hidden": true }),
|
|
1920
|
+
" Abrir"
|
|
1921
|
+
]
|
|
1922
|
+
}
|
|
1923
|
+
) : null,
|
|
1924
|
+
/* @__PURE__ */ jsx(
|
|
1925
|
+
"button",
|
|
1926
|
+
{
|
|
1927
|
+
type: "button",
|
|
1928
|
+
onClick: () => onClose(),
|
|
1929
|
+
"aria-label": "Fechar",
|
|
1930
|
+
className: "shrink-0 rounded-lg border border-white/20 px-2.5 py-1.5 text-xs font-medium text-white hover:bg-white/10",
|
|
1931
|
+
children: "Fechar"
|
|
1932
|
+
}
|
|
1933
|
+
)
|
|
1934
|
+
] }),
|
|
1935
|
+
/* @__PURE__ */ jsx("div", { className: "flex min-h-0 flex-1 items-center justify-center rounded-xl bg-white/5", children: doc.pending || !doc.url ? /* @__PURE__ */ jsxs("p", { className: "max-w-sm p-8 text-center text-sm text-white/70", children: [
|
|
1936
|
+
"O registro deste anexo atravessou, o arquivo ainda n\xE3o. Ele continua no V1 \u2014 procure por ",
|
|
1937
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-xs", children: doc.fileName }),
|
|
1938
|
+
"."
|
|
1939
|
+
] }) : ehImagem ? /* @__PURE__ */ jsx("img", { src: doc.url, alt: doc.fileName, className: "max-h-full max-w-full object-contain" }) : ehPdf ? /* @__PURE__ */ jsx("iframe", { src: doc.url, title: doc.fileName, className: "h-full w-full rounded-xl bg-white" }) : /* @__PURE__ */ jsxs("p", { className: "p-8 text-center text-sm text-white/70", children: [
|
|
1940
|
+
"Este tipo de arquivo n\xE3o tem pr\xE9-visualiza\xE7\xE3o. Use ",
|
|
1941
|
+
/* @__PURE__ */ jsx("strong", { children: "Abrir" }),
|
|
1942
|
+
"."
|
|
1943
|
+
] }) }),
|
|
1944
|
+
attachments.length > 1 && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2", children: [
|
|
1945
|
+
/* @__PURE__ */ jsx(
|
|
1946
|
+
"button",
|
|
1947
|
+
{
|
|
1948
|
+
type: "button",
|
|
1949
|
+
onClick: () => irPara(-1),
|
|
1950
|
+
className: "rounded-lg border border-white/20 px-3 py-1.5 text-xs font-medium text-white hover:bg-white/10",
|
|
1951
|
+
children: "Anterior"
|
|
1952
|
+
}
|
|
1953
|
+
),
|
|
1954
|
+
/* @__PURE__ */ jsx(
|
|
1955
|
+
"button",
|
|
1956
|
+
{
|
|
1957
|
+
type: "button",
|
|
1958
|
+
onClick: () => irPara(1),
|
|
1959
|
+
className: "rounded-lg border border-white/20 px-3 py-1.5 text-xs font-medium text-white hover:bg-white/10",
|
|
1960
|
+
children: "Pr\xF3ximo"
|
|
1961
|
+
}
|
|
1962
|
+
)
|
|
1963
|
+
] })
|
|
1964
|
+
]
|
|
1965
|
+
}
|
|
1966
|
+
)
|
|
1967
|
+
}
|
|
1968
|
+
),
|
|
1969
|
+
document.body
|
|
1970
|
+
);
|
|
1971
|
+
}
|
|
1071
1972
|
var KIND_LABELS = {
|
|
1072
1973
|
supplier: "Fornecedor",
|
|
1073
1974
|
customer: "Cliente",
|
|
@@ -1173,49 +2074,61 @@ function StatusBadge({ status }) {
|
|
|
1173
2074
|
opt ? t2(opt.labelKey) : status
|
|
1174
2075
|
] });
|
|
1175
2076
|
}
|
|
1176
|
-
function
|
|
2077
|
+
function formatDueDate(iso) {
|
|
2078
|
+
if (!iso) return "\u2014";
|
|
2079
|
+
const [y, m, d] = iso.slice(0, 10).split("-");
|
|
2080
|
+
return d && m && y ? `${d}/${m}/${y}` : iso;
|
|
2081
|
+
}
|
|
2082
|
+
function useInvoiceColumns(currency, paymentMethods2, paymentMethodTypes, bankAccounts, consolidar, consolidating, openAttachments) {
|
|
1177
2083
|
const t2 = useTranslation();
|
|
1178
2084
|
return useMemo(() => [
|
|
1179
|
-
{
|
|
1180
|
-
accessorKey: "fiscalNumber",
|
|
1181
|
-
header: "#",
|
|
1182
|
-
size: 110,
|
|
1183
|
-
cell: ({ getValue }) => {
|
|
1184
|
-
const num2 = getValue();
|
|
1185
|
-
return num2 ? /* @__PURE__ */ jsxs("span", { className: "text-xs font-mono text-muted-foreground", children: [
|
|
1186
|
-
"#",
|
|
1187
|
-
num2
|
|
1188
|
-
] }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2014" });
|
|
1189
|
-
}
|
|
1190
|
-
},
|
|
1191
2085
|
{
|
|
1192
2086
|
accessorKey: "dueDate",
|
|
1193
2087
|
header: t2("financial.invoice.columnDueDate"),
|
|
1194
|
-
cell: ({
|
|
2088
|
+
cell: ({ row }) => {
|
|
2089
|
+
const total = row.original.totalInstallments ?? 1;
|
|
2090
|
+
const atual = row.original.installmentNumber ?? 1;
|
|
2091
|
+
return /* @__PURE__ */ jsxs("div", { className: "whitespace-nowrap", children: [
|
|
2092
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm tabular-nums", children: [
|
|
2093
|
+
formatDueDate(row.original.dueDate),
|
|
2094
|
+
total > 1 && /* @__PURE__ */ jsxs("span", { className: "ml-1.5 text-xs text-muted-foreground", title: t2("financial.invoice.columnInst"), children: [
|
|
2095
|
+
atual,
|
|
2096
|
+
"/",
|
|
2097
|
+
total
|
|
2098
|
+
] })
|
|
2099
|
+
] }),
|
|
2100
|
+
row.original.fiscalNumber && /* @__PURE__ */ jsxs("p", { className: "font-mono text-[10px] text-muted-foreground", children: [
|
|
2101
|
+
"#",
|
|
2102
|
+
row.original.fiscalNumber
|
|
2103
|
+
] })
|
|
2104
|
+
] });
|
|
2105
|
+
}
|
|
1195
2106
|
},
|
|
1196
2107
|
{
|
|
1197
2108
|
id: "description",
|
|
1198
2109
|
header: t2("financial.invoice.columnDescription"),
|
|
1199
|
-
cell: ({ row }) =>
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
2110
|
+
cell: ({ row }) => (
|
|
2111
|
+
// Full cell width with the badge pinned right, so the statuses line up in
|
|
2112
|
+
// one column instead of trailing each name.
|
|
2113
|
+
/* @__PURE__ */ jsxs("div", { className: "flex w-full min-w-[250px] items-center gap-3", onClick: (e) => e.stopPropagation(), children: [
|
|
2114
|
+
/* @__PURE__ */ jsxs("div", { className: "w-0 min-w-0 flex-1", children: [
|
|
2115
|
+
row.original.contactName ? /* @__PURE__ */ jsx(PersonLink, { personId: row.original.contactId, name: row.original.contactName, size: "sm", className: "block max-w-full truncate text-sm font-medium" }) : /* @__PURE__ */ jsx("p", { className: "font-medium", children: "\u2014" }),
|
|
2116
|
+
row.original.itemsSummary && /* @__PURE__ */ jsx("p", { className: "truncate text-[10px] text-muted-foreground", children: row.original.itemsSummary })
|
|
2117
|
+
] }),
|
|
2118
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0", children: /* @__PURE__ */ jsx(StatusBadge, { status: row.original.status }) })
|
|
2119
|
+
] })
|
|
2120
|
+
)
|
|
1203
2121
|
},
|
|
1204
2122
|
{
|
|
1205
2123
|
accessorKey: "totalAmount",
|
|
1206
2124
|
header: t2("financial.invoice.columnAmount"),
|
|
1207
2125
|
enableSorting: true,
|
|
1208
|
-
cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: `font-medium ${row.original.status === "cancelled" ? "line-through text-muted-foreground" : ""}`, children: formatCurrency(row.original.totalAmount, currency) })
|
|
2126
|
+
cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: `whitespace-nowrap font-medium ${row.original.status === "cancelled" ? "line-through text-muted-foreground" : ""}`, children: formatCurrency(row.original.totalAmount, currency) })
|
|
1209
2127
|
},
|
|
1210
2128
|
{
|
|
1211
2129
|
accessorKey: "paidAmount",
|
|
1212
2130
|
header: t2("financial.invoice.columnPaid"),
|
|
1213
|
-
cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: row.original.status === "cancelled" ? "line-through text-muted-foreground" : "text-muted-foreground"
|
|
1214
|
-
},
|
|
1215
|
-
{
|
|
1216
|
-
accessorKey: "status",
|
|
1217
|
-
header: t2("financial.invoice.columnStatus"),
|
|
1218
|
-
cell: ({ getValue }) => /* @__PURE__ */ jsx(StatusBadge, { status: getValue() })
|
|
2131
|
+
cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: `whitespace-nowrap ${row.original.status === "cancelled" ? "line-through text-muted-foreground" : "text-muted-foreground"}`, children: formatCurrency(row.original.paidAmount, currency) })
|
|
1219
2132
|
},
|
|
1220
2133
|
{
|
|
1221
2134
|
id: "paymentMethod",
|
|
@@ -1225,7 +2138,7 @@ function useInvoiceColumns(currency, paymentMethods2, paymentMethodTypes, bankAc
|
|
|
1225
2138
|
const type = paymentMethodTypes.find((candidate) => candidate.id === (method?.paymentMethodTypeId ?? row.original.paymentMethodTypeId));
|
|
1226
2139
|
const account = bankAccounts.find((candidate) => candidate.id === row.original.paymentAccountId);
|
|
1227
2140
|
if (!type && !method && !account) return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2014" });
|
|
1228
|
-
return /* @__PURE__ */ jsxs("div", { className: "max-w-[
|
|
2141
|
+
return /* @__PURE__ */ jsxs("div", { className: "max-w-[150px] text-xs", children: [
|
|
1229
2142
|
/* @__PURE__ */ jsxs("p", { className: "truncate font-medium", children: [
|
|
1230
2143
|
method?.name ?? type?.name ?? "\u2014",
|
|
1231
2144
|
row.original.paymentCardInstallments ? ` \xB7 ${row.original.paymentCardInstallments}x` : ""
|
|
@@ -1235,69 +2148,62 @@ function useInvoiceColumns(currency, paymentMethods2, paymentMethodTypes, bankAc
|
|
|
1235
2148
|
}
|
|
1236
2149
|
},
|
|
1237
2150
|
{
|
|
1238
|
-
id: "
|
|
2151
|
+
id: "actions",
|
|
1239
2152
|
header: "",
|
|
1240
2153
|
// O ÚNICO CAMINHO QUE TRANSFORMA PREVISÃO EM DÍVIDA.
|
|
1241
2154
|
//
|
|
1242
2155
|
// O V1 troca as ações da linha recorrente por este botão só — não há
|
|
1243
2156
|
// anexo, nem estorno, nem fatura para abrir: a ocorrência ainda não é
|
|
1244
2157
|
// nada disso. Consolidar é o clique que a faz existir.
|
|
2158
|
+
//
|
|
2159
|
+
// Fora dela, o clipe, como no V1: diz que a conta tem comprovante sem
|
|
2160
|
+
// obrigar a abri-la. Sem anexo não desenha nada.
|
|
1245
2161
|
cell: ({ row }) => {
|
|
1246
|
-
if (
|
|
1247
|
-
|
|
1248
|
-
|
|
2162
|
+
if (row.original.isProvisionedOccurrence) {
|
|
2163
|
+
const id = row.original.occurrenceId;
|
|
2164
|
+
return /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs(
|
|
2165
|
+
Button,
|
|
2166
|
+
{
|
|
2167
|
+
size: "sm",
|
|
2168
|
+
variant: "outline",
|
|
2169
|
+
className: "h-8 px-2",
|
|
2170
|
+
disabled: !id || consolidating === id,
|
|
2171
|
+
title: t2("financial.invoice.consolidate"),
|
|
2172
|
+
onClick: (e) => {
|
|
2173
|
+
e.stopPropagation();
|
|
2174
|
+
if (id) void consolidar(id);
|
|
2175
|
+
},
|
|
2176
|
+
children: [
|
|
2177
|
+
consolidating === id ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": true }) : /* @__PURE__ */ jsx(Zap, { className: "h-3.5 w-3.5", "aria-hidden": true }),
|
|
2178
|
+
/* @__PURE__ */ jsx("span", { className: "ml-1 hidden xl:inline", children: t2("financial.invoice.consolidate") })
|
|
2179
|
+
]
|
|
2180
|
+
}
|
|
2181
|
+
) });
|
|
2182
|
+
}
|
|
2183
|
+
const n = row.original.attachmentCount ?? 0;
|
|
2184
|
+
if (!n) return null;
|
|
2185
|
+
const label = n === 1 ? "1 anexo" : `${n} anexos`;
|
|
2186
|
+
return /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs(
|
|
1249
2187
|
Button,
|
|
1250
2188
|
{
|
|
1251
|
-
size: "
|
|
1252
|
-
variant: "
|
|
1253
|
-
|
|
2189
|
+
size: "icon",
|
|
2190
|
+
variant: "ghost",
|
|
2191
|
+
className: "relative h-8 w-8 text-muted-foreground hover:text-foreground",
|
|
2192
|
+
title: label,
|
|
2193
|
+
"aria-label": label,
|
|
1254
2194
|
onClick: (e) => {
|
|
1255
2195
|
e.stopPropagation();
|
|
1256
|
-
|
|
2196
|
+
openAttachments(row.original.invoiceId ?? row.original.id);
|
|
1257
2197
|
},
|
|
1258
2198
|
children: [
|
|
1259
|
-
|
|
1260
|
-
|
|
2199
|
+
/* @__PURE__ */ jsx(Paperclip, { className: "h-4 w-4", "aria-hidden": true }),
|
|
2200
|
+
n > 1 && /* @__PURE__ */ jsx("span", { className: "absolute -right-0.5 -top-0.5 flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[10px] font-semibold leading-none text-primary-foreground tabular-nums", children: n })
|
|
1261
2201
|
]
|
|
1262
2202
|
}
|
|
1263
|
-
);
|
|
1264
|
-
}
|
|
1265
|
-
},
|
|
1266
|
-
{
|
|
1267
|
-
id: "attachments",
|
|
1268
|
-
header: "",
|
|
1269
|
-
// O clipe, como no V1: diz que a conta tem comprovante sem obrigar a
|
|
1270
|
-
// abri-la. Sem anexo não desenha nada — uma coluna de clipes apagados é
|
|
1271
|
-
// ruído do tamanho da lista.
|
|
1272
|
-
cell: ({ row }) => {
|
|
1273
|
-
const n = row.original.attachmentCount ?? 0;
|
|
1274
|
-
if (!n) return null;
|
|
1275
|
-
return /* @__PURE__ */ jsxs(
|
|
1276
|
-
"span",
|
|
1277
|
-
{
|
|
1278
|
-
className: "inline-flex items-center gap-0.5 text-muted-foreground",
|
|
1279
|
-
title: n === 1 ? "1 anexo" : `${n} anexos`,
|
|
1280
|
-
children: [
|
|
1281
|
-
/* @__PURE__ */ jsx(Paperclip, { className: "h-3.5 w-3.5", "aria-hidden": true }),
|
|
1282
|
-
n > 1 ? /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium tabular-nums", children: n }) : null,
|
|
1283
|
-
/* @__PURE__ */ jsx("span", { className: "sr-only", children: n === 1 ? "1 anexo" : `${n} anexos` })
|
|
1284
|
-
]
|
|
1285
|
-
}
|
|
1286
|
-
);
|
|
1287
|
-
}
|
|
1288
|
-
},
|
|
1289
|
-
{
|
|
1290
|
-
accessorKey: "totalInstallments",
|
|
1291
|
-
header: t2("financial.invoice.columnInst"),
|
|
1292
|
-
// "3/4", como o V1 — a linha é uma parcela, e o número sozinho não diz
|
|
1293
|
-
// QUAL delas. Parcela única fica só "1".
|
|
1294
|
-
cell: ({ row }) => {
|
|
1295
|
-
const total = row.original.totalInstallments ?? 1;
|
|
1296
|
-
const atual = row.original.installmentNumber ?? 1;
|
|
1297
|
-
return /* @__PURE__ */ jsx("span", { className: "tabular-nums text-muted-foreground", children: total > 1 ? `${atual}/${total}` : total });
|
|
2203
|
+
) });
|
|
1298
2204
|
}
|
|
1299
2205
|
}
|
|
1300
|
-
], [currency, t2, paymentMethods2, paymentMethodTypes, bankAccounts]);
|
|
2206
|
+
], [currency, t2, paymentMethods2, paymentMethodTypes, bankAccounts, consolidar, consolidating, openAttachments]);
|
|
1301
2207
|
}
|
|
1302
2208
|
function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
1303
2209
|
const t2 = useTranslation();
|
|
@@ -1430,7 +2336,7 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
1430
2336
|
setConsolidating(occurrenceId);
|
|
1431
2337
|
void provider.consolidateOccurrence(occurrenceId).then((invoiceId) => {
|
|
1432
2338
|
toast.success(t2("financial.invoice.consolidated"));
|
|
1433
|
-
if (invoiceId && onEdit) onEdit(invoiceId);
|
|
2339
|
+
if (invoiceId && onEdit) onEdit(invoiceId, "open");
|
|
1434
2340
|
else void fetchInvoices(query);
|
|
1435
2341
|
}).catch((err) => {
|
|
1436
2342
|
toast.error(t2("financial.invoice.consolidateFailed"), {
|
|
@@ -1439,7 +2345,14 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
1439
2345
|
}).finally(() => setConsolidating(null));
|
|
1440
2346
|
}, [provider, t2, fetchInvoices, query, onEdit]);
|
|
1441
2347
|
const filtered = invoices.filter((inv) => inv.direction === direction);
|
|
1442
|
-
const
|
|
2348
|
+
const [viewer, setViewer] = useState(null);
|
|
2349
|
+
const openAttachments = useCallback((invoiceId) => {
|
|
2350
|
+
if (!provider.getInvoiceAttachments) return;
|
|
2351
|
+
provider.getInvoiceAttachments(invoiceId).then((attachments) => {
|
|
2352
|
+
if (attachments.length) setViewer({ attachments, index: 0 });
|
|
2353
|
+
}).catch(() => toast.error("N\xE3o foi poss\xEDvel abrir os anexos"));
|
|
2354
|
+
}, [provider]);
|
|
2355
|
+
const columns = useInvoiceColumns(currency, paymentMethods2, paymentMethodTypes, bankAccounts, consolidar, consolidating, openAttachments);
|
|
1443
2356
|
const contactLookup = useMemo(() => createFinancialCounterpartyLookup(direction), [direction]);
|
|
1444
2357
|
const tags = useMemo(
|
|
1445
2358
|
// `provisioned` fora da barra: ele não é um recorte do que existe, é o
|
|
@@ -1451,14 +2364,14 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
1451
2364
|
const summary = useMemo(() => {
|
|
1452
2365
|
let totalAmount = 0;
|
|
1453
2366
|
let paidAmount = 0;
|
|
1454
|
-
let
|
|
2367
|
+
let openAmount2 = 0;
|
|
1455
2368
|
for (const inv of filtered) {
|
|
1456
2369
|
if (inv.status === "cancelled") continue;
|
|
1457
2370
|
totalAmount += inv.totalAmount;
|
|
1458
2371
|
paidAmount += inv.paidAmount;
|
|
1459
2372
|
}
|
|
1460
|
-
|
|
1461
|
-
return { totalAmount, paidAmount, openAmount };
|
|
2373
|
+
openAmount2 = totalAmount - paidAmount;
|
|
2374
|
+
return { totalAmount, paidAmount, openAmount: openAmount2 };
|
|
1462
2375
|
}, [filtered]);
|
|
1463
2376
|
const [cancelTarget, setCancelTarget] = useState(null);
|
|
1464
2377
|
const [cnabTarget, setCnabTarget] = useState(null);
|
|
@@ -1816,7 +2729,7 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
1816
2729
|
actions: newActions,
|
|
1817
2730
|
bulkActions,
|
|
1818
2731
|
onRowClick: (row) => {
|
|
1819
|
-
if (!row.isProvisionedOccurrence) onEdit?.(row.invoiceId ?? row.id);
|
|
2732
|
+
if (!row.isProvisionedOccurrence) onEdit?.(row.invoiceId ?? row.id, row.status);
|
|
1820
2733
|
},
|
|
1821
2734
|
rowClassName: (row) => row.isProvisionedOccurrence ? "border-dashed !cursor-default bg-muted/10 text-muted-foreground" : void 0,
|
|
1822
2735
|
emptyIcon: FileText,
|
|
@@ -1825,6 +2738,15 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
1825
2738
|
onEmptyAction: onNewGated
|
|
1826
2739
|
}
|
|
1827
2740
|
),
|
|
2741
|
+
viewer && /* @__PURE__ */ jsx(
|
|
2742
|
+
AttachmentViewer,
|
|
2743
|
+
{
|
|
2744
|
+
attachments: viewer.attachments,
|
|
2745
|
+
index: viewer.index,
|
|
2746
|
+
onIndexChange: (index) => setViewer((v) => v ? { ...v, index } : v),
|
|
2747
|
+
onClose: () => setViewer(null)
|
|
2748
|
+
}
|
|
2749
|
+
),
|
|
1828
2750
|
filtered.length > 0 && !invoicesLoading && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-6 rounded-lg border bg-muted/30 px-5 py-3 text-sm", children: [
|
|
1829
2751
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1830
2752
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t2("financial.invoice.summaryTotal") }),
|
|
@@ -2123,12 +3045,12 @@ async function settlePayable(provider, input) {
|
|
|
2123
3045
|
};
|
|
2124
3046
|
}
|
|
2125
3047
|
const invoice = await provider.getInvoiceById(input.invoiceId).catch(() => null);
|
|
2126
|
-
const
|
|
3048
|
+
const money4 = (n) => n.toLocaleString("pt-BR", { style: "currency", currency: input.currency ?? "BRL" });
|
|
2127
3049
|
const invoiceLabel = [
|
|
2128
3050
|
invoice?.fiscalNumber ? `#${invoice.fiscalNumber}` : null,
|
|
2129
3051
|
invoice?.contactName ?? null,
|
|
2130
|
-
|
|
2131
|
-
].filter(Boolean).join(" \u2014 ") ||
|
|
3052
|
+
money4(bill.amount)
|
|
3053
|
+
].filter(Boolean).join(" \u2014 ") || money4(bill.amount);
|
|
2132
3054
|
const needsAccount = input.method.transactionType !== "cash";
|
|
2133
3055
|
const accounts = needsAccount ? await eligibleAccounts(provider, input.method) : [];
|
|
2134
3056
|
if (needsAccount && !accounts.length) {
|
|
@@ -2318,7 +3240,7 @@ function createPayableTools({ config, provider }) {
|
|
|
2318
3240
|
ok: true,
|
|
2319
3241
|
read: {
|
|
2320
3242
|
supplier: supplierName,
|
|
2321
|
-
supplierDocument:
|
|
3243
|
+
supplierDocument: onlyDigits2(args.supplierDocument) || void 0,
|
|
2322
3244
|
paymentMethod: text(args.paymentMethod),
|
|
2323
3245
|
cardBrand: text(args.cardBrand),
|
|
2324
3246
|
cardLast4: lastFour(args.cardLast4) || void 0
|
|
@@ -2686,16 +3608,16 @@ function normalizeCompanyName(raw) {
|
|
|
2686
3608
|
if (typeof raw !== "string") return "";
|
|
2687
3609
|
return raw.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((w) => w && !COMPANY_NOISE.has(w)).join(" ").trim();
|
|
2688
3610
|
}
|
|
2689
|
-
function
|
|
3611
|
+
function onlyDigits2(raw) {
|
|
2690
3612
|
return typeof raw === "string" ? raw.replace(/\D/g, "") : "";
|
|
2691
3613
|
}
|
|
2692
3614
|
function lastFour(raw) {
|
|
2693
|
-
const
|
|
2694
|
-
return
|
|
3615
|
+
const digits2 = onlyDigits2(raw);
|
|
3616
|
+
return digits2.length >= 4 ? digits2.slice(-4) : "";
|
|
2695
3617
|
}
|
|
2696
3618
|
async function findSupplier(lookup, rawName, rawDocument) {
|
|
2697
3619
|
const wanted = normalizeCompanyName(rawName);
|
|
2698
|
-
const doc =
|
|
3620
|
+
const doc = onlyDigits2(rawDocument);
|
|
2699
3621
|
if (!wanted) return { status: "missing", candidates: [] };
|
|
2700
3622
|
const core = wanted.split(" ")[0] ?? wanted;
|
|
2701
3623
|
const seen = /* @__PURE__ */ new Map();
|
|
@@ -2705,7 +3627,7 @@ async function findSupplier(lookup, rawName, rawDocument) {
|
|
|
2705
3627
|
seen.set(hit.id, {
|
|
2706
3628
|
id: hit.id,
|
|
2707
3629
|
name: hit.label,
|
|
2708
|
-
document:
|
|
3630
|
+
document: onlyDigits2(row.document_number) || void 0,
|
|
2709
3631
|
why: ""
|
|
2710
3632
|
});
|
|
2711
3633
|
}
|
|
@@ -3616,7 +4538,7 @@ function PaymentModal({ movement, receiptUrl, onClose, onPaid }) {
|
|
|
3616
4538
|
);
|
|
3617
4539
|
}
|
|
3618
4540
|
var NONE = "__none__";
|
|
3619
|
-
var TRIGGER_CLASS =
|
|
4541
|
+
var TRIGGER_CLASS = `h-auto w-auto justify-start gap-1 rounded-none border-0 bg-transparent p-0 text-xs font-normal shadow-none ring-offset-0 focus:ring-0 focus:ring-offset-0 [&>svg]:h-3 [&>svg]:w-3 ${INLINE_LINK_TONE.dashed}`;
|
|
3620
4542
|
function InstallmentSourceFields({
|
|
3621
4543
|
direction,
|
|
3622
4544
|
value,
|
|
@@ -3636,7 +4558,7 @@ function InstallmentSourceFields({
|
|
|
3636
4558
|
const conta = elegiveis.length === 1 ? elegiveis[0].id : elegiveis.some((a) => a.id === value.payAccountId) ? value.payAccountId : void 0;
|
|
3637
4559
|
onChange({ payMethodTypeId: typeId || void 0, payAccountId: typeId ? conta : void 0 });
|
|
3638
4560
|
}
|
|
3639
|
-
const tone = (filled) => filled ? "text-foreground" : "text-
|
|
4561
|
+
const tone = (filled) => filled ? "text-foreground" : "text-destructive border-b border-dashed border-destructive";
|
|
3640
4562
|
return /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-3", children: [
|
|
3641
4563
|
/* @__PURE__ */ jsxs(
|
|
3642
4564
|
Select,
|
|
@@ -3739,10 +4661,10 @@ function useStockedItemTypes(itemTypes, entityLookups) {
|
|
|
3739
4661
|
}
|
|
3740
4662
|
|
|
3741
4663
|
// src/lib/invoice-list.ts
|
|
3742
|
-
var S\u00D3_RECEBIVEL = /* @__PURE__ */ new Set(["service", "gift_card"]);
|
|
4664
|
+
var S\u00D3_RECEBIVEL = /* @__PURE__ */ new Set(["service", "package", "gift_card"]);
|
|
3743
4665
|
function availableInvoiceItemTypes(itemTypes, lookupKinds, otherLabel, direction) {
|
|
3744
4666
|
const installed = new Set(lookupKinds);
|
|
3745
|
-
const visible = itemTypes.filter((type) => (!(direction === "debit" || direction === "payable") || !S\u00D3_RECEBIVEL.has(type.value)) && (type.value === "other" || !["product", "service"].includes(type.value) || installed.has(type.value)));
|
|
4667
|
+
const visible = itemTypes.filter((type) => (!(direction === "debit" || direction === "payable") || !S\u00D3_RECEBIVEL.has(type.value)) && (type.value === "other" || !["product", "service", "package"].includes(type.value) || installed.has(type.value)));
|
|
3746
4668
|
if (visible.some((type) => type.value === "other")) return visible;
|
|
3747
4669
|
return [...visible, { value: "other", label: otherLabel, icon: "MoreHorizontal" }];
|
|
3748
4670
|
}
|
|
@@ -3807,6 +4729,9 @@ function summarizeInvoiceLines(rows, visible = 2) {
|
|
|
3807
4729
|
}
|
|
3808
4730
|
return summaries;
|
|
3809
4731
|
}
|
|
4732
|
+
function invoiceOpensForEditing(status) {
|
|
4733
|
+
return status !== "paid" && status !== "cancelled";
|
|
4734
|
+
}
|
|
3810
4735
|
function LocationSelector({ locations, value, onChange }) {
|
|
3811
4736
|
const t2 = useTranslation();
|
|
3812
4737
|
const [open, setOpen] = useState(false);
|
|
@@ -3925,11 +4850,11 @@ function itemsKey(list) {
|
|
|
3925
4850
|
it.costCenterId ?? ""
|
|
3926
4851
|
]));
|
|
3927
4852
|
}
|
|
3928
|
-
function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fromCardId }) {
|
|
4853
|
+
function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fromCardId, embedded, onChanged }) {
|
|
3929
4854
|
const t2 = useTranslation();
|
|
3930
4855
|
const config = useFinancialConfig();
|
|
3931
4856
|
const provider = useFinancialProvider();
|
|
3932
|
-
const { currency, itemTypes: itemTypesRaw, locations } = config;
|
|
4857
|
+
const { currency, itemTypes: itemTypesRaw = [], locations = [] } = config;
|
|
3933
4858
|
const unitMode = useUnitMode();
|
|
3934
4859
|
const activeUnitId = useActiveUnitId();
|
|
3935
4860
|
const reachableUnits = useUnitFilter().options;
|
|
@@ -4006,10 +4931,9 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
4006
4931
|
const can = usePermissionOptional();
|
|
4007
4932
|
const isEdit = !!editId;
|
|
4008
4933
|
useEffect(() => {
|
|
4009
|
-
if (isEdit) return;
|
|
4010
4934
|
void fetchBankAccounts();
|
|
4011
4935
|
void fetchPaymentMethods();
|
|
4012
|
-
}, [
|
|
4936
|
+
}, []);
|
|
4013
4937
|
useEffect(() => {
|
|
4014
4938
|
let cancelled = false;
|
|
4015
4939
|
void Promise.all([provider.getChartOfAccounts(), provider.getCostCenters()]).then(([accountRows, costCenterRows]) => {
|
|
@@ -4164,7 +5088,8 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
4164
5088
|
discount: it.discount,
|
|
4165
5089
|
surcharge: it.surcharge,
|
|
4166
5090
|
accountId: it.accountId,
|
|
4167
|
-
costCenterId: it.costCenterId
|
|
5091
|
+
costCenterId: it.costCenterId,
|
|
5092
|
+
metadata: it.metadata
|
|
4168
5093
|
};
|
|
4169
5094
|
}));
|
|
4170
5095
|
const aAbrir = consumirItemParaAbrir(editId);
|
|
@@ -4240,6 +5165,39 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
4240
5165
|
function updateItem(id, data) {
|
|
4241
5166
|
setItems((current) => current.map((it) => it._id === id ? { ...it, ...data } : it));
|
|
4242
5167
|
}
|
|
5168
|
+
async function applyItemPatch(item, patch) {
|
|
5169
|
+
const expand = patch.referenceId ? entityLookups[item.itemKind]?.expand : void 0;
|
|
5170
|
+
if (!expand) {
|
|
5171
|
+
updateItem(item._id, patch);
|
|
5172
|
+
return;
|
|
5173
|
+
}
|
|
5174
|
+
let linhas = [];
|
|
5175
|
+
try {
|
|
5176
|
+
linhas = await expand(patch.referenceId);
|
|
5177
|
+
} catch (err) {
|
|
5178
|
+
console.error("[financial] failed to expand package", err);
|
|
5179
|
+
}
|
|
5180
|
+
if (!linhas.length) {
|
|
5181
|
+
updateItem(item._id, patch);
|
|
5182
|
+
return;
|
|
5183
|
+
}
|
|
5184
|
+
const oferecidos = new Set(itemTypes.map((tp) => tp.value));
|
|
5185
|
+
const novas = linhas.map((linha) => ({
|
|
5186
|
+
_id: fid(),
|
|
5187
|
+
itemKind: linha.itemKind && oferecidos.has(linha.itemKind) ? linha.itemKind : item.itemKind,
|
|
5188
|
+
referenceId: linha.referenceId,
|
|
5189
|
+
description: linha.description,
|
|
5190
|
+
quantity: linha.quantity,
|
|
5191
|
+
unitPrice: linha.unitPrice,
|
|
5192
|
+
...linha.discount ? { discount: linha.discount } : {},
|
|
5193
|
+
...linha.surcharge ? { surcharge: linha.surcharge } : {},
|
|
5194
|
+
...linha.metadata ? { metadata: linha.metadata } : {},
|
|
5195
|
+
accountId: item.accountId,
|
|
5196
|
+
costCenterId: item.costCenterId
|
|
5197
|
+
}));
|
|
5198
|
+
setItems((current) => current.flatMap((it) => it._id === item._id ? novas : [it]));
|
|
5199
|
+
setExpandedItemId(null);
|
|
5200
|
+
}
|
|
4243
5201
|
function removeItem(id) {
|
|
4244
5202
|
setItems((current) => current.filter((it) => it._id !== id));
|
|
4245
5203
|
}
|
|
@@ -4505,7 +5463,12 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
4505
5463
|
dirty,
|
|
4506
5464
|
// Criar ou editar: a barra é o caminho de salvar, e sumir porque nada mudou
|
|
4507
5465
|
// ainda deixa a tela sem botão. Ela some quando o formulário fecha.
|
|
4508
|
-
|
|
5466
|
+
//
|
|
5467
|
+
// EMBUTIDO é o contrário: o modal já tem o próprio fechar, então a barra não
|
|
5468
|
+
// é o único caminho de saída — e «Alterações não salvas» sobre um formulário
|
|
5469
|
+
// que ninguém tocou é uma mentira que ensina a ignorar o aviso. Ali ela
|
|
5470
|
+
// aparece quando há o que salvar.
|
|
5471
|
+
alwaysVisible: !embedded,
|
|
4509
5472
|
saving,
|
|
4510
5473
|
onSave: () => {
|
|
4511
5474
|
void handleSave();
|
|
@@ -4515,8 +5478,8 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
4515
5478
|
});
|
|
4516
5479
|
const title = isEdit ? t2("financial.invoiceForm.editTitle") : t2("financial.invoiceForm.newTitle");
|
|
4517
5480
|
const subtitle = isEdit ? t2("financial.invoiceForm.editing") : t2("financial.invoiceForm.creating");
|
|
4518
|
-
if (loading) return /* @__PURE__ */ jsx(OrderFormSkeleton, { parentLabel: direction === "debit" ? t2("financial.nav.payables") : t2("financial.nav.receivables"), onBack: () => onSaved?.() });
|
|
4519
|
-
const
|
|
5481
|
+
if (loading) return /* @__PURE__ */ jsx(OrderFormSkeleton, { parentLabel: embedded ? void 0 : direction === "debit" ? t2("financial.nav.payables") : t2("financial.nav.receivables"), onBack: embedded ? void 0 : () => onSaved?.() });
|
|
5482
|
+
const money4 = (v) => formatCurrency(v, currency);
|
|
4520
5483
|
const actions = isEdit ? /* @__PURE__ */ jsxs("div", { className: "relative", ref: menuRef, children: [
|
|
4521
5484
|
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "icon", onClick: () => setMenuOpen(!menuOpen), "aria-label": "More actions", children: /* @__PURE__ */ jsx(MoreVertical, { className: "h-4 w-4" }) }),
|
|
4522
5485
|
menuOpen && /* @__PURE__ */ jsx("div", { className: "fayz-glass-surface absolute right-0 top-full z-20 mt-1 w-44 rounded-lg border bg-popover py-1 shadow-md", style: { animation: "field-slide-in 150ms ease-out" }, children: /* @__PURE__ */ jsxs(
|
|
@@ -4582,7 +5545,7 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
4582
5545
|
"#",
|
|
4583
5546
|
i + 1
|
|
4584
5547
|
] }),
|
|
4585
|
-
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm font-semibold tabular-nums", children:
|
|
5548
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm font-semibold tabular-nums", children: money4(inst.amount) }),
|
|
4586
5549
|
saved && /* @__PURE__ */ jsxs("span", { className: `inline-flex shrink-0 items-center gap-1 rounded-full px-1.5 py-0.5 text-[10px] font-medium ${isPaid2 ? "bg-success-soft text-success-soft-foreground" : isPartial ? "bg-warning-soft text-warning-soft-foreground" : "bg-muted text-muted-foreground"}`, children: [
|
|
4587
5550
|
/* @__PURE__ */ jsx(StatusIcon, { className: "h-2.5 w-2.5" }),
|
|
4588
5551
|
t2(isPaid2 ? "financial.invoice.statusPaid" : isPartial ? "financial.invoice.statusPartial" : "financial.invoice.statusPending")
|
|
@@ -4815,10 +5778,11 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
4815
5778
|
/* @__PURE__ */ jsxs(
|
|
4816
5779
|
OrderFormView,
|
|
4817
5780
|
{
|
|
5781
|
+
embedded,
|
|
4818
5782
|
title,
|
|
4819
5783
|
subtitle,
|
|
4820
|
-
parentLabel: direction === "debit" ? t2("financial.nav.payables") : t2("financial.nav.receivables"),
|
|
4821
|
-
onBack: () => onSaved?.(),
|
|
5784
|
+
parentLabel: embedded ? void 0 : direction === "debit" ? t2("financial.nav.payables") : t2("financial.nav.receivables"),
|
|
5785
|
+
onBack: embedded ? void 0 : () => onSaved?.(),
|
|
4822
5786
|
actions,
|
|
4823
5787
|
banner: isEdit && hasPayments ? /* @__PURE__ */ jsx("p", { className: "rounded-card border border-warning/40 bg-warning-soft px-4 py-2.5 text-xs text-warning-soft-foreground", children: t2("financial.invoiceForm.issuedLocked") }) : void 0,
|
|
4824
5788
|
sideSections,
|
|
@@ -4836,7 +5800,7 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
4836
5800
|
count: items.length,
|
|
4837
5801
|
countLabel: t2("financial.invoiceForm.itemCount", { count: items.length, plural: items.length !== 1 ? "s" : "" }),
|
|
4838
5802
|
totalLabel: t2("financial.invoiceForm.total"),
|
|
4839
|
-
total:
|
|
5803
|
+
total: money4(totalAmount),
|
|
4840
5804
|
emptyLabel: contactId ? t2("financial.invoiceForm.noItems") : t2("financial.invoiceForm.selectContactFirst"),
|
|
4841
5805
|
emptyActionLabel: t2("financial.invoiceForm.addFirstItem"),
|
|
4842
5806
|
header: addingItem && /* @__PURE__ */ jsx(
|
|
@@ -4862,11 +5826,13 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
4862
5826
|
referenceId: item.referenceId,
|
|
4863
5827
|
itemTypes,
|
|
4864
5828
|
currency,
|
|
4865
|
-
formatMoney:
|
|
5829
|
+
formatMoney: money4,
|
|
4866
5830
|
expanded: expandedItemId === item._id,
|
|
4867
5831
|
readOnly: !canEditItems,
|
|
4868
5832
|
onToggle: () => setExpandedItemId(expandedItemId === item._id ? null : item._id),
|
|
4869
|
-
onChange: (patch) =>
|
|
5833
|
+
onChange: (patch) => {
|
|
5834
|
+
void applyItemPatch(item, patch);
|
|
5835
|
+
},
|
|
4870
5836
|
onRemove: () => {
|
|
4871
5837
|
removeItem(item._id);
|
|
4872
5838
|
if (expandedItemId === item._id) setExpandedItemId(null);
|
|
@@ -4927,6 +5893,7 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
4927
5893
|
}] : []
|
|
4928
5894
|
],
|
|
4929
5895
|
entityLookups,
|
|
5896
|
+
pickOnly: Boolean(entityLookups[item.itemKind]?.expand) && !item.referenceId,
|
|
4930
5897
|
showTypeSelector: hasItemLookups,
|
|
4931
5898
|
allowSurcharge: true,
|
|
4932
5899
|
labels: {
|
|
@@ -4982,6 +5949,7 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
4982
5949
|
setSavedMovements(
|
|
4983
5950
|
movements.data.filter((m) => m.invoiceId === editId && m.movementKind === "bill").sort((a, b) => (a.installmentNumber ?? 0) - (b.installmentNumber ?? 0))
|
|
4984
5951
|
);
|
|
5952
|
+
onChanged?.();
|
|
4985
5953
|
})();
|
|
4986
5954
|
}
|
|
4987
5955
|
}
|
|
@@ -5665,96 +6633,14 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel, em
|
|
|
5665
6633
|
}
|
|
5666
6634
|
),
|
|
5667
6635
|
receiptPicker.input,
|
|
5668
|
-
viewing !== null &&
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
{
|
|
5677
|
-
"data-modal-passthrough": true,
|
|
5678
|
-
className: "pointer-events-auto fixed inset-0 z-[200] flex flex-col bg-black/80 p-4",
|
|
5679
|
-
onClick: () => setViewing(null),
|
|
5680
|
-
role: "dialog",
|
|
5681
|
-
"aria-modal": "true",
|
|
5682
|
-
"aria-label": doc.fileName,
|
|
5683
|
-
children: /* @__PURE__ */ jsxs(
|
|
5684
|
-
"div",
|
|
5685
|
-
{
|
|
5686
|
-
className: "mx-auto flex w-full max-w-4xl flex-1 flex-col gap-3 overflow-hidden",
|
|
5687
|
-
onClick: (e) => e.stopPropagation(),
|
|
5688
|
-
children: [
|
|
5689
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-white", children: [
|
|
5690
|
-
/* @__PURE__ */ jsx(Paperclip, { className: "h-4 w-4 shrink-0 opacity-70", "aria-hidden": true }),
|
|
5691
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
5692
|
-
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium", title: doc.fileName, children: doc.fileName }),
|
|
5693
|
-
/* @__PURE__ */ jsxs("p", { className: "text-[11px] opacity-60", children: [
|
|
5694
|
-
attachments.length > 1 ? `${viewing + 1} de ${attachments.length}` : null,
|
|
5695
|
-
doc.fileSize ? `${attachments.length > 1 ? " \xB7 " : ""}${Math.round(doc.fileSize / 1024)} KB` : null
|
|
5696
|
-
] })
|
|
5697
|
-
] }),
|
|
5698
|
-
doc.url ? /* @__PURE__ */ jsxs(
|
|
5699
|
-
"a",
|
|
5700
|
-
{
|
|
5701
|
-
href: doc.url,
|
|
5702
|
-
target: "_blank",
|
|
5703
|
-
rel: "noreferrer",
|
|
5704
|
-
className: "inline-flex shrink-0 items-center gap-1 rounded-lg border border-white/20 px-2.5 py-1.5 text-xs font-medium text-white hover:bg-white/10",
|
|
5705
|
-
children: [
|
|
5706
|
-
/* @__PURE__ */ jsx(ExternalLink, { className: "h-3.5 w-3.5", "aria-hidden": true }),
|
|
5707
|
-
" Abrir"
|
|
5708
|
-
]
|
|
5709
|
-
}
|
|
5710
|
-
) : null,
|
|
5711
|
-
/* @__PURE__ */ jsx(
|
|
5712
|
-
"button",
|
|
5713
|
-
{
|
|
5714
|
-
type: "button",
|
|
5715
|
-
onClick: () => setViewing(null),
|
|
5716
|
-
"aria-label": "Fechar",
|
|
5717
|
-
className: "shrink-0 rounded-lg border border-white/20 px-2.5 py-1.5 text-xs font-medium text-white hover:bg-white/10",
|
|
5718
|
-
children: "Fechar"
|
|
5719
|
-
}
|
|
5720
|
-
)
|
|
5721
|
-
] }),
|
|
5722
|
-
/* @__PURE__ */ jsx("div", { className: "flex min-h-0 flex-1 items-center justify-center rounded-xl bg-white/5", children: doc.pending || !doc.url ? /* @__PURE__ */ jsxs("p", { className: "max-w-sm p-8 text-center text-sm text-white/70", children: [
|
|
5723
|
-
"O registro deste anexo atravessou, o arquivo ainda n\xE3o. Ele continua no V1 \u2014 procure por ",
|
|
5724
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono text-xs", children: doc.fileName }),
|
|
5725
|
-
"."
|
|
5726
|
-
] }) : ehImagem ? /* @__PURE__ */ jsx("img", { src: doc.url, alt: doc.fileName, className: "max-h-full max-w-full object-contain" }) : ehPdf ? /* @__PURE__ */ jsx("iframe", { src: doc.url, title: doc.fileName, className: "h-full w-full rounded-xl bg-white" }) : /* @__PURE__ */ jsxs("p", { className: "p-8 text-center text-sm text-white/70", children: [
|
|
5727
|
-
"Este tipo de arquivo n\xE3o tem pr\xE9-visualiza\xE7\xE3o. Use ",
|
|
5728
|
-
/* @__PURE__ */ jsx("strong", { children: "Abrir" }),
|
|
5729
|
-
"."
|
|
5730
|
-
] }) }),
|
|
5731
|
-
attachments.length > 1 && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2", children: [
|
|
5732
|
-
/* @__PURE__ */ jsx(
|
|
5733
|
-
"button",
|
|
5734
|
-
{
|
|
5735
|
-
type: "button",
|
|
5736
|
-
onClick: () => irPara(-1),
|
|
5737
|
-
className: "rounded-lg border border-white/20 px-3 py-1.5 text-xs font-medium text-white hover:bg-white/10",
|
|
5738
|
-
children: "Anterior"
|
|
5739
|
-
}
|
|
5740
|
-
),
|
|
5741
|
-
/* @__PURE__ */ jsx(
|
|
5742
|
-
"button",
|
|
5743
|
-
{
|
|
5744
|
-
type: "button",
|
|
5745
|
-
onClick: () => irPara(1),
|
|
5746
|
-
className: "rounded-lg border border-white/20 px-3 py-1.5 text-xs font-medium text-white hover:bg-white/10",
|
|
5747
|
-
children: "Pr\xF3ximo"
|
|
5748
|
-
}
|
|
5749
|
-
)
|
|
5750
|
-
] })
|
|
5751
|
-
]
|
|
5752
|
-
}
|
|
5753
|
-
)
|
|
5754
|
-
}
|
|
5755
|
-
);
|
|
5756
|
-
})(),
|
|
5757
|
-
document.body
|
|
6636
|
+
viewing !== null && /* @__PURE__ */ jsx(
|
|
6637
|
+
AttachmentViewer,
|
|
6638
|
+
{
|
|
6639
|
+
attachments,
|
|
6640
|
+
index: viewing,
|
|
6641
|
+
onIndexChange: setViewing,
|
|
6642
|
+
onClose: () => setViewing(null)
|
|
6643
|
+
}
|
|
5758
6644
|
),
|
|
5759
6645
|
receiptBill && /* @__PURE__ */ jsx(
|
|
5760
6646
|
PaymentReceiptModal,
|
|
@@ -5924,7 +6810,7 @@ function PayablesView({ intent, onNavigate, previousView, back }) {
|
|
|
5924
6810
|
direction: "debit",
|
|
5925
6811
|
onNew: () => onNavigate("payables-new"),
|
|
5926
6812
|
onNewReceipt: () => onNavigate("payables-new-receipt"),
|
|
5927
|
-
onEdit: (id) => onNavigate(`payables
|
|
6813
|
+
onEdit: (id, status) => onNavigate(`payables-${invoiceOpensForEditing(status) ? "edit" : "detail"}:${id}`)
|
|
5928
6814
|
}
|
|
5929
6815
|
);
|
|
5930
6816
|
}
|
|
@@ -5958,7 +6844,7 @@ function ReceivablesView({ intent, onNavigate, previousView, back }) {
|
|
|
5958
6844
|
direction: "credit",
|
|
5959
6845
|
onNew: () => onNavigate("receivables-new"),
|
|
5960
6846
|
onNewReceipt: () => onNavigate("receivables-new-receipt"),
|
|
5961
|
-
onEdit: (id) => onNavigate(`receivables
|
|
6847
|
+
onEdit: (id, status) => onNavigate(`receivables-${invoiceOpensForEditing(status) ? "edit" : "detail"}:${id}`)
|
|
5962
6848
|
}
|
|
5963
6849
|
);
|
|
5964
6850
|
}
|
|
@@ -7255,7 +8141,7 @@ function firstOfMonth() {
|
|
|
7255
8141
|
function today2() {
|
|
7256
8142
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
7257
8143
|
}
|
|
7258
|
-
function
|
|
8144
|
+
function money2(v) {
|
|
7259
8145
|
return v.toLocaleString("pt-BR", { style: "currency", currency: "BRL" });
|
|
7260
8146
|
}
|
|
7261
8147
|
function Step({ n, label, done, state }) {
|
|
@@ -7309,7 +8195,7 @@ function CommissionsView() {
|
|
|
7309
8195
|
setError(null);
|
|
7310
8196
|
try {
|
|
7311
8197
|
const r = await generateCommissions({ periodStart: from, periodEnd: to });
|
|
7312
|
-
setLastRun(r ? `${r.entries} lan\xE7amento(s) \xB7 ${
|
|
8198
|
+
setLastRun(r ? `${r.entries} lan\xE7amento(s) \xB7 ${money2(r.total)}` : null);
|
|
7313
8199
|
await loadGeneration();
|
|
7314
8200
|
} catch (e) {
|
|
7315
8201
|
setError(e?.message ?? String(e));
|
|
@@ -7337,7 +8223,7 @@ function CommissionsView() {
|
|
|
7337
8223
|
setError(null);
|
|
7338
8224
|
try {
|
|
7339
8225
|
const r = await provider.payRunCommissions?.(generation.id);
|
|
7340
|
-
setLastRun(`${r?.payables ?? 0} conta(s) a pagar \xB7 ${
|
|
8226
|
+
setLastRun(`${r?.payables ?? 0} conta(s) a pagar \xB7 ${money2(r?.total ?? 0)}`);
|
|
7341
8227
|
await Promise.all([loadGeneration(), fetchCommissions({ periodStart: from, periodEnd: to })]);
|
|
7342
8228
|
} catch (e) {
|
|
7343
8229
|
setError(e?.message ?? String(e));
|
|
@@ -7383,7 +8269,7 @@ function CommissionsView() {
|
|
|
7383
8269
|
lastRun && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: lastRun }),
|
|
7384
8270
|
/* @__PURE__ */ jsxs("div", { className: "ml-auto text-right", children: [
|
|
7385
8271
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Total do per\xEDodo" }),
|
|
7386
|
-
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold tabular-nums", children:
|
|
8272
|
+
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold tabular-nums", children: money2(total) })
|
|
7387
8273
|
] })
|
|
7388
8274
|
] }),
|
|
7389
8275
|
generation && /* @__PURE__ */ jsxs("div", { className: "rounded-2xl border border-border bg-card px-4 py-3", children: [
|
|
@@ -7449,7 +8335,7 @@ function CommissionsView() {
|
|
|
7449
8335
|
p.count,
|
|
7450
8336
|
" lan\xE7amento(s)"
|
|
7451
8337
|
] }),
|
|
7452
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tabular-nums", children:
|
|
8338
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tabular-nums", children: money2(p.total) })
|
|
7453
8339
|
] })
|
|
7454
8340
|
] }, id)) })
|
|
7455
8341
|
] }),
|
|
@@ -7468,9 +8354,9 @@ function CommissionsView() {
|
|
|
7468
8354
|
/* @__PURE__ */ jsx("td", { className: "px-4 py-2 text-muted-foreground", children: c.itemName ?? "\u2014" }),
|
|
7469
8355
|
/* @__PURE__ */ jsx("td", { className: "px-4 py-2 text-muted-foreground", children: c.invoiceNumber ?? "\u2014" }),
|
|
7470
8356
|
/* @__PURE__ */ jsx("td", { className: "px-4 py-2 text-muted-foreground", children: c.ruleName ?? "\u2014" }),
|
|
7471
|
-
/* @__PURE__ */ jsx("td", { className: "px-4 py-2 text-right tabular-nums text-muted-foreground", children:
|
|
8357
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-2 text-right tabular-nums text-muted-foreground", children: money2(c.baseAmount) }),
|
|
7472
8358
|
/* @__PURE__ */ jsx("td", { className: "px-4 py-2 text-right tabular-nums text-muted-foreground", children: c.rate }),
|
|
7473
|
-
/* @__PURE__ */ jsx("td", { className: "px-4 py-2 text-right font-semibold tabular-nums", children:
|
|
8359
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-2 text-right font-semibold tabular-nums", children: money2(c.amount) })
|
|
7474
8360
|
] }, c.id)) })
|
|
7475
8361
|
] }) })
|
|
7476
8362
|
] })
|
|
@@ -7787,7 +8673,7 @@ var MESES = [
|
|
|
7787
8673
|
"Novembro",
|
|
7788
8674
|
"Dezembro"
|
|
7789
8675
|
];
|
|
7790
|
-
function
|
|
8676
|
+
function money3(v) {
|
|
7791
8677
|
return v.toLocaleString("pt-BR", { style: "currency", currency: "BRL" });
|
|
7792
8678
|
}
|
|
7793
8679
|
function GoalsView() {
|
|
@@ -7867,11 +8753,11 @@ function GoalsView() {
|
|
|
7867
8753
|
] }),
|
|
7868
8754
|
/* @__PURE__ */ jsxs("div", { className: "ml-auto text-right", children: [
|
|
7869
8755
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Realizado no ano" }),
|
|
7870
|
-
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold tabular-nums", children:
|
|
8756
|
+
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold tabular-nums", children: money3(totalRealizado) })
|
|
7871
8757
|
] }),
|
|
7872
8758
|
totalMeta > 0 && /* @__PURE__ */ jsxs("div", { className: "text-right", children: [
|
|
7873
8759
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Meta do ano" }),
|
|
7874
|
-
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold tabular-nums", children:
|
|
8760
|
+
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold tabular-nums", children: money3(totalMeta) })
|
|
7875
8761
|
] })
|
|
7876
8762
|
] }),
|
|
7877
8763
|
semMeta > 0 && /* @__PURE__ */ jsxs("p", { className: "rounded-card border border-warning/30 bg-warning/5 px-4 py-2 text-xs", children: [
|
|
@@ -7900,7 +8786,7 @@ function GoalsView() {
|
|
|
7900
8786
|
/* @__PURE__ */ jsx("td", {})
|
|
7901
8787
|
] }, i)) : rows.map((r) => /* @__PURE__ */ jsxs("tr", { className: "border-b last:border-0 hover:bg-muted/40", children: [
|
|
7902
8788
|
/* @__PURE__ */ jsx("td", { className: "px-4 py-2 font-medium", children: MESES[r.month - 1] }),
|
|
7903
|
-
/* @__PURE__ */ jsx("td", { className: "px-4 py-2 text-right tabular-nums", children:
|
|
8789
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-2 text-right tabular-nums", children: money3(r.achievedAmount) }),
|
|
7904
8790
|
/* @__PURE__ */ jsx(Degrau, { valor: r.targetAmount, percentual: r.targetPercent }),
|
|
7905
8791
|
/* @__PURE__ */ jsx(Degrau, { valor: r.superAmount, percentual: r.superPercent }),
|
|
7906
8792
|
/* @__PURE__ */ jsx(Degrau, { valor: r.hyperAmount, percentual: r.hyperPercent }),
|
|
@@ -7936,7 +8822,7 @@ function Degrau({ valor, percentual }) {
|
|
|
7936
8822
|
}
|
|
7937
8823
|
const bateu = percentual >= 100;
|
|
7938
8824
|
return /* @__PURE__ */ jsxs("td", { className: "px-4 py-2 text-right", children: [
|
|
7939
|
-
/* @__PURE__ */ jsx("div", { className: "tabular-nums", children:
|
|
8825
|
+
/* @__PURE__ */ jsx("div", { className: "tabular-nums", children: money3(valor) }),
|
|
7940
8826
|
/* @__PURE__ */ jsxs("div", { className: `text-[10px] tabular-nums ${bateu ? "font-semibold text-success" : "text-muted-foreground"}`, children: [
|
|
7941
8827
|
percentual.toFixed(2),
|
|
7942
8828
|
"%"
|
|
@@ -7991,7 +8877,7 @@ function GoalDialog({
|
|
|
7991
8877
|
] }),
|
|
7992
8878
|
/* @__PURE__ */ jsxs(ModalDescription, { className: "text-xs", children: [
|
|
7993
8879
|
"Realizado at\xE9 agora: ",
|
|
7994
|
-
/* @__PURE__ */ jsx("strong", { children:
|
|
8880
|
+
/* @__PURE__ */ jsx("strong", { children: money3(goal.achievedAmount) })
|
|
7995
8881
|
] })
|
|
7996
8882
|
] }),
|
|
7997
8883
|
/* @__PURE__ */ jsx(ModalBody, { className: "fz-container px-5 py-4", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
@@ -8056,7 +8942,7 @@ function GiftCardsView() {
|
|
|
8056
8942
|
const [maxValue, setMaxValue] = React23__default.useState("");
|
|
8057
8943
|
const [from, setFrom] = React23__default.useState("");
|
|
8058
8944
|
const [to, setTo] = React23__default.useState("");
|
|
8059
|
-
const
|
|
8945
|
+
const money4 = React23__default.useCallback((v) => formatCurrency(v, currency), [currency]);
|
|
8060
8946
|
const seq = React23__default.useRef(0);
|
|
8061
8947
|
const load = React23__default.useCallback(async () => {
|
|
8062
8948
|
const mine = ++seq.current;
|
|
@@ -8100,12 +8986,12 @@ function GiftCardsView() {
|
|
|
8100
8986
|
{
|
|
8101
8987
|
accessorKey: "faceValue",
|
|
8102
8988
|
header: "Valor",
|
|
8103
|
-
cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "block text-right tabular-nums", children:
|
|
8989
|
+
cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "block text-right tabular-nums", children: money4(row.original.faceValue) })
|
|
8104
8990
|
},
|
|
8105
8991
|
{
|
|
8106
8992
|
accessorKey: "balance",
|
|
8107
8993
|
header: "Saldo",
|
|
8108
|
-
cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "block text-right tabular-nums", children: row.original.balance === null ? /* @__PURE__ */ jsx(Dash, {}) :
|
|
8994
|
+
cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "block text-right tabular-nums", children: row.original.balance === null ? /* @__PURE__ */ jsx(Dash, {}) : money4(row.original.balance) })
|
|
8109
8995
|
},
|
|
8110
8996
|
{
|
|
8111
8997
|
accessorKey: "state",
|
|
@@ -8145,7 +9031,7 @@ function GiftCardsView() {
|
|
|
8145
9031
|
/* @__PURE__ */ jsx(TooltipContent, { className: "text-xs", children: "Ativar na venda \u2014 o saldo passa a valer o valor de face" })
|
|
8146
9032
|
] }) }) : null
|
|
8147
9033
|
}
|
|
8148
|
-
], [activating, load,
|
|
9034
|
+
], [activating, load, money4, provider]);
|
|
8149
9035
|
const outstanding = (cards ?? []).filter((c) => c.state === "active").reduce((s, c) => s + (c.balance ?? 0), 0);
|
|
8150
9036
|
const faceIssued = (cards ?? []).reduce((s, c) => s + c.faceValue, 0);
|
|
8151
9037
|
if (enabled === false) {
|
|
@@ -8180,11 +9066,11 @@ function GiftCardsView() {
|
|
|
8180
9066
|
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap gap-6 rounded-card border bg-card px-4 py-3", children: [
|
|
8181
9067
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
8182
9068
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Valor de face emitido" }),
|
|
8183
|
-
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold tabular-nums", children:
|
|
9069
|
+
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold tabular-nums", children: money4(faceIssued) })
|
|
8184
9070
|
] }),
|
|
8185
9071
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
8186
9072
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Passivo em aberto" }),
|
|
8187
|
-
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold tabular-nums", children:
|
|
9073
|
+
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold tabular-nums", children: money4(outstanding) }),
|
|
8188
9074
|
/* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground", children: "s\xF3 os cart\xF5es j\xE1 vendidos" })
|
|
8189
9075
|
] })
|
|
8190
9076
|
] }),
|
|
@@ -8196,7 +9082,7 @@ function GiftCardsView() {
|
|
|
8196
9082
|
] }),
|
|
8197
9083
|
" ",
|
|
8198
9084
|
"\u2014 ",
|
|
8199
|
-
|
|
9085
|
+
money4(issued.total),
|
|
8200
9086
|
" em valor de face. Eles nascem sem saldo: ative cada um na venda."
|
|
8201
9087
|
] }),
|
|
8202
9088
|
error && /* @__PURE__ */ jsx("div", { className: "mt-3 rounded-card border border-destructive/30 bg-destructive/5 px-4 py-3 text-xs text-destructive", children: error }),
|
|
@@ -8255,7 +9141,7 @@ function GiftCardsView() {
|
|
|
8255
9141
|
issuing && /* @__PURE__ */ jsx(
|
|
8256
9142
|
IssueDialog,
|
|
8257
9143
|
{
|
|
8258
|
-
money:
|
|
9144
|
+
money: money4,
|
|
8259
9145
|
onClose: () => setIssuing(false),
|
|
8260
9146
|
onIssued: async (count, total) => {
|
|
8261
9147
|
setIssuing(false);
|
|
@@ -8267,7 +9153,7 @@ function GiftCardsView() {
|
|
|
8267
9153
|
] }) });
|
|
8268
9154
|
}
|
|
8269
9155
|
function IssueDialog({
|
|
8270
|
-
money:
|
|
9156
|
+
money: money4,
|
|
8271
9157
|
onClose,
|
|
8272
9158
|
onIssued
|
|
8273
9159
|
}) {
|
|
@@ -8360,7 +9246,7 @@ function IssueDialog({
|
|
|
8360
9246
|
] }),
|
|
8361
9247
|
/* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-muted/40 px-4 py-2 text-sm", children: [
|
|
8362
9248
|
"Total do lote: ",
|
|
8363
|
-
/* @__PURE__ */ jsx("strong", { className: "tabular-nums", children:
|
|
9249
|
+
/* @__PURE__ */ jsx("strong", { className: "tabular-nums", children: money4(total) })
|
|
8364
9250
|
] }),
|
|
8365
9251
|
invalid && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: invalid }),
|
|
8366
9252
|
error && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: error })
|
|
@@ -8642,7 +9528,7 @@ function stripFence(text2) {
|
|
|
8642
9528
|
const end = trimmed.lastIndexOf("}");
|
|
8643
9529
|
return start >= 0 && end > start ? trimmed.slice(start, end + 1) : trimmed;
|
|
8644
9530
|
}
|
|
8645
|
-
var
|
|
9531
|
+
var num2 = (v, fallback = 0) => {
|
|
8646
9532
|
const n = typeof v === "string" ? Number(v.replace(",", ".")) : Number(v);
|
|
8647
9533
|
return Number.isFinite(n) ? n : fallback;
|
|
8648
9534
|
};
|
|
@@ -8661,8 +9547,8 @@ function parseReceiptReply(content) {
|
|
|
8661
9547
|
const item = row ?? {};
|
|
8662
9548
|
const description = str(item.description);
|
|
8663
9549
|
if (!description) return null;
|
|
8664
|
-
const quantity =
|
|
8665
|
-
const unitPrice = item.unitPrice != null ?
|
|
9550
|
+
const quantity = num2(item.quantity, 1) || 1;
|
|
9551
|
+
const unitPrice = item.unitPrice != null ? num2(item.unitPrice) : num2(item.total) / quantity;
|
|
8666
9552
|
return { description, quantity, unitPrice: Math.round(unitPrice * 100) / 100 };
|
|
8667
9553
|
}).filter((i) => i !== null) : [];
|
|
8668
9554
|
const warnings = Array.isArray(raw.warnings) ? raw.warnings.map((w) => str(w)).filter((w) => !!w) : [];
|
|
@@ -8805,16 +9691,10 @@ function ReconcileFlow({ line, initialIntent, onClose, onDone }) {
|
|
|
8805
9691
|
}, [planoDoTipo]);
|
|
8806
9692
|
const valorAQuitar = intent === "link" && target ? target.openAmount : round23(line.amount);
|
|
8807
9693
|
const quita = intent === "bill" || intent === "link" && target?.mode === "settle";
|
|
8808
|
-
const agentConnection = useMemo(() => resolveFayzAgentConnection(), []);
|
|
8809
9694
|
const leitor = useMemo(() => {
|
|
8810
|
-
|
|
8811
|
-
|
|
8812
|
-
|
|
8813
|
-
chat: (input) => client.chat(input),
|
|
8814
|
-
toInlineFile: toInlineFile,
|
|
8815
|
-
shrinkForVision
|
|
8816
|
-
});
|
|
8817
|
-
}, [agentConnection]);
|
|
9695
|
+
const ask = createOneShotAsk();
|
|
9696
|
+
return ask ? createAgentReceiptReader({ chat: ask, toInlineFile: toInlineFile, shrinkForVision }) : null;
|
|
9697
|
+
}, []);
|
|
8818
9698
|
async function lerComprovante(file) {
|
|
8819
9699
|
if (!leitor) return;
|
|
8820
9700
|
if (file.size > 10 * 1024 * 1024) {
|
|
@@ -8979,7 +9859,7 @@ function ReconcileFlow({ line, initialIntent, onClose, onDone }) {
|
|
|
8979
9859
|
candidatesLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
8980
9860
|
/* @__PURE__ */ jsx(Skeleton, { className: "h-12 w-full rounded-md" }),
|
|
8981
9861
|
/* @__PURE__ */ jsx(Skeleton, { className: "h-12 w-full rounded-md" })
|
|
8982
|
-
] }) : candidates.length === 0 ? /* @__PURE__ */ jsx("p", { className: "py-4 text-center text-xs text-muted-foreground", children: t2("financial.reconciliation.noCandidates") }) : candidates.map(({ candidate: c, mode, openAmount }) => {
|
|
9862
|
+
] }) : candidates.length === 0 ? /* @__PURE__ */ jsx("p", { className: "py-4 text-center text-xs text-muted-foreground", children: t2("financial.reconciliation.noCandidates") }) : candidates.map(({ candidate: c, mode, openAmount: openAmount2 }) => {
|
|
8983
9863
|
const escolhido = targetId === c.movement.id;
|
|
8984
9864
|
return /* @__PURE__ */ jsxs(
|
|
8985
9865
|
"button",
|
|
@@ -8994,7 +9874,7 @@ function ReconcileFlow({ line, initialIntent, onClose, onDone }) {
|
|
|
8994
9874
|
/* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground", children: [
|
|
8995
9875
|
shortDate2(c.movement.dueDate),
|
|
8996
9876
|
/* @__PURE__ */ jsx("span", { className: "ml-1.5 inline-flex items-center rounded bg-magic/10 px-1 py-px font-medium text-magic-soft-foreground", children: t2("financial.reconciliation.matchScore", { score: Math.round(c.score * 100) }) }),
|
|
8997
|
-
/* @__PURE__ */ jsx("span", { className: `ml-1.5 inline-flex items-center rounded px-1 py-px font-medium ${mode === "settle" ? "bg-warning/10 text-warning" : "bg-success/10 text-success"}`, children: mode === "settle" ? t2("financial.reconciliation.candidateOpen", { amount: fmt(
|
|
9877
|
+
/* @__PURE__ */ jsx("span", { className: `ml-1.5 inline-flex items-center rounded px-1 py-px font-medium ${mode === "settle" ? "bg-warning/10 text-warning" : "bg-success/10 text-success"}`, children: mode === "settle" ? t2("financial.reconciliation.candidateOpen", { amount: fmt(openAmount2) }) : t2("financial.reconciliation.candidatePaid") })
|
|
8998
9878
|
] })
|
|
8999
9879
|
] }),
|
|
9000
9880
|
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm font-semibold", children: fmt(c.movement.amount) })
|
|
@@ -11109,13 +11989,13 @@ function BalanceHero() {
|
|
|
11109
11989
|
const navigate = useDashboardNavigate();
|
|
11110
11990
|
useEnsureSummary();
|
|
11111
11991
|
const [hidden, setHidden] = React23__default.useState(false);
|
|
11112
|
-
const
|
|
11992
|
+
const money4 = (v) => hidden ? "\u2022\u2022\u2022\u2022\u2022\u2022" : formatCurrency(v ?? 0, currency);
|
|
11113
11993
|
const inflow = summary?.monthlyInflow ?? 0;
|
|
11114
11994
|
const outflow = summary?.monthlyOutflow ?? 0;
|
|
11115
11995
|
return /* @__PURE__ */ jsxs(GoldCard, { branded: true, className: "px-5 py-4 text-center md:px-8 md:py-5", children: [
|
|
11116
11996
|
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium uppercase tracking-wide opacity-75", children: t2("financial.home.balance") }),
|
|
11117
11997
|
/* @__PURE__ */ jsxs("div", { className: "mt-0.5 flex items-center justify-center gap-2.5", children: [
|
|
11118
|
-
/* @__PURE__ */ jsx("span", { className: "text-3xl font-bold tracking-tight tabular-nums md:text-4xl", children:
|
|
11998
|
+
/* @__PURE__ */ jsx("span", { className: "text-3xl font-bold tracking-tight tabular-nums md:text-4xl", children: money4(summary?.totalBalance) }),
|
|
11119
11999
|
/* @__PURE__ */ jsx(
|
|
11120
12000
|
"button",
|
|
11121
12001
|
{
|
|
@@ -11139,7 +12019,7 @@ function BalanceHero() {
|
|
|
11139
12019
|
/* @__PURE__ */ jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-emerald-500/90", children: /* @__PURE__ */ jsx(ArrowUpRight, { className: "h-4 w-4 text-white" }) }),
|
|
11140
12020
|
/* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
|
|
11141
12021
|
/* @__PURE__ */ jsx("span", { className: "block text-[11px] uppercase tracking-wide opacity-70", children: t2("financial.home.inflow") }),
|
|
11142
|
-
/* @__PURE__ */ jsx("span", { className: "block truncate text-sm font-semibold tabular-nums", children:
|
|
12022
|
+
/* @__PURE__ */ jsx("span", { className: "block truncate text-sm font-semibold tabular-nums", children: money4(inflow) })
|
|
11143
12023
|
] })
|
|
11144
12024
|
]
|
|
11145
12025
|
}
|
|
@@ -11155,7 +12035,7 @@ function BalanceHero() {
|
|
|
11155
12035
|
/* @__PURE__ */ jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-rose-500/90", children: /* @__PURE__ */ jsx(ArrowDownRight, { className: "h-4 w-4 text-white" }) }),
|
|
11156
12036
|
/* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
|
|
11157
12037
|
/* @__PURE__ */ jsx("span", { className: "block text-[11px] uppercase tracking-wide opacity-70", children: t2("financial.home.outflow") }),
|
|
11158
|
-
/* @__PURE__ */ jsx("span", { className: "block truncate text-sm font-semibold tabular-nums", children:
|
|
12038
|
+
/* @__PURE__ */ jsx("span", { className: "block truncate text-sm font-semibold tabular-nums", children: money4(outflow) })
|
|
11159
12039
|
] })
|
|
11160
12040
|
]
|
|
11161
12041
|
}
|
|
@@ -12846,9 +13726,17 @@ function createSupabaseFinancialProvider() {
|
|
|
12846
13726
|
totalAmount: i.total ?? 0,
|
|
12847
13727
|
discount: i.discount ?? 0,
|
|
12848
13728
|
surcharge: 0,
|
|
12849
|
-
|
|
13729
|
+
// `product_id` quando a linha veio de uma porta que o preenche (a
|
|
13730
|
+
// RPC da agenda); do metadata quando veio DESTA tela, que é onde a
|
|
13731
|
+
// escolha de catálogo é gravada. Lendo só a coluna, reabrir uma
|
|
13732
|
+
// fatura apagava o item escolhido de toda linha e deixava o texto
|
|
13733
|
+
// solto — e salvar de novo confirmava a perda.
|
|
13734
|
+
referenceId: i.productId ?? i.metadata?.referenceId,
|
|
12850
13735
|
accountId: i.chartAccountId,
|
|
12851
13736
|
costCenterId: i.costCenterId,
|
|
13737
|
+
// De onde a linha veio — o pacote que a gerou, por exemplo. Sem
|
|
13738
|
+
// devolvê-lo, editar a fatura desfazia o vínculo em silêncio.
|
|
13739
|
+
metadata: i.metadata ?? void 0,
|
|
12852
13740
|
createdAt: i.createdAt
|
|
12853
13741
|
};
|
|
12854
13742
|
});
|
|
@@ -14631,6 +15519,237 @@ function createSupabaseFinancialProvider() {
|
|
|
14631
15519
|
const { data, error } = await pub.from(T.notas).update({ status: "cancelled", error_message: reason ?? null }).eq("id", id).select().single();
|
|
14632
15520
|
if (error) throw error;
|
|
14633
15521
|
return snakeToCamel(data);
|
|
15522
|
+
},
|
|
15523
|
+
// --- Remessa CNAB 240 (080) ---
|
|
15524
|
+
/**
|
|
15525
|
+
* The Bacen registry, by COMPE code or by name.
|
|
15526
|
+
*
|
|
15527
|
+
* A bank is the country's, not the tenant's, so `public.banks` is global and
|
|
15528
|
+
* read-only. A pool where migration 167 has not run yet answers with an
|
|
15529
|
+
* empty list, and the caller falls back to the static catalogue — a missing
|
|
15530
|
+
* table costs a longer list, never the screen.
|
|
15531
|
+
*/
|
|
15532
|
+
async searchBanks(query, limit = 12) {
|
|
15533
|
+
const term = query.trim();
|
|
15534
|
+
const { pub } = getClients();
|
|
15535
|
+
let qb = pub.from("banks").select("ispb,compe,name,legal_name,brand_color,logo_url").eq("is_active", true);
|
|
15536
|
+
if (term) {
|
|
15537
|
+
const digits2 = term.replace(/\D/g, "");
|
|
15538
|
+
qb = digits2.length >= 2 && digits2.length <= 3 ? qb.like("compe", `${digits2}%`) : qb.or(`name.ilike.%${term}%,legal_name.ilike.%${term}%`);
|
|
15539
|
+
}
|
|
15540
|
+
const { data, error } = await qb.order("compe", { nullsFirst: false }).limit(limit);
|
|
15541
|
+
if (error) return [];
|
|
15542
|
+
return (data ?? []).map((row) => ({
|
|
15543
|
+
ispb: row.ispb,
|
|
15544
|
+
compe: row.compe ?? void 0,
|
|
15545
|
+
name: row.name,
|
|
15546
|
+
legalName: row.legal_name ?? void 0,
|
|
15547
|
+
brandColor: row.brand_color ?? void 0,
|
|
15548
|
+
logoUrl: row.logo_url ?? void 0
|
|
15549
|
+
}));
|
|
15550
|
+
},
|
|
15551
|
+
/**
|
|
15552
|
+
* Who gets paid, who pays, and which file number is next.
|
|
15553
|
+
*
|
|
15554
|
+
* The payee's document lives on `people`, the account on `payee_accounts`
|
|
15555
|
+
* and the address on `addresses` — three tables because each answers a
|
|
15556
|
+
* different question, and the CNPJ of the paying company is a fourth. The
|
|
15557
|
+
* wizard asks once and gets all of it, so step 3 can show a form with the
|
|
15558
|
+
* blanks already filled instead of a sentence saying data is missing.
|
|
15559
|
+
*/
|
|
15560
|
+
async getCnabContext({ contactIds, bankAccountId, unitId }) {
|
|
15561
|
+
const { pub } = getClients();
|
|
15562
|
+
const ids = [...new Set(contactIds.filter(Boolean))];
|
|
15563
|
+
const [peopleRes, accountsRes, addressRes, companyRes, unitRes, seqRes] = await Promise.all([
|
|
15564
|
+
ids.length ? pub.from("people").select("id,name,document_number").in("id", ids) : Promise.resolve({ data: [], error: null }),
|
|
15565
|
+
ids.length ? pub.from(T.payeeAccounts).select("*").eq("owner_type", "person").in("owner_id", ids).order("is_default", { ascending: false }).order("created_at") : Promise.resolve({ data: [], error: null }),
|
|
15566
|
+
ids.length ? pub.from("addresses").select("*").eq("owner_type", "person").in("owner_id", ids) : Promise.resolve({ data: [], error: null }),
|
|
15567
|
+
// As empresas do tenant. São unidades de linhas, então vêm todas e a
|
|
15568
|
+
// escolha é feita aqui — a da unidade da conta, senão a sem unidade,
|
|
15569
|
+
// senão a única que existe.
|
|
15570
|
+
pub.from("company_legal_entities").select("id,unit_id,kind,legal_name,trade_name,document_number").eq("is_active", true).limit(20),
|
|
15571
|
+
// O nome da unidade é o que a tela escreve enquanto não há empresa
|
|
15572
|
+
// cadastrada — melhor que um campo em branco pedindo razão social.
|
|
15573
|
+
unitId ? pub.from(T.unitCompanies).select("unit_id,unit_name").eq("unit_id", unitId).maybeSingle() : Promise.resolve({ data: null, error: null }),
|
|
15574
|
+
pub.from(T.cnabBatches).select("sequence_number").eq("bank_account_id", bankAccountId).order("sequence_number", { ascending: false }).limit(1).maybeSingle()
|
|
15575
|
+
]);
|
|
15576
|
+
if (peopleRes.error) throw peopleRes.error;
|
|
15577
|
+
if (accountsRes.error) throw accountsRes.error;
|
|
15578
|
+
const accountOf = /* @__PURE__ */ new Map();
|
|
15579
|
+
for (const row of accountsRes.data ?? []) {
|
|
15580
|
+
if (!accountOf.has(row.owner_id)) accountOf.set(row.owner_id, row);
|
|
15581
|
+
}
|
|
15582
|
+
const addressOf = /* @__PURE__ */ new Map();
|
|
15583
|
+
for (const row of addressRes.data ?? []) {
|
|
15584
|
+
if (!addressOf.has(row.owner_id)) addressOf.set(row.owner_id, row);
|
|
15585
|
+
}
|
|
15586
|
+
const payees = (peopleRes.data ?? []).map((person) => {
|
|
15587
|
+
const account = accountOf.get(person.id);
|
|
15588
|
+
const address = addressOf.get(person.id);
|
|
15589
|
+
return {
|
|
15590
|
+
contactId: person.id,
|
|
15591
|
+
name: person.name ?? "",
|
|
15592
|
+
document: person.document_number ?? "",
|
|
15593
|
+
payeeAccountId: account?.id,
|
|
15594
|
+
bankCode: account?.bank_code ?? "",
|
|
15595
|
+
bankName: account?.bank_name ?? "",
|
|
15596
|
+
agencyNumber: account?.agency_number ?? "",
|
|
15597
|
+
agencyDigit: account?.agency_digit ?? "",
|
|
15598
|
+
accountNumber: account?.account_number ?? "",
|
|
15599
|
+
accountDigit: account?.account_digit ?? "",
|
|
15600
|
+
accountType: account?.account_type ?? "",
|
|
15601
|
+
pixKey: account?.pix_key ?? "",
|
|
15602
|
+
pixKeyType: account?.pix_key_type ?? "",
|
|
15603
|
+
street: address?.street ?? "",
|
|
15604
|
+
number: address?.number ?? "",
|
|
15605
|
+
complement: address?.complement ?? "",
|
|
15606
|
+
district: address?.district ?? "",
|
|
15607
|
+
city: address?.city ?? "",
|
|
15608
|
+
state: address?.state ?? "",
|
|
15609
|
+
postalCode: address?.postal_code ?? ""
|
|
15610
|
+
};
|
|
15611
|
+
});
|
|
15612
|
+
const companies = companyRes.data ?? [];
|
|
15613
|
+
const companyRow = companies.find((row) => unitId && row.unit_id === unitId) ?? companies.find((row) => !row.unit_id) ?? companies[0];
|
|
15614
|
+
const unitName = unitRes.data?.unit_name ?? "";
|
|
15615
|
+
const company = {
|
|
15616
|
+
id: companyRow?.id ?? void 0,
|
|
15617
|
+
unitId,
|
|
15618
|
+
name: companyRow?.trade_name || companyRow?.legal_name || unitName,
|
|
15619
|
+
document: companyRow?.document_number || ""
|
|
15620
|
+
};
|
|
15621
|
+
return {
|
|
15622
|
+
company,
|
|
15623
|
+
nextSequence: Number(seqRes.data?.sequence_number ?? 0) + 1,
|
|
15624
|
+
payees
|
|
15625
|
+
};
|
|
15626
|
+
},
|
|
15627
|
+
/**
|
|
15628
|
+
* A empresa pagadora, cadastrada de dentro da remessa.
|
|
15629
|
+
*
|
|
15630
|
+
* `company_legal_entities` nasceu sem tela: nenhuma superfície do SDK
|
|
15631
|
+
* escreve nela, e o resultado é um pool com treze unidades e zero empresas.
|
|
15632
|
+
* Como o CNPJ é obrigatório no cabeçalho do CNAB, é aqui que a pessoa o
|
|
15633
|
+
* digita — e gravá-lo é o que faz a PRÓXIMA remessa já vir preenchida.
|
|
15634
|
+
*
|
|
15635
|
+
* `unit_id` na linha é o que recorta a empresa por unidade. O elo inverso
|
|
15636
|
+
* (`app.units.company_legal_entity_id`) fica para quem tiver a RPC: o schema
|
|
15637
|
+
* `app` não é alcançável por aqui, e a leitura já resolve pelos dois lados.
|
|
15638
|
+
*/
|
|
15639
|
+
async saveCompany(input) {
|
|
15640
|
+
const { pub } = getClients();
|
|
15641
|
+
const tenantId = getTenantId();
|
|
15642
|
+
const digits2 = (input.document || "").replace(/\D/g, "");
|
|
15643
|
+
const name = input.name.trim();
|
|
15644
|
+
if (!name) throw new Error("A empresa precisa de um nome.");
|
|
15645
|
+
if (digits2.length !== 11 && digits2.length !== 14) {
|
|
15646
|
+
throw new Error("CNPJ com 14 d\xEDgitos ou CPF com 11 \u2014 o cabe\xE7alho do CNAB n\xE3o aceita outro.");
|
|
15647
|
+
}
|
|
15648
|
+
const row = {
|
|
15649
|
+
kind: digits2.length === 14 ? "company" : "individual",
|
|
15650
|
+
legal_name: name,
|
|
15651
|
+
trade_name: name,
|
|
15652
|
+
document_number: digits2
|
|
15653
|
+
};
|
|
15654
|
+
if (input.id) {
|
|
15655
|
+
const { error: error2 } = await pub.from("company_legal_entities").update(row).eq("id", input.id);
|
|
15656
|
+
if (error2) throw error2;
|
|
15657
|
+
return { id: input.id };
|
|
15658
|
+
}
|
|
15659
|
+
const { data, error } = await pub.from("company_legal_entities").insert({ ...row, tenant_id: tenantId, unit_id: input.unitId ?? null, is_active: true }).select("id").single();
|
|
15660
|
+
if (error) throw error;
|
|
15661
|
+
return { id: data.id };
|
|
15662
|
+
},
|
|
15663
|
+
/**
|
|
15664
|
+
* What the wizard collected goes back to the cadastro, not to the file only.
|
|
15665
|
+
* Otherwise the same CPF is typed again on the next remittance, and the two
|
|
15666
|
+
* typings disagree.
|
|
15667
|
+
*/
|
|
15668
|
+
async saveCnabPayees(payees) {
|
|
15669
|
+
const { pub } = getClients();
|
|
15670
|
+
const tenantId = getTenantId();
|
|
15671
|
+
const text2 = (v) => {
|
|
15672
|
+
const s = (v ?? "").trim();
|
|
15673
|
+
return s === "" ? null : s;
|
|
15674
|
+
};
|
|
15675
|
+
for (const payee of payees) {
|
|
15676
|
+
if (text2(payee.document)) {
|
|
15677
|
+
await pub.from("people").update({ document_number: text2(payee.document) }).eq("id", payee.contactId);
|
|
15678
|
+
}
|
|
15679
|
+
const row = {
|
|
15680
|
+
bank_code: text2(payee.bankCode),
|
|
15681
|
+
bank_name: text2(payee.bankName),
|
|
15682
|
+
agency_number: text2(payee.agencyNumber),
|
|
15683
|
+
agency_digit: text2(payee.agencyDigit),
|
|
15684
|
+
account_number: text2(payee.accountNumber),
|
|
15685
|
+
account_digit: text2(payee.accountDigit),
|
|
15686
|
+
account_type: text2(payee.accountType),
|
|
15687
|
+
pix_key: text2(payee.pixKey),
|
|
15688
|
+
pix_key_type: text2(payee.pixKeyType)
|
|
15689
|
+
};
|
|
15690
|
+
if (!row.account_number && !row.pix_key) continue;
|
|
15691
|
+
let targetId = payee.payeeAccountId;
|
|
15692
|
+
if (!targetId) {
|
|
15693
|
+
const { data: existing } = await pub.from(T.payeeAccounts).select("id").eq("owner_type", "person").eq("owner_id", payee.contactId).order("is_default", { ascending: false }).order("created_at").limit(1).maybeSingle();
|
|
15694
|
+
targetId = existing?.id;
|
|
15695
|
+
}
|
|
15696
|
+
if (targetId) {
|
|
15697
|
+
await pub.from(T.payeeAccounts).update(row).eq("id", targetId);
|
|
15698
|
+
} else {
|
|
15699
|
+
await pub.from(T.payeeAccounts).insert({
|
|
15700
|
+
...row,
|
|
15701
|
+
tenant_id: tenantId,
|
|
15702
|
+
owner_type: "person",
|
|
15703
|
+
owner_id: payee.contactId,
|
|
15704
|
+
label: "Principal",
|
|
15705
|
+
is_default: true
|
|
15706
|
+
});
|
|
15707
|
+
}
|
|
15708
|
+
}
|
|
15709
|
+
},
|
|
15710
|
+
/**
|
|
15711
|
+
* The file that already left for the bank. `sequence_number` is the bank's
|
|
15712
|
+
* count, not the file's — two remittances carrying the same one is a
|
|
15713
|
+
* rejected remittance, so it is written with the batch and never derived
|
|
15714
|
+
* again.
|
|
15715
|
+
*/
|
|
15716
|
+
async recordCnabBatch(input) {
|
|
15717
|
+
const { pub } = getClients();
|
|
15718
|
+
const tenantId = getTenantId();
|
|
15719
|
+
const { data, error } = await pub.from(T.cnabBatches).insert({
|
|
15720
|
+
tenant_id: tenantId,
|
|
15721
|
+
unit_id: input.unitId ?? null,
|
|
15722
|
+
bank_account_id: input.bankAccountId,
|
|
15723
|
+
layout: input.layout,
|
|
15724
|
+
sequence_number: input.sequenceNumber,
|
|
15725
|
+
total_amount: input.totalAmount,
|
|
15726
|
+
items_count: input.items.length,
|
|
15727
|
+
file_name: input.fileName,
|
|
15728
|
+
// The file's home is `public.documents` through `document_id`; until the
|
|
15729
|
+
// upload path exists it rides in metadata, which keeps a generated
|
|
15730
|
+
// remittance downloadable instead of gone.
|
|
15731
|
+
metadata: { file_content: input.fileContent }
|
|
15732
|
+
}).select("id").single();
|
|
15733
|
+
if (error) throw error;
|
|
15734
|
+
const batchId = data.id;
|
|
15735
|
+
if (input.items.length) {
|
|
15736
|
+
const ids = [...new Set(input.items.map((item) => item.movementId))];
|
|
15737
|
+
const { data: movementRows } = await pub.from(T.movements).select("id").in("id", ids);
|
|
15738
|
+
const isMovement = new Set((movementRows ?? []).map((row) => row.id));
|
|
15739
|
+
const { error: itemsError } = await pub.from(T.cnabItems).insert(
|
|
15740
|
+
input.items.map((item) => ({
|
|
15741
|
+
tenant_id: tenantId,
|
|
15742
|
+
batch_id: batchId,
|
|
15743
|
+
installment_id: item.movementId,
|
|
15744
|
+
movement_id: isMovement.has(item.movementId) ? item.movementId : null,
|
|
15745
|
+
payment_form: item.paymentForm,
|
|
15746
|
+
payment_date: item.paymentDate,
|
|
15747
|
+
amount: item.amount
|
|
15748
|
+
}))
|
|
15749
|
+
);
|
|
15750
|
+
if (itemsError) throw itemsError;
|
|
15751
|
+
}
|
|
15752
|
+
return { id: batchId };
|
|
14634
15753
|
}
|
|
14635
15754
|
};
|
|
14636
15755
|
return provider;
|
|
@@ -15591,6 +16710,7 @@ var costCenterEntity = {
|
|
|
15591
16710
|
};
|
|
15592
16711
|
var isCard = (t2) => t2 === "card_receivable_credit" || t2 === "card_receivable_debit" || t2 === "card_payable";
|
|
15593
16712
|
var isBank = (t2) => t2 === "bank";
|
|
16713
|
+
var hasIssuer = (t2) => isBank(t2) || t2 === "card_payable";
|
|
15594
16714
|
var ACCOUNT_KINDS = ["cash_physical", "bank", "card_receivable_credit", "card_receivable_debit", "card_payable"];
|
|
15595
16715
|
var bankAccountEntity = {
|
|
15596
16716
|
name: "Conta",
|
|
@@ -15611,7 +16731,12 @@ var bankAccountEntity = {
|
|
|
15611
16731
|
// sozinho quando nenhum dos seus campos se aplica.
|
|
15612
16732
|
fieldGroups: [
|
|
15613
16733
|
{ id: "identity", label: "Identifica\xE7\xE3o", columns: 2 },
|
|
15614
|
-
{
|
|
16734
|
+
{
|
|
16735
|
+
id: "bank",
|
|
16736
|
+
label: "Onde o dinheiro est\xE1",
|
|
16737
|
+
columns: 2,
|
|
16738
|
+
labelWhen: (v) => v.kind === "card_payable" ? "Quem emitiu o cart\xE3o" : void 0
|
|
16739
|
+
},
|
|
15615
16740
|
{ id: "holder", label: "Titular", columns: 2, description: "A conta de uma rede raramente est\xE1 no nome da rede." },
|
|
15616
16741
|
{ id: "card", label: "Cart\xE3o", columns: 2 },
|
|
15617
16742
|
{ ...RECORD_GROUP }
|
|
@@ -15651,16 +16776,26 @@ var bankAccountEntity = {
|
|
|
15651
16776
|
label: "N\xFAmero do banco",
|
|
15652
16777
|
type: "text",
|
|
15653
16778
|
showInTable: false,
|
|
15654
|
-
visibleWhen: (v) =>
|
|
16779
|
+
visibleWhen: (v) => hasIssuer(v.kind),
|
|
15655
16780
|
group: "bank"
|
|
15656
16781
|
},
|
|
16782
|
+
// Picking the bank fills its number too, so the two cannot disagree.
|
|
15657
16783
|
{
|
|
15658
16784
|
key: "bankName",
|
|
15659
16785
|
label: "Banco",
|
|
15660
|
-
type: "
|
|
16786
|
+
type: "custom",
|
|
15661
16787
|
showInTable: false,
|
|
15662
|
-
|
|
15663
|
-
|
|
16788
|
+
labelWhen: (v) => v.kind === "card_payable" ? "Banco emissor" : void 0,
|
|
16789
|
+
visibleWhen: (v) => hasIssuer(v.kind),
|
|
16790
|
+
group: "bank",
|
|
16791
|
+
renderInput: ({ value, values, onChange, setValue }) => React23__default.createElement(BankPicker, {
|
|
16792
|
+
code: values.bankCode ?? "",
|
|
16793
|
+
name: value ?? "",
|
|
16794
|
+
onChange: (next) => {
|
|
16795
|
+
onChange(next.name);
|
|
16796
|
+
if (next.code || !next.name) setValue?.("bankCode", next.code);
|
|
16797
|
+
}
|
|
16798
|
+
})
|
|
15664
16799
|
},
|
|
15665
16800
|
{
|
|
15666
16801
|
key: "agencyNumber",
|
|
@@ -16701,6 +17836,7 @@ var en = {
|
|
|
16701
17836
|
"financial.invoiceForm.contactRequired": "Select who will pay or receive before adding items.",
|
|
16702
17837
|
"financial.invoiceForm.additionalNotes": "Additional notes...",
|
|
16703
17838
|
"financial.itemType.service": "Service",
|
|
17839
|
+
"financial.itemType.package": "Package",
|
|
16704
17840
|
"financial.itemType.product": "Product",
|
|
16705
17841
|
"financial.itemType.other": "Other",
|
|
16706
17842
|
"financial.itemType.gift_card": "Gift Card",
|
|
@@ -17590,6 +18726,7 @@ var ptBR = {
|
|
|
17590
18726
|
"financial.invoiceForm.contactRequired": "Selecione quem vai pagar ou receber antes de adicionar itens.",
|
|
17591
18727
|
"financial.invoiceForm.additionalNotes": "Observa\xE7\xF5es adicionais...",
|
|
17592
18728
|
"financial.itemType.service": "Servi\xE7o",
|
|
18729
|
+
"financial.itemType.package": "Pacote",
|
|
17593
18730
|
"financial.itemType.product": "Produto",
|
|
17594
18731
|
"financial.itemType.other": "Outro",
|
|
17595
18732
|
"financial.itemType.gift_card": "Gift Card",
|
|
@@ -42900,6 +44037,145 @@ COMMENT ON VIEW public.v_financial_invoice_lines_due IS
|
|
|
42900
44037
|
ALTER VIEW public.v_financial_invoice_lines_due SET (security_invoker = true);
|
|
42901
44038
|
GRANT SELECT ON public.v_financial_invoice_lines_due TO authenticated, service_role;
|
|
42902
44039
|
`;
|
|
44040
|
+
var MIGRATION_115_A_REMESSA_APONTA_PARA_A_CONTA_QUE_EXISTE = `-- ---------------------------------------------------------------------------
|
|
44041
|
+
-- 115 \u2014 a remessa aponta para a conta que EXISTE.
|
|
44042
|
+
--
|
|
44043
|
+
-- \`plg_financial_cnab_batches.bank_account_id\` carrega DUAS chaves estrangeiras
|
|
44044
|
+
-- para a mesma coluna:
|
|
44045
|
+
--
|
|
44046
|
+
-- plg_financial_cnab_batches_account_fkey \u2192 plg_financial_bank_accounts
|
|
44047
|
+
-- plg_financial_cnab_batches_ledger_account_fkey \u2192 plg_financial_accounts
|
|
44048
|
+
--
|
|
44049
|
+
-- A segunda \xE9 a certa. A primeira aponta para a tabela LEGADA \u2014 aquela que a
|
|
44050
|
+
-- 021 aposentou e que hoje \xE9 s\xF3 o encaminhamento por gatilho. Neste cluster ela
|
|
44051
|
+
-- tem 5 linhas contra 63 contas banc\xE1rias no raz\xE3o, e o efeito \xE9 exato: gerar
|
|
44052
|
+
-- uma remessa por qualquer conta criada depois do porte morre com
|
|
44053
|
+
--
|
|
44054
|
+
-- violates foreign key constraint "plg_financial_cnab_batches_account_fkey"
|
|
44055
|
+
-- Key is not present in table "plg_financial_bank_accounts"
|
|
44056
|
+
--
|
|
44057
|
+
-- Uma coluna com duas FKs exige que o id esteja nas DUAS tabelas, e \xE9 por isso
|
|
44058
|
+
-- que a tela nunca conseguiu gravar: ela escreve onde \`getBankAccounts\` l\xEA, que
|
|
44059
|
+
-- \xE9 o raz\xE3o, e o legado nunca soube daquela conta.
|
|
44060
|
+
--
|
|
44061
|
+
-- A 080 trouxe a tabela do V1 com a FK que o V1 tinha. O que faltou foi
|
|
44062
|
+
-- aposentar a chave junto com a tabela a que ela se referia.
|
|
44063
|
+
--
|
|
44064
|
+
-- Derrubar a FK legada n\xE3o perde nada: a do raz\xE3o continua guardando a coluna, e
|
|
44065
|
+
-- \xE9 ela que diz a verdade. As 60 remessas j\xE1 migradas apontam para contas que
|
|
44066
|
+
-- est\xE3o nas duas tabelas e seguem v\xE1lidas.
|
|
44067
|
+
--
|
|
44068
|
+
-- Idempotente.
|
|
44069
|
+
-- ---------------------------------------------------------------------------
|
|
44070
|
+
|
|
44071
|
+
ALTER TABLE public.plg_financial_cnab_batches
|
|
44072
|
+
DROP CONSTRAINT IF EXISTS plg_financial_cnab_batches_account_fkey;
|
|
44073
|
+
|
|
44074
|
+
-- A do raz\xE3o pode n\xE3o existir num pool que recebeu a 080 e n\xE3o a corre\xE7\xE3o; \xE9
|
|
44075
|
+
-- ela que fica, ent\xE3o se faltar, nasce aqui.
|
|
44076
|
+
DO $$
|
|
44077
|
+
BEGIN
|
|
44078
|
+
IF NOT EXISTS (
|
|
44079
|
+
SELECT 1 FROM pg_constraint
|
|
44080
|
+
WHERE conrelid = 'public.plg_financial_cnab_batches'::regclass
|
|
44081
|
+
AND conname = 'plg_financial_cnab_batches_ledger_account_fkey'
|
|
44082
|
+
) THEN
|
|
44083
|
+
ALTER TABLE public.plg_financial_cnab_batches
|
|
44084
|
+
ADD CONSTRAINT plg_financial_cnab_batches_ledger_account_fkey
|
|
44085
|
+
FOREIGN KEY (bank_account_id)
|
|
44086
|
+
REFERENCES public.plg_financial_accounts(id) ON DELETE SET NULL;
|
|
44087
|
+
END IF;
|
|
44088
|
+
END $$;
|
|
44089
|
+
|
|
44090
|
+
COMMENT ON COLUMN public.plg_financial_cnab_batches.bank_account_id IS
|
|
44091
|
+
'A conta do RAZ\xC3O que o arquivo debita \u2014 plg_financial_accounts, a mesma que getBankAccounts l\xEA. N\xE3o \xE9 a tabela legada plg_financial_bank_accounts: uma conta criada depois do porte s\xF3 existe no raz\xE3o.';
|
|
44092
|
+
|
|
44093
|
+
-- \u2500\u2500 a parcela que n\xE3o tem movimento g\xEAmeo \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
44094
|
+
--
|
|
44095
|
+
-- \`plg_financial_cnab_items.movement_id\` aponta para \`plg_financial_movements\`.
|
|
44096
|
+
-- A tela manda a PARCELA, e o porte deixou parcela e movimento com o mesmo id
|
|
44097
|
+
-- na maioria das linhas \u2014 mas n\xE3o em todas: aqui s\xE3o 171.978 parcelas contra
|
|
44098
|
+
-- 143.175 com movimento de mesmo id. As outras 28.803 fariam a remessa morrer
|
|
44099
|
+
-- na hora de arquivar um arquivo que J\xC1 foi baixado e j\xE1 foi para o banco.
|
|
44100
|
+
--
|
|
44101
|
+
-- O item passa a aceitar a parcela em coluna pr\xF3pria. \`movement_id\` continua
|
|
44102
|
+
-- sendo o elo com o raz\xE3o onde ele existe; \`installment_id\` \xE9 o que a tela
|
|
44103
|
+
-- sempre teve na m\xE3o, e \xE9 por ele que \xABesta conta j\xE1 foi numa remessa\xBB tem
|
|
44104
|
+
-- resposta.
|
|
44105
|
+
ALTER TABLE public.plg_financial_cnab_items
|
|
44106
|
+
ADD COLUMN IF NOT EXISTS installment_id uuid;
|
|
44107
|
+
|
|
44108
|
+
DO $$
|
|
44109
|
+
BEGIN
|
|
44110
|
+
IF NOT EXISTS (
|
|
44111
|
+
SELECT 1 FROM pg_constraint
|
|
44112
|
+
WHERE conrelid = 'public.plg_financial_cnab_items'::regclass
|
|
44113
|
+
AND conname = 'plg_financial_cnab_items_installment_fkey'
|
|
44114
|
+
) THEN
|
|
44115
|
+
ALTER TABLE public.plg_financial_cnab_items
|
|
44116
|
+
ADD CONSTRAINT plg_financial_cnab_items_installment_fkey
|
|
44117
|
+
FOREIGN KEY (installment_id)
|
|
44118
|
+
REFERENCES public.plg_financial_invoice_installments(id) ON DELETE SET NULL;
|
|
44119
|
+
END IF;
|
|
44120
|
+
END $$;
|
|
44121
|
+
|
|
44122
|
+
-- As linhas j\xE1 migradas: onde o movimento tem parcela de mesmo id, o elo novo
|
|
44123
|
+
-- se preenche sozinho. O que n\xE3o casar fica nulo, que \xE9 a verdade.
|
|
44124
|
+
UPDATE public.plg_financial_cnab_items ci
|
|
44125
|
+
SET installment_id = ci.movement_id
|
|
44126
|
+
WHERE ci.installment_id IS NULL
|
|
44127
|
+
AND ci.movement_id IS NOT NULL
|
|
44128
|
+
AND EXISTS (
|
|
44129
|
+
SELECT 1 FROM public.plg_financial_invoice_installments i WHERE i.id = ci.movement_id
|
|
44130
|
+
);
|
|
44131
|
+
|
|
44132
|
+
CREATE INDEX IF NOT EXISTS plg_financial_cnab_items_installment_idx
|
|
44133
|
+
ON public.plg_financial_cnab_items (tenant_id, installment_id)
|
|
44134
|
+
WHERE installment_id IS NOT NULL;
|
|
44135
|
+
|
|
44136
|
+
COMMENT ON COLUMN public.plg_financial_cnab_items.installment_id IS
|
|
44137
|
+
'A PARCELA que esta linha da remessa paga \u2014 \xE9 o que a tela de contas tem na m\xE3o. movement_id continua apontando para o raz\xE3o onde o movimento g\xEAmeo existe.';
|
|
44138
|
+
|
|
44139
|
+
-- \u2500\u2500 uma conta padr\xE3o por favorecido, sem derrubar a remessa \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
44140
|
+
--
|
|
44141
|
+
-- \`payee_accounts_one_default_uidx\` \xE9 \xFAnico em (tenant_id, owner_type, owner_id)
|
|
44142
|
+
-- WHERE is_default, e est\xE1 certo: duas contas padr\xE3o fazem a tela mostrar uma e
|
|
44143
|
+
-- a remessa pagar a outra.
|
|
44144
|
+
--
|
|
44145
|
+
-- O que estava errado era quem escrevia: a remessa inseria sempre com
|
|
44146
|
+
-- \`is_default = true\`, ent\xE3o a SEGUNDA tentativa de gerar o mesmo arquivo \u2014
|
|
44147
|
+
-- depois de a primeira morrer na FK acima \u2014 batia em
|
|
44148
|
+
--
|
|
44149
|
+
-- duplicate key value violates unique constraint
|
|
44150
|
+
--
|
|
44151
|
+
-- O conserto \xE9 do lado de quem escreve (\`saveCnabPayees\` procura antes de
|
|
44152
|
+
-- inserir), e esta fun\xE7\xE3o \xE9 a rede: um \`insert\` que j\xE1 tem padr\xE3o vira o padr\xE3o
|
|
44153
|
+
-- da pessoa sem precisar que o chamador saiba disso.
|
|
44154
|
+
CREATE OR REPLACE FUNCTION public.trg_payee_accounts_one_default()
|
|
44155
|
+
RETURNS trigger
|
|
44156
|
+
LANGUAGE plpgsql
|
|
44157
|
+
SECURITY INVOKER
|
|
44158
|
+
SET search_path = public, pg_catalog
|
|
44159
|
+
AS $$
|
|
44160
|
+
BEGIN
|
|
44161
|
+
IF NEW.is_default THEN
|
|
44162
|
+
UPDATE public.payee_accounts
|
|
44163
|
+
SET is_default = false
|
|
44164
|
+
WHERE tenant_id = NEW.tenant_id
|
|
44165
|
+
AND owner_type = NEW.owner_type
|
|
44166
|
+
AND owner_id = NEW.owner_id
|
|
44167
|
+
AND is_default
|
|
44168
|
+
AND id <> NEW.id;
|
|
44169
|
+
END IF;
|
|
44170
|
+
RETURN NEW;
|
|
44171
|
+
END
|
|
44172
|
+
$$;
|
|
44173
|
+
|
|
44174
|
+
DROP TRIGGER IF EXISTS payee_accounts_one_default ON public.payee_accounts;
|
|
44175
|
+
CREATE TRIGGER payee_accounts_one_default
|
|
44176
|
+
BEFORE INSERT OR UPDATE OF is_default ON public.payee_accounts
|
|
44177
|
+
FOR EACH ROW EXECUTE FUNCTION public.trg_payee_accounts_one_default();
|
|
44178
|
+
`;
|
|
42903
44179
|
var MIGRATIONS = [
|
|
42904
44180
|
{ id: "000_baseline", sql: MIGRATION_000_BASELINE },
|
|
42905
44181
|
{ id: "001_the_comanda_becomes_a_receivable_when_it_closes", sql: MIGRATION_001_THE_COMANDA_BECOMES_A_RECEIVABLE_WHEN_IT_CLOSES },
|
|
@@ -43015,11 +44291,10 @@ var MIGRATIONS = [
|
|
|
43015
44291
|
{ id: "111_reparcelar_sem_apagar_a_parcela", sql: MIGRATION_111_REPARCELAR_SEM_APAGAR_A_PARCELA },
|
|
43016
44292
|
{ id: "112_a_linha_do_extrato_nao_e_um_lancamento_ainda", sql: MIGRATION_112_A_LINHA_DO_EXTRATO_NAO_E_UM_LANCAMENTO_AINDA },
|
|
43017
44293
|
{ id: "113_so_a_conta_integrada_aparece_na_conciliacao", sql: MIGRATION_113_SO_A_CONTA_INTEGRADA_APARECE_NA_CONCILIACAO },
|
|
43018
|
-
{ id: "114_a_parcela_diz_por_qual_conta_ela_foi_paga", sql: MIGRATION_114_A_PARCELA_DIZ_POR_QUAL_CONTA_ELA_FOI_PAGA }
|
|
44294
|
+
{ id: "114_a_parcela_diz_por_qual_conta_ela_foi_paga", sql: MIGRATION_114_A_PARCELA_DIZ_POR_QUAL_CONTA_ELA_FOI_PAGA },
|
|
44295
|
+
{ id: "115_a_remessa_aponta_para_a_conta_que_existe", sql: MIGRATION_115_A_REMESSA_APONTA_PARA_A_CONTA_QUE_EXISTE }
|
|
43019
44296
|
];
|
|
43020
|
-
var STANDALONE_CONFIG =
|
|
43021
|
-
currency: { code: "BRL", locale: "pt-BR", symbol: "R$" }
|
|
43022
|
-
};
|
|
44297
|
+
var STANDALONE_CONFIG = resolveConfig();
|
|
43023
44298
|
registerTranslations(financialLocales);
|
|
43024
44299
|
function ReceivePaymentModal({
|
|
43025
44300
|
installment,
|
|
@@ -43105,7 +44380,7 @@ function ClientAccountPanel({
|
|
|
43105
44380
|
const provider = useFinancialProvider();
|
|
43106
44381
|
const [account, setAccount] = React23__default.useState(null);
|
|
43107
44382
|
const [busy, setBusy] = React23__default.useState(false);
|
|
43108
|
-
const [
|
|
44383
|
+
const [viewInvoiceId, setViewInvoiceId] = React23__default.useState(null);
|
|
43109
44384
|
const [comprando, setComprando] = React23__default.useState(false);
|
|
43110
44385
|
const [buscaServico, setBuscaServico] = React23__default.useState("");
|
|
43111
44386
|
const [catalogo, setCatalogo] = React23__default.useState([]);
|
|
@@ -43119,6 +44394,8 @@ function ClientAccountPanel({
|
|
|
43119
44394
|
React23__default.useEffect(() => {
|
|
43120
44395
|
void load();
|
|
43121
44396
|
}, [load]);
|
|
44397
|
+
const viewInvoice = account?.invoices.find((i) => i.id === viewInvoiceId) ?? null;
|
|
44398
|
+
const viewEditable = invoiceOpensForEditing(viewInvoice?.status);
|
|
43122
44399
|
const forThis = account?.invoices.filter((i) => orderId && i.orderId === orderId) ?? [];
|
|
43123
44400
|
const hasForThis = forThis.length > 0;
|
|
43124
44401
|
React23__default.useEffect(() => {
|
|
@@ -43331,7 +44608,7 @@ function ClientAccountPanel({
|
|
|
43331
44608
|
"button",
|
|
43332
44609
|
{
|
|
43333
44610
|
type: "button",
|
|
43334
|
-
onClick: () =>
|
|
44611
|
+
onClick: () => setViewInvoiceId(i.id),
|
|
43335
44612
|
className: "inline-flex shrink-0 items-center gap-1 rounded-button border border-input px-2.5 py-1.5 text-xs font-medium hover:bg-muted",
|
|
43336
44613
|
children: i.balance > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
43337
44614
|
/* @__PURE__ */ jsx(CircleDollarSign, { className: "h-3.5 w-3.5" }),
|
|
@@ -43362,17 +44639,31 @@ function ClientAccountPanel({
|
|
|
43362
44639
|
hint: clientName ?? void 0,
|
|
43363
44640
|
width: "max-w-3xl",
|
|
43364
44641
|
onClose: () => {
|
|
43365
|
-
|
|
44642
|
+
setViewInvoiceId(null);
|
|
43366
44643
|
void load();
|
|
43367
44644
|
},
|
|
43368
|
-
children: /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto px-4 py-3", children: /* @__PURE__ */ jsx(
|
|
44645
|
+
children: /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto px-4 py-3", children: viewEditable ? /* @__PURE__ */ jsx(
|
|
44646
|
+
InvoiceFormView,
|
|
44647
|
+
{
|
|
44648
|
+
embedded: true,
|
|
44649
|
+
direction: "credit",
|
|
44650
|
+
editId: viewInvoice.id,
|
|
44651
|
+
onSaved: () => {
|
|
44652
|
+
void load();
|
|
44653
|
+
},
|
|
44654
|
+
onChanged: () => {
|
|
44655
|
+
setViewInvoiceId(null);
|
|
44656
|
+
void load();
|
|
44657
|
+
}
|
|
44658
|
+
}
|
|
44659
|
+
) : /* @__PURE__ */ jsx(
|
|
43369
44660
|
InvoiceDetailView,
|
|
43370
44661
|
{
|
|
43371
44662
|
embedded: true,
|
|
43372
44663
|
invoiceId: viewInvoice.id,
|
|
43373
44664
|
direction: "credit",
|
|
43374
44665
|
onBack: () => {
|
|
43375
|
-
|
|
44666
|
+
setViewInvoiceId(null);
|
|
43376
44667
|
void load();
|
|
43377
44668
|
}
|
|
43378
44669
|
}
|
|
@@ -43412,30 +44703,6 @@ function createSafeFinancialProvider() {
|
|
|
43412
44703
|
);
|
|
43413
44704
|
}
|
|
43414
44705
|
var ACCOUNTS_ROUTE = "/registry/accounts";
|
|
43415
|
-
var DEFAULT_LABELS = {
|
|
43416
|
-
pageTitle: "Financial",
|
|
43417
|
-
pageSubtitle: "Financial overview and cash flow management",
|
|
43418
|
-
summary: "Summary",
|
|
43419
|
-
payables: "Accounts Payable",
|
|
43420
|
-
payablesNew: "New",
|
|
43421
|
-
payablesList: "List",
|
|
43422
|
-
payablesRecurring: "Recurring Expenses",
|
|
43423
|
-
receivables: "Accounts Receivable",
|
|
43424
|
-
receivablesNew: "New",
|
|
43425
|
-
receivablesList: "List",
|
|
43426
|
-
cashRegisters: "Cash Registers",
|
|
43427
|
-
statements: "Statements",
|
|
43428
|
-
commissions: "Commissions",
|
|
43429
|
-
commissionsOverview: "Overview",
|
|
43430
|
-
commissionsRules: "Rules",
|
|
43431
|
-
cards: "Cards",
|
|
43432
|
-
goals: "Metas",
|
|
43433
|
-
giftCards: "Gift Cards",
|
|
43434
|
-
cardsOverview: "Overview",
|
|
43435
|
-
cardsReconciliation: "Reconciliation",
|
|
43436
|
-
notasFiscais: "Fiscal Invoices"
|
|
43437
|
-
};
|
|
43438
|
-
var DEFAULT_CURRENCY = { code: "BRL", locale: "pt-BR", symbol: "R$" };
|
|
43439
44706
|
var FINANCIAL_NAV_FEATURES = {
|
|
43440
44707
|
reconciliation: "fin_reconciliation",
|
|
43441
44708
|
cards: "fin_cards",
|
|
@@ -43445,55 +44712,6 @@ var FINANCIAL_NAV_FEATURES = {
|
|
|
43445
44712
|
payables: "fin_payables",
|
|
43446
44713
|
"cash-registers": "fin_cash_registers"
|
|
43447
44714
|
};
|
|
43448
|
-
var DEFAULT_ITEM_TYPES = [
|
|
43449
|
-
{ value: "service", label: "Service", icon: "Briefcase" },
|
|
43450
|
-
{ value: "product", label: "Product", icon: "Package" },
|
|
43451
|
-
{ value: "other", label: "Other", icon: "MoreHorizontal" }
|
|
43452
|
-
];
|
|
43453
|
-
function resolveConfig(options) {
|
|
43454
|
-
return {
|
|
43455
|
-
modules: {
|
|
43456
|
-
payables: options?.modules?.payables !== false,
|
|
43457
|
-
receivables: options?.modules?.receivables !== false,
|
|
43458
|
-
cashRegisters: options?.modules?.cashRegisters !== false,
|
|
43459
|
-
statements: options?.modules?.statements !== false,
|
|
43460
|
-
// Era opt-in porque a comissão era um cadastro que nada lia: o item de
|
|
43461
|
-
// menu só podia levar a uma tela vazia. Desde 009 existe apuração e desde
|
|
43462
|
-
// 012 existe borderô, e a tela mostra o que eles produziram — então o
|
|
43463
|
-
// motivo do opt-in acabou junto com a tela vazia.
|
|
43464
|
-
commissions: options?.modules?.commissions !== false,
|
|
43465
|
-
cards: options?.modules?.cards !== false,
|
|
43466
|
-
goals: options?.modules?.goals !== false,
|
|
43467
|
-
// Ligado como as outras: quando a loja não está instalada a própria tela
|
|
43468
|
-
// diz isso, então o item de menu nunca leva a um beco.
|
|
43469
|
-
giftCards: options?.modules?.giftCards !== false,
|
|
43470
|
-
// Opt-in: only shown when an app explicitly enables reconciliation (bank connector present).
|
|
43471
|
-
reconciliation: options?.modules?.reconciliation === true,
|
|
43472
|
-
// Opt-in: only shown when an app explicitly enables fiscal-invoice tracking.
|
|
43473
|
-
// LIGADA como as outras (`!== false`), e não opt-in. Estava em
|
|
43474
|
-
// `=== true`: quem não passasse a opção nunca via a tela, e o V1 tem
|
|
43475
|
-
// Notas Fiscais no menu do Financeiro desde sempre. A tela se explica
|
|
43476
|
-
// sozinha quando o provider não expõe notas, então ligá-la não deixa
|
|
43477
|
-
// ninguém diante de um beco.
|
|
43478
|
-
notasFiscais: options?.modules?.notasFiscais !== false
|
|
43479
|
-
},
|
|
43480
|
-
labels: { ...DEFAULT_LABELS, ...options?.labels },
|
|
43481
|
-
currency: { ...DEFAULT_CURRENCY, ...options?.currency },
|
|
43482
|
-
moduleNav: options?.moduleNav,
|
|
43483
|
-
itemTypes: options?.itemTypes ?? DEFAULT_ITEM_TYPES,
|
|
43484
|
-
enableServiceExecution: options?.enableServiceExecution ?? false,
|
|
43485
|
-
contactEntity: {
|
|
43486
|
-
archetypeKind: options?.contactEntity?.archetypeKind ?? "client",
|
|
43487
|
-
label: options?.contactEntity?.label ?? "Contact"
|
|
43488
|
-
},
|
|
43489
|
-
locations: options?.locations ?? [],
|
|
43490
|
-
entityLookups: options?.entityLookups ?? {},
|
|
43491
|
-
contactLookup: options?.contactLookup,
|
|
43492
|
-
fiscalProvider: options?.fiscalProvider,
|
|
43493
|
-
onBookingClick: options?.onBookingClick,
|
|
43494
|
-
navFeatureMap: { ...options?.navFeatureMap }
|
|
43495
|
-
};
|
|
43496
|
-
}
|
|
43497
44715
|
function createFinancialPlugin(options) {
|
|
43498
44716
|
const config = resolveConfig(options);
|
|
43499
44717
|
registerTranslations(financialLocales);
|