D390 — EU recapitulative statement (VIES)¶
The declarație recapitulativă privind livrările/achizițiile/prestările
intracomunitare is the monthly informative that feeds the EU-wide VIES
system: it summarizes, per partner and per operation type, the intra-EU
goods/services you supplied and acquired so member states can cross-match each
other's declarations. This file is a completion guide: what the declaration
reports, who files it and when, and how each field maps onto the XSD attribute
anafpy authors — followed by the technical XSD/DUK layer. ANAF's validator jar
(D390Validator.jar) and the OPANAF 705/2020 instructions are the authority;
this guide is a distilled bridge, not a re-implementation of the rules.
Everything below the "Common scenarios" instances was proven hands-on on
2026-07-17 against J4.1.2 (XSD v3).
Purpose & legal basis¶
D390 carries no tax and settles no payment — it is a reconciliation feed. Every intra-EU transaction is reverse-charged (the tax is accounted for in the acquirer's member state), so the recapitulative statement exists purely so that national administrations can match a Romanian supplier's declared supply against the counterpart's declared acquisition. It reports six operation types (see the code table below): supplies and acquisitions of goods, triangular supplies, services rendered and received, and farmer-regime supplies.
- Current model: approved by OPANAF 705/2020 (M.Of. 234/18.03.2020),
used for reporting periods from 02/2020 onward. It introduced the
v3schema (namespacemfp:anaf:dgti:d390:declaratie:v3), which added the call-off-stock annex (cos) and theRoperation type (farmer regime). Legal basis: art. 325 of the Fiscal Code (Legea 227/2015); obligated persons per art. 316 / 317. - Periodicity: monthly only — D390 has no quarterly election (unlike
D300). The reporting period is carried by
luna+an. - e-TVA note: OUG 70/2024 (pre-filled RO e-TVA) amended art. 325 so that the deadline is set by ANAF order rather than fixed in the Code; the order kept it at the 25th (see Who files & when).
Who files & when¶
- Who: taxable persons registered for VAT under art. 316 or art. 317 of the Fiscal Code who, in a given month, carried out at least one intra-EU operation of the reportable types. Art. 317 (special registration, e.g. for intra-EU acquisitions above the threshold or for services) covers persons who are not registered under the full art. 316 regime but still owe a D390 for their intra-EU services/acquisitions.
- Deadline: the 25th (inclusive) of the month following the reporting month. If the 25th is a non-working day, it rolls to the next working day. Confirmed two ways: the OPANAF 705/2020 instructions ("până la data de 25 inclusiv a lunii următoare unei luni calendaristice") and the CECCAR Business Magazine deadline note (the 25th was kept after the e-TVA / OUG 70/2024 art.325 change — a proposal to move it earlier did not take effect).
- Only file when there ARE intra-EU operations — a nil D390 is NOT
filed. This is the sharpest departure from D300 (which requires a decont for
every period, nil included). The recapitulative summarizes operations that
occurred; a month with no intra-EU supplies, acquisitions, triangular
operations or services produces no D390 at all. Do not file an empty
statement to "keep the streak" — there is no zero-line valid instance (the
schema requires at least one
operatie; see the gotchas). - Corrections —
d_rec(declarație rectificativă):d_rec="0"is the initial statement,d_rec="1"a rectificative. A rectificative is a full replacement, not a delta: per the instructions, "se completează toate rubricile formularului cu datele valabile la momentul declarării" — re-file the entire corrected month with all lines (the unchanged ones too), each carrying the value valid at declaration time. A rectificative can also touch transactions from any prior reporting period. There is no separate "annulment" document — you overwrite the period by re-declaring it.
Filling guide¶
The document is a small tree, not a flat attribute bag like D300. The root
<declaratie390> carries the header/identification attributes; its children,
in this exact XSD order, are one <rezumat> (the totals block), zero or
more <cos> (the call-off-stock annex), and one or more <operatie> (the
per-partner-per-type summary lines). Every euro of intra-EU activity is folded
into an operatie line; rezumat is the arithmetic roll-up of those lines and
must reconcile with them (the jar recomputes and checks every total).
One line per (country, partner code, name, type). You do not list
invoices — you list partners. All of a month's supplies of goods to one
German customer collapse into a single operatie tip="L" line with the summed
baza. The 4-tuple (tara + codO + denO + tip) must be unique across lines
(duplicate = error); aggregate before authoring.
Header / identification block (root attributes)¶
| Attribute | Form field | Notes |
|---|---|---|
luna |
Perioada de raportare – Luna | reporting month 1–12 |
an |
Perioada de raportare – An | reporting year (≥ 2020 for v3; XSD range 2017–2100) |
d_rec |
Declarație rectificativă | 0 = inițială, 1 = rectificativă (full re-file) |
nume_declar / prenume_declar / functie_declar |
Nume / Prenume / Funcție declarant | required text |
cui |
Cod de înregistrare în scopuri de TVA | CUI check-digit enforced by the jar (N(10)) |
den |
Denumire / Nume și Prenume | required |
adresa |
Adresă domiciliu fiscal | required |
telefon / fax / mail |
contact | optional |
totalPlata_A |
Suma de control | control sum — not a payment; formula below |
totalPlata_A = nrOPI + bazaL + bazaT + bazaA + bazaP + bazaS + bazaR
(rule R13 — the operator count plus the six per-type base totals). For the
minimal one-supply instance below that is 1 + 1000 = 1001. Compose it from the
rezumat values; the jar recomputes and rejects a wrong sum.
rezumat — the totals block (all attributes required)¶
| Attribute | Form field | Formula (jar-enforced) |
|---|---|---|
nr_pag |
Număr total pagini anexă | annex page count, N(1) (0–9) |
nrOPI |
Numărul total al operatorilor intracomunitari | = COUNT(operatie) |
bazaL |
L – Livrări intracomunitare de bunuri | = Σ baza where tip="L" |
bazaT |
T – Livrări în cadrul unei operațiuni triunghiulare | = Σ baza where tip="T" |
bazaA |
A – Achiziții intracomunitare de bunuri | = Σ baza where tip="A" |
bazaP |
P – Prestări intracomunitare de servicii | = Σ baza where tip="P" |
bazaS |
S – Achiziții intracomunitare de servicii | = Σ baza where tip="S" |
bazaR |
R – Livrări IC de bunuri (regim special agricultori) | = Σ baza where tip="R" |
total_baza |
Total bază de impozitare din anexă | = Σ baza (all lines) |
Every baza* total the type produced no lines is "0" (the attribute is
required, so it is present as zero, never omitted).
operatie — the summary lines (one or more)¶
| Attribute | Form field | Notes |
|---|---|---|
tip |
Tip operațiune | enum L/T/A/P/S/R (see code table) |
tara |
Țara operator intracomunitar | EU 2-letter code — includes XI, no GB |
codO |
Cod operator intracomunitar (fără prefixul de țară) | partner VAT number without country prefix; per-country checksum; required for L/T/P/R, may be absent for A/S |
denO |
Denumire operator intracomunitar | required |
baza |
Bază impozabilă | required, integer RON |
Operation-type codes (tip enum)¶
| Code | Meaning (ANAF label) | Side |
|---|---|---|
L |
Livrări intracomunitare de bunuri (intra-EU supplies of goods) | output |
T |
Livrări în cadrul unei operațiuni triunghiulare (triangular supplies) | output |
A |
Achiziții intracomunitare de bunuri (intra-EU acquisitions of goods) | input |
P |
Prestări intracomunitare de servicii (intra-EU services rendered) | output |
S |
Achiziții intracomunitare de servicii (intra-EU services received) | input |
R |
Livrări IC de bunuri în cadrul regimului special pentru agricultori | output |
The direction matters: P is a service you supply to an EU customer (place
of supply in their state), S a service you receive from an EU supplier;
L/A are the goods analogues. Get the letter wrong and the counterpart's
member state cannot match the line.
cos — call-off-stock annex (zero or more, optional)¶
Only for the stocuri la dispoziția clientului regime (goods moved to an EU
warehouse before the customer takes title). Most SMEs never file a cos line.
| Attribute | Form field | Notes |
|---|---|---|
tip |
Secțiunea (A/B) | A = dispatches under the regime; B = changes to a prior registration |
tara_m1 / cod_m1 |
Țara / Cod persoană impozabilă | required (the customer's state + VAT code) |
motiv |
Motivul modificării | 1 = retur bunuri, 2 = înlocuire client; required if tip="B", must be null for tip="A" |
tara_m2 / cod_m2 |
al doilea operator | required if motiv="2" (the substitute customer); null otherwise |
Common scenarios¶
Both instances below validated clean (ok) against D390Validator.jar
J4.1.2 on 2026-07-17; the minimal one also rendered to a PDF via -p. The
synthetic cui="1234565" is check-digit valid; DE136695976 is the classic
TAXUD test VAT number that passes the German algorithm, and FR40303265045 is a
key-valid French number.
1 — Minimal (one intra-EU supply of goods): 1 000 RON of goods supplied to
one German customer, June 2026. nrOPI="1", bazaL="1000", total_baza="1000",
totalPlata_A="1001" (= 1 + 1000).
<?xml version="1.0" encoding="UTF-8"?>
<declaratie390 xmlns="mfp:anaf:dgti:d390:declaratie:v3"
luna="6" an="2026" d_rec="0"
nume_declar="Popescu" prenume_declar="Ion" functie_declar="Administrator"
cui="1234565" den="EXEMPLU TEST SRL" adresa="Mun. Bucuresti, Str. Exemplu nr. 1"
totalPlata_A="1001">
<rezumat nr_pag="1" nrOPI="1" bazaL="1000" bazaT="0" bazaA="0" bazaP="0"
bazaS="0" bazaR="0" total_baza="1000"/>
<operatie tip="L" tara="DE" codO="136695976" denO="Partner GmbH" baza="1000"/>
</declaratie390>
2 — Several partners and types: a 1 000 RON supply of goods to a German
customer (L), a 2 000 RON acquisition of goods from a French supplier (A),
and a 500 RON service rendered to the German customer (P). Three operatie
lines → nrOPI="3" — nrOPI counts lines, not distinct partners (the DE
partner appears on both an L and a P line); per-type totals bazaL=1000,
bazaA=2000, bazaP=500; total_baza="3500"; totalPlata_A="3503"
(= 3 + 3500).
<?xml version="1.0" encoding="UTF-8"?>
<declaratie390 xmlns="mfp:anaf:dgti:d390:declaratie:v3"
luna="6" an="2026" d_rec="0"
nume_declar="Popescu" prenume_declar="Ion" functie_declar="Administrator"
cui="1234565" den="EXEMPLU TEST SRL" adresa="Mun. Bucuresti, Str. Exemplu nr. 1"
totalPlata_A="3503">
<rezumat nr_pag="1" nrOPI="3" bazaL="1000" bazaT="0" bazaA="2000" bazaP="500"
bazaS="0" bazaR="0" total_baza="3500"/>
<operatie tip="L" tara="DE" codO="136695976" denO="Partner GmbH" baza="1000"/>
<operatie tip="A" tara="FR" codO="40303265045" denO="Fournisseur SARL" baza="2000"/>
<operatie tip="P" tara="DE" codO="136695976" denO="Partner GmbH" baza="500"/>
</declaratie390>
3 — Correction (rectificativă): same shape as the initial statement but
d_rec="1", re-declaring the whole corrected month with all partners and
their now-correct baza (not only the changed line). If a partner drops out
entirely, omit its line and adjust the rezumat totals; the rectificative
stands in place of the original.
Nil month: there is no instance — a month with no intra-EU operations is not filed (see Who files & when).
Special attention & gotchas¶
The pitfalls below trace either to direct XSD/validator/annex verification
(the totalPlata_A formula, the codO-required-vs-optional split, the
per-country checksum, the cos minOccurs discrepancy, and the rezumat
reconciliation were each observed against D390Validator.jar J4.1.2 / the
structure annex) or to an authoritative source (OPANAF 705/2020 for the
obligated persons, deadline, nil-not-filed and rectificative semantics; CECCAR
for the deadline confirmation). No uncorroborated practitioner claim is carried.
- A nil D390 is not filed. Unlike D300, there is no such thing as an empty
D390 — the schema requires at least one
operatie, and the obligation only arises in months with reportable intra-EU operations. Filing "nothing" means filing nothing at all. codOis mandatory forL/T/P/Rbut may be absent forA/S(structure annex rule 24). For your own supplies of goods, triangular supplies, services rendered and farmer-regime supplies the partner's VAT code is required; for acquisitions of goods and services received the code may be missing (e.g. a supplier who did not give a valid code). OmittingcodOon anL/T/P/Rline is a hard error.codOis checksum-validated with the per-country VIES/TAXUD algorithm (rule R24.1: "trebuie să respecte algoritmul specific ''" ). The number is carried without the country prefix — the country goes intara. Synthetic data needs genuinely key-valid foreign VAT numbers (DE136695976,FR40303265045pass). Verify every partner code in VIES before filing — an invalid or de-registered partner code is the single most common rejection/query cause.totalPlata_Ais a control sum, not a payment:= nrOPI + bazaL + bazaT + bazaA + bazaP + bazaS + bazaR(rule R13). D390 never moves money. (This formula is documented in the structure annex — it supersedes the earlier "let the validator dictate it" note in the v3 section below.)rezumatmust reconcile with theoperatielines exactly.nrOPI = COUNT(operatie), eachbaza<X> = Σ bazaovertip="X",total_baza = Σallbaza(rules R14–R21). A single miscounted operator or mistyped total fails.(tara + codO + denO + tip)must be unique. Duplicate lines are rejected (rule R24, "aparții multiple"); aggregate a partner's month into one line per type. Exception observed in the annex: fortip="A"the samecodOmay carry multipledenOvalues; for any other type one code maps to one name.XI(Northern Ireland) replacesGB; Great Britain is out of VIES. Post-Brexit, goods to/from Northern Ireland usetara="XI"(validated with the old GB algorithm); the country list has noGB. Transactions with Great Britain proper are exports/imports (third country), not D390 lines — putting a GB partner on a D390 is a category error.- The
coselement is XSD-required but validator-optional (XSD vs jar). The schema's<xs:sequence>declarescoswith the defaultminOccurs="1", which would demand at least one call-off-stock row; the structure annex documents it as "0-n apariții" and the validator accepts zerocoselements (both instances above validate clean without one). The validator/annex win — for an ordinary filercosis absent. Only emitcosfor the call-off-stock regime. - Rectificative is a full re-file, not a patch. With
d_rec="1"you resubmit the entire corrected month; the new statement replaces the prior one. Sending only the changed line loses every other partner. - Cross-form: D390 must reconcile with the D300 decont's intra-EU rows. ANAF
cross-checks the recapitulative against the VAT return of the same period and
the partner codes against VIES; mismatches generate query letters. The
correspondences:
L↔ D300 Rd.1 (intra-EU supplies of goods, exempt),A↔ D300 Rd.5/Rd.20 (intra-EU acquisitions, reverse-charged on both legs),P(services rendered, place of supply abroad) ↔ D300 Rd.3/3.1,S(services received) ↔ D300 Rd.7.1/Rd.22.1. Reconcile the per-partner totals with the sales/purchase journals before filing so the D390 sum ties to the decont's intra-EU lines. cuicheck digit is enforced beyond the XSD pattern ([1-9]\d{1,9}) — the jar rejects a wrong checksum (1234565is valid synthetic data).
v3 (current; XSD d390_12022021.xsd, jar J4.1.2)¶
- Namespace
mfp:anaf:dgti:d390:declaratie:v3, rootdeclaratie390. Older schemas on the page: v2 =d390_20170131.xsd, v1 =d390_20130729.xsd. Caution: a stale validator jar (see the trap below) rejects v3 and demands v1 — if DUK ever answers "Valoarea corecta este xmlns='…v1'" for a current period, the installed jar is outdated, the XML is not wrong. - Shape: header attributes on the root (
luna,an,d_rec0/1 rectifying flag, declarant name/function,cui,den,adresa,totalPlata_A), then children: onerezumat+ repeatedoperatierows (andcosrows for call-off-stock). rezumat(all required):nr_pag,nrOPI(operator count), per-type basesbazaL/bazaT/bazaA/bazaP/bazaS/bazaR,total_baza. They must agree with theoperatielines.operatie:tip(enumL/T/A/P/S/R— supplies, triangular, acquisitions, services rendered, services received, R added in v3),tara(EU list — includesXINorthern Ireland, noGB),codO(partner VAT number without country prefix),denO,baza.codOis checksum-validated with the per-country algorithm (rule R24.1: "trebuie sa respecte algoritmul specific 'DE'"). Synthetic data needs genuinely valid foreign VAT numbers — the classic German test number136695976(DE136695976) passes.cuicheck digit enforced (as on every form;1234565works).totalPlata_Ais a control sum (rule R13), not an amount. The formula isnrOPI + bazaL + bazaT + bazaA + bazaP + bazaS + bazaR(documented in the structure annex — Special attention & gotchas).- Minimal valid instance (one 1000 RON intra-EU supply to a German partner, June 2026):
<?xml version="1.0" encoding="UTF-8"?>
<declaratie390 xmlns="mfp:anaf:dgti:d390:declaratie:v3"
luna="6" an="2026" d_rec="0"
nume_declar="Popescu" prenume_declar="Ion" functie_declar="Administrator"
cui="1234565" den="EXEMPLU TEST SRL" adresa="Mun. Bucuresti, Str. Exemplu nr. 1"
totalPlata_A="1001">
<rezumat nr_pag="1" nrOPI="1" bazaL="1000" bazaT="0" bazaA="0" bazaP="0"
bazaS="0" bazaR="0" total_baza="1000"/>
<operatie tip="L" tara="DE" codO="136695976" denO="Partner GmbH" baza="1000"/>
</declaratie390>
The stale-jar trap (how this page was almost wrong)¶
http://static.anaf.ro/static/10/Anaf/update5/D390/D390Validator.jar — the
unversioned directory one would guess from the feed's layout — serves the
original 2012 release (J1.0.0), which only knows namespace v1 and
predates bazaR/tip="R". The feed's real JURL points into a versioned
directory (update5/D390_11/ at J4.1.2). Always install jars from the
feed's JURL/PURL verbatim; never construct the URL by pattern. (The same
applies to every form — D394's unversioned directory likewise serves a 2012
jar.)