@fayz-ai/plugin-fiscal-br 0.3.5 → 0.3.7

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/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import { recordRailFields, RECORD_GROUP, activeFacet, connectionSettings, connectionStatus, definePlugin, createConnectionStore, settingsStatus, getSupabaseClientOptional } from '@fayz-ai/core';
2
- import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
3
- import { Inbox, Loader2, UploadCloud, Trash2, FileDown, Gift, RefreshCw, Check, Plus, AlertTriangle, X, ShieldCheck, FileText, ExternalLink } from 'lucide-react';
4
- import { PageHeaderActions, Button, Card, ListFilterBar, Skeleton, SubpageHeader, EmBreve, SearchSelect, Input, DatePicker, Hint, ComingSoon } from '@fayz-ai/ui';
2
+ import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
3
+ import { Trash2, Inbox, Loader2, UploadCloud, FileDown, Gift, RefreshCw, AlertTriangle, ChevronRight, Check, Plus, X, ShieldCheck, FileText, ExternalLink } from 'lucide-react';
4
+ import { useFilterDraft, Button, PageHeaderActions, Card, FilterPanel, FilterPanelField, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, SearchField, DataTable, SubpageHeader, Skeleton, EmBreve, SearchSelect, Input, DatePicker, Hint, ListFilterBar, ComingSoon } from '@fayz-ai/ui';
5
5
  export { ComingSoon, EmBreve } from '@fayz-ai/ui';
6
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
6
+ import { useUnitFilter } from '@fayz-ai/admin';
7
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
8
 
8
9
  // src/index.ts
9
10
  var PLUGNOTAS_CONNECTOR_ID = "plugnotas";
@@ -3289,8 +3290,11 @@ function rowToNote(row) {
3289
3290
  };
3290
3291
  }
3291
3292
  async function listInboxNotes(options = {}) {
3292
- const { filter = "all", search, limit = 200 } = options;
3293
+ const { filter = "all", search, unitId, from, to, limit = 200 } = options;
3293
3294
  let query = sb().from("plg_fiscalbr_inbox_documents").select(NOTE_COLUMNS);
3295
+ if (unitId) query = query.eq("unit_id", unitId);
3296
+ if (from) query = query.or(`issue_date.gte.${from},issue_date.is.null`);
3297
+ if (to) query = query.or(`issue_date.lte.${to},issue_date.is.null`);
3294
3298
  if (filter === "stocked") query = query.not("stocked_at", "is", null);
3295
3299
  if (filter === "pending") query = query.is("stocked_at", null);
3296
3300
  if (filter === "rejected") query = query.eq("status", "rejected");
@@ -3489,13 +3493,15 @@ async function listMeasurementUnits() {
3489
3493
  }));
3490
3494
  }
