@fayz-ai/plugin-fiscal-br 0.6.0 → 0.7.1

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
@@ -2,8 +2,8 @@ import { recordRailFields, RECORD_GROUP, activeFacet, describeError, getSupabase
2
2
  import React7, { useState, useCallback, useEffect, useMemo, useRef } from 'react';
3
3
  import { ModalHeader, ModalTitle, ModalBody, Skeleton, AiActionButton, Dropdown, DropdownTrigger, Button, DropdownContent, DropdownItem, Hint, Card, SearchSelect, Input, EmBreve, DatePicker, ModalFooter, SubpageHeader, useFilterDraft, RowActions, PageHeaderActions, FilterPanel, FilterPanelField, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, SearchField, TooltipProvider, DataTable, Tooltip, TooltipTrigger, TooltipContent, ListFilterBar, Modal, ModalContent, ModalDescription, Badge, SegmentedControl, ComingSoon, toast, CardHeader, CardTitle, CardContent, CardDescription, Switch } from '@fayz-ai/ui';
4
4
  export { ComingSoon, EmBreve } from '@fayz-ai/ui';
5
- import { registerDocumentOpener, PersonLink, getActiveTenantId, useUnitFilter, usePermissionOptional, UNIT_DETAIL_ZONES, adminNavigateTo } from '@fayz-ai/admin';
6
- import { Loader2, Download, ChevronDown, FileCode2, FileText, RefreshCw, UserPlus, Gift, AlertTriangle, ChevronRight, Check, Trash2, Plus, Image, UploadCloud, Paperclip, ScanLine, Eye, ListChecks, Inbox, KeyRound, FileDown, ShieldCheck, ExternalLink, CheckCircle2, FileCheck2, XCircle, X, Info } from 'lucide-react';
5
+ import { registerDocumentOpener, PersonLink, getActiveTenantId, useUnitFilter, PageActions, usePermissionOptional, UNIT_DETAIL_ZONES, adminNavigateTo } from '@fayz-ai/admin';
6
+ import { Loader2, Download, ChevronDown, FileCode2, FileText, RefreshCw, UserPlus, Gift, AlertTriangle, ChevronRight, Check, Trash2, Plus, Image, UploadCloud, Paperclip, ScanLine, Eye, ListChecks, KeyRound, Inbox, FileDown, ShieldCheck, ExternalLink, CheckCircle2, FileCheck2, XCircle, X, Info } from 'lucide-react';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
8
  import { FINANCIAL_INVOICE_ZONES } from '@fayz-ai/plugin-financial';
9
9
 
@@ -3884,6 +3884,51 @@ UPDATE public.plg_financial_invoices i
3884
3884
  AND d.supplier_party_id IS NOT NULL
3885
3885
  AND i.counterparty_person_id IS NULL;
3886
3886
  `;
3887
+ var MIGRATION_021_USO_E_CONSUMO_ENTRA_NO_ESTOQUE = `-- "Materiais de Uso e Consumo" vira um tipo de registro da nota recebida.
3888
+ --
3889
+ -- \xC9 o que a casa gasta sem vender nem transformar \u2014 luva, descart\xE1vel, material
3890
+ -- de limpeza \u2014 e que ainda assim se conta no estoque. Entra no estoque como o
3891
+ -- insumo: precisa de produto vinculado e gera movimento ao aceitar.
3892
+ --
3893
+ -- Aditivo e idempotente. A fun\xE7\xE3o de aceite \xE9 REMENDADA, n\xE3o reescrita (mesmo
3894
+ -- padr\xE3o da 015): copiar o corpo da 010 apagaria o \`reason\` que a 015 enxertou.
3895
+
3896
+ ALTER TABLE public.plg_fiscalbr_inbox_items
3897
+ DROP CONSTRAINT IF EXISTS plg_fiscalbr_inbox_items_entry_type_chk;
3898
+ ALTER TABLE public.plg_fiscalbr_inbox_items
3899
+ ADD CONSTRAINT plg_fiscalbr_inbox_items_entry_type_chk
3900
+ CHECK (entry_type IS NULL OR entry_type IN ('insumo','revenda','patrimonio','uso_consumo','despesa','servico'));
3901
+
3902
+ ALTER TABLE public.plg_fiscalbr_supplier_product_map
3903
+ DROP CONSTRAINT IF EXISTS plg_fiscalbr_supplier_product_map_entry_type_chk;
3904
+ ALTER TABLE public.plg_fiscalbr_supplier_product_map
3905
+ ADD CONSTRAINT plg_fiscalbr_supplier_product_map_entry_type_chk
3906
+ CHECK (entry_type IS NULL OR entry_type IN ('insumo','revenda','patrimonio','uso_consumo','despesa','servico'));
3907
+
3908
+ DO $m$
3909
+ DECLARE
3910
+ v_def text;
3911
+ v_antes text := $q$entry_type IN ('insumo','revenda','patrimonio')$q$;
3912
+ v_depois text := $q$entry_type IN ('insumo','revenda','patrimonio','uso_consumo')$q$;
3913
+ BEGIN
3914
+ SELECT pg_get_functiondef(p.oid) INTO v_def
3915
+ FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace
3916
+ WHERE n.nspname = 'public' AND p.proname = 'fiscalbr_accept_inbox_document';
3917
+
3918
+ IF v_def IS NULL THEN
3919
+ RAISE EXCEPTION 'fiscalbr/021: fiscalbr_accept_inbox_document n\xE3o existe \u2014 aplicar 009/010 antes';
3920
+ END IF;
3921
+
3922
+ IF position('uso_consumo' in v_def) > 0 THEN RETURN; END IF;
3923
+
3924
+ IF position(v_antes in v_def) = 0 THEN
3925
+ RAISE EXCEPTION 'fiscalbr/021: o corpo de fiscalbr_accept_inbox_document n\xE3o \xE9 o esperado \u2014 confira \xE0 m\xE3o em vez de deixar passar calado';
3926
+ END IF;
3927
+
3928
+ EXECUTE replace(v_def, v_antes, v_depois);
3929
+ END
3930
+ $m$;
3931
+ `;
3887
3932
  var MIGRATIONS = [
3888
3933
  { id: "000_baseline", sql: MIGRATION_000_BASELINE },
3889
3934
  { id: "001_preserve_terminal_results_and_recover_leases", sql: MIGRATION_001_PRESERVE_TERMINAL_RESULTS_AND_RECOVER_LEASES },
@@ -3905,7 +3950,8 @@ var MIGRATIONS = [
3905
3950
  { id: "017_quem_emite_e_o_cnpj_nao_a_loja", sql: MIGRATION_017_QUEM_EMITE_E_O_CNPJ_NAO_A_LOJA },
3906
3951
  { id: "018_a_nota_do_v1_alcanca_a_conta", sql: MIGRATION_018_A_NOTA_DO_V1_ALCANCA_A_CONTA },
3907
3952
  { id: "019_a_nota_emitida_se_cancela_pelo_v2", sql: MIGRATION_019_A_NOTA_EMITIDA_SE_CANCELA_PELO_V2 },
3908
- { id: "020_a_nota_acha_o_fornecedor_pelo_cnpj", sql: MIGRATION_020_A_NOTA_ACHA_O_FORNECEDOR_PELO_CNPJ }
3953
+ { id: "020_a_nota_acha_o_fornecedor_pelo_cnpj", sql: MIGRATION_020_A_NOTA_ACHA_O_FORNECEDOR_PELO_CNPJ },
3954
+ { id: "021_uso_e_consumo_entra_no_estoque", sql: MIGRATION_021_USO_E_CONSUMO_ENTRA_NO_ESTOQUE }
3909
3955
  ];
3910
3956
  var taxGroupEntity = {
3911
3957
  name: "Grupo fiscal",
@@ -5061,7 +5107,7 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
5061
5107
  id: "supplier",
5062
5108
  header: "Fornecedor",
5063
5109
  accessorFn: (note) => note.supplierName,
5064
- meta: { truncate: true },
5110
+ meta: { truncate: true, mobile: "title" },
5065
5111
  cell: ({ row }) => /* @__PURE__ */ jsxs("div", { children: [
5066
5112
  /* @__PURE__ */ jsx("p", { className: "font-medium leading-tight", children: row.original.supplierName || "\u2014" }),
5067
5113
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground tabular-nums", children: cnpj(row.original.supplierCnpj) })
@@ -5148,24 +5194,37 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
5148
5194
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "As notas chegam da SEFAZ pela fila de manifesta\xE7\xE3o, ou voc\xEA arrasta o XML aqui." })
5149
5195
  ] });
5150
5196
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
5151
- /* @__PURE__ */ jsxs(PageHeaderActions, { children: [
5152
- onOpenManifest && /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: onOpenManifest, children: [
5153
- /* @__PURE__ */ jsx(Inbox, { className: "h-4 w-4 mr-1.5" }),
5154
- "Fila da SEFAZ"
5155
- ] }),
5156
- /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => setPullOpen(true), "data-testid": "pull-by-key", children: [
5157
- /* @__PURE__ */ jsx(KeyRound, { className: "h-4 w-4 mr-1.5" }),
5158
- "Puxar por chave"
5159
- ] }),
5160
- /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: () => fileInput.current?.click(), disabled: busy, children: [
5161
- busy ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 mr-1.5 animate-spin" }) : /* @__PURE__ */ jsx(UploadCloud, { className: "h-4 w-4 mr-1.5" }),
5162
- "Importar XML"
5163
- ] })
5164
- ] }),
5197
+ /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx(
5198
+ PageActions,
5199
+ {
5200
+ actions: [
5201
+ {
5202
+ // Picking an XML off a filesystem is a desk job.
5203
+ id: "import-xml",
5204
+ label: "Importar XML",
5205
+ icon: busy ? Loader2 : UploadCloud,
5206
+ intent: "primary",
5207
+ creates: true,
5208
+ desktopOnly: true,
5209
+ disabled: busy,
5210
+ onSelect: () => fileInput.current?.click()
5211
+ },
5212
+ {
5213
+ id: "pull-by-key",
5214
+ label: "Puxar por chave",
5215
+ icon: KeyRound,
5216
+ creates: true,
5217
+ testId: "pull-by-key",
5218
+ onSelect: () => setPullOpen(true)
5219
+ },
5220
+ ...onOpenManifest ? [{ id: "manifest", label: "Fila da SEFAZ", icon: Inbox, onSelect: onOpenManifest }] : []
5221
+ ]
5222
+ }
5223
+ ) }),
5165
5224
  /* @__PURE__ */ jsxs(
5166
5225
  Card,
5167
5226
  {
5168
- className: `border-dashed p-6 text-center transition-colors ${dragging ? "border-primary bg-primary/5" : ""}`,
5227
+ className: `hidden border-dashed p-6 text-center transition-colors md:block ${dragging ? "border-primary bg-primary/5" : ""}`,
5169
5228
  onDragOver: (e) => {
5170
5229
  e.preventDefault();
5171
5230
  setDragging(true);
@@ -5284,7 +5343,8 @@ function InboundNotesView({ onOpenNote, onOpenManifest }) {
5284
5343
  loading,
5285
5344
  getRowId: (note) => note.id,
5286
5345
  onRowClick: (note) => onOpenNote(note.id),
5287
- emptyMessage
5346
+ emptyMessage,
5347
+ mobileLayout: "cards"
5288
5348
  }
5289
5349
  ) })
