diff --git a/.github/workflows/openapi.yml b/.github/workflows/openapi.yml index f93e68258..bd59c5a17 100644 --- a/.github/workflows/openapi.yml +++ b/.github/workflows/openapi.yml @@ -5,6 +5,9 @@ on: paths: - website/openapi_v2*.yaml - website/scripts/check-openapi.mjs + - website/docusaurus.config.js + - website/plugins/** + - website/src/** - website/test/openapi-types.fixture.txt - website/package.json - website/pnpm-lock.yaml @@ -31,3 +34,5 @@ jobs: working-directory: website - run: pnpm test:openapi working-directory: website + - run: pnpm build + working-directory: website diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 709f1f74f..7ed3b70f9 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -46,12 +46,10 @@ const darkCodeTheme = require("prism-react-renderer").themes.dracula; specs: [ { id: "api-es", - route: "/api-es", spec: 'openapi_v2.yaml', }, { id: "api-en", - route: "/api-en", spec: 'openapi_v2.en.yaml', }, ], diff --git a/website/openapi_v2.en.yaml b/website/openapi_v2.en.yaml index 9749c8dee..0823d4ab4 100644 --- a/website/openapi_v2.en.yaml +++ b/website/openapi_v2.en.yaml @@ -13686,9 +13686,32 @@ components: ieps_mode: $ref: "#/components/schemas/IepsMode" Stamp: + allOf: + - $ref: '#/components/schemas/StampProperties' + - type: object + properties: + complement_string: + type: [string, 'null'] + description: Original string of the fiscal stamp. + InvoiceStamp: + allOf: + - $ref: '#/components/schemas/StampProperties' + - type: object + properties: + complement_string: + type: string + description: Original string of the fiscal stamp. + required: [date, sat_signature, sat_cert_number, signature, complement_string] + StampProperties: type: object description: Information about the digital stamp added by the PAC. properties: + version: + type: string + description: Digital fiscal stamp version. + legend: + type: string + description: Optional legend included in the digital fiscal stamp. signature: type: string description: Digital signature of the fiscal document. @@ -13696,9 +13719,6 @@ components: type: string description: "SAT FechaTimbrado: local date and time without a timezone offset. Preserved as text." example: "2026-09-17T06:59:16" - complement_string: - type: [string, 'null'] - description: Original string of the fiscal stamp. rfc_provider_cert: type: string description: Tax ID of the provider that certified the document. @@ -16423,25 +16443,51 @@ components: description: ISO 3166-1 alpha-3. CustomerUpdateInput: description: On update without createEditLink=true, omitted fields retain their value. A null value clears fields that accept null; an optional field can be omitted without necessarily accepting null. Null is not allowed for legal_name, tax_id, tax_system, or address. For email, phone, and the optional address fields street, exterior, interior, neighborhood, city, municipality, and state, empty or whitespace-only strings normalize to null. Empty postal codes are not accepted. - allOf: - - $ref: '#/components/schemas/CustomerEditableInput' - - type: object + type: object + properties: + legal_name: + type: string + minLength: 1 + description: | + Legal name or business name of the customer. *without* the corporate regime (e.g.: S.A. de C.V.). + example: Dunder Mifflin + tax_id: + type: string + minLength: 1 + example: ABC101010111 + description: | + In Mexican clients, it contains the customer's RFC. For foreigners, it is optional and represents the tax identification number, that is, the equivalent to the RFC in the customer's country. + email: + $ref: '#/components/schemas/CustomerEmailUpdateInput' + phone: *customer_phone + external_id: *customer_external_id_input + default_invoice_use: *customer_default_invoice_use_input + tax_system: + type: string + example: '601' + maxLength: 3 + minLength: 3 + description: | + Required for national clients. Key of the customer's tax regime, from the [Tax Regime Catalog](#tax-regime). + address: + type: object properties: - legal_name: + street: *customer_address_street_input + exterior: *customer_address_exterior_input + interior: *customer_address_interior_input + neighborhood: *customer_address_neighborhood_input + city: *customer_address_city_input + municipality: *customer_address_municipality_input + zip: type: string minLength: 1 - tax_id: + description: Postal code + example: '86500' + state: *customer_address_state_input + country: type: string - minLength: 1 - tax_system: - allOf: - - *customer_tax_system - - type: string - address: - type: object - properties: - country: - type: string + default: MEX + description: ISO 3166-1 alpha-3. CustomerUpdateWithEditLinkInput: description: With createEditLink=true, omitted fields retain their value and null clears the existing value, including fiscal fields. Non-null values must be valid. For email, phone, and the optional address fields street, exterior, interior, neighborhood, city, municipality, and state, empty or whitespace-only strings normalize to null. Empty postal codes are not accepted. type: object @@ -17399,12 +17445,7 @@ components: type: string format: uuid stamp: - allOf: - - $ref: '#/components/schemas/Stamp' - required: [date, sat_signature, sat_cert_number, signature, complement_string] - properties: - complement_string: - type: string + $ref: '#/components/schemas/InvoiceStamp' InvoicePending: title: Pending allOf: @@ -17431,7 +17472,7 @@ components: required: [status] properties: status: - $ref: '#/components/schemas/InvoiceCommonProperties/properties/cancellation_status' + $ref: '#/components/schemas/InvoiceCancellationStatus' requested_at: type: string format: date-time @@ -17451,6 +17492,18 @@ components: type: string format: uuid description: UUID of the replacement invoice, when applicable. + InvoiceCancellationStatus: + type: string + enum: + - none + - verifying + - pending + - accepted + - rejected + - expired + description: | + Current status of the cancellation request, if it has been made. `verifying` means the SAT has received the request and is validating it; Facturapi will keep checking until it changes to a final status. You can read more in the [Cancel Invoice](#tag/invoice/operation/deleteInvoice) section. + example: none InvoiceDraft: title: Invoice object with draft status allOf: @@ -17506,17 +17559,7 @@ components: cancellation: $ref: '#/components/schemas/InvoiceCancellation' cancellation_status: - type: string - enum: - - none - - verifying - - pending - - accepted - - rejected - - expired - description: | - Current status of the cancellation request, if it has been made. `verifying` means the SAT has received the request and is validating it; Facturapi will keep checking until it changes to a final status. You can read more in the [Cancel Invoice](#tag/invoice/operation/deleteInvoice) section. - example: none + $ref: '#/components/schemas/InvoiceCancellationStatus' canceled_at: type: - string diff --git a/website/openapi_v2.yaml b/website/openapi_v2.yaml index 2249e199d..d00cadc0a 100644 --- a/website/openapi_v2.yaml +++ b/website/openapi_v2.yaml @@ -13638,9 +13638,32 @@ components: ieps_mode: $ref: "#/components/schemas/IepsMode" Stamp: + allOf: + - $ref: '#/components/schemas/StampProperties' + - type: object + properties: + complement_string: + type: [string, 'null'] + description: Cadena original del timbre fiscal digital. + InvoiceStamp: + allOf: + - $ref: '#/components/schemas/StampProperties' + - type: object + properties: + complement_string: + type: string + description: Cadena original del timbre fiscal digital. + required: [date, sat_signature, sat_cert_number, signature, complement_string] + StampProperties: type: object description: Información sobre el timbre fiscal digital agregado por el PAC. properties: + version: + type: string + description: Versión del timbre fiscal digital. + legend: + type: string + description: Leyenda opcional incluida en el timbre fiscal digital. signature: type: string description: Sello digital del comprobante fiscal. @@ -13648,9 +13671,6 @@ components: type: string description: "FechaTimbrado del SAT: fecha y hora local sin offset de zona horaria. Se conserva como texto." example: "2026-09-17T06:59:16" - complement_string: - type: [string, 'null'] - description: Cadena original del timbre fiscal digital. rfc_provider_cert: type: string description: RFC del proveedor que certificó el comprobante. @@ -16423,25 +16443,49 @@ components: description: ISO 3166-1 alpha-3. CustomerUpdateInput: description: En una actualización sin createEditLink=true, los campos omitidos conservan su valor. Un valor null borra los campos que admiten null; un campo opcional puede omitirse sin que necesariamente admita null. No se permite null en legal_name, tax_id, tax_system ni address. En email, phone y los campos opcionales street, exterior, interior, neighborhood, city, municipality y state del domicilio, una cadena vacía o sólo espacios se normaliza a null. El código postal vacío no se acepta. - allOf: - - $ref: '#/components/schemas/CustomerEditableInput' - - type: object + type: object + properties: + legal_name: + type: string + minLength: 1 + description: | + Nombre Fiscal o Razón Social del cliente. *sin* el régimen societario (ej.: S.A. de C.V.). + example: Dunder Mifflin + tax_id: + type: string + minLength: 1 + example: ABC101010111 + description: En clientes de México contiene el RFC del cliente. Para extranjeros es opcional y representa el número de registro de identificación tributaria, es decir, el equivalente al RFC en el país del cliente. + email: + $ref: '#/components/schemas/CustomerEmailUpdateInput' + phone: *customer_phone + external_id: *customer_external_id_input + default_invoice_use: *customer_default_invoice_use_input + tax_system: + type: string + example: '601' + maxLength: 3 + minLength: 3 + description: Requerido para clientes nacionales. Clave del régimen fiscal del cliente, del catálogo de [Regímenes Fiscales](#r%C3%A9gimen-fiscal). + address: + type: object properties: - legal_name: + street: *customer_address_street_input + exterior: *customer_address_exterior_input + interior: *customer_address_interior_input + neighborhood: *customer_address_neighborhood_input + city: *customer_address_city_input + municipality: *customer_address_municipality_input + zip: type: string minLength: 1 - tax_id: + description: Código postal + example: '86500' + state: *customer_address_state_input + country: type: string - minLength: 1 - tax_system: - allOf: - - *customer_tax_system - - type: string - address: - type: object - properties: - country: - type: string + default: MEX + description: ISO 3166-1 alpha-3. CustomerUpdateWithEditLinkInput: description: Con createEditLink=true, los campos omitidos conservan su valor y null borra el valor existente, incluidos los datos fiscales. Los valores no nulos deben ser válidos. En email, phone y los campos opcionales street, exterior, interior, neighborhood, city, municipality y state del domicilio, una cadena vacía o sólo espacios se normaliza a null. El código postal vacío no se acepta. type: object @@ -17353,12 +17397,7 @@ components: type: string format: uuid stamp: - allOf: - - $ref: '#/components/schemas/Stamp' - required: [date, sat_signature, sat_cert_number, signature, complement_string] - properties: - complement_string: - type: string + $ref: '#/components/schemas/InvoiceStamp' InvoicePending: title: Pendiente allOf: @@ -17385,7 +17424,7 @@ components: required: [status] properties: status: - $ref: '#/components/schemas/InvoiceCommonProperties/properties/cancellation_status' + $ref: '#/components/schemas/InvoiceCancellationStatus' requested_at: type: string format: date-time @@ -17405,6 +17444,18 @@ components: type: string format: uuid description: UUID de la factura sustituta, cuando aplica. + InvoiceCancellationStatus: + type: string + enum: + - none + - pending + - accepted + - rejected + - expired + - verifying + description: | + Estado actual de la solicitud de cancelación, en caso de haberla realizado. Puedes leer más a detalle en la sección de [Cancelar Factura](#tag/invoice/operation/deleteInvoice)). + example: none InvoiceDraft: title: Objeto Invoice con status draft allOf: @@ -17460,17 +17511,7 @@ components: cancellation: $ref: '#/components/schemas/InvoiceCancellation' cancellation_status: - type: string - enum: - - none - - pending - - accepted - - rejected - - expired - - verifying - description: | - Estado actual de la solicitud de cancelación, en caso de haberla realizado. Puedes leer más a detalle en la sección de [Cancelar Factura](#tag/invoice/operation/deleteInvoice)). - example: none + $ref: '#/components/schemas/InvoiceCancellationStatus' canceled_at: type: - string diff --git a/website/src/css/custom.css b/website/src/css/custom.css index 69d27e296..55935fba4 100644 --- a/website/src/css/custom.css +++ b/website/src/css/custom.css @@ -525,3 +525,10 @@ html[data-theme='dark'] .redocusaurus div[aria-hidden='false'] [tabindex='0'] { margin-bottom: 0.75rem; } } + +/* The customer selectors already name each alternative. */ +[id='operation/createInvoice'] tr:has(> td[title='customer']) > td + td > div > div:first-child, +[id='operation/createInvoice'] tr:has(> td[title='customer']) + tr > td > div > div > span:first-child, +[id='operation/createInvoice'] tr:has(> td[title='customer']) + tr > td > div > div > div > span:first-child { + display: none; +} diff --git a/website/src/pages/api.tsx b/website/src/pages/api.tsx index a5c9eec96..90e65d814 100644 --- a/website/src/pages/api.tsx +++ b/website/src/pages/api.tsx @@ -10,6 +10,31 @@ function CustomPage() { const specData = useSpecData(`api-${locale}`); const displaySpecData = useMemo(() => { const spec = structuredClone(specData.spec); + // Presentation labels do not change the downloadable API contract. + spec.components.schemas.InvoiceCustomerInput.oneOf = [ + { + ...spec.components.schemas.CustomerCreateInput, + title: locale === 'es' ? 'Datos del cliente' : 'Customer details', + anyOf: spec.components.schemas.CustomerCreateInput.anyOf.map( + (variant: object, index: number) => ({ + ...variant, + title: (locale === 'es' + ? ['Nacional', 'Extranjero', 'Público en general'] + : ['Mexican customer', 'Foreign customer', 'General public'])[index], + }), + ), + }, + { + ...spec.components.schemas.InvoiceCustomerInput.oneOf[1], + title: locale === 'es' ? 'Cliente registrado' : 'Registered customer', + }, + ]; + spec.components.schemas.InvoiceCommonInput.allOf[0].properties.customer = { + ...spec.components.schemas.InvoiceCustomerInput, + description: locale === 'es' + ? 'Datos del cliente o ID de un cliente registrado.' + : 'Customer details or the ID of a registered customer.', + }; // ReDoc needs named discriminator variants to render a status selector. // Keep this presentation hint out of the contract: status may be omitted. spec.components.schemas.InvoiceCreateInput.oneOf = @@ -30,7 +55,7 @@ function CustomPage() { }, ); return {...specData, spec}; - }, [specData]); + }, [specData, locale]); return ( + + + + + + API reference + + +API reference + diff --git a/website/static/api-es/index.html b/website/static/api-es/index.html new file mode 100644 index 000000000..5dc33d943 --- /dev/null +++ b/website/static/api-es/index.html @@ -0,0 +1,11 @@ + + + + + + + Referencia API + + +Referencia API + diff --git a/website/test/openapi-types.fixture.txt b/website/test/openapi-types.fixture.txt index 21d386c4f..bc7fb1408 100644 --- a/website/test/openapi-types.fixture.txt +++ b/website/test/openapi-types.fixture.txt @@ -167,13 +167,15 @@ const createdInvoiceStamp: components["schemas"]["Stamp"] | null | undefined = c void createdInvoiceDate; void createdInvoiceStamp; if (createdInvoiceResponse.status === 'valid' || createdInvoiceResponse.status === 'canceled') { - const stampedFields: [string, string, string, string, string, string] = [ + const stampedFields: [string, string, string, string, string, string, string | undefined, string | undefined] = [ createdInvoiceResponse.uuid, createdInvoiceResponse.stamp.date, createdInvoiceResponse.stamp.signature, createdInvoiceResponse.stamp.sat_signature, createdInvoiceResponse.stamp.sat_cert_number, createdInvoiceResponse.stamp.complement_string, + createdInvoiceResponse.stamp.version, + createdInvoiceResponse.stamp.legend, ]; void stampedFields; }