3491
3495
  async function listChartAccounts() {
3492
- const { data, error } = await sb().from("plg_financial_chart_of_accounts").select("id, code, name, kind, is_active").order("code");
3496
+ const { data, error } = await sb().from("plg_financial_chart_of_accounts").select("id, code, name, account_type, node_type, parent_id, is_active").order("code");
3493
3497
  if (error) throw error;
3494
- return (data ?? []).filter((a) => a.is_active !== false).map((a) => ({
3498
+ const rows = (data ?? []).filter((a) => a.is_active !== false);
3499
+ const parents = new Set(rows.map((a) => a.parent_id).filter(Boolean).map(String));
3500
+ return rows.filter((a) => a.node_type !== "group" && !parents.has(String(a.id))).map((a) => ({
3495
3501
  id: String(a.id),
3496
3502
  code: a.code ?? "",
3497
3503
  name: a.name ?? "",
3498
- kind: a.kind ?? null
3504
+ kind: a.account_type ?? null
3499
3505
  }));
3500
3506
  }
3501
3507
  async function listStockLocations() {
@@ -3521,22 +3527,53 @@ var cnpj = (digits2) => {
3521
3527
  if (d.length !== 14) return digits2 ?? "\u2014";
3522
3528
  return `${d.slice(0, 2)}.${d.slice(2, 5)}.${d.slice(5, 8)}/${d.slice(8, 12)}-${d.slice(12)}`;
3523
3529
  };
3530
+ var CAMPO = "h-9 w-full rounded-md border border-input bg-background px-2 text-sm";
3531
+ var STATE_OPTIONS = [
3532
+ { value: "all", label: "Todos" },
3533
+ { value: "pending", label: "Estoque pendente" },
3534
+ { value: "stocked", label: "Estocado" },
3535
+ { value: "to_manifest", label: "A manifestar" },
3536
+ { value: "rejected", label: "Recusada" }
3537
+ ];
3538
+ var isoDay = (d) => d.toISOString().slice(0, 10);
3524
3539
  function InboundNotesView({ onOpenNote, onOpenManifest }) {
3525
3540
  const [notes, setNotes] = useState([]);
3526
3541
  const [loading, setLoading] = useState(true);
3527
- const [filter, setFilter] = useState("all");
3542
+ const [search, setSearch] = useState("");
3528
3543
  const [busy, setBusy] = useState(false);
3529
3544
  const [report, setReport] = useState(null);
3530
3545
  const [dragging, setDragging] = useState(false);
3531
3546
  const fileInput = useRef(null);
3547
+ const unidades = useUnitFilter({ scope: "list" });
3548
+ const { draft, applied, patch, setDraft, dirty, apply, reset } = useFilterDraft({
3549
+ filter: "all",
3550
+ unitId: unidades.value ?? "",
3551
+ // Sem período por padrão: o V1 abre em «Todos os registros», e uma janela
3552
+ // de 30 dias esconderia justamente a nota velha que nunca foi estocada.
3553
+ from: "",
3554
+ to: ""
3555
+ });
3556
+ const ancora = unidades.value ?? "";
3557
+ const ancoraAnterior = useRef(ancora);
3558
+ useEffect(() => {
3559
+ if (ancoraAnterior.current === ancora) return;
3560
+ ancoraAnterior.current = ancora;
3561
+ setDraft((atual) => ({ ...atual, unitId: ancora }));
3562
+ apply();
3563
+ }, [ancora, setDraft, apply]);
3532
3564
  const reload = useCallback(async () => {
3533
3565
  setLoading(true);
3534
3566
  try {
3535
- setNotes(await listInboxNotes({ filter }));
3567
+ setNotes(await listInboxNotes({
3568
+ filter: applied.filter,
3569
+ unitId: applied.unitId || void 0,
3570
+ from: applied.from || void 0,
3571
+ to: applied.to || void 0
3572
+ }));
3536
3573
  } finally {
3537
3574
  setLoading(false);
3538
3575
  }
3539
- }, [filter]);
3576
+ }, [applied]);
3540
3577
  useEffect(() => {
3541
3578
  void reload();
3542
3579
  }, [reload]);
@@ -3570,19 +3607,112 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
3570
3607
  await rejectNote(note.id, "Recusada na lista de entrada");
3571
3608
  await reload();
3572
3609
  }, [reload]);
3573
- const filters = useMemo(() => [{
3574
- id: "estado",
3575
- label: "Estado",
3576
- allLabel: "Todos",
3577
- value: filter === "all" ? void 0 : filter,
3578
- onChange: (value) => setFilter(value ?? "all"),
3579
- options: [
3580
- { value: "pending", label: "Estoque pendente" },
3581
- { value: "stocked", label: "Estocado" },
3582
- { value: "to_manifest", label: "A manifestar" },
3583
- { value: "rejected", label: "Recusada" }
3584
- ]
3585
- }], [filter]);
3610
+ const activeCount = (applied.filter === "all" ? 0 : 1) + (applied.unitId ? 1 : 0) + (applied.from || applied.to ? 1 : 0);
3611
+ const limpar = useCallback(() => {
3612
+ reset({ filter: "all", unitId: ancora, from: "", to: "" });
3613
+ }, [reset, ancora]);
3614
+ const setFrom = (from) => patch({ from, to: draft.to && from > draft.to ? from : draft.to });
3615
+ const setTo = (to) => patch({ to, from: draft.from && to < draft.from ? to : draft.from });
3616
+ const query = search.trim().toLowerCase();
3617
+ const shown = useMemo(() => {
3618
+ if (!query) return notes;
3619
+ const digits2 = query.replace(/\D/g, "");
3620
+ return notes.filter((note) => {
3621
+ if (`${note.number} ${note.series} ${note.supplierName}`.toLowerCase().includes(query)) return true;
3622
+ if (digits2.length < 2) return false;
3623
+ if ((note.supplierCnpj ?? "").replace(/\D/g, "").includes(digits2)) return true;
3624
+ if ((note.accessKey ?? "").includes(digits2)) return true;
3625
+ return day(note.issueDate).replace(/\D/g, "").includes(digits2);
3626
+ });
3627
+ }, [notes, query]);
3628
+ const columns = useMemo(() => [
3629
+ {
3630
+ id: "issueDate",
3631
+ header: "Emiss\xE3o",
3632
+ accessorFn: (note) => note.issueDate ?? "",
3633
+ cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "tabular-nums text-muted-foreground", children: day(row.original.issueDate) })
3634
+ },
3635
+ {
3636
+ id: "number",
3637
+ header: "N\xBA",
3638
+ accessorFn: (note) => Number(note.number) || note.number,
3639
+ cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "tabular-nums font-medium", children: row.original.number })
3640
+ },
3641
+ {
3642
+ id: "supplier",
3643
+ header: "Fornecedor",
3644
+ accessorFn: (note) => note.supplierName,
3645
+ cell: ({ row }) => /* @__PURE__ */ jsxs("div", { children: [
3646
+ /* @__PURE__ */ jsx("p", { className: "font-medium leading-tight", children: row.original.supplierName || "\u2014" }),
3647
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground tabular-nums", children: cnpj(row.original.supplierCnpj) })
3648
+ ] })
3649
+ },
3650
+ {
3651
+ id: "items",
3652
+ header: () => /* @__PURE__ */ jsx("span", { className: "block text-right", children: "Itens" }),
3653
+ accessorFn: (note) => note.itemCount,
3654
+ cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "block text-right tabular-nums text-muted-foreground", children: row.original.itemCount })
3655
+ },
3656
+ {
3657
+ id: "reconciled",
3658
+ header: () => /* @__PURE__ */ jsx("span", { className: "block text-center", children: "Concilia\xE7\xE3o" }),
3659
+ accessorFn: (note) => note.itemCount > 0 ? note.reconciledCount / note.itemCount : -1,
3660
+ cell: ({ row }) => /* @__PURE__ */ jsx("div", { className: "text-center", children: /* @__PURE__ */ jsx(ReconcileBadge, { done: row.original.reconciledCount, total: row.original.itemCount }) })
3661
+ },
3662
+ {
3663
+ id: "total",
3664
+ header: () => /* @__PURE__ */ jsx("span", { className: "block text-right", children: "Valor" }),
3665
+ accessorFn: (note) => note.total,
3666
+ cell: ({ row }) => {
3667
+ const note = row.original;
3668
+ return /* @__PURE__ */ jsxs("div", { className: "text-right tabular-nums", children: [
3669
+ money(note.total),
3670
+ note.instalmentCount > 0 && Math.abs(note.instalmentTotal - note.total) > 0.01 && /* @__PURE__ */ jsxs("p", { className: "text-[11px] text-warning-soft-foreground", children: [
3671
+ "parcelas: ",
3672
+ money(note.instalmentTotal)
3673
+ ] })
3674
+ ] });
3675
+ }
3676
+ },
3677
+ {
3678
+ id: "stock",
3679
+ header: () => /* @__PURE__ */ jsx("span", { className: "block text-center", children: "Estoque" }),
3680
+ accessorFn: (note) => note.stockedAt ? 2 : note.status === "rejected" ? 0 : 1,
3681
+ cell: ({ row }) => /* @__PURE__ */ jsx("div", { className: "text-center", children: /* @__PURE__ */ jsx(StockBadge, { stockedAt: row.original.stockedAt, status: row.original.status }) })
3682
+ },
3683
+ {
3684
+ id: "actions",
3685
+ header: () => /* @__PURE__ */ jsx("span", { className: "block text-right", children: "A\xE7\xF5es" }),
3686
+ enableSorting: false,
3687
+ cell: ({ row }) => {
3688
+ const note = row.original;
3689
+ return /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-1", children: [
3690
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: note.stockedAt ? "ghost" : "outline", onClick: () => onOpenNote(note.id), children: note.stockedAt ? "Ver" : "Conciliar" }),
3691
+ !note.stockedAt && !note.invoiceId && /* @__PURE__ */ jsx(
3692
+ Button,
3693
+ {
3694
+ size: "sm",
3695
+ variant: "ghost",
3696
+ "aria-label": `Recusar a nota ${note.number}`,
3697
+ onClick: () => {
3698
+ void remove(note);
3699
+ },
3700
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
3701
+ }
3702
+ )
3703
+ ] });
3704
+ }
3705
+ }
3706
+ ], [onOpenNote, remove]);
3707
+ const emptyMessage = query || activeCount > 0 ? /* @__PURE__ */ jsxs("div", { children: [
3708
+ /* @__PURE__ */ jsx(Inbox, { className: "mx-auto h-8 w-8 text-muted-foreground", "aria-hidden": true }),
3709
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm font-medium", children: "Nenhuma nota encontrada" }),
3710
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Tente outro n\xFAmero, fornecedor ou CNPJ." })
3711
+ ] }) : /* @__PURE__ */ jsxs("div", { children: [
3712
+ /* @__PURE__ */ jsx(Inbox, { className: "mx-auto h-8 w-8 text-muted-foreground", "aria-hidden": true }),
3713
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm font-medium", children: "Nenhuma nota de entrada" }),
3714
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "As notas chegam da SEFAZ pelo Manifesto, ou voc\xEA arrasta o XML aqui." })
3715
+ ] });
3586
3716
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
3587
3717
  /* @__PURE__ */ jsxs(PageHeaderActions, { children: [
3588
3718
  onOpenManifest && /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: onOpenManifest, children: [
@@ -3645,56 +3775,64 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
3645
3775
  ] }, r.file)),
