Repository navigation
[16.0][IMP][FIX] pms_l10n_es: add the INE tourist apartments occupancy survey (EOAP) and fix the hotel one for IRIA - #457
Open
davidpachecop wants to merge 12 commits into
Open
davidpachecop wants to merge 12 commits into
davidpachecop wants to merge 12 commits into
Conversation
… tourist apartments (EOAP) The INE replaced the ARCE platform with IRIA. IRIA questionnaire schemas are namespace-qualified, so the XML files generated so far are rejected at upload time (XSD error on line 1: cannot find the declaration of the root element). IRIA also removed the in-portal editing that ARCE allowed, so the generated file must be exact. Hotel survey (EOH) fixes: - Qualify the root element with the IRIA namespace, configurable through the ir.config_parameter pms_l10n_es.ine_xml_namespace_hotel. - Declare and encode the file as UTF-8 (it declared ISO-8859-1 while encoding UTF-8 bytes). - Print DIAS_ABIERTO_MES_REFERENCIA zero-padded to 2 digits and omit the URL element when the property has no website (both rejected by the IRIA schema). - Emit every decimal with exactly 2 digits and normalize the occupancy percentages in integer hundredths so the printed values add up to exactly 100.00: float artifacts have been a recurrent source of files rejected by the INE content validations. - New INE province literal on res.country.state: the survey header expects the province literal of the INE specification (max. 25 chars) and several Odoo state names exceed it. New tourist apartments survey (EOAP): - New survey_type on the INE categories and seed data for the tourist apartments categories (0-4 keys). The wizard picks the questionnaire format from the property INE category. - New apartment typology on rooms (studio / 2-4 pax / 4-6 pax / other), inferred from the room capacity when not set. - New informant fields on the property (required block in EOAP). - New XML builder with the EOAP blocks (CABECERA_APARTAMENTOS, INFORMANTE, CAPACIDAD, OCUPACION and PRECIOS per typology), sharing the guest movements and staff blocks with the hotel survey. The whole occupancy of each typology is reported under the normal rate with its ADR, which satisfies the INE content validations and has been accepted by the INE in a real submission. Tests validate both generated files against the current IRIA schemas and the exactness of the printed decimals and percentage sums. (cherry picked from commit 1bd8f95) Assisted-by: Claude Opus 5
…nexes The INE publishes the survey annexes as XML files. Checking the seed data against them: - The province literal expected for province 48 is VIZCAKAIA (as published in the province annex), not VIZCAYA. - The hotel type/category table includes one pair that was missing. (cherry picked from commit 1703247) Assisted-by: Claude Opus 5
The INE confirmed that the reference schema of each survey is the one they publish, which declares no target namespace, and not the one the IRIA application generates for the questionnaire. Both variants have been accepted on upload so far (hotel files without namespace, an apartments file with it), so the files are built after the published schema and the qualified variant can be enabled per survey with a config parameter for the questionnaires that need it. This keeps the hotel file byte-compatible with what the establishments have been uploading successfully. (cherry picked from commit 03f7b5f) Assisted-by: Claude Opus 5
Generating the file of every property with an INE category in the fleet and checking it against the schema published by the INE and its official content validations (XML_0..XML_17 of the web service documentation) surfaced three cases that build a file the INE rejects: - Properties whose phone has less than 9 digits: the schema requires between 9 and 13 characters in TELEFONO_1. - Periods without guest movements: the schema requires at least one RESIDENCIA under ALOJAMIENTO, so an empty block cannot be uploaded. - A client type reporting a rate with a zero percentage, or the other way around: the INE requires both to be consistent, on top of the percentages adding up to 100. The first two now raise a message explaining what to fix instead of building an invalid file, and the percentages are normalized keeping their consistency with the rates. The file must also carry the whole reference month even though the questionnaire asks for a single week (hotels) or fortnight (apartments), so a partial period is rejected up front. (cherry picked from commit 82e911e) Assisted-by: Claude Opus 5
The order number identifies the establishment in the INE questionnaire and is fixed, so it is stored in the property and prefilled in the INE wizard, where it can also be filled in for the first time. The control code is deliberately not stored: it belongs to a single questionnaire and is single use. (cherry picked from commit c3672ab) Assisted-by: Claude Opus 5
The establishment used to find out about a rejected file days later, on the INE portal, with a message naming an XML element. Two changes move that feedback to the moment the file is generated. The reference period is now expanded to the natural month of the start date instead of being rejected when it is not the whole month. A range reaching into the next month was the worst case: it repeats day numbers inside a place of residence, and the day is a key in the survey schema, so the INE answered with a duplicate key error that says nothing to a receptionist who only picked the wrong end date. The content validations the INE runs after the schema are now applied to the built file, and a failure names the day and the figures involved instead of the XML element. Checked against 97 files generated from two real fleets: the only two files reported are the two the INE would reject, both for overnight stays exceeding the seats declared in the property. The empty month error now points at the check-in data, which is where the survey is built from: several establishments with real occupancy and no completed check-ins would otherwise report an empty month. (cherry picked from commit 088e92e) Assisted-by: Claude Opus 5
The configuration checks move from the wizard to the property, as ine_configuration_problems(), and report every missing field at once instead of only the first one. Two computed fields expose the result, ine_ready and ine_blocking_reasons, so the interface can list what to fix instead of offering a download that fails. The seats check joins them: it used to live in the middle of the file generation, and it is the most frequent blocker in the fleet, where establishments have the seats unset while their rooms already offer capacity. Spanish translations for the new messages are included, since the app shows them to the end user through the API. The help texts are left to Weblate. (cherry picked from commit 8f074a1) Assisted-by: Claude Opus 5
The INE settings page now lists what is missing before the occupancy survey can be built, instead of leaving the operator to find it out when the download fails. (cherry picked from commit 75b1478) Assisted-by: Claude Opus 5
The INE counts as an extra bed every bed without a fixed character that is not among the declared places, cots included (survey methodology, 5.10), and it counts every guest who stays the night as a traveller, with no exception by age. The file, however, only reported the extra beds sold as a service, and hardly any establishment of the fleet has an extra bed product configured: a baby in a cot was counted as a traveller while the bed they slept in was not, and the survey was rejected as soon as the guests of a night exceeded the declared seats. Extra beds are now also derived from the guests registered in a room beyond the places it offers, and the larger of the two sources wins, so establishments that do sell the service keep reporting it. The declared seats are already required to be at least the capacity of the rooms reported to the INE, which makes the derived figure enough for the overnight stays to never exceed the seats plus the extra beds. Since a room whose capacity is set too low would then report an extra bed every single night, the wizard lists the nights it reported one. (cherry picked from commit 91727cf) Assisted-by: Claude Opus 5
The survey carries the country of residence in ID_PAIS, taken from the ISO 3166-1 alpha-3 code, but the INE publishes its own country list next to the survey schemas and it does not match ISO everywhere. Kosovo has no alpha-3 in ISO, so Odoo leaves it empty and the file came out with an empty ID_PAIS, rejected by the schema with nothing pointing at the guest who caused it. The INE codes it as KOS. The code of the country of residence now goes through the INE list, and a country left without one is reported by name instead of producing a file the establishment can only see rejected days later, on the portal. The exception lives in the code because the countries of the base module carry noupdate and a data row over them is silently ignored. The new ine_country_code field is there for whatever the INE changes later without waiting for a release. Checked against the whole fleet, 226 countries of residence in use over 50 databases: Kosovo is the only one missing a code, and Spain the only one outside the INE list, which is correct because Spanish residents travel in ID_PROVINCIA_ISLA. (cherry picked from commit a69e00f) Assisted-by: Claude Opus 5
The wizard had grown to hold three different jobs at once: reading the occupancy out of the PMS, building the XML, and checking the result against the rules the INE applies after the schema. The last one only needs the built file, so it moves to ine_content_checks, where each rule carries the identifier the INE publishes it under and can be read, and tested, without a record in between. The module docstring now says what the two surveys are, why two schema variants coexist and in what order a file is built, which is the context missing to read the rest. Two defects found while going through it: - The hotel survey cut the phone at 12 characters while the schema takes 13 and the apartments survey already used 13, so an international number lost its last digit. - The file was named INE_7_2026.xml while the REST API named the same file INE_07_2026.xml. (cherry picked from commit 949e4a1) Assisted-by: Claude Opus 5
The module readme gains what has to be configured on the property for the surveys, how the file is built and the limits that are left: the surveys with no XML channel, the web service submission that is not implemented and the days open being reported as the days of the month. Assisted-by: Claude Opus 5
Author
|
cc @DarioLodeiros as maintainer of this module. A note on the size, since it looks heavier than it is: 2,234 of the 4,351 added lines are the four Happy to split it if you would rather review the apartments survey and the hotel fixes separately, although they share the |
davidpachecop
force-pushed
the
david/ine-iria-surveys-oca
branch
from
September 29, 2026 12:20
0ce6f22 to
6571d56
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The INE (Spanish National Statistics Institute) shut down its ARCE platform and moved the occupancy surveys to IRIA, where a questionnaire can no longer be edited once uploaded. Whatever is wrong in the file comes back as a rejection, days later, with a message that names neither the guest nor the setting behind it.
This PR adds the tourist apartments occupancy survey (EOAP), which the module did not build at all, and fixes the hotel one (EOH) for the rejection causes seen in production, reporting them while the file is being built instead of handing over a file the INE will reject.
Tourist apartments occupancy survey (EOAP)
An establishment listed as tourist apartments answers a different questionnaire from a hotel (root element
APARTAMENTOSinstead ofENCUESTA). Until now the module built the hotel one for everybody, so those establishments had no way to answer their survey from the PMS.survey_typeonpms.ine.tourism.type.category), so there is no new setting to decide. The 53 existing categories are seeded ashoteland the 5 apartment ones (0–4 keys) are added.pms.room(studio / 2-4 pax / 4-6 pax / other), inferred from the capacity of the room when left empty, so it works unconfigured.INFORMANTEblock onpms.property, mandatory in this survey.CABECERA,CABECERA_APARTAMENTOS,INFORMANTE,ALOJAMIENTO(shared with the hotel survey, extracted to a common helper),CAPACIDADandOCUPACIONper typology,PRECIOSandPERSONAL_OCUPADO.A file built by this code for a real establishment was uploaded to IRIA and accepted by the INE, which issued the receipt.
Hotel occupancy survey (EOH)
Without changing the format establishments already upload successfully.
Configuration checks move to the property.
ine_configuration_problems()returns everything that is missing at once instead of only the first thing, exposed asine_ready/ine_blocking_reasonsso the interface can list it. Before, each attempt produced one generic error and another download.The period is always the natural month. The questionnaire asks for a week (hotels) or a fortnight (apartments), but the file must carry the whole month. A range was accepted as given, and it failed in both directions:
RESIDENCIA, and the schema rejects it because the day is a key there.XML_8) on the first day of the file, because the INE assumes no overnight stays the day before.The period is now expanded to the month of the start date.
Extra beds are derived from the occupancy. Only the ones sold as a service were counted, and hardly any establishment configures that product. A baby in a cot counted as a traveller while the bed they slept in did not, and the INE rejected the file as soon as the guests of a night exceeded the declared seats. The survey methodology (5.10) is explicit that cots count as extra beds, and travellers (5.4) have no exception by age.
Two sources are now combined and the larger one wins: the extra beds sold as a service, and the guests registered beyond the places their room offers. Since the declared seats are already required to be at least the capacity of the rooms reported, the per-room figure always dominates the aggregate shortfall, which makes
XML_12unfailable by construction.Country codes of the INE. The INE publishes its own country list next to the survey schemas. It follows ISO 3166-1 alpha-3 except where ISO has no code: Kosovo is
KOSfor them and has no alpha-3 in Odoo, soID_PAIScame out empty and the schema rejected the file. A newine_country_codeonres.countrycovers the exceptions, and a country left without a code is now reported by name.Formatting. Decimals with 2 positions and percentages normalised in whole hundredths, so the printed sum is exactly 100.00 by construction. Real UTF-8: the file declared ISO-8859-1 while encoding UTF-8. Province literal of the specification (max. 25 characters), seeded for the 52 provinces from the published annex: several Odoo names exceed it.
How it is organised
Building a file is three steps, each in its own place:
models/pms_property.pywizards/wizard_ine.pywizards/ine_content_checks.pyStep 3 is plain code with no ORM: it takes the XML and returns the list of problems. Each rule carries the identifier the INE publishes it under (
XML_0…XML_17), reads without the wizard in front of it and is tested without creating records.Reviewing this
Half of the diff is not code:
.xsdfixturesSuggested order: the module docstring of
wizards/wizard_ine.py(what the two surveys are, why two schema variants coexist, in what order a file is built), thenwizards/ine_content_checks.py, thenmodels/pms_property.py, then the two builders, then the tests.Tests
34 tests, covering validation against the 4 schemas (published and versioned, hotel and apartments), exact percentage sum, rate and percentage consistency, period expansion in both directions, extra beds through both sources, country codes, the configuration checklist and each content rule.
Beyond the test suite, every correction was checked by regenerating the real files of two months for 125 properties across 50 databases, read-only, and comparing the result against the schemas and the content rules.
Notes
hotel, and a property without one falls back tohotel.Not in this PR
Submitting the questionnaire through the INE web service, so the portal does not have to be visited at all. IRIA requires no prior registration for it: the credential is the order number printed on the questionnaire, which this PR already keeps in the property, plus a single-use control code that is never stored.
Size
This is over the 500 line reference point of the AI policy, so to be explicit about what it is made of: 2,234 of the added lines are the four
.xsdschema fixtures published by the INE, 564 the tests and 218 the reference data from the official annexes. The code is around 1,300 lines, of which roughly half is the new apartments survey.It can be split into the apartments survey and the hotel fixes if a maintainer prefers, though both share the
ALOJAMIENTObuilder and the test fixtures, and several of the hotel fixes are what make the apartments file pass validation too. Happy to do it either way — say the word before reviewing and I will resubmit rather than have anyone read it twice.Disclosure
Developed with AI assistance, declared with an
Assisted-by: Claude Opus 5trailer on every commit, per the OCA Generative AI / LLM policy. The work was directed, reviewed and tested by me against the INE specification and against real data, and I take full responsibility for it: ask about any line and I will answer for it.