Zum Inhalt springen

Elements headless

Derselbe Zustand und dieselben Regeln wie im Builder von Cord, ohne eine einzige Zeile UI: Das Design bringen Sie mit.

@flouviahq/elements/headless ist der Kern von Cord Elements. <CordBuilder>, die Hooks für React und Vue und die Web Components nutzen alle diesen Kern. Der Import registriert keine Elemente und berührt das DOM nicht: nur Zustand, Validierung und Netzwerk. Verwenden Sie ihn, wenn Sie eine eigene Oberfläche möchten, mit jedem Framework oder ganz ohne.

npm install @flouviahq/elements
import { createCordClient, createQuoteBuilder, createFiscalForm } from '@flouviahq/elements/headless';
Export Wofür
createCordClient HTTP-Client im Browser zu Cord oder zu Ihrem Proxy.
createQuoteBuilder Der vollständige Zustand beim Erstellen eines Angebots: Kunde, Positionen, Steuern, Gesamtbeträge, Absenden.
createFiscalForm Der Zustand des Steuerformulars des Rechnungsempfängers.
createStore Der beobachtbare Store, den alle drei nutzen (get, subscribe, set).
reduceQuoteView, INITIAL_QUOTE_VIEW Der Reducer für den Live-Zustand eines eingebetteten Angebots.
calculateDocumentTotals, roundMoney Die Berechnungslogik für Gesamtbeträge, die auch der Server verwendet.
validateFiscalReceptor, validRfc, validSpainTaxId, validEin, fiscalText Steuerliche Validierung und ihre Texte auf Spanisch, Englisch, Portugiesisch, Französisch und Deutsch.
sanitizeAppearance, appearanceToCss Der Validator der Appearance-API.
CordError Der typisierte Fehler, mit code und requestId.

createCordClient

const client = createCordClient({ publishableKey: 'pk_test_...' });
// oder mit Ihrem Backend dazwischen:
const viaProxy = createCordClient({ proxyUrl: '/api/cord' });

Übergeben Sie eine der beiden Optionen: publishableKey (ein pk_-Schlüssel, der nur die Elements-Routen nutzen kann) oder proxyUrl (die Basis Ihres Server-Proxys, aufgebaut wie /api/v1). Mit beiden oder mit einem sk_-Schlüssel wirft der Client einen Fehler.

Option Standard Detail
publishableKey — Ein pk_live_- oder pk_test_-Schlüssel.
proxyUrl — Zum Beispiel /api/cord. Die Anfragen gehen mit den Cookies Ihrer Website.
baseUrl https://cordhq.app Origin von Cord im veröffentlichbaren Modus.
timeoutMs 15000 Bricht jeden Versuch ab.
maxRetries 2 (maximal 5) Wiederholungen bei Netzwerkausfall, 429 und 5xx, unter Beachtung von Retry-After.
debug false Protokolliert jede Anfrage mit ihrer request_id in der Konsole.
fetch globales fetch Für Tests.
Methode Was sie tut
config({ force? }) GET /elements/config: Währungen, Steuern des Landes, Bedingungen und Gültigkeit Ihrer Organisation. Wird mit ihrem ETag gespeichert, und der nächste Aufruf sendet If-None-Match: Hat sich nichts geändert, antwortet Cord mit 304, und die Kopie wird verwendet. force: true fordert sie vollständig an.
products() GET /productos: der Katalog, ohne Kosten.
createQuote(input, { idempotencyKey? }) POST /cotizaciones mit einem Idempotency-Key (Ihrem oder einer neuen UUID).
aiDraft(input, onItem, { signal? }) POST /elements/ai-draft: füllt Positionen mit KI aus einem Text (texto, bis zu 4.000 Zeichen) oder einer Datei (archivo, JPG, PNG oder PDF bis 4 MB). Ruft onItem für jede Position auf, sobald sie gelesen ist.
const config = await client.config();
console.log(config.org.moneda, config.impuestos.etiqueta, config.monedas);

createQuoteBuilder

const builder = createQuoteBuilder({
  client,
  onCreated: (quote) => console.log(quote.folio, quote.link_publico),
});

const unsubscribe = builder.subscribe((state) => render(state));