3646
3776
  /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => setReport(null), children: "Fechar" })
3647
3777
  ] }),
3648
- /* @__PURE__ */ jsx(ListFilterBar, { filters, onClear: () => setFilter("all"), activeCount: filter === "all" ? 0 : 1 }),
3649
- loading ? /* @__PURE__ */ jsx("div", { className: "space-y-2", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-14 w-full" }, i)) }) : notes.length === 0 ? /* @__PURE__ */ jsxs(Card, { className: "p-10 text-center", children: [
3650
- /* @__PURE__ */ jsx(Inbox, { className: "mx-auto h-8 w-8 text-muted-foreground", "aria-hidden": true }),
3651
- /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm font-medium", children: "Nenhuma nota de entrada" }),
3652
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "As notas chegam da SEFAZ pelo Manifesto, ou voc\xEA arrasta o XML aqui." })
3653
- ] }) : /* @__PURE__ */ jsx(Card, { className: "overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
3654
- /* @__PURE__ */ jsx("thead", { className: "border-b bg-muted/40 text-xs text-muted-foreground", children: /* @__PURE__ */ jsxs("tr", { children: [
3655
- /* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-medium", children: "Emiss\xE3o" }),
3656
- /* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-medium", children: "N\xBA" }),
3657
- /* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-medium", children: "Fornecedor" }),
3658
- /* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium", children: "Itens" }),
3659
- /* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-center font-medium", children: "Concilia\xE7\xE3o" }),
3660
- /* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium", children: "Valor" }),
3661
- /* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-center font-medium", children: "Estoque" }),
3662
- /* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium", children: "A\xE7\xF5es" })
3663
- ] }) }),
3664
- /* @__PURE__ */ jsx("tbody", { children: notes.map((note) => /* @__PURE__ */ jsxs("tr", { className: "border-b last:border-0 hover:bg-muted/30", children: [
3665
- /* @__PURE__ */ jsx("td", { className: "px-3 py-2 tabular-nums text-muted-foreground", children: day(note.issueDate) }),
3666
- /* @__PURE__ */ jsx("td", { className: "px-3 py-2 tabular-nums font-medium", children: note.number }),
3667
- /* @__PURE__ */ jsxs("td", { className: "px-3 py-2", children: [
3668
- /* @__PURE__ */ jsx("p", { className: "font-medium leading-tight", children: note.supplierName || "\u2014" }),
3669
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground tabular-nums", children: cnpj(note.supplierCnpj) })
3670
- ] }),
3671
- /* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right tabular-nums text-muted-foreground", children: note.itemCount }),
3672
- /* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-center", children: /* @__PURE__ */ jsx(ReconcileBadge, { done: note.reconciledCount, total: note.itemCount }) }),
3673
- /* @__PURE__ */ jsxs("td", { className: "px-3 py-2 text-right tabular-nums", children: [
3674
- money(note.total),
3675
- note.instalmentCount > 0 && Math.abs(note.instalmentTotal - note.total) > 0.01 && /* @__PURE__ */ jsxs("p", { className: "text-[11px] text-warning-soft-foreground", children: [
3676
- "parcelas: ",
3677
- money(note.instalmentTotal)
3678
- ] })
3679
- ] }),
3680
- /* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-center", children: /* @__PURE__ */ jsx(StockBadge, { stockedAt: note.stockedAt, status: note.status }) }),
3681
- /* @__PURE__ */ jsx("td", { className: "px-3 py-2", children: /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-1", children: [
3682
- /* @__PURE__ */ jsx(Button, { size: "sm", variant: note.stockedAt ? "ghost" : "outline", onClick: () => onOpenNote(note.id), children: note.stockedAt ? "Ver" : "Conciliar" }),
3683
- !note.stockedAt && !note.invoiceId && /* @__PURE__ */ jsx(
3684
- Button,
3778
+ /* @__PURE__ */ jsxs(
3779
+ FilterPanel,
3780
+ {
3781
+ title: "Filtros",
3782
+ dirty,
3783
+ onApply: apply,
3784
+ applyLabel: "Buscar",
3785
+ pendingTitle: "Filtros n\xE3o aplicados",
3786
+ pendingHint: "A lista ainda mostra o recorte anterior.",
3787
+ onClear: activeCount > 0 ? limpar : void 0,
3788
+ clearLabel: "Limpar",
3789
+ busy: loading,
3790
+ children: [
3791
+ unidades.visible && /* @__PURE__ */ jsx(FilterPanelField, { label: unidades.label, children: /* @__PURE__ */ jsxs(
3792
+ Select,
3685
3793
  {
3686
- size: "sm",
3687
- variant: "ghost",
3688
- "aria-label": `Recusar a nota ${note.number}`,
3689
- onClick: () => {
3690
- void remove(note);
3691
- },
3692
- children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
3794
+ value: draft.unitId || "__all__",
3795
+ onValueChange: (v) => patch({ unitId: v === "__all__" ? "" : v }),
3796
+ children: [
3797
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
3798
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
3799
+ /* @__PURE__ */ jsx(SelectItem, { value: "__all__", children: unidades.allLabel }),
3800
+ unidades.options.map((u) => /* @__PURE__ */ jsx(SelectItem, { value: u.id, children: u.name }, u.id))
3801
+ ] })
3802
+ ]
3693
3803
  }
3694
- )
3695
- ] }) })
3696
- ] }, note.id)) })
3697
- ] }) }) })
3804
+ ) }),
3805
+ /* @__PURE__ */ jsx(FilterPanelField, { label: "Estado", children: /* @__PURE__ */ jsxs(Select, { value: draft.filter, onValueChange: (v) => patch({ filter: v }), children: [
3806
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
3807
+ /* @__PURE__ */ jsx(SelectContent, { children: STATE_OPTIONS.map((o) => /* @__PURE__ */ jsx(SelectItem, { value: o.value, children: o.label }, o.value)) })
3808
+ ] }) }),
3809
+ /* @__PURE__ */ jsx(FilterPanelField, { label: "Emiss\xE3o de", children: /* @__PURE__ */ jsx("input", { type: "date", value: draft.from, max: isoDay(/* @__PURE__ */ new Date()), onChange: (e) => setFrom(e.target.value), className: CAMPO }) }),
3810
+ /* @__PURE__ */ jsx(FilterPanelField, { label: "Emiss\xE3o at\xE9", children: /* @__PURE__ */ jsx("input", { type: "date", value: draft.to, max: isoDay(/* @__PURE__ */ new Date()), onChange: (e) => setTo(e.target.value), className: CAMPO }) })
3811
+ ]
3812
+ }
3813
+ ),
3814
+ /* @__PURE__ */ jsxs(Card, { className: "overflow-hidden", children: [
3815
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2 border-b border-border px-3 py-2", children: /* @__PURE__ */ jsx("div", { className: "min-w-[10rem] flex-1", children: /* @__PURE__ */ jsx(
3816
+ SearchField,
3817
+ {
3818
+ value: search,
3819
+ onChange: setSearch,
3820
+ placeholder: "Buscar por n\xFAmero, fornecedor, CNPJ\u2026",
3821
+ "data-testid": "inbound-notes-search"
3822
+ }
3823
+ ) }) }),
3824
+ /* @__PURE__ */ jsx(
3825
+ DataTable,
3826
+ {
3827
+ variant: "bare",
3828
+ columns,
3829
+ data: loading ? [] : shown,
3830
+ loading,
3831
+ getRowId: (note) => note.id,
3832
+ emptyMessage
3833
+ }
3834
+ )
3835
+ ] })
3698
3836
  ] });