5290
5350
  ] })
@@ -5497,7 +5557,7 @@ function NoteAttachmentsCard({ noteId, invoiceId, readOnly, instalments, onInsta
5497
5557
  }
5498
5558
 
5499
5559
  // src/types.ts
5500
- var STOCK_BEARING_ENTRY_TYPES = ["insumo", "revenda", "patrimonio"];
5560
+ var STOCK_BEARING_ENTRY_TYPES = ["insumo", "revenda", "patrimonio", "uso_consumo"];
5501
5561
  var isStockBearing = (type) => STOCK_BEARING_ENTRY_TYPES.includes(type);
5502
5562
 
5503
5563
  // src/lib/note-suggestion.ts
@@ -5524,6 +5584,7 @@ var ENTRY_TYPES = [
5524
5584
  { value: "insumo", label: "Insumo" },
5525
5585
  { value: "revenda", label: "Produto Revenda" },
5526
5586
  { value: "patrimonio", label: "Patrim\xF4nio" },
5587
+ { value: "uso_consumo", label: "Uso e Consumo", hint: "com estoque" },
5527
5588
  { value: "despesa", label: "Despesa", hint: "n\xE3o vira estoque" },
5528
5589
  { value: "servico", label: "Servi\xE7o", hint: "n\xE3o vira estoque" }
5529
5590
  ];
@@ -5757,13 +5818,16 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
5757
5818
  ] })