builder.setCliente({ empresa: 'Acme', email: 'purchasing@acme.com' });
const key = builder.addItem({ descripcion: 'Installation', cantidad: 1, precio_unitario: 12500 });
builder.updateItem(key, { cantidad: 2 });

const quote = await builder.submit(); // null bei Fehlern oder wenn der Versuch fehlschlägt

Beim Erstellen lädt der Builder Konfiguration und Katalog selbstständig (status wechselt von loading zu ready). Jede Änderung berechnet die Gesamtbeträge mit derselben Logik wie der Server neu, und der Steuersatz jeder Position stammt aus dem echten Steuerkatalog der Organisation.

Methode Was sie tut
setCliente(patch) empresa, email, contacto, telefono.
setFiscal(recipient | null) Der steuerliche Rechnungsempfänger, der mit dem Angebot übertragen wird.
setMoneda(code), setTerminos(t), setVigencia(days), setNotas(text), setPreciosIncluyenImpuesto(bool) Die Bedingungen des Angebots.
addItem(item?) Fügt eine Position hinzu und gibt ihren key zurück. Ohne tax_rate wird der Standardsteuersatz verwendet.
addProduct(product, quantity?) Fügt ein Produkt aus dem Katalog hinzu.
updateItem(key, patch), removeItem(key) Bearbeitet oder entfernt eine Position anhand ihres key.
searchProducts(text, limit?) Durchsucht den geladenen Katalog nach Name oder SKU.
validate() Gibt die issues zurück und speichert sie.
submit() Validiert, erstellt das Angebot und gibt die Antwort zurück, oder null.
draftWithAi(input, { signal? }) Füllt Positionen mit KI; jede vorgeschlagene Position trägt sugerida.
reset(), load() Leert das Angebot oder lädt Konfiguration und Katalog neu.

submit() verwendet einen einzigen Idempotency-Key pro Sendeversuch: Ein Doppelklick oder eine Wiederholung wegen des Netzwerks erzeugt nie zwei Angebote. Der Schlüssel wird erneuert, sobald Sie etwas ändern.

Der Zustand (builder.get()) enthält:

Feld Inhalt
status loading, ready, submitting, success oder error.
config, products Was aus Cord geladen wurde.
cliente, fiscal, moneda, terminos, vigencia_dias, notas, precios_incluyen_impuesto Was die Person eingegeben hat.
items Die Positionen, jede mit ihrem key.
totals subtotal, impuestos, retencionTotal, total, die Aufschlüsselung nach Steuersatz (porTasa), die retenciones und die berechneten lineas.
issues { field, code } für jedes Problem, zum Beispiel { field: 'items', code: 'no_items' }.
result, error Das erstellte Angebot oder der CordError des letzten Versuchs.
ai status, count und error der KI-Befüllung.

Für Ihre Analytics erhält onEvent(event, data) die Ereignisse builder.loaded, builder.item_added, builder.item_removed, builder.submitted, builder.created, builder.failed und builder.ai_drafted. Die Namen bleiben über Versionen hinweg stabil.

createFiscalForm

const fiscal = createFiscalForm({ country: 'MX' });

fiscal.subscribe((s) => {
  console.log(s.visibleErrors, s.regimenes.map((r) => r.codigo));
});

fiscal.set('tax_id', 'EKU9003173C9');
fiscal.touch('tax_id');
const recipient = fiscal.submit(); // der normalisierte Rechnungsempfänger oder null

builder.setFiscal(recipient);

Der Zustand enthält values, touched, validation (mit ok, value, errors, warnings und persona), visibleErrors, die regimenes und usosCfdi, die für den RFC-Typ gelten, sowie die fields des Landes. Die Regeln finden Sie unter Steuerformular.

Mit Ihrem Framework

Der Store folgt dem Vertrag get() + subscribe() und lässt sich daher an jedes reaktive System anbinden:

import { useSyncExternalStore } from 'react';

function useBuilderState(builder) {
  return useSyncExternalStore(builder.subscribe, builder.get, builder.get);
}

Live-Zustand eines eingebetteten Angebots

reduceQuoteView(state, event) wandelt die Ereignisse, die das iframe von <cord-quote> weiterleitet, in einen Zustand um (ready, status, folio, moneda, total, approved, rejected, paid, signedBy, lastEvent). Darauf beruhen die Eigenschaft state und das Ereignis statechange des Web Component; siehe Web Components.