3699
3837
  }
3700
3838
  function ReconcileBadge({ done, total }) {
@@ -3761,6 +3899,9 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice }) {
3761
3899
  const [locations, setLocations] = useState([]);
3762
3900
  const [saving, setSaving] = useState(false);
3763
3901
  const [accepting, setAccepting] = useState(false);
3902
+ const [openIds, setOpenIds] = useState([]);
3903
+ const [checked, setChecked] = useState({});
3904
+ const [registryWarning, setRegistryWarning] = useState(null);
3764
3905
  const [error, setError] = useState(null);
3765
3906
  const [accepted, setAccepted] = useState(null);
3766
3907
  const load = useCallback(async () => {
@@ -3768,16 +3909,31 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice }) {
3768
3909
  try {
3769
3910
  const detail = await getInboxNote(noteId);
3770
3911
  setNote(detail);
3912
+ const failed = [];
3771
3913
  const [u, a, l] = await Promise.all([
3772
- listMeasurementUnits().catch(() => []),
3773
- listChartAccounts().catch(() => []),
3774
- listStockLocations().catch(() => [])
3914
+ listMeasurementUnits().catch(() => {
3915
+ failed.push("unidades");
3916
+ return [];
3917
+ }),
3918
+ listChartAccounts().catch(() => {
3919
+ failed.push("plano de contas");
3920
+ return [];
3921
+ }),
3922
+ listStockLocations().catch(() => {
3923
+ failed.push("locais de estoque");
3924
+ return [];
3925
+ })
3775
3926
  ]);
3776
3927
  setUnits(u);
3777
3928
  setAccounts(a);
3778
3929
  setLocations(l);
3930
+ setRegistryWarning(failed.length ? `N\xE3o consegui carregar ${failed.join(", ")}. Estes seletores v\xE3o aparecer vazios.` : null);
3779
3931
  if (detail) {
3780
3932
  setDrafts(Object.fromEntries(detail.items.map((i) => [i.id, draftOf(i)])));
3933
+ const done = Object.fromEntries(detail.items.filter((i) => i.reconciled).map((i) => [i.id, true]));
3934
+ setChecked((prev) => ({ ...prev, ...done }));
3935
+ const pending = detail.items.find((i) => !i.reconciled);
3936
+ setOpenIds(pending ? [pending.id] : []);
3781
3937
  setInstalments(detail.instalments.map((d) => ({
3782
3938
  index: d.index,
3783
3939
  number: d.number,
@@ -3834,6 +3990,17 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice }) {
3834
3990
  const missingType = decisions.filter((d) => !d.entryType).length;
3835
3991
  const instalmentSum = instalments.reduce((acc, d) => acc + (Number(d.amount) || 0), 0);
3836
3992
  const readOnly = Boolean(note?.stockedAt || note?.invoiceId);
3993
+ const isReady = (d) => Boolean(d.entryType) && (!isStockBearing(d.entryType) || Boolean(d.productId));
3994
+ const doneCount = note ? note.items.filter((i) => checked[i.id]).length : 0;
3995
+ const toggleItem = (id) => setOpenIds((prev) => prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]);
3996
+ const confirmItem = (id) => {
3997
+ setChecked((prev) => ({ ...prev, [id]: true }));
3998
+ setOpenIds((prev) => {
3999
+ const rest = prev.filter((x) => x !== id);
4000
+ const next = note?.items.find((i) => i.id !== id && !checked[i.id]);
4001
+ return next && !rest.includes(next.id) ? [...rest, next.id] : rest;
4002
+ });
4003
+ };
3837
4004
  const save = async () => {
3838
4005
  if (!note) return;
3839
4006
  setSaving(true);
@@ -3876,8 +4043,29 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice }) {
3876
4043
  if (loading) {
3877
4044
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
3878
4045
  /* @__PURE__ */ jsx(SubpageHeader, { title: "Concilia\xE7\xE3o", onBack, parentLabel: "Entrada de Notas" }),
3879
- /* @__PURE__ */ jsx(Skeleton, { className: "h-24 w-full" }),
3880
- /* @__PURE__ */ jsx(Skeleton, { className: "h-48 w-full" })
4046
+ /* @__PURE__ */ jsxs(Card, { className: "p-4", children: [
4047
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-32" }),
4048
+ /* @__PURE__ */ jsx("div", { className: "mt-3 grid gap-x-6 gap-y-3 sm:grid-cols-2 lg:grid-cols-3", children: [0, 1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ jsxs("div", { children: [
4049
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-20" }),
4050
+ /* @__PURE__ */ jsx(Skeleton, { className: "mt-1.5 h-4 w-36" })
4051
+ ] }, i)) })
4052
+ ] }),
4053
+ /* @__PURE__ */ jsxs(Card, { className: "p-4", children: [
4054
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-36" }),
4055
+ /* @__PURE__ */ jsx("div", { className: "mt-3 grid gap-4 sm:grid-cols-2", children: [0, 1].map((i) => /* @__PURE__ */ jsxs("div", { children: [
4056
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-28" }),
4057
+ /* @__PURE__ */ jsx(Skeleton, { className: "mt-1.5 h-9 w-full" })
4058
+ ] }, i)) })
4059
+ ] }),
4060
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-24" }),
4061
+ /* @__PURE__ */ jsx("div", { className: "space-y-2", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsxs(Card, { className: "flex items-center gap-3 px-4 py-3", children: [
4062
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-4 rounded-full" }),
4063
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
4064
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-2/3" }),
4065
+ /* @__PURE__ */ jsx(Skeleton, { className: "mt-1.5 h-3 w-1/3" })
4066
+ ] }),
4067
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-20" })
4068
+ ] }, i)) })
3881
4069
  ] });
3882
4070
  }
3883
4071
  if (!note) {
@@ -3954,6 +4142,10 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice }) {
3954
4142
  ] }),
