-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinvoices.go
More file actions
191 lines (174 loc) · 5.25 KB
/
Copy pathinvoices.go
File metadata and controls
191 lines (174 loc) · 5.25 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
183
184
185
186
187
188
189
190
191
package facturae
import (
"github.com/invopop/gobl/addons/es/facturae"
"github.com/invopop/gobl/bill"
"github.com/invopop/gobl/cal"
"github.com/invopop/gobl/num"
"github.com/invopop/gobl/org"
"github.com/invopop/gobl/tax"
)
// Invoices contains info about a batch of invoices.
// In our case there will only be one invoice per batch
type Invoices struct {
List []*Invoice `xml:"Invoice"`
}
// Invoice contains info about a single invoice
type Invoice struct {
InvoiceHeader *InvoiceHeader
InvoiceIssueData *InvoiceIssueData
TaxesOutputs *TaxSummary `xml:",omitempty"`
TaxesWithheld *TaxSummary `xml:",omitempty"`
InvoiceTotals *InvoiceTotals
Items *Items
PaymentDetails *PaymentDetails `xml:",omitempty"`
LegalLiterals *LegalLiterals `xml:",omitempty"`
AdditionalData *AdditionalData `xml:",omitempty"`
}
// InvoiceHeader contains general information of a single invoice
type InvoiceHeader struct {
InvoiceNumber string
InvoiceSeriesCode string `xml:",omitempty"`
InvoiceDocumentType string
InvoiceClass string
Corrective *Corrective `xml:",omitempty"`
}
// PeriodDates is used in corrective tax periods to define a date
// range.
type PeriodDates struct {
StartDate string
EndDate string
}
// InvoiceIssueData contains information about dates, lang and currencies
type InvoiceIssueData struct {
IssueDate string
OperationDate string `xml:",omitempty"`
PlaceOfIssue string `xml:",omitempty"`
InvoiceingPeriod *PeriodDates `xml:",omitempty"`
InvoiceCurrencyCode string
ExchangeRateDetails *ExchangeRateDetails `xml:",omitempty"` // TODO!
TaxCurrencyCode string
LanguageName string // FIXME: [JUANJO] are we going to support multiple languages?
InvoiceDescription string `xml:",omitempty"` // TODO
ReceiverTransactionReference string `xml:",omitempty"` // TODO
FileReference string `xml:",omitempty"` // TODO
ReceiverContractReference string `xml:",omitempty"` // TODO
}
// LegalLiterals contains an array of legal texts to add to the Invoice in certain situations.
type LegalLiterals struct {
LegalReference []string
}
// ExchangeRateDetails describes how to exchange from the invoices currency
// to euros.
type ExchangeRateDetails struct {
ExchangeRate string
ExchangeRateDate string
}
// NewInvoice creates a new invoice with facturae format
func (d *Document) newInvoice(invoice *bill.Invoice) *Invoice {
valueDate := &invoice.IssueDate
if invoice.ValueDate != nil {
valueDate = invoice.ValueDate
}
xmlInvoice := &Invoice{
InvoiceHeader: newInvoiceHeader(invoice),
InvoiceIssueData: &InvoiceIssueData{
IssueDate: invoice.IssueDate.String(),
OperationDate: valueDate.String(),
InvoiceCurrencyCode: string(invoice.Currency),
TaxCurrencyCode: string(invoice.Currency),
LanguageName: "es",
},
InvoiceTotals: newInvoiceTotals(invoice),
AdditionalData: newAdditionalData(invoice),
PaymentDetails: newPaymentDetails(invoice.Payment),
LegalLiterals: newLegalLiterals(invoice),
Items: newItems(invoice.Lines, invoice.Totals.Taxes),
}
xmlInvoice.setTaxes(invoice.Totals.Taxes)
return xmlInvoice
}
// setTaxes performs a set of steps to convert the GOBL tax list into something
// that FacturaE expects.
func (inv *Invoice) setTaxes(taxes *tax.Total) {
if taxes == nil {
return
}
regular := make([]*Tax, 0)
retained := make([]*Tax, 0)
// First loop for bases
for _, ct := range taxes.Categories {
for _, rt := range ct.Rates {
percent := rt.Percent
if percent == nil {
percent = num.NewPercentage(0, 0)
}
tax := &Tax{
Code: categoryTaxCodeMap[ct.Code],
Rate: percent.StringWithoutSymbol(),
Base: makeAmount(rt.Base),
Amount: makeAmount(rt.Amount),
}
if ct.Retained {
retained = append(retained, tax)
} else {
if rt.Surcharge != nil {
st := rt.Surcharge
p := st.Percent
p = p.Rescale(4) // we need 2 decimal places
tax.Surcharge = p.StringWithoutSymbol()
v := makeAmount(st.Amount)
tax.SurchargeAmount = &v
}
regular = append(regular, tax)
}
}
}
if len(regular) > 0 {
inv.TaxesOutputs = &TaxSummary{
List: regular,
}
}
if len(retained) > 0 {
inv.TaxesWithheld = &TaxSummary{
List: retained,
}
}
}
func newInvoiceHeader(inv *bill.Invoice) *InvoiceHeader {
h := &InvoiceHeader{
InvoiceNumber: inv.Code.String(),
InvoiceSeriesCode: inv.Series.String(),
}
if inv.Tax != nil {
h.InvoiceDocumentType = inv.Tax.Ext.Get(facturae.ExtKeyDocType).String()
h.InvoiceClass = inv.Tax.Ext.Get(facturae.ExtKeyInvoiceClass).String()
}
// Only one preceding document currently supported
if len(inv.Preceding) > 0 {
h.Corrective = newCorrective(inv)
}
return h
}
func newLegalLiterals(inv *bill.Invoice) *LegalLiterals {
lits := make([]string, 0)
if len(lits) == 0 {
return nil
}
for _, n := range inv.Notes {
if n.Key == org.NoteKeyLegal {
lits = append(lits, n.Text)
}
}
return &LegalLiterals{
LegalReference: lits,
}
}
func newPeriodDates(p *cal.Period) *PeriodDates {
if p == nil {
return nil
}
return &PeriodDates{
StartDate: p.Start.String(),
EndDate: p.End.String(),
}
}