verifactu-rails 0.1.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/COMPLIANCE.md +149 -0
- data/LICENSE +21 -0
- data/README.md +404 -0
- data/doc/FUENTES.md +505 -0
- data/lib/generators/verifactu/install/install_generator.rb +61 -0
- data/lib/generators/verifactu/install/templates/instalar_verifactu.rb.tt +19 -0
- data/lib/generators/verifactu/install/templates/verifactu.rb.tt +64 -0
- data/lib/verifactu-rails.rb +31 -0
- data/lib/verifactu_rails/certificado.rb +122 -0
- data/lib/verifactu_rails/consulta.rb +279 -0
- data/lib/verifactu_rails/desglose.rb +396 -0
- data/lib/verifactu_rails/envio.rb +153 -0
- data/lib/verifactu_rails/error.rb +31 -0
- data/lib/verifactu_rails/formato.rb +199 -0
- data/lib/verifactu_rails/huella.rb +130 -0
- data/lib/verifactu_rails/importe.rb +80 -0
- data/lib/verifactu_rails/libro/autochequeo.rb +67 -0
- data/lib/verifactu_rails/libro/cadena.rb +110 -0
- data/lib/verifactu_rails/libro/migracion.rb +92 -0
- data/lib/verifactu_rails/libro/reconciliacion.rb +224 -0
- data/lib/verifactu_rails/libro/registro.rb +74 -0
- data/lib/verifactu_rails/libro/remesa.rb +120 -0
- data/lib/verifactu_rails/libro.rb +89 -0
- data/lib/verifactu_rails/qr.rb +56 -0
- data/lib/verifactu_rails/railtie.rb +30 -0
- data/lib/verifactu_rails/registro.rb +833 -0
- data/lib/verifactu_rails/respuesta.rb +154 -0
- data/lib/verifactu_rails/schemas/ConsultaLR.xsd +54 -0
- data/lib/verifactu_rails/schemas/EventosSIF.xsd +823 -0
- data/lib/verifactu_rails/schemas/PROCEDENCIA.md +64 -0
- data/lib/verifactu_rails/schemas/RespuestaConsultaLR.xsd +201 -0
- data/lib/verifactu_rails/schemas/RespuestaSuministro.xsd +139 -0
- data/lib/verifactu_rails/schemas/RespuestaValRegistNoVeriFactu.xsd +103 -0
- data/lib/verifactu_rails/schemas/SuministroInformacion.xsd +1390 -0
- data/lib/verifactu_rails/schemas/SuministroLR.xsd +25 -0
- data/lib/verifactu_rails/schemas/catalog.xml +5 -0
- data/lib/verifactu_rails/schemas/xmldsig-core-schema.xsd +318 -0
- data/lib/verifactu_rails/sistema_informatico.rb +88 -0
- data/lib/verifactu_rails/transporte.rb +127 -0
- data/lib/verifactu_rails/version.rb +5 -0
- metadata +124 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!-- editado con XMLSpy v2019 sp1 (x64) (http://www.altova.com) por Puesto de Trabajo (Agencia Estatal de Administracion Tributaria ((AEAT))) -->
|
|
3
|
+
<!-- edited with XMLSpy v2009 sp1 (http://www.altova.com) by PC Corporativo (AGENCIA TRIBUTARIA) -->
|
|
4
|
+
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:sfLR="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd" xmlns:sf="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd" targetNamespace="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd" elementFormDefault="qualified">
|
|
5
|
+
<import namespace="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd" schemaLocation="SuministroInformacion.xsd"/>
|
|
6
|
+
<element name="RegFactuSistemaFacturacion">
|
|
7
|
+
<complexType>
|
|
8
|
+
<sequence>
|
|
9
|
+
<element name="Cabecera" type="sf:CabeceraType"/>
|
|
10
|
+
<element name="RegistroFactura" type="sfLR:RegistroFacturaType" maxOccurs="1000"/>
|
|
11
|
+
</sequence>
|
|
12
|
+
</complexType>
|
|
13
|
+
</element>
|
|
14
|
+
<complexType name="RegistroFacturaType">
|
|
15
|
+
<annotation>
|
|
16
|
+
<documentation xml:lang="es">Datos correspondientes a los registros de facturacion</documentation>
|
|
17
|
+
</annotation>
|
|
18
|
+
<sequence>
|
|
19
|
+
<choice>
|
|
20
|
+
<element ref="sf:RegistroAlta"/>
|
|
21
|
+
<element ref="sf:RegistroAnulacion"/>
|
|
22
|
+
</choice>
|
|
23
|
+
</sequence>
|
|
24
|
+
</complexType>
|
|
25
|
+
</schema>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<?xml version="1.0"?>
|
|
2
|
+
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
|
|
3
|
+
<uri name="http://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd" uri="xmldsig-core-schema.xsd"/>
|
|
4
|
+
<system systemId="http://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd" uri="xmldsig-core-schema.xsd"/>
|
|
5
|
+
</catalog>
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!DOCTYPE schema
|
|
3
|
+
PUBLIC "-//W3C//DTD XMLSchema 200102//EN" "http://www.w3.org/2001/XMLSchema.dtd"
|
|
4
|
+
[
|
|
5
|
+
<!ATTLIST schema
|
|
6
|
+
xmlns:ds CDATA #FIXED "http://www.w3.org/2000/09/xmldsig#">
|
|
7
|
+
<!ENTITY dsig 'http://www.w3.org/2000/09/xmldsig#'>
|
|
8
|
+
<!ENTITY % p ''>
|
|
9
|
+
<!ENTITY % s ''>
|
|
10
|
+
]>
|
|
11
|
+
|
|
12
|
+
<!-- Schema for XML Signatures
|
|
13
|
+
http://www.w3.org/2000/09/xmldsig#
|
|
14
|
+
$Revision: 1.2 $ on $Date: 2013/04/16 12:48:49 $ by $Author: denis $
|
|
15
|
+
|
|
16
|
+
Copyright 2001 The Internet Society and W3C (Massachusetts Institute
|
|
17
|
+
of Technology, Institut National de Recherche en Informatique et en
|
|
18
|
+
Automatique, Keio University). All Rights Reserved.
|
|
19
|
+
http://www.w3.org/Consortium/Legal/
|
|
20
|
+
|
|
21
|
+
This document is governed by the W3C Software License [1] as described
|
|
22
|
+
in the FAQ [2].
|
|
23
|
+
|
|
24
|
+
[1] http://www.w3.org/Consortium/Legal/copyright-software-19980720
|
|
25
|
+
[2] http://www.w3.org/Consortium/Legal/IPR-FAQ-20000620.html#DTD
|
|
26
|
+
-->
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
<schema xmlns="http://www.w3.org/2001/XMLSchema"
|
|
30
|
+
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
|
|
31
|
+
targetNamespace="http://www.w3.org/2000/09/xmldsig#"
|
|
32
|
+
version="0.1" elementFormDefault="qualified">
|
|
33
|
+
|
|
34
|
+
<!-- Basic Types Defined for Signatures -->
|
|
35
|
+
|
|
36
|
+
<simpleType name="CryptoBinary">
|
|
37
|
+
<restriction base="base64Binary">
|
|
38
|
+
</restriction>
|
|
39
|
+
</simpleType>
|
|
40
|
+
|
|
41
|
+
<!-- Start Signature -->
|
|
42
|
+
|
|
43
|
+
<element name="Signature" type="ds:SignatureType"/>
|
|
44
|
+
<complexType name="SignatureType">
|
|
45
|
+
<sequence>
|
|
46
|
+
<element ref="ds:SignedInfo"/>
|
|
47
|
+
<element ref="ds:SignatureValue"/>
|
|
48
|
+
<element ref="ds:KeyInfo" minOccurs="0"/>
|
|
49
|
+
<element ref="ds:Object" minOccurs="0" maxOccurs="unbounded"/>
|
|
50
|
+
</sequence>
|
|
51
|
+
<attribute name="Id" type="ID" use="optional"/>
|
|
52
|
+
</complexType>
|
|
53
|
+
|
|
54
|
+
<element name="SignatureValue" type="ds:SignatureValueType"/>
|
|
55
|
+
<complexType name="SignatureValueType">
|
|
56
|
+
<simpleContent>
|
|
57
|
+
<extension base="base64Binary">
|
|
58
|
+
<attribute name="Id" type="ID" use="optional"/>
|
|
59
|
+
</extension>
|
|
60
|
+
</simpleContent>
|
|
61
|
+
</complexType>
|
|
62
|
+
|
|
63
|
+
<!-- Start SignedInfo -->
|
|
64
|
+
|
|
65
|
+
<element name="SignedInfo" type="ds:SignedInfoType"/>
|
|
66
|
+
<complexType name="SignedInfoType">
|
|
67
|
+
<sequence>
|
|
68
|
+
<element ref="ds:CanonicalizationMethod"/>
|
|
69
|
+
<element ref="ds:SignatureMethod"/>
|
|
70
|
+
<element ref="ds:Reference" maxOccurs="unbounded"/>
|
|
71
|
+
</sequence>
|
|
72
|
+
<attribute name="Id" type="ID" use="optional"/>
|
|
73
|
+
</complexType>
|
|
74
|
+
|
|
75
|
+
<element name="CanonicalizationMethod" type="ds:CanonicalizationMethodType"/>
|
|
76
|
+
<complexType name="CanonicalizationMethodType" mixed="true">
|
|
77
|
+
<sequence>
|
|
78
|
+
<any namespace="##any" minOccurs="0" maxOccurs="unbounded"/>
|
|
79
|
+
<!-- (0,unbounded) elements from (1,1) namespace -->
|
|
80
|
+
</sequence>
|
|
81
|
+
<attribute name="Algorithm" type="anyURI" use="required"/>
|
|
82
|
+
</complexType>
|
|
83
|
+
|
|
84
|
+
<element name="SignatureMethod" type="ds:SignatureMethodType"/>
|
|
85
|
+
<complexType name="SignatureMethodType" mixed="true">
|
|
86
|
+
<sequence>
|
|
87
|
+
<element name="HMACOutputLength" minOccurs="0" type="ds:HMACOutputLengthType"/>
|
|
88
|
+
<any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
|
|
89
|
+
<!-- (0,unbounded) elements from (1,1) external namespace -->
|
|
90
|
+
</sequence>
|
|
91
|
+
<attribute name="Algorithm" type="anyURI" use="required"/>
|
|
92
|
+
</complexType>
|
|
93
|
+
|
|
94
|
+
<!-- Start Reference -->
|
|
95
|
+
|
|
96
|
+
<element name="Reference" type="ds:ReferenceType"/>
|
|
97
|
+
<complexType name="ReferenceType">
|
|
98
|
+
<sequence>
|
|
99
|
+
<element ref="ds:Transforms" minOccurs="0"/>
|
|
100
|
+
<element ref="ds:DigestMethod"/>
|
|
101
|
+
<element ref="ds:DigestValue"/>
|
|
102
|
+
</sequence>
|
|
103
|
+
<attribute name="Id" type="ID" use="optional"/>
|
|
104
|
+
<attribute name="URI" type="anyURI" use="optional"/>
|
|
105
|
+
<attribute name="Type" type="anyURI" use="optional"/>
|
|
106
|
+
</complexType>
|
|
107
|
+
|
|
108
|
+
<element name="Transforms" type="ds:TransformsType"/>
|
|
109
|
+
<complexType name="TransformsType">
|
|
110
|
+
<sequence>
|
|
111
|
+
<element ref="ds:Transform" maxOccurs="unbounded"/>
|
|
112
|
+
</sequence>
|
|
113
|
+
</complexType>
|
|
114
|
+
|
|
115
|
+
<element name="Transform" type="ds:TransformType"/>
|
|
116
|
+
<complexType name="TransformType" mixed="true">
|
|
117
|
+
<choice minOccurs="0" maxOccurs="unbounded">
|
|
118
|
+
<any namespace="##other" processContents="lax"/>
|
|
119
|
+
<!-- (1,1) elements from (0,unbounded) namespaces -->
|
|
120
|
+
<element name="XPath" type="string"/>
|
|
121
|
+
</choice>
|
|
122
|
+
<attribute name="Algorithm" type="anyURI" use="required"/>
|
|
123
|
+
</complexType>
|
|
124
|
+
|
|
125
|
+
<!-- End Reference -->
|
|
126
|
+
|
|
127
|
+
<element name="DigestMethod" type="ds:DigestMethodType"/>
|
|
128
|
+
<complexType name="DigestMethodType" mixed="true">
|
|
129
|
+
<sequence>
|
|
130
|
+
<any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
|
|
131
|
+
</sequence>
|
|
132
|
+
<attribute name="Algorithm" type="anyURI" use="required"/>
|
|
133
|
+
</complexType>
|
|
134
|
+
|
|
135
|
+
<element name="DigestValue" type="ds:DigestValueType"/>
|
|
136
|
+
<simpleType name="DigestValueType">
|
|
137
|
+
<restriction base="base64Binary"/>
|
|
138
|
+
</simpleType>
|
|
139
|
+
|
|
140
|
+
<!-- End SignedInfo -->
|
|
141
|
+
|
|
142
|
+
<!-- Start KeyInfo -->
|
|
143
|
+
|
|
144
|
+
<element name="KeyInfo" type="ds:KeyInfoType"/>
|
|
145
|
+
<complexType name="KeyInfoType" mixed="true">
|
|
146
|
+
<choice maxOccurs="unbounded">
|
|
147
|
+
<element ref="ds:KeyName"/>
|
|
148
|
+
<element ref="ds:KeyValue"/>
|
|
149
|
+
<element ref="ds:RetrievalMethod"/>
|
|
150
|
+
<element ref="ds:X509Data"/>
|
|
151
|
+
<element ref="ds:PGPData"/>
|
|
152
|
+
<element ref="ds:SPKIData"/>
|
|
153
|
+
<element ref="ds:MgmtData"/>
|
|
154
|
+
<any processContents="lax" namespace="##other"/>
|
|
155
|
+
<!-- (1,1) elements from (0,unbounded) namespaces -->
|
|
156
|
+
</choice>
|
|
157
|
+
<attribute name="Id" type="ID" use="optional"/>
|
|
158
|
+
</complexType>
|
|
159
|
+
|
|
160
|
+
<element name="KeyName" type="string"/>
|
|
161
|
+
<element name="MgmtData" type="string"/>
|
|
162
|
+
|
|
163
|
+
<element name="KeyValue" type="ds:KeyValueType"/>
|
|
164
|
+
<complexType name="KeyValueType" mixed="true">
|
|
165
|
+
<choice>
|
|
166
|
+
<element ref="ds:DSAKeyValue"/>
|
|
167
|
+
<element ref="ds:RSAKeyValue"/>
|
|
168
|
+
<any namespace="##other" processContents="lax"/>
|
|
169
|
+
</choice>
|
|
170
|
+
</complexType>
|
|
171
|
+
|
|
172
|
+
<element name="RetrievalMethod" type="ds:RetrievalMethodType"/>
|
|
173
|
+
<complexType name="RetrievalMethodType">
|
|
174
|
+
<sequence>
|
|
175
|
+
<element ref="ds:Transforms" minOccurs="0"/>
|
|
176
|
+
</sequence>
|
|
177
|
+
<attribute name="URI" type="anyURI"/>
|
|
178
|
+
<attribute name="Type" type="anyURI" use="optional"/>
|
|
179
|
+
</complexType>
|
|
180
|
+
|
|
181
|
+
<!-- Start X509Data -->
|
|
182
|
+
|
|
183
|
+
<element name="X509Data" type="ds:X509DataType"/>
|
|
184
|
+
<complexType name="X509DataType">
|
|
185
|
+
<sequence maxOccurs="unbounded">
|
|
186
|
+
<choice>
|
|
187
|
+
<element name="X509IssuerSerial" type="ds:X509IssuerSerialType"/>
|
|
188
|
+
<element name="X509SKI" type="base64Binary"/>
|
|
189
|
+
<element name="X509SubjectName" type="string"/>
|
|
190
|
+
<element name="X509Certificate" type="base64Binary"/>
|
|
191
|
+
<element name="X509CRL" type="base64Binary"/>
|
|
192
|
+
<any namespace="##other" processContents="lax"/>
|
|
193
|
+
</choice>
|
|
194
|
+
</sequence>
|
|
195
|
+
</complexType>
|
|
196
|
+
|
|
197
|
+
<complexType name="X509IssuerSerialType">
|
|
198
|
+
<sequence>
|
|
199
|
+
<element name="X509IssuerName" type="string"/>
|
|
200
|
+
<element name="X509SerialNumber" type="integer"/>
|
|
201
|
+
</sequence>
|
|
202
|
+
</complexType>
|
|
203
|
+
|
|
204
|
+
<!-- End X509Data -->
|
|
205
|
+
|
|
206
|
+
<!-- Begin PGPData -->
|
|
207
|
+
|
|
208
|
+
<element name="PGPData" type="ds:PGPDataType"/>
|
|
209
|
+
<complexType name="PGPDataType">
|
|
210
|
+
<choice>
|
|
211
|
+
<sequence>
|
|
212
|
+
<element name="PGPKeyID" type="base64Binary"/>
|
|
213
|
+
<element name="PGPKeyPacket" type="base64Binary" minOccurs="0"/>
|
|
214
|
+
<any namespace="##other" processContents="lax" minOccurs="0"
|
|
215
|
+
maxOccurs="unbounded"/>
|
|
216
|
+
</sequence>
|
|
217
|
+
<sequence>
|
|
218
|
+
<element name="PGPKeyPacket" type="base64Binary"/>
|
|
219
|
+
<any namespace="##other" processContents="lax" minOccurs="0"
|
|
220
|
+
maxOccurs="unbounded"/>
|
|
221
|
+
</sequence>
|
|
222
|
+
</choice>
|
|
223
|
+
</complexType>
|
|
224
|
+
|
|
225
|
+
<!-- End PGPData -->
|
|
226
|
+
|
|
227
|
+
<!-- Begin SPKIData -->
|
|
228
|
+
|
|
229
|
+
<element name="SPKIData" type="ds:SPKIDataType"/>
|
|
230
|
+
<complexType name="SPKIDataType">
|
|
231
|
+
<sequence maxOccurs="unbounded">
|
|
232
|
+
<element name="SPKISexp" type="base64Binary"/>
|
|
233
|
+
<any namespace="##other" processContents="lax" minOccurs="0"/>
|
|
234
|
+
</sequence>
|
|
235
|
+
</complexType>
|
|
236
|
+
|
|
237
|
+
<!-- End SPKIData -->
|
|
238
|
+
|
|
239
|
+
<!-- End KeyInfo -->
|
|
240
|
+
|
|
241
|
+
<!-- Start Object (Manifest, SignatureProperty) -->
|
|
242
|
+
|
|
243
|
+
<element name="Object" type="ds:ObjectType"/>
|
|
244
|
+
<complexType name="ObjectType" mixed="true">
|
|
245
|
+
<sequence minOccurs="0" maxOccurs="unbounded">
|
|
246
|
+
<any namespace="##any" processContents="lax"/>
|
|
247
|
+
</sequence>
|
|
248
|
+
<attribute name="Id" type="ID" use="optional"/>
|
|
249
|
+
<attribute name="MimeType" type="string" use="optional"/> <!-- add a grep facet -->
|
|
250
|
+
<attribute name="Encoding" type="anyURI" use="optional"/>
|
|
251
|
+
</complexType>
|
|
252
|
+
|
|
253
|
+
<element name="Manifest" type="ds:ManifestType"/>
|
|
254
|
+
<complexType name="ManifestType">
|
|
255
|
+
<sequence>
|
|
256
|
+
<element ref="ds:Reference" maxOccurs="unbounded"/>
|
|
257
|
+
</sequence>
|
|
258
|
+
<attribute name="Id" type="ID" use="optional"/>
|
|
259
|
+
</complexType>
|
|
260
|
+
|
|
261
|
+
<element name="SignatureProperties" type="ds:SignaturePropertiesType"/>
|
|
262
|
+
<complexType name="SignaturePropertiesType">
|
|
263
|
+
<sequence>
|
|
264
|
+
<element ref="ds:SignatureProperty" maxOccurs="unbounded"/>
|
|
265
|
+
</sequence>
|
|
266
|
+
<attribute name="Id" type="ID" use="optional"/>
|
|
267
|
+
</complexType>
|
|
268
|
+
|
|
269
|
+
<element name="SignatureProperty" type="ds:SignaturePropertyType"/>
|
|
270
|
+
<complexType name="SignaturePropertyType" mixed="true">
|
|
271
|
+
<choice maxOccurs="unbounded">
|
|
272
|
+
<any namespace="##other" processContents="lax"/>
|
|
273
|
+
<!-- (1,1) elements from (1,unbounded) namespaces -->
|
|
274
|
+
</choice>
|
|
275
|
+
<attribute name="Target" type="anyURI" use="required"/>
|
|
276
|
+
<attribute name="Id" type="ID" use="optional"/>
|
|
277
|
+
</complexType>
|
|
278
|
+
|
|
279
|
+
<!-- End Object (Manifest, SignatureProperty) -->
|
|
280
|
+
|
|
281
|
+
<!-- Start Algorithm Parameters -->
|
|
282
|
+
|
|
283
|
+
<simpleType name="HMACOutputLengthType">
|
|
284
|
+
<restriction base="integer"/>
|
|
285
|
+
</simpleType>
|
|
286
|
+
|
|
287
|
+
<!-- Start KeyValue Element-types -->
|
|
288
|
+
|
|
289
|
+
<element name="DSAKeyValue" type="ds:DSAKeyValueType"/>
|
|
290
|
+
<complexType name="DSAKeyValueType">
|
|
291
|
+
<sequence>
|
|
292
|
+
<sequence minOccurs="0">
|
|
293
|
+
<element name="P" type="ds:CryptoBinary"/>
|
|
294
|
+
<element name="Q" type="ds:CryptoBinary"/>
|
|
295
|
+
</sequence>
|
|
296
|
+
<element name="G" type="ds:CryptoBinary" minOccurs="0"/>
|
|
297
|
+
<element name="Y" type="ds:CryptoBinary"/>
|
|
298
|
+
<element name="J" type="ds:CryptoBinary" minOccurs="0"/>
|
|
299
|
+
<sequence minOccurs="0">
|
|
300
|
+
<element name="Seed" type="ds:CryptoBinary"/>
|
|
301
|
+
<element name="PgenCounter" type="ds:CryptoBinary"/>
|
|
302
|
+
</sequence>
|
|
303
|
+
</sequence>
|
|
304
|
+
</complexType>
|
|
305
|
+
|
|
306
|
+
<element name="RSAKeyValue" type="ds:RSAKeyValueType"/>
|
|
307
|
+
<complexType name="RSAKeyValueType">
|
|
308
|
+
<sequence>
|
|
309
|
+
<element name="Modulus" type="ds:CryptoBinary"/>
|
|
310
|
+
<element name="Exponent" type="ds:CryptoBinary"/>
|
|
311
|
+
</sequence>
|
|
312
|
+
</complexType>
|
|
313
|
+
|
|
314
|
+
<!-- End KeyValue Element-types -->
|
|
315
|
+
|
|
316
|
+
<!-- End Signature -->
|
|
317
|
+
|
|
318
|
+
</schema>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'formato'
|
|
4
|
+
require_relative 'error'
|
|
5
|
+
|
|
6
|
+
module VerifactuRails
|
|
7
|
+
# Identificación del sistema informático de facturación, obligatoria en CADA
|
|
8
|
+
# registro remitido (sf:SistemaInformaticoType).
|
|
9
|
+
#
|
|
10
|
+
# Ojo a lo que es esto realmente: la declaración de quién produce el SIF, en
|
|
11
|
+
# cada envío. Los valores describen al SIF DESPLEGADO, no a esta gema, y los
|
|
12
|
+
# aporta quien lo despliega. Esta librería es un componente del SIF, no el SIF.
|
|
13
|
+
class SistemaInformatico
|
|
14
|
+
# TipoUsoPosibleSoloVerifactu va fijo a 'S' y no se puede configurar.
|
|
15
|
+
#
|
|
16
|
+
# No es un capricho: declarar 'N' significa que el SIF puede operar en modo
|
|
17
|
+
# NO VERI*FACTU, y eso obliga a llevar registro de eventos (RD 1007/2023;
|
|
18
|
+
# FAQs Desarrolladores v1.3, ap. 15, nota 1). Esta gema no implementa
|
|
19
|
+
# eventos, así que permitir 'N' sería dejar construir un sistema incompleto
|
|
20
|
+
# sin avisar. Si necesitas 'N', esta no es la librería.
|
|
21
|
+
SOLO_VERIFACTU = 'S'
|
|
22
|
+
|
|
23
|
+
attr_reader :nombre_razon, :nif, :nombre_sistema, :id_sistema, :version,
|
|
24
|
+
:numero_instalacion, :multi_ot, :multiples_ot
|
|
25
|
+
|
|
26
|
+
# @param multi_ot [Boolean] TipoUsoPosibleMultiOT: si el SIF puede usarse
|
|
27
|
+
# para varios obligados tributarios.
|
|
28
|
+
# @param multiples_ot [Boolean] IndicadorMultiplesOT: si de hecho se está
|
|
29
|
+
# usando para varios en este momento.
|
|
30
|
+
def initialize(nombre_razon:, nif:, nombre_sistema:, id_sistema:, version:,
|
|
31
|
+
numero_instalacion:, multi_ot: false, multiples_ot: false)
|
|
32
|
+
@nombre_razon = Formato.limitar(nombre_razon, 'NombreRazon', 120)
|
|
33
|
+
@nif = Formato.nif(nif, 'NIF del productor')
|
|
34
|
+
@nombre_sistema = Formato.limitar(nombre_sistema, 'NombreSistemaInformatico', 30)
|
|
35
|
+
@id_sistema = validar_id_sistema(id_sistema)
|
|
36
|
+
@version = Formato.limitar(version, 'Version', 50)
|
|
37
|
+
@numero_instalacion = Formato.limitar(numero_instalacion, 'NumeroInstalacion', 100)
|
|
38
|
+
# Por Formato.si_no y no por valor de verdad: 'N' es truthy en Ruby y se
|
|
39
|
+
# emitía como 'S', invirtiendo lo que el usuario había declarado.
|
|
40
|
+
@multi_ot = Formato.si_no(multi_ot, 'TipoUsoPosibleMultiOT') == 'S'
|
|
41
|
+
@multiples_ot = Formato.si_no(multiples_ot, 'IndicadorMultiplesOT') == 'S'
|
|
42
|
+
|
|
43
|
+
if @multiples_ot && !@multi_ot
|
|
44
|
+
raise ValidacionError,
|
|
45
|
+
'IndicadorMultiplesOT no puede ser cierto si TipoUsoPosibleMultiOT ' \
|
|
46
|
+
'es falso: no puedes estar usándolo para varios obligados si el ' \
|
|
47
|
+
'sistema declara que no lo admite'
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Ap. 3.1.5: "deberá tener rellenas siempre las dos posiciones, cada una de
|
|
52
|
+
# las cuales deberá ser una letra mayúscula, excepto la Ñ, o un dígito
|
|
53
|
+
# numérico". El XSD solo limita la longitud a 2, así que sin esto pasaban
|
|
54
|
+
# valores de una posición o en minúscula.
|
|
55
|
+
PATRON_ID_SISTEMA = /\A[A-Z0-9]{2}\z/
|
|
56
|
+
|
|
57
|
+
def solo_verifactu = SOLO_VERIFACTU
|
|
58
|
+
def multi_ot? = @multi_ot
|
|
59
|
+
def multiples_ot? = @multiples_ot
|
|
60
|
+
|
|
61
|
+
# Orden EXACTO de sf:SistemaInformaticoType. Es una <sequence>: alterarlo
|
|
62
|
+
# invalida el documento.
|
|
63
|
+
def a_pares
|
|
64
|
+
{
|
|
65
|
+
'NombreRazon' => nombre_razon,
|
|
66
|
+
'NIF' => nif,
|
|
67
|
+
'NombreSistemaInformatico' => nombre_sistema,
|
|
68
|
+
'IdSistemaInformatico' => id_sistema,
|
|
69
|
+
'Version' => version,
|
|
70
|
+
'NumeroInstalacion' => numero_instalacion,
|
|
71
|
+
'TipoUsoPosibleSoloVerifactu' => SOLO_VERIFACTU,
|
|
72
|
+
'TipoUsoPosibleMultiOT' => multi_ot ? 'S' : 'N',
|
|
73
|
+
'IndicadorMultiplesOT' => multiples_ot ? 'S' : 'N'
|
|
74
|
+
}
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def validar_id_sistema(valor)
|
|
80
|
+
cadena = Formato.limitar(valor, 'IdSistemaInformatico', 2)
|
|
81
|
+
return cadena if cadena.match?(PATRON_ID_SISTEMA)
|
|
82
|
+
|
|
83
|
+
raise ValidacionError,
|
|
84
|
+
'IdSistemaInformatico debe tener exactamente dos posiciones, cada una ' \
|
|
85
|
+
"una letra mayúscula (salvo la Ñ) o un dígito: #{cadena.inspect}"
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'uri'
|
|
5
|
+
require_relative 'certificado'
|
|
6
|
+
require_relative 'error'
|
|
7
|
+
require_relative 'formato'
|
|
8
|
+
|
|
9
|
+
module VerifactuRails
|
|
10
|
+
class TransporteError < StandardError
|
|
11
|
+
include Error
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Cliente HTTP con autenticación mutua TLS contra el servicio VERI*FACTU.
|
|
15
|
+
#
|
|
16
|
+
# Sin Savon: el servicio es un único endpoint con un sobre SOAP fijo, así que
|
|
17
|
+
# Net::HTTP de la stdlib sobra. Nos ahorra httpi, wasabi, gyoku, akami y nori.
|
|
18
|
+
class Transporte
|
|
19
|
+
# Preproducción y producción se corresponden uno a uno: prewww1 <-> www1,
|
|
20
|
+
# prewww2 <-> www2, prewww10 <-> www10 (este último, el de certificado de
|
|
21
|
+
# sello). Fuente: portal de pruebas externas de la AEAT.
|
|
22
|
+
#
|
|
23
|
+
# Aviso operativo del propio portal: preproducción es para pruebas puntuales,
|
|
24
|
+
# NO para pruebas masivas ni para validaciones integradas en procesos de
|
|
25
|
+
# producción. Un uso que consideren abusivo puede acabar en bloqueo.
|
|
26
|
+
ENDPOINTS = {
|
|
27
|
+
[:pruebas, false] => 'https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP',
|
|
28
|
+
[:pruebas, true] => 'https://prewww10.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP',
|
|
29
|
+
[:produccion, false] => 'https://www1.agenciatributaria.gob.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP',
|
|
30
|
+
[:produccion, true] => 'https://www10.agenciatributaria.gob.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP'
|
|
31
|
+
}.freeze
|
|
32
|
+
|
|
33
|
+
SOAP_NS = 'http://schemas.xmlsoap.org/soap/envelope/'
|
|
34
|
+
|
|
35
|
+
attr_reader :certificado, :entorno, :url
|
|
36
|
+
|
|
37
|
+
# @param sello [Boolean, nil] fuerza el endpoint de sello de entidad.
|
|
38
|
+
# Si es nil se deduce del propio certificado.
|
|
39
|
+
def initialize(certificado:, entorno: :pruebas, sello: nil, url: nil,
|
|
40
|
+
ca_file: nil, timeout: 30)
|
|
41
|
+
unless %i[pruebas produccion].include?(entorno)
|
|
42
|
+
raise ValidacionError, "Entorno inválido: #{entorno.inspect} (usa :pruebas o :produccion)"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
@certificado = Formato.objeto(certificado, 'certificado', Certificado)
|
|
46
|
+
@entorno = entorno
|
|
47
|
+
# Sin normalizar, un sello: 'S' llegaba a ENDPOINTS.fetch([entorno, 'S']) y
|
|
48
|
+
# daba un KeyError, justo al lado de la comprobación de entorno que sí da
|
|
49
|
+
# un error del dominio.
|
|
50
|
+
@sello = sello.nil? ? @certificado.sello? : Formato.si_no(sello, 'sello') == 'S'
|
|
51
|
+
@url = url || ENDPOINTS.fetch([entorno, @sello])
|
|
52
|
+
@ca_file = ca_file
|
|
53
|
+
@timeout = timeout
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def sello? = @sello
|
|
57
|
+
|
|
58
|
+
# Envía el XML del registro ya construido. Devuelve la respuesta cruda:
|
|
59
|
+
# el parseo es responsabilidad de la capa superior.
|
|
60
|
+
def enviar(xml_registro)
|
|
61
|
+
peticion = Net::HTTP::Post.new(uri)
|
|
62
|
+
peticion['Content-Type'] = 'text/xml; charset=utf-8'
|
|
63
|
+
peticion['SOAPAction'] = '""'
|
|
64
|
+
peticion.body = envolver(xml_registro)
|
|
65
|
+
|
|
66
|
+
respuesta = cliente.request(peticion)
|
|
67
|
+
{ codigo: respuesta.code.to_i, cuerpo: respuesta.body }
|
|
68
|
+
rescue OpenSSL::SSL::SSLError => e
|
|
69
|
+
raise TransporteError, mensaje_ssl(e)
|
|
70
|
+
rescue Net::OpenTimeout, Net::ReadTimeout => e
|
|
71
|
+
raise TransporteError, "Timeout contra #{uri.host}: #{e.class}"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Una declaración <?xml?> solo puede ir al principio del documento, y
|
|
75
|
+
# Envio#to_xml emite la suya. Incrustarla tal cual dentro del Body producía
|
|
76
|
+
# un sobre MAL FORMADO con dos declaraciones, que la AEAT contestó con
|
|
77
|
+
# "Codigo[102].Error interno en el servidor": el fallo era de parseo, no de
|
|
78
|
+
# validación, así que el mensaje no orientaba en absoluto.
|
|
79
|
+
DECLARACION_XML = /\A\s*<\?xml[^>]*\?>\s*/
|
|
80
|
+
|
|
81
|
+
def envolver(xml_registro)
|
|
82
|
+
cuerpo = xml_registro.to_s.sub(DECLARACION_XML, '')
|
|
83
|
+
# document/literal según el WSDL: el Body lleva directamente el elemento,
|
|
84
|
+
# sin envoltorio de operación.
|
|
85
|
+
<<~XML
|
|
86
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
87
|
+
<soapenv:Envelope xmlns:soapenv="#{SOAP_NS}">
|
|
88
|
+
<soapenv:Header/>
|
|
89
|
+
<soapenv:Body>#{cuerpo}</soapenv:Body>
|
|
90
|
+
</soapenv:Envelope>
|
|
91
|
+
XML
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def cliente
|
|
95
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
96
|
+
http.use_ssl = true
|
|
97
|
+
http.cert = certificado.certificado
|
|
98
|
+
http.key = certificado.clave
|
|
99
|
+
http.extra_chain_cert = certificado.cadena if certificado.cadena.any?
|
|
100
|
+
# NUNCA VERIFY_NONE. Si falla la verificación, el arreglo es aportar la
|
|
101
|
+
# cadena de la CA correcta en ca_file, no desactivar la comprobación.
|
|
102
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
103
|
+
http.ca_file = @ca_file if @ca_file
|
|
104
|
+
http.open_timeout = @timeout
|
|
105
|
+
http.read_timeout = @timeout
|
|
106
|
+
http
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
private
|
|
110
|
+
|
|
111
|
+
def uri = @uri ||= URI.parse(@url)
|
|
112
|
+
|
|
113
|
+
def mensaje_ssl(error)
|
|
114
|
+
if error.message.include?('certificate verify failed')
|
|
115
|
+
"Fallo al verificar el certificado de servidor de #{uri.host}. " \
|
|
116
|
+
'Desde la renovación de noviembre de 2025 la AEAT usa CA públicas ' \
|
|
117
|
+
'(Entrust/Sectigo bajo USERTrust RSA), así que la causa habitual NO es ' \
|
|
118
|
+
'que falte la cadena: mira si el almacén de confianza del sistema está ' \
|
|
119
|
+
'anticuado o si un proxy corporativo intercepta el TLS. Si aun así ' \
|
|
120
|
+
'necesitas anclar la cadena, pásala en ca_file. No uses VERIFY_NONE: ' \
|
|
121
|
+
"(#{error.message})"
|
|
122
|
+
else
|
|
123
|
+
"Error TLS contra #{uri.host}: #{error.message}"
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|