3955
4143
  /* @__PURE__ */ jsxs(Card, { className: "p-4", children: [
3956
4144
  /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Padr\xF5es desta nota" }),
4145
+ registryWarning && /* @__PURE__ */ jsx("div", { className: "mt-2 rounded-md border border-warning bg-warning-soft px-3 py-2", children: /* @__PURE__ */ jsxs("p", { className: "text-xs text-warning-soft-foreground", children: [
4146
+ /* @__PURE__ */ jsx(AlertTriangle, { className: "mr-1 inline h-3.5 w-3.5", "aria-hidden": true }),
4147
+ registryWarning
4148
+ ] }) }),
3957
4149
  /* @__PURE__ */ jsxs("div", { className: "mt-3 grid gap-4 sm:grid-cols-2", children: [
3958
4150
  /* @__PURE__ */ jsxs("label", { className: "block", children: [
3959
4151
  /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Plano de contas padr\xE3o" }),
@@ -3968,6 +4160,7 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice }) {
3968
4160
  placeholder: "Escolher plano de contas",
3969
4161
  noResultsLabel: "Nenhum plano de contas",
3970
4162
  searchingLabel: "Buscando\u2026",
4163
+ clearLabel: "Limpar",
3971
4164
  disabled: readOnly
3972
4165
  }
3973
4166
  ),
@@ -3986,6 +4179,7 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice }) {
3986
4179
  placeholder: "Escolher local",
3987
4180
  noResultsLabel: "Nenhum local de estoque",
3988
4181
  searchingLabel: "Buscando\u2026",
4182
+ clearLabel: "Limpar",
3989
4183
  disabled: readOnly
3990
4184
  }
3991
4185
  ),
@@ -3993,14 +4187,38 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice }) {
3993
4187
  ] })
3994
4188
  ] })
3995
4189
  ] }),
3996
- /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
3997
- /* @__PURE__ */ jsxs("h3", { className: "text-sm font-semibold", children: [
3998
- "Itens ",
3999
- /* @__PURE__ */ jsxs("span", { className: "font-normal text-muted-foreground", children: [
4000
- "(",
4190
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
4191
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
4192
+ /* @__PURE__ */ jsxs("h3", { className: "text-sm font-semibold", children: [
4193
+ "Itens ",
4194
+ /* @__PURE__ */ jsxs("span", { className: "font-normal text-muted-foreground", children: [
4195
+ "(",
4196
+ note.items.length,
4197
+ ")"
4198
+ ] })
4199
+ ] }),
4200
+ /* @__PURE__ */ jsx("div", { className: "h-1.5 min-w-[6rem] flex-1 overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ jsx(
4201
+ "div",
4202
+ {
4203
+ className: "h-full rounded-full bg-primary transition-all",
4204
+ style: { width: `${note.items.length ? doneCount / note.items.length * 100 : 0}%` }
4205
+ }
4206
+ ) }),
4207
+ /* @__PURE__ */ jsxs("span", { className: "text-xs tabular-nums text-muted-foreground", children: [
4208
+ doneCount,
4209
+ "/",
4001
4210
  note.items.length,
4002
- ")"
4003
- ] })
4211
+ " conferidos"
4212
+ ] }),
4213
+ /* @__PURE__ */ jsx(
4214
+ Button,
4215
+ {
4216
+ variant: "ghost",
4217
+ size: "sm",
4218
+ onClick: () => setOpenIds(openIds.length ? [] : note.items.map((i) => i.id)),
4219
+ children: openIds.length ? "Recolher todos" : "Abrir todos"
4220
+ }
4221
+ )
4004
4222
  ] }),