5758
5819
  ] }),
5759
5820
  /* @__PURE__ */ jsx(ModalBody, { className: "px-6 py-4", children: node }),
5760
- rodape2 && /* @__PURE__ */ jsx(ModalFooter, { className: "shrink-0 flex-row items-center justify-between gap-3 space-x-0 border-t px-6 py-3 pr-16", children: rodape2 })
5821
+ rodape2 && /* @__PURE__ */ jsx(ModalFooter, { variant: "bar", start: rodape2.start, children: rodape2.end })
5761
5822
  ] }) : (
5762
5823
  // Em tela cheia não há moldura onde pendurar o rodapé: ele volta para dentro
5763
5824
  // do conteúdo, grudado na base da janela enquanto a página rola.
5764
5825
  /* @__PURE__ */ jsxs(Fragment, { children: [
5765
5826
  node,
5766
- rodape2 && /* @__PURE__ */ jsx("div", { className: `sticky bottom-0 ${sangria} flex items-center justify-between gap-3 border-t bg-card/95 py-3 pr-16 backdrop-blur`, children: rodape2 })
5827
+ rodape2 && /* @__PURE__ */ jsxs("div", { className: `sticky bottom-0 ${sangria} flex items-center justify-between gap-3 border-t bg-card/95 py-3 pr-16 backdrop-blur`, children: [
5828
+ /* @__PURE__ */ jsx("div", { className: "flex min-w-0 items-center gap-3", children: rodape2.start }),
5829
+ /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2", children: rodape2.end })
5830
+ ] })
5767
5831
  ] })
5768
5832
  );
