-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathvalidate_vat_ic.go
More file actions
182 lines (166 loc) · 7.73 KB
/
Copy pathvalidate_vat_ic.go
File metadata and controls
182 lines (166 loc) · 7.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package einvoice
import (
"fmt"
"github.com/shopspring/decimal"
"github.com/speedata/einvoice/rules"
)
// validateVATIntracommunity validates BR-IC-1 through BR-IC-12.
//
// These rules apply to invoices with Intra-community supply VAT (category code 'K').
// This category is for goods or services traded between EU member states.
// Under EU VAT rules, the supplier typically does not charge VAT, and the buyer
// accounts for VAT through the reverse charge mechanism.
//
// Key requirements for Intra-community supply:
// - Both seller and buyer must have VAT identifiers
// - VAT rate must be 0 (reverse charge applies)
// - VAT amount must be 0 in the invoice
// - Must have exemption reason explaining the intra-community nature
// - Must have actual delivery date or invoicing period
// - Must have deliver to country code
func (inv *Invoice) validateVATIntracommunity() {
// BR-IC-1 Innergemeinschaftliche Lieferung (Intra-community supply)
// Invoice with category K must have both seller and buyer VAT IDs
hasIntracommunitySupply := false
for i := range inv.InvoiceLines {
if inv.InvoiceLines[i].TaxCategoryCode == "K" {
hasIntracommunitySupply = true
break
}
}
if !hasIntracommunitySupply {
for i := range inv.SpecifiedTradeAllowanceCharge {
if inv.SpecifiedTradeAllowanceCharge[i].CategoryTradeTaxCategoryCode == "K" {
hasIntracommunitySupply = true
break
}
}
}
if hasIntracommunitySupply {
// Check seller VAT ID
hasSellerTaxID := inv.Seller.VATaxRegistration != "" ||
inv.Seller.FCTaxRegistration != "" ||
(inv.SellerTaxRepresentativeTradeParty != nil && inv.SellerTaxRepresentativeTradeParty.VATaxRegistration != "")
// Check buyer VAT ID
hasBuyerVATID := inv.Buyer.VATaxRegistration != ""
// Also check for buyer legal registration
hasBuyerLegalID := inv.Buyer.SpecifiedLegalOrganization != nil && inv.Buyer.SpecifiedLegalOrganization.ID != ""
if !hasSellerTaxID {
inv.addViolation(rules.BRIC1, "Intra-community supply requires seller VAT identifier")
}
if !hasBuyerVATID && !hasBuyerLegalID {
inv.addViolation(rules.BRIC1, "Intra-community supply requires buyer VAT identifier or legal registration identifier")
}
}
// BR-IC-2 Innergemeinschaftliche Lieferung
// Lines with category K require seller and buyer VAT IDs
for i := range inv.InvoiceLines {
if inv.InvoiceLines[i].TaxCategoryCode == "K" {
hasSellerVATID := inv.Seller.VATaxRegistration != "" ||
inv.Seller.FCTaxRegistration != "" ||
(inv.SellerTaxRepresentativeTradeParty != nil && inv.SellerTaxRepresentativeTradeParty.VATaxRegistration != "")
hasBuyerVATID := inv.Buyer.VATaxRegistration != ""
if !hasSellerVATID {
inv.addViolation(rules.BRIC2, "Intra-community supply line requires seller VAT identifier")
}
if !hasBuyerVATID {
inv.addViolation(rules.BRIC2, "Intra-community supply line requires buyer VAT identifier")
}
break
}
}
// BR-IC-3 Innergemeinschaftliche Lieferung
// VAT rate must be 0 for lines with category K
for i := range inv.InvoiceLines {
if inv.InvoiceLines[i].TaxCategoryCode == "K" && !inv.InvoiceLines[i].TaxRateApplicablePercent.IsZero() {
inv.addViolation(rules.BRIC3, "Intra-community supply VAT rate must be 0")
}
}
// BR-IC-4 Innergemeinschaftliche Lieferung
// VAT rate must be 0 for allowances with category K
for i := range inv.SpecifiedTradeAllowanceCharge {
if !inv.SpecifiedTradeAllowanceCharge[i].ChargeIndicator && inv.SpecifiedTradeAllowanceCharge[i].CategoryTradeTaxCategoryCode == "K" && !inv.SpecifiedTradeAllowanceCharge[i].CategoryTradeTaxRateApplicablePercent.IsZero() {
inv.addViolation(rules.BRIC4, "Intra-community supply allowance VAT rate must be 0")
}
}
// BR-IC-5 Innergemeinschaftliche Lieferung
// VAT rate must be 0 for charges with category K
for i := range inv.SpecifiedTradeAllowanceCharge {
if inv.SpecifiedTradeAllowanceCharge[i].ChargeIndicator && inv.SpecifiedTradeAllowanceCharge[i].CategoryTradeTaxCategoryCode == "K" && !inv.SpecifiedTradeAllowanceCharge[i].CategoryTradeTaxRateApplicablePercent.IsZero() {
inv.addViolation(rules.BRIC5, "Intra-community supply charge VAT rate must be 0")
}
}
// BR-IC-6 Innergemeinschaftliche Lieferung
// Verify taxable amount calculation for category K
for i := range inv.TradeTaxes {
if inv.TradeTaxes[i].CategoryCode == "K" {
// Detail lines only; aggregation lines (GROUP / INFORMATION) are
// excluded so they are not double counted (EXTENDED).
expectedBasis, _ := inv.sumDetailLineBasis("K", decimal.Zero, false)
if !inv.TradeTaxes[i].BasisAmount.Equal(expectedBasis) {
inv.addViolation(rules.BRIC6, fmt.Sprintf("Intra-community supply taxable amount mismatch: got %s, expected %s", inv.TradeTaxes[i].BasisAmount.StringFixed(2), expectedBasis.StringFixed(2)))
}
}
}
// BR-IC-7 Innergemeinschaftliche Lieferung
// VAT amount must be 0 for category K
for i := range inv.TradeTaxes {
if inv.TradeTaxes[i].CategoryCode == "K" && !inv.TradeTaxes[i].CalculatedAmount.IsZero() {
inv.addViolation(rules.BRIC7, "Intra-community supply VAT amount must be 0")
}
}
// BR-IC-8 Innergemeinschaftliche Lieferung
// For each different VAT rate, verify taxable amount calculation
// Note: This validation only applies to profiles with line items (>= Basic, level 3).
// BasicWL profile (level 2) provides BasisAmount directly without line items.
if inv.ProfileLevel() >= levelBasic || (inv.ProfileLevel() == 0 && len(inv.InvoiceLines) > 0) {
for i := range inv.TradeTaxes {
if inv.TradeTaxes[i].CategoryCode == "K" {
// Detail lines only; aggregation lines (GROUP / INFORMATION) are
// excluded so they are not double counted (EXTENDED).
expectedBasis, _ := inv.sumDetailLineBasis("K", inv.TradeTaxes[i].Percent, true)
if !inv.TradeTaxes[i].BasisAmount.Equal(expectedBasis) {
inv.addViolation(rules.BRIC8, fmt.Sprintf("Intra-community supply taxable amount for rate %s: got %s, expected %s", inv.TradeTaxes[i].Percent.StringFixed(2), inv.TradeTaxes[i].BasisAmount.StringFixed(2), expectedBasis.StringFixed(2)))
}
}
}
}
// BR-IC-9 Innergemeinschaftliche Lieferung
// VAT amount must be 0 for category K (duplicate of BR-IC-7, but specified separately in spec)
for i := range inv.TradeTaxes {
if inv.TradeTaxes[i].CategoryCode == "K" && !inv.TradeTaxes[i].CalculatedAmount.IsZero() {
inv.addViolation(rules.BRIC9, "Intra-community supply VAT amount must be 0")
}
}
// BR-IC-10 Innergemeinschaftliche Lieferung
// Intra-community supply breakdown must have exemption reason code or text
for i := range inv.TradeTaxes {
if inv.TradeTaxes[i].CategoryCode == "K" && inv.TradeTaxes[i].ExemptionReason == "" && inv.TradeTaxes[i].ExemptionReasonCode == "" {
inv.addViolation(rules.BRIC10, "Intra-community supply VAT breakdown must have exemption reason")
}
}
// BR-IC-11 Innergemeinschaftliche Lieferung
// Must have actual delivery date or invoicing period
hasIntraCommunityInVATBreakdown := false
for i := range inv.TradeTaxes {
if inv.TradeTaxes[i].CategoryCode == "K" {
hasIntraCommunityInVATBreakdown = true
break
}
}
if hasIntraCommunityInVATBreakdown {
hasDeliveryDate := !inv.OccurrenceDateTime.IsZero()
hasBillingPeriod := !inv.BillingSpecifiedPeriodStart.IsZero() || !inv.BillingSpecifiedPeriodEnd.IsZero()
if !hasDeliveryDate && !hasBillingPeriod {
inv.addViolation(rules.BRIC11, "Intra-community supply requires actual delivery date or invoicing period")
}
}
// BR-IC-12 Innergemeinschaftliche Lieferung
// Must have deliver to country code
if hasIntraCommunityInVATBreakdown {
hasDeliverToCountry := inv.ShipTo != nil && inv.ShipTo.PostalAddress != nil && inv.ShipTo.PostalAddress.CountryID != ""
if !hasDeliverToCountry {
inv.addViolation(rules.BRIC12, "Intra-community supply requires deliver to country code")
}
}
}