4005
4223
  note.items.map((item) => {
4006
4224
  const d = drafts[item.id] ?? draftOf(item);
@@ -4009,184 +4227,241 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice }) {
4009
4227
  const stockQty = item.quantity * factor - shortage;
4010
4228
  const unitCost = factor > 0 ? item.unitPrice / factor : item.unitPrice;
4011
4229
  const bearing = isStockBearing(d.entryType);
4012
- return /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [
4013
- /* @__PURE__ */ jsxs("div", { children: [
4014
- /* @__PURE__ */ jsx("p", { className: "text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: "Item na nota" }),
4015
- /* @__PURE__ */ jsxs("p", { className: "mt-1 font-medium leading-tight", children: [
4016
- item.itemNumber,
4017
- ". ",
4018
- item.description
4019
- ] }),
4020
- /* @__PURE__ */ jsxs("dl", { className: "mt-2 grid grid-cols-2 gap-x-4 gap-y-1 text-xs sm:grid-cols-4", children: [
4021
- /* @__PURE__ */ jsx(Field, { small: true, label: "Qtd", value: `${qty(item.quantity)} ${item.unit ?? ""}` }),
4022
- /* @__PURE__ */ jsx(Field, { small: true, label: "Vl. unit.", value: money2(item.unitPrice) }),
4023
- /* @__PURE__ */ jsx(Field, { small: true, label: "Total", value: money2(item.total) }),
4024
- /* @__PURE__ */ jsx(Field, { small: true, label: "C\xF3d", value: item.supplierProductCode || "\u2014", mono: true }),
4025
- /* @__PURE__ */ jsx(Field, { small: true, label: "EAN", value: item.eanBarcode ?? "\u2014", mono: true }),
4026
- /* @__PURE__ */ jsx(Field, { small: true, label: "NCM", value: item.ncm ?? "\u2014", mono: true }),
4027
- /* @__PURE__ */ jsx(Field, { small: true, label: "CFOP", value: item.cfop ?? "\u2014", mono: true }),
4028
- /* @__PURE__ */ jsx(Field, { small: true, label: "CST/CSOSN", value: item.icmsCst ?? item.icmsCsosn ?? "\u2014", mono: true })
4029
- ] })
4030
- ] }),
4031
- /* @__PURE__ */ jsxs("div", { className: "space-y-3 lg:border-l lg:pl-4", children: [
4032
- /* @__PURE__ */ jsx("p", { className: "text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: "Item no estoque" }),
4033
- /* @__PURE__ */ jsxs("label", { className: "block", children: [
4034
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Tipo do registro" }),
4035
- /* @__PURE__ */ jsx("div", { className: "mt-1 flex flex-wrap gap-1", children: ENTRY_TYPES.map((t) => /* @__PURE__ */ jsxs(
4036
- "button",
4037
- {
4038
- type: "button",
4039
- disabled: readOnly,
4040
- onClick: () => patch(item.id, { entryType: t.value }),
4041
- className: `rounded-full border px-2.5 py-1 text-[11px] transition-colors disabled:opacity-60 ${d.entryType === t.value ? "border-primary bg-primary text-primary-foreground" : "border-input hover:bg-muted"}`,
4042
- children: [
4043
- t.label,
4044
- t.hint && /* @__PURE__ */ jsxs("span", { className: "ml-1 opacity-70", children: [
4045
- "\xB7 ",
4046
- t.hint
4047
- ] })
4048
- ]
4049
- },
4050
- t.value
4051
- )) })
4052
- ] }),
4053
- bearing && /* @__PURE__ */ jsxs(Fragment, { children: [
4054
- /* @__PURE__ */ jsxs("label", { className: "block", children: [
4055
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Produto vinculado" }),
4230
+ const open = openIds.includes(item.id);
4231
+ const done = Boolean(checked[item.id]);
4232
+ const ready = isReady(d);
4233
+ const typeLabel = ENTRY_TYPES.find((t) => t.value === d.entryType)?.label;
4234
+ return /* @__PURE__ */ jsxs(Card, { className: "overflow-hidden", children: [
4235
+ /* @__PURE__ */ jsxs(
4236
+ "button",
4237
+ {
4238
+ type: "button",
4239
+ onClick: () => toggleItem(item.id),
4240
+ "aria-expanded": open,
4241
+ className: "flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-muted/30",
4242
+ children: [
4056
4243
  /* @__PURE__ */ jsx(
4057
- SearchSelect,
4244
+ ChevronRight,
4058
4245
  {
4059
- value: d.productId ?? void 0,
4060
- displayValue: d.productLabel || void 0,
4061
- onChange: (id, option) => patch(item.id, { productId: id || null, productLabel: option?.label ?? "" }),
4062
- onSearch: async (term) => {
4063
- const found = await searchProducts(term || item.description.slice(0, 24));
4064
- return found.map((prod) => ({
4065
- id: prod.id,
4066
- label: prod.name,
4067
- subtitle: prod.sku ?? void 0
4068
- }));
4069
- },
4070
- minChars: 0,
4071
- placeholder: "Buscar produto do cat\xE1logo",
4072
- noResultsLabel: "Nenhum produto encontrado",
4073
- searchingLabel: "Buscando\u2026",
4074
- disabled: readOnly
4246
+ className: `h-4 w-4 shrink-0 text-muted-foreground transition-transform ${open ? "rotate-90" : ""}`,
4247
+ "aria-hidden": true
4075
4248
  }
4076
- )
4249
+ ),
4250
+ /* @__PURE__ */ jsx("span", { className: "w-5 shrink-0 text-xs tabular-nums text-muted-foreground", children: item.itemNumber }),
4251
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
4252
+ /* @__PURE__ */ jsx("span", { className: "block truncate text-sm font-medium leading-tight", children: item.description }),
4253
+ /* @__PURE__ */ jsxs("span", { className: "block truncate text-xs text-muted-foreground", children: [
4254
+ qty(item.quantity),
4255
+ " ",
4256
+ item.unit ?? "",
4257
+ " \xB7 ",
4258
+ money2(item.unitPrice),
4259
+ typeLabel ? ` \xB7 ${typeLabel}` : " \xB7 sem tipo"
4260
+ ] })
4261
+ ] }),
4262
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm tabular-nums", children: money2(item.total) }),
4263
+ /* @__PURE__ */ jsx(ItemState, { done, ready })
4264
+ ]
4265
+ }
4266
+ ),
4267
+ open && /* @__PURE__ */ jsxs("div", { className: "grid gap-4 border-t p-4 lg:grid-cols-2", children: [
4268
+ /* @__PURE__ */ jsxs("div", { children: [
4269
+ /* @__PURE__ */ jsx("p", { className: "text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: "Item na nota" }),
4270
+ /* @__PURE__ */ jsxs("p", { className: "mt-1 font-medium leading-tight", children: [
4271
+ item.itemNumber,
4272
+ ". ",
4273
+ item.description
4274
+ ] }),
4275
+ /* @__PURE__ */ jsxs("dl", { className: "mt-2 grid grid-cols-2 gap-x-4 gap-y-1 text-xs sm:grid-cols-4", children: [
4276
+ /* @__PURE__ */ jsx(Field, { small: true, label: "Qtd", value: `${qty(item.quantity)} ${item.unit ?? ""}` }),
4277
+ /* @__PURE__ */ jsx(Field, { small: true, label: "Vl. unit.", value: money2(item.unitPrice) }),
4278
+ /* @__PURE__ */ jsx(Field, { small: true, label: "Total", value: money2(item.total) }),
4279
+ /* @__PURE__ */ jsx(Field, { small: true, label: "C\xF3d", value: item.supplierProductCode || "\u2014", mono: true }),
4280
+ /* @__PURE__ */ jsx(Field, { small: true, label: "EAN", value: item.eanBarcode ?? "\u2014", mono: true }),
4281
+ /* @__PURE__ */ jsx(Field, { small: true, label: "NCM", value: item.ncm ?? "\u2014", mono: true }),
4282
+ /* @__PURE__ */ jsx(Field, { small: true, label: "CFOP", value: item.cfop ?? "\u2014", mono: true }),
4283
+ /* @__PURE__ */ jsx(Field, { small: true, label: "CST/CSOSN", value: item.icmsCst ?? item.icmsCsosn ?? "\u2014", mono: true })
4284
+ ] })
4285
+ ] }),
4286
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3 lg:border-l lg:pl-4", children: [
4287
+ /* @__PURE__ */ jsx("p", { className: "text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: "Item no estoque" }),
4288
+ /* @__PURE__ */ jsxs("label", { className: "block", children: [
4289
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Tipo do registro" }),
4290
+ /* @__PURE__ */ jsx("div", { className: "mt-1 flex flex-wrap gap-1", children: ENTRY_TYPES.map((t) => /* @__PURE__ */ jsxs(
4291
+ "button",
4292
+ {
4293
+ type: "button",
4294
+ disabled: readOnly,
4295
+ onClick: () => patch(item.id, { entryType: t.value }),
4296
+ className: `rounded-full border px-2.5 py-1 text-[11px] transition-colors disabled:opacity-60 ${d.entryType === t.value ? "border-primary bg-primary text-primary-foreground" : "border-input hover:bg-muted"}`,
4297
+ children: [
4298
+ t.label,
4299
+ t.hint && /* @__PURE__ */ jsxs("span", { className: "ml-1 opacity-70", children: [
4300
+ "\xB7 ",
4301
+ t.hint
4302
+ ] })
4303
+ ]
4304
+ },
4305
+ t.value
4306
+ )) })
4077
4307
  ] }),
4078
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
4308
+ bearing && /* @__PURE__ */ jsxs(Fragment, { children: [
4079
4309
  /* @__PURE__ */ jsxs("label", { className: "block", children: [
4080
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Fator de convers\xE3o" }),
4310
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Produto vinculado" }),
4081
4311
  /* @__PURE__ */ jsx(
4082
- Input,
4312
+ SearchSelect,
4083
4313
  {
4084
- value: d.conversionFactor,
4085
- onChange: (e) => patch(item.id, { conversionFactor: e.target.value }),
4086
- inputMode: "decimal",
4314
+ value: d.productId ?? void 0,
4315
+ displayValue: d.productLabel || void 0,
4316
+ onChange: (id, option) => patch(item.id, { productId: id || null, productLabel: option?.label ?? "" }),
4317
+ onSearch: async (term) => {
4318
+ const found = await searchProducts(term || item.description.slice(0, 24));
4319
+ return found.map((prod) => ({
4320
+ id: prod.id,
4321
+ label: prod.name,
4322
+ subtitle: prod.sku ?? void 0
4323
+ }));
4324
+ },
4325
+ minChars: 0,
4326
+ placeholder: "Buscar produto do cat\xE1logo",
4327
+ noResultsLabel: "Nenhum produto encontrado",
4328
+ searchingLabel: "Buscando\u2026",
4329
+ clearLabel: "Limpar",
4087
4330
  disabled: readOnly
4088
4331
  }
4089
4332
  )
4090
4333
  ] }),
4091
- /* @__PURE__ */ jsxs("label", { className: "block", children: [
4092
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Unidade de estoque" }),
4334
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
4335
+ /* @__PURE__ */ jsxs("label", { className: "block", children: [
4336
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Fator de convers\xE3o" }),
4337
+ /* @__PURE__ */ jsx(
4338
+ Input,
4339
+ {
4340
+ value: d.conversionFactor,
4341
+ onChange: (e) => patch(item.id, { conversionFactor: e.target.value }),
4342
+ inputMode: "decimal",
4343
+ disabled: readOnly
4344
+ }
4345
+ )
4346
+ ] }),
4347
+ /* @__PURE__ */ jsxs("label", { className: "block", children: [
4348
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Unidade de estoque" }),
4349
+ /* @__PURE__ */ jsx(
4350
+ SearchSelect,
4351
+ {
4352
+ onSearch: localSearch(unitOptions),
4353
+ value: d.stockUnitId ?? void 0,
4354
+ displayValue: labelOf(unitOptions, d.stockUnitId),
4355
+ onChange: (id) => patch(item.id, { stockUnitId: id || null }),
4356
+ minChars: 0,
4357
+ placeholder: "Unidade",
4358
+ noResultsLabel: "Nenhuma unidade",
4359
+ searchingLabel: "Buscando\u2026",
4360
+ clearLabel: "Limpar",
4361
+ disabled: readOnly
4362
+ }
4363
+ )
4364
+ ] })
4365
+ ] }),
4366
+ /* @__PURE__ */ jsxs("div", { className: "rounded-md bg-muted/50 px-3 py-2", children: [
4367
+ /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between text-sm", children: [
4368
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Qtd estoque (calc.)" }),
4369
+ /* @__PURE__ */ jsx("span", { className: "font-semibold tabular-nums", children: qty(stockQty) })
4370
+ ] }),
4371
+ /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between text-sm", children: [
4372
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Novo custo unit\xE1rio" }),
4373
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: money2(unitCost) })
4374
+ ] })
4375
+ ] }),
4376
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
4093
4377
  /* @__PURE__ */ jsx(
4094
- SearchSelect,
4378
+ EmBreve,
4095
4379
  {
4096
- onSearch: localSearch(unitOptions),
4097
- value: d.stockUnitId ?? void 0,
4098
- displayValue: labelOf(unitOptions, d.stockUnitId),
4099
- onChange: (id) => patch(item.id, { stockUnitId: id || null }),
4100
- minChars: 0,
4101
- placeholder: "Unidade",
4102
- noResultsLabel: "Nenhuma unidade",
4103
- searchingLabel: "Buscando\u2026",
4104
- disabled: readOnly
4380
+ label: "Criar produto daqui",
4381
+ o_que: "Cadastrar o produto a partir desta linha da nota, com nome, c\xF3digo e unidade j\xE1 preenchidos",
4382
+ quantas: "ela criou 33 produtos no V1 no meio de importa\xE7\xF5es"
4383
+ }
4384
+ ),
4385
+ /* @__PURE__ */ jsx(
4386
+ EmBreve,
4387
+ {
4388
+ label: "Lote e validade",
4389
+ o_que: "Informar lote e vencimento desta entrada \u2014 a coluna j\xE1 existe no movimento de estoque",
4390
+ quantas: "usado 5 vezes no V1; numa churrascaria \xE9 validade de carne"
4105
4391
  }
4106
4392
  )
4107
- ] })
4108
- ] }),
4109
- /* @__PURE__ */ jsxs("div", { className: "rounded-md bg-muted/50 px-3 py-2", children: [
4110
- /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between text-sm", children: [
4111
- /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Qtd estoque (calc.)" }),
4112
- /* @__PURE__ */ jsx("span", { className: "font-semibold tabular-nums", children: qty(stockQty) })
4113
4393
  ] }),
4114
- /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between text-sm", children: [
4115
- /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Novo custo unit\xE1rio" }),
4116
- /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: money2(unitCost) })
4394
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
4395
+ /* @__PURE__ */ jsxs("label", { className: "block", children: [
4396
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Modo de custo" }),
4397
+ /* @__PURE__ */ jsx("div", { className: "mt-1 flex gap-1", children: ["novo", "media"].map((mode) => /* @__PURE__ */ jsx(
4398
+ "button",
4399
+ {
4400
+ type: "button",
4401
+ disabled: readOnly,
4402
+ onClick: () => patch(item.id, { costMode: mode }),
4403
+ className: `flex-1 rounded-md border px-2 py-1 text-[11px] disabled:opacity-60 ${d.costMode === mode ? "border-primary bg-primary/10" : "border-input hover:bg-muted"}`,
4404
+ children: mode === "novo" ? "Novo" : "M\xE9dia"
4405
+ },
4406
+ mode
4407
+ )) })
4408
+ ] }),
4409
+ /* @__PURE__ */ jsxs("label", { className: "block", children: [
4410
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Falta (qtd)" }),
4411
+ /* @__PURE__ */ jsx(
4412
+ Input,
4413
+ {
4414
+ value: d.shortageQuantity,
4415
+ onChange: (e) => patch(item.id, { shortageQuantity: e.target.value }),
4416
+ inputMode: "decimal",
4417
+ placeholder: "0",
4418
+ disabled: readOnly
4419
+ }
4420
+ )
4421
+ ] })
4117
4422
  ] })
4118
4423
  ] }),
4119
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
4424
+ /* @__PURE__ */ jsxs("label", { className: "block", children: [
4425
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Plano de contas" }),
4120
4426
  /* @__PURE__ */ jsx(
4121
- EmBreve,
4427
+ SearchSelect,
4122
4428
  {
4123
- label: "Criar produto daqui",
4124
- o_que: "Cadastrar o produto a partir desta linha da nota, com nome, c\xF3digo e unidade j\xE1 preenchidos",
4125
- quantas: "ela criou 33 produtos no V1 no meio de importa\xE7\xF5es"
4429
+ onSearch: localSearch(accountOptions),
4430
+ value: d.chartAccountId ?? defaultAccount ?? void 0,
4431
+ displayValue: labelOf(accountOptions, d.chartAccountId ?? defaultAccount),
4432
+ onChange: (id) => patch(item.id, { chartAccountId: id || null }),
4433
+ minChars: 0,
4434
+ placeholder: "Herda o padr\xE3o da nota",
4435
+ noResultsLabel: "Nenhum plano de contas",
4436
+ searchingLabel: "Buscando\u2026",
4437
+ clearLabel: "Limpar",
4438
+ disabled: readOnly
4126
4439
  }
4127
- ),
4128
- /* @__PURE__ */ jsx(
4129
- EmBreve,
4440
+ )
4441
+ ] }),
4442
+ item.stockMovementId && /* @__PURE__ */ jsxs("p", { className: "text-[11px] text-success-soft-foreground", children: [
4443
+ /* @__PURE__ */ jsx(Check, { className: "mr-1 inline h-3 w-3", "aria-hidden": true }),
4444
+ "Esta linha j\xE1 deu entrada no estoque."
4445
+ ] }),
4446
+ !readOnly && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-3 border-t pt-3", children: [
4447
+ !ready && /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground", children: !d.entryType ? "Escolha o tipo do registro" : "Vincule o produto" }),
4448
+ /* @__PURE__ */ jsxs(
4449
+ Button,
4130
4450
  {
4131
- label: "Lote e validade",
4132
- o_que: "Informar lote e vencimento desta entrada \u2014 a coluna j\xE1 existe no movimento de estoque",
4133
- quantas: "usado 5 vezes no V1; numa churrascaria \xE9 validade de carne"
4451
+ size: "sm",
4452
+ variant: done ? "ghost" : "outline",
4453
+ disabled: !ready,
4454
+ onClick: () => done ? setChecked((prev) => ({ ...prev, [item.id]: false })) : confirmItem(item.id),
4455
+ children: [
4456
+ /* @__PURE__ */ jsx(Check, { className: "mr-1.5 h-4 w-4" }),
4457
+ done ? "Conferido" : "Conferir item"
4458
+ ]
4134
4459
  }
4135
4460
  )
4136
- ] }),
4137
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
4138
- /* @__PURE__ */ jsxs("label", { className: "block", children: [
4139
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Modo de custo" }),
4140
- /* @__PURE__ */ jsx("div", { className: "mt-1 flex gap-1", children: ["novo", "media"].map((mode) => /* @__PURE__ */ jsx(
4141
- "button",
4142
- {
4143
- type: "button",
4144
- disabled: readOnly,
4145
- onClick: () => patch(item.id, { costMode: mode }),
4146
- className: `flex-1 rounded-md border px-2 py-1 text-[11px] disabled:opacity-60 ${d.costMode === mode ? "border-primary bg-primary/10" : "border-input hover:bg-muted"}`,
4147
- children: mode === "novo" ? "Novo" : "M\xE9dia"
4148
- },
4149
- mode
4150
- )) })
4151
- ] }),
4152
- /* @__PURE__ */ jsxs("label", { className: "block", children: [
4153
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Falta (qtd)" }),
4154
- /* @__PURE__ */ jsx(
4155
- Input,
4156
- {
4157
- value: d.shortageQuantity,
4158
- onChange: (e) => patch(item.id, { shortageQuantity: e.target.value }),
4159
- inputMode: "decimal",
4160
- placeholder: "0",
4161
- disabled: readOnly
4162
- }
4163
- )
4164
- ] })
4165
4461
  ] })