5769
5833
  const [cadastrando, setCadastrando] = useState(false);
@@ -5923,51 +5987,55 @@ function NoteReconcileView({ noteId, onBack, onOpenInvoice, chrome = "page" }) {
5923
5987
  ] }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Nota n\xE3o encontrada." }) })
5924
5988
  ] }), "Concilia\xE7\xE3o");
5925
5989
  }
5926
- const rodape = readOnly ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
5927
- /* @__PURE__ */ jsxs(
5928
- Button,
5929
- {
5930
- variant: "ghost",
5931
- onClick: async () => {
5932
- await rejectNote(note.id);
5933
- onBack();
5934
- },
5935
- disabled: saving || accepting,
5936
- children: [
5937
- /* @__PURE__ */ jsx(X, { className: "mr-1.5 h-4 w-4" }),
5938
- "Rejeitar"
5939
- ]
5940
- }
5941
- ),
5942
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
5943
- (missingType > 0 || missingProduct > 0) && /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
5944
- unverifiedFactor > 0 && `${unverifiedFactor} com fator por conferir`,
5945
- unverifiedFactor > 0 && (missingType > 0 || missingProduct > 0) && " \xB7 ",
5946
- missingType > 0 && `${missingType} sem tipo`,
5947
- missingType > 0 && missingProduct > 0 && " \xB7 ",
5948
- missingProduct > 0 && `${missingProduct} sem produto`
5949
- ] }),
5950
- /* @__PURE__ */ jsxs(Button, { variant: "outline", onClick: () => {
5990
+ const pendencias = [
5991
+ unverifiedFactor > 0 && `${unverifiedFactor} com fator por conferir`,
5992
+ missingType > 0 && `${missingType} sem tipo`,
5993
+ missingProduct > 0 && `${missingProduct} sem produto`
5994
+ ].filter(Boolean).join(" \xB7 ");
5995
+ const rodape = readOnly ? null : {
5996
+ start: /* @__PURE__ */ jsxs(Fragment, { children: [
5997
+ /* @__PURE__ */ jsxs(
5998
+ Button,
5999
+ {
6000
+ variant: "ghost",
6001
+ size: "sm",
6002
+ className: "text-destructive hover:bg-destructive/10 hover:text-destructive",
6003
+ onClick: async () => {
6004
+ await rejectNote(note.id);
6005
+ onBack();
6006
+ },
6007
+ disabled: saving || accepting,
6008
+ children: [
6009
+ /* @__PURE__ */ jsx(X, { className: "mr-1.5 h-3.5 w-3.5" }),
6010
+ "Rejeitar"
6011
+ ]
6012
+ }
6013
+ ),
6014
+ pendencias && /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: pendencias })
6015
+ ] }),
6016
+ end: /* @__PURE__ */ jsxs(Fragment, { children: [
6017
+ /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => {
5951
6018
  void save();
5952
6019
  }, disabled: saving || accepting, children: [
5953
- saving && /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-4 w-4 animate-spin" }),
6020
+ saving && /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-3.5 w-3.5 animate-spin" }),
5954
6021
  "Salvar concilia\xE7\xE3o"
5955
6022
  ] }),
5956
6023
  /* @__PURE__ */ jsxs(
5957
6024
  Button,
5958
6025
  {
6026
+ size: "sm",
5959
6027
  onClick: () => {
5960
6028
  void accept();
5961
6029
  },
5962
6030
  disabled: accepting || saving || missingType > 0 || missingProduct > 0 || unverifiedFactor > 0,
5963
6031
  children: [
5964
- accepting ? /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Check, { className: "mr-1.5 h-4 w-4" }),
6032
+ accepting ? /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Check, { className: "mr-1.5 h-3.5 w-3.5" }),
5965
6033
  "Aceitar"
5966
6034
  ]
5967
6035
  }
5968
6036
  )
5969
6037
  ] })
5970
- ] });
6038
+ };
5971
6039
  return moldura(/* @__PURE__ */ jsxs("div", { className: `space-y-4 ${emModal ? "" : "pb-24"}`, children: [
5972
6040
  cabecalho(`Concilia\xE7\xE3o \xB7 NF-e ${note.number}`),
5973
6041
  accepted && /* @__PURE__ */ jsxs(Card, { className: "border-success bg-success-soft p-4", children: [