# CUI prüfen: rumänische Steuer-ID mit TypeScript und Zod

> CUI prüfen in TypeScript: die Prüfziffer mit dem Schlüssel 753217532 Schritt für Schritt, das Präfix RO, eine zod-Regel, ein React-Feld und die ANAF-Abfrage.

- Autor: [Serban Rusu](https://wingo-ui.com/blog/authors/serban), Founder of Wingo UI
- Veröffentlicht: 9. Okt. 2026
- Kategorie: [Komponenten-Guides](https://wingo-ui.com/de/blog/category/components)
- Lesezeit: 10 Min.
- Kanonische URL: https://wingo-ui.com/de/blog/romanian-cui-validation-javascript
- Englische Fassung: https://wingo-ui.com/blog/romanian-cui-validation-javascript

## Kurz gesagt

Um einen rumänischen CUI zu prüfen, rechnest du eine gewichtete Prüfsumme: Entferne das optionale Präfix RO, richte die Ziffern vor der letzten rechtsbündig am Schlüssel 753217532 aus, multipliziere und addiere, und nimm zum Schluss die Summe mal 10 mod 11, wobei 10 zu 0 wird. Das Ergebnis muss der letzten Ziffer entsprechen. Eine gültige Prüfziffer beweist nur, dass die Nummer korrekt aufgebaut ist, also prüfst du mit dem öffentlichen Webservice von ANAF, ob die Firma existiert, noch eingetragen ist und Mehrwertsteuer zahlt.

Dein Registrierungs- oder Rechnungsformular hat ein Feld für die Steuer-ID einer rumänischen Firma, und ein Regex für „RO plus Ziffern“ lässt jede vertippte Ziffer durch. Wer einen CUI prüfen will, braucht drei Dinge: einen Parser, der die vielen Schreibweisen der Nummer versteht, die gewichtete Prüfziffer und, wenn es darauf ankommt, eine Abfrage, die beweist, dass die Firma echt ist. Dieses Tutorial baut alle drei in TypeScript und bindet sie dann in eine zod-Regel, ein React-Feld, das beim Tippen prüft, und einen Registrierungsschritt ein, der die Firma bei ANAF nachschlägt. Die Oberfläche nutzt [Input](https://wingo-ui.com/components/input) und [Field](https://wingo-ui.com/components/field) aus Wingo UI, der Bibliothek, die wir bauen. Wir sind also nicht neutral. Der Code für die Prüfsumme ist reines TypeScript, das du überall einfügen kannst.

## Was ist ein CUI, und wie hängt er mit CIF und USt-IdNr. zusammen?

Es ist eine Nummer mit drei Namen. Der CUI (Codul Unic de Înregistrare) ist der Code, den eine rumänische Firma bei der Registrierung bekommt: 2 bis 10 Ziffern, die letzte davon eine Prüfziffer. Der CIF (cod de identificare fiscală) ist die Steuer-ID, und bei einer Firma sind es dieselben Ziffern. Ist die Firma für die Mehrwertsteuer registriert, wird laut dem [rumänischen Wikipedia-Artikel zum CIF](https://ro.wikipedia.org/wiki/Cod_de_identificare_fiscal%C4%83) aus dem CUI ein CIF mit dem Präfix „RO“.

Das Format der rumänischen USt-IdNr. ist also RO, gefolgt von denselben 2 bis 10 Ziffern, so wie es die [Tabelle der Umsatzsteuer-Identifikationsnummern auf Wikipedia](https://en.wikipedia.org/wiki/VAT_identification_number) aufführt. In einem echten Formular bekommst du `RO12345674`, `ro 12345674`, `12 345 674` und `RO-12345674`, und alle vier sind dieselbe Firma.

Daraus folgen zwei Regeln:

- **Speichere die Ziffern und ein Mehrwertsteuer-Flag**, nie den String, so wie er getippt wurde. Gib `RO` + Ziffern aus, wenn du die USt-IdNr. brauchst.
- **Das getippte Präfix beweist keinen Mehrwertsteuer-Status.** Viele schreiben RO aus Gewohnheit davor. Das Flag sollte von ANAF kommen, so wie wir es weiter unten machen.

## Wie funktioniert die CUI-Prüfziffer?

Die letzte Ziffer wird mit einem festen Schlüssel, 753217532, aus den anderen berechnet. Der rumänische Wikipedia-Artikel oben beschreibt den Algorithmus „validare CUI“, und der [Quellcode von stdnum.ro.cui in python-stdnum](https://github.com/arthurdejong/python-stdnum/blob/master/stdnum/ro/cui.py) setzt dieselben fünf Schritte um:

1. Entferne das Präfix RO und alle Leerzeichen, Punkte oder Bindestriche. Übrig bleiben müssen 2 bis 10 Ziffern.
2. Leg die letzte Ziffer beiseite. Das ist die Prüfziffer.
3. Füll die restlichen Ziffern links mit Nullen auf, bis es neun sind, damit sie am rechten Ende des Schlüssels ausgerichtet sind. (Wikipedia dreht stattdessen beide Strings um, was dieselben Paare ergibt.)
4. Multipliziere jede Ziffer mit der Schlüsselziffer an derselben Position und addiere die Produkte.
5. Multipliziere die Summe mit 10, nimm den Rest der Division durch 11 und mach aus einem Rest von 10 eine 0. Der CUI ist gültig, wenn das der Prüfziffer entspricht.

Hier ist `12345674` von Hand durchgerechnet. Der Rumpf ist `1234567`, aufgefüllt zu `001234567`:

| Position | Ziffer | Schlüssel | Produkt |
|---|---|---|---|
| 1 | 0 | 7 | 0 |
| 2 | 0 | 5 | 0 |
| 3 | 1 | 3 | 3 |
| 4 | 2 | 2 | 4 |
| 5 | 3 | 1 | 3 |
| 6 | 4 | 7 | 28 |
| 7 | 5 | 5 | 25 |
| 8 | 6 | 3 | 18 |
| 9 | 7 | 2 | 14 |

Die Summe ist 95. 950 mod 11 ist 4, das passt zur letzten Ziffer, also ist `RO12345674` gültig und `RO12345678` nicht.

An einem Fall scheitern selbst geschriebene Validatoren: am Rest 10. Bei `18547290` ergeben die Produkte zusammen 111, und 1110 mod 11 ist 10, also ist die CUI-Prüfziffer 0. Ein Validator, der diese Umwandlung auslässt, lehnt jede Firma ab, deren Prüfziffer dort landet.

## Wie prüfe ich einen CUI in TypeScript?

Erst parsen, dann prüfen. Ein Parser, der die Ziffern, das Präfix und das Ergebnis zurückgibt, ist für Formular, zod-Regel und Abfrage die gemeinsame Single Source of Truth:

```ts
// lib/cui.ts
export type CuiInfo = {
  // die Ziffern ohne RO: "12345674"
  digits: string;
  // mit RO geschrieben, so wie ein Mehrwertsteuerzahler sie angibt
  vatPayer: boolean;
  valid: boolean;
};

// der Prüfschlüssel 753217532, ein Gewicht pro Ziffer vor der Prüfziffer
const KEY = [7, 5, 3, 2, 1, 7, 5, 3, 2];

export function parseCui(input: string): CuiInfo | null {
  const clean = input.replace(/[\s.\-/]/g, "").toUpperCase();
  const match = /^(RO)?(\d{2,10})$/.exec(clean);
  if (!match) return null;

  const digits = match[2];
  // den Rumpf rechtsbündig am Schlüssel ausrichten: "1234567" -> "001234567"
  const body = digits.slice(0, -1).padStart(9, "0");
  let sum = 0;
  for (let i = 0; i < 9; i++) sum += Number(body[i]) * KEY[i];
  // mal 10, mod 11, und ein Rest von 10 wird zu 0
  const control = ((sum * 10) % 11) % 10;

  return {
    digits,
    vatPayer: match[1] === "RO",
    valid: control === Number(digits[digits.length - 1]),
  };
}

export function isValidCui(input: string): boolean {
  return parseCui(input)?.valid ?? false;
}

// "ro 1234 5674" -> "RO12345674"; vatPayer setzt das Präfix oder entfernt es
export function formatCui(input: string, options: { vatPayer?: boolean } = {}): string {
  const info = parseCui(input);
  if (!info) return input;
  return `${(options.vatPayer ?? info.vatPayer) ? "RO" : ""}${info.digits}`;
}
```

```ts
parseCui("RO12345674"); // { digits: "12345674", vatPayer: true, valid: true }
isValidCui("12345678"); // false, die Prüfziffer müsste 4 sein
isValidCui("RO 185 472 90"); // true, Rest 10 wird zu 0
formatCui("ro 1234 5674"); // "RO12345674"
formatCui("RO12345674", { vatPayer: false }); // "12345674"
```

Prüf vier Dinge, bevor du einem Snippet aus einem Forum vertraust, das die CIF-Prüfsumme in JavaScript berechnet:

- Es wandelt in Großbuchstaben um, bevor es `RO` entfernt, sonst scheitert `ro12345674`.
- Es macht aus einem Rest von 10 eine 0.
- Es richtet die Ziffern von rechts aus. Linksbündig funktioniert nur bei 10-stelligen Codes.
- Es lehnt 11 Ziffern ab.

python-stdnum lehnt außerdem einen Code ab, der mit 0 beginnt. `parseCui` akzeptiert ihn, also schreib die Zifferngruppe als `([1-9]\d{1,9})`, wenn du dieselbe Regel willst.

Wir haben jede Änderung einer einzelnen Ziffer und jede Vertauschung zweier benachbarter Ziffern über eine große Stichprobe gültiger 8-stelliger Codes laufen lassen: Die Prüfung hat jeweils etwa 98 % erkannt, und jeder übersehene Tippfehler bei einer einzelnen Ziffer ging auf die Umwandlung von 10 in 0 zurück. Eine zufällige 8-stellige Zahl besteht den Test in einem von zehn Fällen, also heißt eine gültige Prüfziffer nie, dass die Firma existiert.

## Wie schreibe ich eine zod-Regel für einen CUI?

Verkette drei Prüfungen und eine Transformation, damit die Person immer nur eine Meldung sieht und deine Datenbank immer dieselbe Form bekommt:

```ts
// lib/cui-schema.ts
import { z } from "zod";
import { formatCui, isValidCui, parseCui } from "@/lib/cui";

export const cui = z
  .string()
  .trim()
  .min(1, { error: "Gib die Steuer-ID der Firma ein", abort: true })
  .refine((value) => parseCui(value) !== null, {
    error: "Nutze 2 bis 10 Ziffern, mit oder ohne RO",
    abort: true,
  })
  .refine(isValidCui, { error: "Die Prüfziffer dieser Steuer-ID stimmt nicht" })
  .transform((value) => formatCui(value));

// für ein Formular, in dem die Steuer-ID optional ist
export const optionalCui = z.union([z.literal(""), cui]);
```

`abort: true` (zod 4) stoppt bei der ersten fehlgeschlagenen Prüfung, also meldet die Eingabe „abc“, welches Format erwartet wird, statt zusätzlich die Prüfziffer zu bemängeln. Die Transformation macht beim Absenden aus `" ro 1234 5674 "` ein `RO12345674`. So musst du den Text nie umschreiben, während jemand tippt.

In Wingo UI Pro liefert die Lib [form-validation](https://wingo-ui.com/components/form-validation) eine CUI-Regel als `createValidators().cui()`, mit einer Meldung für jeden ungültigen Code auf Englisch oder mit `VALIDATION_MESSAGES_RO` auf Rumänisch. Sie transformiert nicht, also normalisierst du vor dem Speichern mit `formatCui` aus der Lib [invoicing](https://wingo-ui.com/components/invoicing); diese Lib exportiert dieselben drei Funktionen wie der Code oben, dazu Prüfungen für IBAN und CNP.

## Wie baue ich ein CUI-Feld, das beim Tippen prüft?

Zeig den Fehler, nachdem die Person das Feld verlassen hat, und prüf danach bei jedem Tastendruck neu, damit er verschwindet, sobald die Nummer stimmt. Das Häkchen zeigst du, sobald die Nummer gültig ist, denn es unterbricht niemanden. Der Modus `onTouched` von React Hook Form gibt dem Fehler genau dieses Timing, und `useWatch` steuert das Häkchen:

```tsx
"use client";

import { useForm, useWatch } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { CircleCheck } from "lucide-react";
import { z } from "zod";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { isValidCui } from "@/lib/cui";
import { cui } from "@/lib/cui-schema";

const schema = z.object({
  company: z.string().trim().min(1, { error: "Gib den Firmennamen ein" }),
  cui,
});

type Values = z.output<typeof schema>;

export function BillingForm({ onSave }: { onSave: (values: Values) => Promise<void> }) {
  const form = useForm<z.input<typeof schema>, unknown, Values>({
    resolver: zodResolver(schema),
    // der erste Fehler wartet auf blur, danach prüft das Feld bei jedem Tastendruck neu
    mode: "onTouched",
    defaultValues: { company: "", cui: "" },
  });
  const typed = useWatch({ control: form.control, name: "cui" });
  const { errors, isSubmitting } = form.formState;

  return (
    <form noValidate onSubmit={form.handleSubmit(onSave)} className="flex flex-col gap-4">
      <Input
        label="Firmenname"
        autoComplete="organization"
        {...form.register("company")}
        error={errors.company?.message}
      />
      <Input
        label="Steuer-ID (CUI)"
        description="Mit oder ohne RO, zum Beispiel RO12345674."
        placeholder="RO12345674"
        autoComplete="off"
        autoCapitalize="characters"
        spellCheck={false}
        {...form.register("cui")}
        error={errors.cui?.message}
        rightIcon={isValidCui(typed) ? <CircleCheck className="text-success-soft-foreground" /> : undefined}
      />
      <Button type="submit" loading={isSubmitting}>
        Speichern
      </Button>
    </form>
  );
}
```

Das Feld behält die Texttastatur, denn ein Ziffernblock hat kein R und kein O. `autoCapitalize="characters"` macht auf dem Smartphone aus „ro“ ein „RO“, und die Rechtschreibprüfung bleibt aus, damit der Browser keine Nummer unterstreicht. Der Input nutzt auf Smartphones 16px Schrift, damit iOS nicht zoomt, und sein `error` ersetzt den Hinweis, setzt `aria-invalid` und verknüpft die Meldung über `aria-describedby`, die Anatomie, die [Field](https://wingo-ui.com/components/field) jedem Control gibt. Beide sind kostenlos: `npx wingo-ui@latest add input field`.

> Live-Demo (Form validation): Gib RO12345678 in „Company tax ID“ ein und drück Tab: Der Fehler zur Prüfziffer erscheint. Ändere die letzte Ziffer auf 4, und er verschwindet noch beim Tippen. Probier sie unter [Form validation](https://wingo-ui.com/components/form-validation) aus und installier sie mit `npx wingo-ui@latest add form-validation`.

## Wie prüfe ich, ob die Firma existiert und Mehrwertsteuer zahlt?

Frag ANAF. Der öffentliche Webservice PlatitorTvaRest beantwortet für eine Liste von CUIs und ein Datum, ob jede Firma gefunden wird, wie sie heißt, wo sie sitzt und ob sie an diesem Datum für die Mehrwertsteuer registriert war. Stand Oktober 2026 gelten laut der [Dokumentation zu v9](https://static.anaf.ro/static/10/Anaf/Informatii_R/Servicii_web/doc_WS_V9.txt) diese Regeln: ein POST an `https://webservicesp.anaf.ro/api/PlatitorTvaRest/v9/tva` mit einem JSON-Array aus `{ "cui": number, "data": "YYYY-MM-DD" }`, höchstens 100 CUIs pro Request und höchstens ein Request pro Sekunde und Client. Die Antwort hat eine Liste `found` und eine Liste `notFound`.

Wir haben den Service am 8. Oktober 2026 aufgerufen, und zwei Ergebnisse haben uns überrascht. Eine Nummer mit falscher Prüfziffer landet ohne Fehler in `notFound`, also spart eine vorherige Prüfung einen Request gegen dieses Limit. Und unsere eigene Demo-Nummer, `RO12345674`, kam als gefunden zurück: eine Firma, die 2006 aus dem Register gelöscht wurde, mit `stare_inregistrare` auf „RADIERE din data 29.06.2006“, `scpTVA` auf false und dem Inaktiv-Flag `statusInactivi` ebenfalls auf false. `18547290`, das Beispiel aus python-stdnum, kam genauso zurück, gelöscht 2016. Gefunden heißt nicht aktiv, und in beiden Datensätzen blieb das Inaktiv-Flag false, also lies `stare_inregistrare`. Bei einer aktiven Firma steht dort „INREGISTRAT din data“ und danach ein Datum.

Lass die ANAF-Abfrage auf deinem Server laufen. Die Feldnamen sind rumänisch und die Version steckt im Pfad (die [Dokumentation zu v5](https://static.anaf.ro/static/10/Anaf/Informatii_R/doc_WS_V5.txt) nutzte `/PlatitorTvaRest/api/v5/ws/tva`), also musst du nur eine Datei anpassen, wenn ANAF umzieht: den Route Handler. Er liest den CUI aus dem Query-String, der schon dekodiert ankommt, und macht aus einem Timeout oder einer Fehlerseite eine einheitliche 502:

```ts
// app/api/company/route.ts
import type { NextRequest } from "next/server";
import { parseCui } from "@/lib/cui";

const ANAF = "https://webservicesp.anaf.ro/api/PlatitorTvaRest/v9/tva";

type AnafCompany = {
  date_generale: { denumire: string; adresa: string; stare_inregistrare: string };
  inregistrare_scop_Tva: { scpTVA: boolean };
  stare_inactiv: { statusInactivi: boolean };
};

export async function GET(request: NextRequest) {
  const info = parseCui(request.nextUrl.searchParams.get("cui") ?? "");
  // ein Tippfehler kostet nie einen Request gegen das Limit von einem pro Sekunde
  if (!info?.valid) return Response.json({ error: "invalid-cui" }, { status: 400 });

  // ANAF antwortet für einen Kalendertag in Rumänien, nicht in UTC
  const today = new Intl.DateTimeFormat("en-CA", { timeZone: "Europe/Bucharest" }).format(new Date());
  let found: AnafCompany[];
  try {
    const response = await fetch(ANAF, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify([{ cui: Number(info.digits), data: today }]),
      signal: AbortSignal.timeout(8000),
    });
    if (!response.ok) throw new Error(`ANAF antwortete mit ${response.status}`);
    ({ found } = (await response.json()) as { found: AnafCompany[]; notFound: number[] });
  } catch {
    // ein Timeout, ein Fehlerstatus oder eine Seite, die kein JSON ist
    return Response.json({ error: "anaf-unavailable" }, { status: 502 });
  }

  const company = found[0];
  if (!company) return Response.json({ error: "not-found" }, { status: 404 });

  return Response.json({
    name: company.date_generale.denumire,
    address: company.date_generale.adresa,
    vatPayer: company.inregistrare_scop_Tva.scpTVA,
    // "INREGISTRAT din data ..." solange aktiv, "RADIERE din data ..." nach der Löschung
    struckOff: company.date_generale.stare_inregistrare.startsWith("RADIERE"),
    inactive: company.stare_inactiv.statusInactivi,
  });
}
```

Für ANAF ist dein Server der Client, also teilen sich alle Registrierungen diesen einen Request pro Sekunde. Cache die Antworten pro CUI, und bündle bei Importen bis zu 100 CUIs pro Request.

ANAF gibt Auskunft über die rumänische Mehrwertsteuerregistrierung. Um eine rumänische USt-IdNr. für eine grenzüberschreitende Rechnung innerhalb der EU zu prüfen, nutzt du VIES. Die [VIES-Seite der Europäischen Kommission](https://europa.eu/youreurope/business/taxation/vat/check-vat-number-vies/index_en.htm) nennt drei Gründe für eine ungültige Antwort: Die Nummer existiert nicht, sie ist nicht für innergemeinschaftliche Umsätze freigeschaltet, oder die Registrierung ist noch nicht abgeschlossen. Eine Firma kann in Rumänien gültig als Mehrwertsteuerzahler geführt werden und in VIES trotzdem ungültig sein.

## Wie sieht ein echter Registrierungsschritt mit CUI-Abfrage aus?

Der Block [Register Page](https://wingo-ui.com/components/register-page) hat einen optionalen Firmenschritt, der auf diesen Teilen aufbaut. Sein CUI-Feld prüft mit `v.cui()` und fragt nur bei Enter oder beim Such-Button ab: Da jede zehnte zufällige Zahl den Test besteht, würde eine Abfrage bei jedem gültigen Tastendruck auch bei halb getippten Nummern feuern. Eine ungültige Prüfziffer zeigt den Fehler und ruft die Abfrage nie auf. Eine gültige Nummer geht über `formatCui` an dein `onLookupCui`. Ein Treffer blendet Name, Adresse und ein Mehrwertsteuer-Badge ein, und das Feld wird mit `formatCui(cui, { vatPayer })` neu geschrieben, also kommt das Präfix von ANAF statt aus Gewohnheit. Wer die Nummer bearbeitet, löscht das Ergebnis, und eine späte Antwort zu einer älteren Nummer wird ignoriert.

Den Route Handler aus dem vorigen Abschnitt schließt du mit einem einzigen Prop an. Das Speichern von Konto, Code und Firma bleibt simuliert, bis du `onRegister`, `onVerify` und `onCompany` übergibst:

```tsx
"use client";

import { RegisterPage } from "@/components/blocks/register-page";

export default function SignUpPage() {
  return (
    <RegisterPage
      onLookupCui={async (cui) => {
        const response = await fetch(`/api/company?cui=${encodeURIComponent(cui)}`);
        if (response.status === 404) return null;
        if (!response.ok) throw new Error("Abfrage fehlgeschlagen");
        const company = await response.json();
        // eine gelöschte Firma wie eine unbekannte behandeln
        if (company.struckOff) return null;
        return { name: company.name, address: company.address, vatPayer: company.vatPayer };
      }}
    />
  );
}
```

Gibt die Funktion `null` zurück, öffnen sich die manuellen Felder mit einem Hinweis, und ein Fehler bietet „Try again“ und „Fill in by hand“ an. Die Demo unten antwortet aus einer Beispieltabelle statt von ANAF, ihre Firmen sind also erfunden.

> Live-Demo (Register Page): Gib RO12345678 ein und drück Enter: Der Fehler zur Prüfziffer erscheint, und keine Abfrage läuft. Korrigier die letzte Ziffer auf 4 und drück noch einmal Enter: Das Beispielregister füllt die Firma und ihr Mehrwertsteuer-Badge aus. Probier sie unter [Register Page](https://wingo-ui.com/components/register-page) aus und installier sie mit `npx wingo-ui@latest add register-page`.

Der Schritt danach fragt nach dem 6-stelligen Code aus der Registrierungs-E-Mail; unser [Guide zum React OTP Input](https://wingo-ui.com/de/blog/react-otp-input) zeigt, was dieses Feld können muss.

## Soll ich den CUI-Code kopieren oder installieren?

Kopier ihn, wenn du ein einziges Feld brauchst: `lib/cui.ts`, die zod-Regel und der kostenlose Input reichen für ein Rechnungs- oder Registrierungsformular. Das fertige Rumänien-Kit (CUI, IBAN, CNP und Mehrwertsteuerberechnung in `invoicing`, gemeinsame Meldungen in `form-validation`) und der komplette Registrierungsablauf gehören zu [Wingo UI Pro](https://wingo-ui.com/de/pricing). Die beiden anderen IDs haben eigene Guides: [IBAN prüfen in JavaScript](https://wingo-ui.com/de/blog/iban-validation-javascript) für Bankkonten und der [rumänische CNP-Validator](https://wingo-ui.com/de/blog/romanian-cnp-validator-javascript) für Personen. Für den Rest der Formular-Anatomie lies [React Form Components: barrierefreie Felder, zod und Mobile](https://wingo-ui.com/de/blog/react-form-components-guide), oder stöber in weiteren [Guides zu Komponenten](https://wingo-ui.com/de/blog/category/components).

## Komponenten in diesem Artikel

- [Invoicing](https://wingo-ui.com/components/invoicing) (Pro): Invoice math for React billing apps: VAT per rate the way e-Factura checks it, Romanian VAT rates, and CUI, IBAN and CNP validation. Installation: `npx wingo-ui@latest add invoicing`
- [Form validation](https://wingo-ui.com/components/form-validation) (Pro): Zod validation rules for React forms with one message set (English or Romanian), react-hook-form glue and a pretend request. Installation: `npx wingo-ui@latest add form-validation`
- [Input](https://wingo-ui.com/components/input) (Free): A React text input: icons and addons inside the box, a clear button, a loading spinner, a character counter and floating labels. Installation: `npx wingo-ui@latest add input`
- [Field](https://wingo-ui.com/components/field) (Free): A React form field wrapper every control shares: label, hint, animated error and counter, fieldsets, and the box recipe all inputs use. Installation: `npx wingo-ui@latest add field`
- [Register Page](https://wingo-ui.com/components/register-page) (Pro): A React register page for a CRM sign-up: an account with password rules, company details from the CUI, an email code and a welcome step. Installation: `npx wingo-ui@latest add register-page`

## Häufige Fragen

### Was ist der Unterschied zwischen CUI und CIF?

Bei einer Firma sind es dieselben Ziffern. Der CUI ist der Registrierungscode, den die Firma bekommt, und der CIF ist ihre Steueridentifikationsnummer, die mit dem Präfix RO geschrieben wird, wenn die Firma für die Mehrwertsteuer registriert ist. Personen werden über ihren 13-stelligen CNP identifiziert, der eine eigene Prüfziffer hat.

### Gehört RO zum CUI?

Nein. RO kennzeichnet eine für die Mehrwertsteuer registrierte Firma und fließt nicht in die Prüfsumme ein. Entferne es vor der Prüfung, speichere die Ziffern und setz RO wieder davor, wenn du die USt-IdNr. einer Firma ausgibst, die ANAF als Mehrwertsteuerzahler führt.

### Wie wird die Prüfziffer des CUI berechnet?

Füll die Ziffern vor der letzten links auf neun Ziffern auf, multipliziere sie mit 7, 5, 3, 2, 1, 7, 5, 3, 2 und addiere die Produkte. Multipliziere die Summe mit 10 und nimm den Rest mod 11; ein Rest von 10 wird zu 0, und das Ergebnis muss der letzten Ziffer entsprechen.

### Heißt ein gültiger CUI, dass die Firma existiert?

Nein. Ungefähr jede zehnte zufällige Zahl besteht den Test. Frag den Webservice PlatitorTvaRest von ANAF ab, um zu sehen, ob die Firma existiert, ob sie noch eingetragen ist und ob sie an einem bestimmten Datum Mehrwertsteuer zahlt.

### Wie prüfe ich eine rumänische USt-IdNr. für eine grenzüberschreitende Rechnung in der EU?

Prüf sie in VIES, der Abfrage der Europäischen Kommission für USt-IdNrn. Eine Antwort „ungültig“ kann heißen, dass die Nummer nicht existiert oder dass sie nicht für innergemeinschaftliche Umsätze freigeschaltet ist.

---

Quelle: https://wingo-ui.com/de/blog/romanian-cui-validation-javascript