4166
- ] }),
4167
- /* @__PURE__ */ jsxs("label", { className: "block", children: [
4168
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Plano de contas" }),
4169
- /* @__PURE__ */ jsx(
4170
- SearchSelect,
4171
- {
4172
- onSearch: localSearch(accountOptions),
4173
- value: d.chartAccountId ?? defaultAccount ?? void 0,
4174
- displayValue: labelOf(accountOptions, d.chartAccountId ?? defaultAccount),
4175
- onChange: (id) => patch(item.id, { chartAccountId: id || null }),
4176
- minChars: 0,
4177
- placeholder: "Herda o padr\xE3o da nota",
4178
- noResultsLabel: "Nenhum plano de contas",
4179
- searchingLabel: "Buscando\u2026",
4180
- disabled: readOnly
4181
- }
4182
- )
4183
- ] }),
4184
- item.stockMovementId && /* @__PURE__ */ jsxs("p", { className: "text-[11px] text-success-soft-foreground", children: [
4185
- /* @__PURE__ */ jsx(Check, { className: "mr-1 inline h-3 w-3", "aria-hidden": true }),
4186
- "Esta linha j\xE1 deu entrada no estoque."
4187
4462
  ] })
4188
4463
  ] })
4189
- ] }) }, item.id);
4464
+ ] }, item.id);
4190
4465
  })
4191
4466
  ] }),
4192
4467
  /* @__PURE__ */ jsxs(Card, { className: "p-4", children: [
@@ -4354,6 +4629,15 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice }) {
4354
4629
  ] })
4355
4630
  ] });
4356
4631
  }
4632
+ function ItemState({ done, ready }) {
4633
+ if (done) {
4634
+ return /* @__PURE__ */ jsxs("span", { className: "inline-flex shrink-0 items-center rounded-full bg-success-soft px-2 py-0.5 text-[11px] font-medium text-success-soft-foreground", children: [
4635
+ /* @__PURE__ */ jsx(Check, { className: "mr-1 h-3 w-3", "aria-hidden": true }),
4636
+ "Conferido"
4637
+ ] });
4638
+ }
4639
+ return /* @__PURE__ */ jsx("span", { className: `inline-flex shrink-0 items-center rounded-full px-2 py-0.5 text-[11px] font-medium ${ready ? "bg-muted text-muted-foreground" : "bg-warning-soft text-warning-soft-foreground"}`, children: ready ? "A conferir" : "Falta decidir" });
4640
+ }
4357
4641
  function Field({ label, value, mono, small }) {
4358
4642
  return /* @__PURE__ */ jsxs("div", { children: [
4359
4643
  /* @__PURE__ */ jsx("dt", { className: `text-muted-foreground ${small ? "text-[10px]" : "text-xs"}`, children: label }),
@@ -4468,13 +4752,21 @@ function viewFromLocation() {
4468
4752
  return { kind: "list" };
4469
4753
  }
4470
4754
  var pathOf = (view) => view.kind === "note" ? `${HASH_PREFIX}/note/${view.id}` : view.kind === "manifest" ? `${HASH_PREFIX}/manifest` : HASH_PREFIX;
4755
+ var STACK_MARK = { inboundNotes: true };
4471
4756
  function InboundNotesPage({ payableRoute } = {}) {
4472
4757
  const [view, setView] = useState(viewFromLocation);
4473
4758
  const go = useCallback((next) => {
4474
4759
  setView(next);
4475
- if (typeof window !== "undefined") {
4476
- window.history.replaceState(null, "", pathOf(next));
4760
+ if (typeof window === "undefined") return;
4761
+ const path = pathOf(next);
4762
+ if (window.location.hash === path) return;
4763
+ const empilhado = window.history.state?.inboundNotes;
4764
+ if (next.kind === "list" && empilhado) {
4765
+ window.history.back();
4766
+ return;
4477
4767
  }
4768
+ if (next.kind === "list") window.history.replaceState(null, "", path);
4769
+ else window.history.pushState(STACK_MARK, "", path);
4478
4770
  }, []);
4479
4771
  useEffect(() => {
4480
4772
  if (typeof window === "undefined") return;